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