Home | History | Annotate | Download | only in xml
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkDOM.h"
     11 
     12 /////////////////////////////////////////////////////////////////////////
     13 
     14 #include "SkXMLParser.h"
     15 
     16 bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node)
     17 {
     18     const char* elemName = dom.getName(node);
     19 
     20     if (this->startElement(elemName))
     21         return false;
     22 
     23     SkDOM::AttrIter iter(dom, node);
     24     const char*     name, *value;
     25 
     26     while ((name = iter.next(&value)) != NULL)
     27         if (this->addAttribute(name, value))
     28             return false;
     29 
     30     if ((node = dom.getFirstChild(node)) != NULL)
     31         do {
     32             if (!this->parse(dom, node))
     33                 return false;
     34         } while ((node = dom.getNextSibling(node)) != NULL);
     35 
     36     return !this->endElement(elemName);
     37 }
     38 
     39 /////////////////////////////////////////////////////////////////////////
     40 
     41 struct SkDOMAttr {
     42     const char* fName;
     43     const char* fValue;
     44 };
     45 
     46 struct SkDOMNode {
     47     const char* fName;
     48     SkDOMNode*  fFirstChild;
     49     SkDOMNode*  fNextSibling;
     50     uint16_t    fAttrCount;
     51     uint8_t     fType;
     52     uint8_t     fPad;
     53 
     54     const SkDOMAttr* attrs() const
     55     {
     56         return (const SkDOMAttr*)(this + 1);
     57     }
     58     SkDOMAttr* attrs()
     59     {
     60         return (SkDOMAttr*)(this + 1);
     61     }
     62 };
     63 
     64 /////////////////////////////////////////////////////////////////////////
     65 
     66 #define kMinChunkSize   512
     67 
     68 SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL)
     69 {
     70 }
     71 
     72 SkDOM::~SkDOM()
     73 {
     74 }
     75 
     76 const SkDOM::Node* SkDOM::getRootNode() const
     77 {
     78     return fRoot;
     79 }
     80 
     81 const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const
     82 {
     83     SkASSERT(node);
     84     const Node* child = node->fFirstChild;
     85 
     86     if (name)
     87     {
     88         for (; child != NULL; child = child->fNextSibling)
     89             if (!strcmp(name, child->fName))
     90                 break;
     91     }
     92     return child;
     93 }
     94 
     95 const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const
     96 {
     97     SkASSERT(node);
     98     const Node* sibling = node->fNextSibling;
     99     if (name)
    100     {
    101         for (; sibling != NULL; sibling = sibling->fNextSibling)
    102             if (!strcmp(name, sibling->fName))
    103                 break;
    104     }
    105     return sibling;
    106 }
    107 
    108 SkDOM::Type SkDOM::getType(const Node* node) const
    109 {
    110     SkASSERT(node);
    111     return (Type)node->fType;
    112 }
    113 
    114 const char* SkDOM::getName(const Node* node) const
    115 {
    116     SkASSERT(node);
    117     return node->fName;
    118 }
    119 
    120 const char* SkDOM::findAttr(const Node* node, const char name[]) const
    121 {
    122     SkASSERT(node);
    123     const Attr* attr = node->attrs();
    124     const Attr* stop = attr + node->fAttrCount;
    125 
    126     while (attr < stop)
    127     {
    128         if (!strcmp(attr->fName, name))
    129             return attr->fValue;
    130         attr += 1;
    131     }
    132     return NULL;
    133 }
    134 
    135 /////////////////////////////////////////////////////////////////////////////////////
    136 
    137 const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
    138 {
    139     return node->fAttrCount ? node->attrs() : NULL;
    140 }
    141 
    142 const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
    143 {
    144     SkASSERT(node);
    145     if (attr == NULL)
    146         return NULL;
    147     return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL;
    148 }
    149 
    150 const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const
    151 {
    152     SkASSERT(node);
    153     SkASSERT(attr);
    154     return attr->fName;
    155 }
    156 
    157 const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const
    158 {
    159     SkASSERT(node);
    160     SkASSERT(attr);
    161     return attr->fValue;
    162 }
    163 
    164 /////////////////////////////////////////////////////////////////////////////////////
    165 
    166 SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node)
    167 {
    168     SkASSERT(node);
    169     fAttr = node->attrs();
    170     fStop = fAttr + node->fAttrCount;
    171 }
    172 
    173 const char* SkDOM::AttrIter::next(const char** value)
    174 {
    175     const char* name = NULL;
    176 
    177     if (fAttr < fStop)
    178     {
    179         name = fAttr->fName;
    180         if (value)
    181             *value = fAttr->fValue;
    182         fAttr += 1;
    183     }
    184     return name;
    185 }
    186 
    187 //////////////////////////////////////////////////////////////////////////////
    188 
    189 #include "SkXMLParser.h"
    190 #include "SkTDArray.h"
    191 
    192 static char* dupstr(SkChunkAlloc* chunk, const char src[])
    193 {
    194     SkASSERT(chunk && src);
    195     size_t  len = strlen(src);
    196     char*   dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
    197     memcpy(dst, src, len + 1);
    198     return dst;
    199 }
    200 
    201 class SkDOMParser : public SkXMLParser {
    202     bool fNeedToFlush;
    203 public:
    204     SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
    205     {
    206         fRoot = NULL;
    207         fLevel = 0;
    208         fNeedToFlush = true;
    209     }
    210     SkDOM::Node* getRoot() const { return fRoot; }
    211     SkXMLParserError fParserError;
    212 protected:
    213     void flushAttributes()
    214     {
    215         int attrCount = fAttrs.count();
    216 
    217         SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr),
    218                                                         SkChunkAlloc::kThrow_AllocFailType);
    219 
    220         node->fName = fElemName;
    221         node->fFirstChild = NULL;
    222         node->fAttrCount = SkToU16(attrCount);
    223         node->fType = SkDOM::kElement_Type;
    224 
    225         if (fRoot == NULL)
    226         {
    227             node->fNextSibling = NULL;
    228             fRoot = node;
    229         }
    230         else    // this adds siblings in reverse order. gets corrected in onEndElement()
    231         {
    232             SkDOM::Node* parent = fParentStack.top();
    233             SkASSERT(fRoot && parent);
    234             node->fNextSibling = parent->fFirstChild;
    235             parent->fFirstChild = node;
    236         }
    237         *fParentStack.push() = node;
    238 
    239         memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
    240         fAttrs.reset();
    241 
    242     }
    243     virtual bool onStartElement(const char elem[])
    244     {
    245         if (fLevel > 0 && fNeedToFlush)
    246             this->flushAttributes();
    247         fNeedToFlush = true;
    248         fElemName = dupstr(fAlloc, elem);
    249         ++fLevel;
    250         return false;
    251     }
    252     virtual bool onAddAttribute(const char name[], const char value[])
    253     {
    254         SkDOM::Attr* attr = fAttrs.append();
    255         attr->fName = dupstr(fAlloc, name);
    256         attr->fValue = dupstr(fAlloc, value);
    257         return false;
    258     }
    259     virtual bool onEndElement(const char elem[])
    260     {
    261         --fLevel;
    262         if (fNeedToFlush)
    263             this->flushAttributes();
    264         fNeedToFlush = false;
    265 
    266         SkDOM::Node* parent;
    267 
    268         fParentStack.pop(&parent);
    269 
    270         SkDOM::Node* child = parent->fFirstChild;
    271         SkDOM::Node* prev = NULL;
    272         while (child)
    273         {
    274             SkDOM::Node* next = child->fNextSibling;
    275             child->fNextSibling = prev;
    276             prev = child;
    277             child = next;
    278         }
    279         parent->fFirstChild = prev;
    280         return false;
    281     }
    282 private:
    283     SkTDArray<SkDOM::Node*> fParentStack;
    284     SkChunkAlloc*   fAlloc;
    285     SkDOM::Node*    fRoot;
    286 
    287     // state needed for flushAttributes()
    288     SkTDArray<SkDOM::Attr>  fAttrs;
    289     char*                   fElemName;
    290     int                     fLevel;
    291 };
    292 
    293 const SkDOM::Node* SkDOM::build(const char doc[], size_t len)
    294 {
    295     fAlloc.reset();
    296     SkDOMParser parser(&fAlloc);
    297     if (!parser.parse(doc, len))
    298     {
    299         SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
    300         fRoot = NULL;
    301         fAlloc.reset();
    302         return NULL;
    303     }
    304     fRoot = parser.getRoot();
    305     return fRoot;
    306 }
    307 
    308 ///////////////////////////////////////////////////////////////////////////
    309 
    310 static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser)
    311 {
    312     const char* elem = dom.getName(node);
    313 
    314     parser->startElement(elem);
    315 
    316     SkDOM::AttrIter iter(dom, node);
    317     const char*     name;
    318     const char*     value;
    319     while ((name = iter.next(&value)) != NULL)
    320         parser->addAttribute(name, value);
    321 
    322     node = dom.getFirstChild(node, NULL);
    323     while (node)
    324     {
    325         walk_dom(dom, node, parser);
    326         node = dom.getNextSibling(node, NULL);
    327     }
    328 
    329     parser->endElement(elem);
    330 }
    331 
    332 const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
    333 {
    334     fAlloc.reset();
    335     SkDOMParser parser(&fAlloc);
    336 
    337     walk_dom(dom, node, &parser);
    338 
    339     fRoot = parser.getRoot();
    340     return fRoot;
    341 }
    342 
    343 //////////////////////////////////////////////////////////////////////////
    344 
    345 int SkDOM::countChildren(const Node* node, const char elem[]) const
    346 {
    347     int count = 0;
    348 
    349     node = this->getFirstChild(node, elem);
    350     while (node)
    351     {
    352         count += 1;
    353         node = this->getNextSibling(node, elem);
    354     }
    355     return count;
    356 }
    357 
    358 //////////////////////////////////////////////////////////////////////////
    359 
    360 #include "SkParse.h"
    361 
    362 bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const
    363 {
    364     const char* vstr = this->findAttr(node, name);
    365     return vstr && SkParse::FindS32(vstr, value);
    366 }
    367 
    368 bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const
    369 {
    370     const char* vstr = this->findAttr(node, name);
    371     return vstr && SkParse::FindScalars(vstr, value, count);
    372 }
    373 
    374 bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const
    375 {
    376     const char* vstr = this->findAttr(node, name);
    377     return vstr && SkParse::FindHex(vstr, value);
    378 }
    379 
    380 bool SkDOM::findBool(const Node* node, const char name[], bool* value) const
    381 {
    382     const char* vstr = this->findAttr(node, name);
    383     return vstr && SkParse::FindBool(vstr, value);
    384 }
    385 
    386 int SkDOM::findList(const Node* node, const char name[], const char list[]) const
    387 {
    388     const char* vstr = this->findAttr(node, name);
    389     return vstr ? SkParse::FindList(vstr, list) : -1;
    390 }
    391 
    392 bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const
    393 {
    394     const char* vstr = this->findAttr(node, name);
    395     return vstr && !strcmp(vstr, value);
    396 }
    397 
    398 bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const
    399 {
    400     const char* vstr = this->findAttr(node, name);
    401     int32_t     value;
    402     return vstr && SkParse::FindS32(vstr, &value) && value == target;
    403 }
    404 
    405 bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const
    406 {
    407     const char* vstr = this->findAttr(node, name);
    408     SkScalar    value;
    409     return vstr && SkParse::FindScalar(vstr, &value) && value == target;
    410 }
    411 
    412 bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const
    413 {
    414     const char* vstr = this->findAttr(node, name);
    415     uint32_t    value;
    416     return vstr && SkParse::FindHex(vstr, &value) && value == target;
    417 }
    418 
    419 bool SkDOM::hasBool(const Node* node, const char name[], bool target) const
    420 {
    421     const char* vstr = this->findAttr(node, name);
    422     bool        value;
    423     return vstr && SkParse::FindBool(vstr, &value) && value == target;
    424 }
    425 
    426 //////////////////////////////////////////////////////////////////////////
    427 
    428 #ifdef SK_DEBUG
    429 
    430 static void tab(int level)
    431 {
    432     while (--level >= 0)
    433         SkDebugf("\t");
    434 }
    435 
    436 void SkDOM::dump(const Node* node, int level) const
    437 {
    438     if (node == NULL)
    439         node = this->getRootNode();
    440     if (node)
    441     {
    442         tab(level);
    443         SkDebugf("<%s", this->getName(node));
    444 
    445         const Attr* attr = node->attrs();
    446         const Attr* stop = attr + node->fAttrCount;
    447         for (; attr < stop; attr++)
    448             SkDebugf(" %s=\"%s\"", attr->fName, attr->fValue);
    449 
    450         const Node* child = this->getFirstChild(node);
    451         if (child)
    452         {
    453             SkDebugf(">\n");
    454             while (child)
    455             {
    456                 this->dump(child, level+1);
    457                 child = this->getNextSibling(child);
    458             }
    459             tab(level);
    460             SkDebugf("</%s>\n", node->fName);
    461         }
    462         else
    463             SkDebugf("/>\n");
    464     }
    465 }
    466 
    467 void SkDOM::UnitTest()
    468 {
    469 #ifdef SK_SUPPORT_UNITTEST
    470     static const char gDoc[] =
    471         "<root a='1' b='2'>"
    472             "<elem1 c='3' />"
    473             "<elem2 d='4' />"
    474             "<elem3 e='5'>"
    475                 "<subelem1/>"
    476                 "<subelem2 f='6' g='7'/>"
    477             "</elem3>"
    478             "<elem4 h='8'/>"
    479         "</root>"
    480         ;
    481 
    482     SkDOM   dom;
    483 
    484     SkASSERT(dom.getRootNode() == NULL);
    485 
    486     const Node* root = dom.build(gDoc, sizeof(gDoc) - 1);
    487     SkASSERT(root && dom.getRootNode() == root);
    488 
    489     const char* v = dom.findAttr(root, "a");
    490     SkASSERT(v && !strcmp(v, "1"));
    491     v = dom.findAttr(root, "b");
    492     SkASSERT(v && !strcmp(v, "2"));
    493     v = dom.findAttr(root, "c");
    494     SkASSERT(v == NULL);
    495 
    496     SkASSERT(dom.getFirstChild(root, "elem1"));
    497     SkASSERT(!dom.getFirstChild(root, "subelem1"));
    498 
    499     dom.dump();
    500 #endif
    501 }
    502 
    503 #endif
    504 
    505