| /* | 
 |  * Copyright 2006 The Android Open Source Project | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 |  | 
 | #include "SkDOM.h" | 
 | #include "SkStream.h" | 
 | #include "SkXMLWriter.h" | 
 |  | 
 | ///////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #include "SkXMLParser.h" | 
 | bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node) | 
 | { | 
 |     const char* elemName = dom.getName(node); | 
 |  | 
 |     if (this->startElement(elemName)) | 
 |         return false; | 
 |  | 
 |     SkDOM::AttrIter iter(dom, node); | 
 |     const char*     name, *value; | 
 |  | 
 |     while ((name = iter.next(&value)) != nullptr) | 
 |         if (this->addAttribute(name, value)) | 
 |             return false; | 
 |  | 
 |     if ((node = dom.getFirstChild(node)) != nullptr) | 
 |         do { | 
 |             if (!this->parse(dom, node)) | 
 |                 return false; | 
 |         } while ((node = dom.getNextSibling(node)) != nullptr); | 
 |  | 
 |     return !this->endElement(elemName); | 
 | } | 
 |  | 
 | ///////////////////////////////////////////////////////////////////////// | 
 |  | 
 | struct SkDOMAttr { | 
 |     const char* fName; | 
 |     const char* fValue; | 
 | }; | 
 |  | 
 | struct SkDOMNode { | 
 |     const char* fName; | 
 |     SkDOMNode*  fFirstChild; | 
 |     SkDOMNode*  fNextSibling; | 
 |     uint16_t    fAttrCount; | 
 |     uint8_t     fType; | 
 |     uint8_t     fPad; | 
 |  | 
 |     const SkDOMAttr* attrs() const | 
 |     { | 
 |         return (const SkDOMAttr*)(this + 1); | 
 |     } | 
 |     SkDOMAttr* attrs() | 
 |     { | 
 |         return (SkDOMAttr*)(this + 1); | 
 |     } | 
 | }; | 
 |  | 
 | ///////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #define kMinChunkSize   512 | 
 |  | 
 | SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nullptr) | 
 | { | 
 | } | 
 |  | 
 | SkDOM::~SkDOM() | 
 | { | 
 | } | 
 |  | 
 | const SkDOM::Node* SkDOM::getRootNode() const | 
 | { | 
 |     return fRoot; | 
 | } | 
 |  | 
 | const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const | 
 | { | 
 |     SkASSERT(node); | 
 |     const Node* child = node->fFirstChild; | 
 |  | 
 |     if (name) | 
 |     { | 
 |         for (; child != nullptr; child = child->fNextSibling) | 
 |             if (!strcmp(name, child->fName)) | 
 |                 break; | 
 |     } | 
 |     return child; | 
 | } | 
 |  | 
 | const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const | 
 | { | 
 |     SkASSERT(node); | 
 |     const Node* sibling = node->fNextSibling; | 
 |     if (name) | 
 |     { | 
 |         for (; sibling != nullptr; sibling = sibling->fNextSibling) | 
 |             if (!strcmp(name, sibling->fName)) | 
 |                 break; | 
 |     } | 
 |     return sibling; | 
 | } | 
 |  | 
 | SkDOM::Type SkDOM::getType(const Node* node) const | 
 | { | 
 |     SkASSERT(node); | 
 |     return (Type)node->fType; | 
 | } | 
 |  | 
 | const char* SkDOM::getName(const Node* node) const | 
 | { | 
 |     SkASSERT(node); | 
 |     return node->fName; | 
 | } | 
 |  | 
 | const char* SkDOM::findAttr(const Node* node, const char name[]) const | 
 | { | 
 |     SkASSERT(node); | 
 |     const Attr* attr = node->attrs(); | 
 |     const Attr* stop = attr + node->fAttrCount; | 
 |  | 
 |     while (attr < stop) | 
 |     { | 
 |         if (!strcmp(attr->fName, name)) | 
 |             return attr->fValue; | 
 |         attr += 1; | 
 |     } | 
 |     return nullptr; | 
 | } | 
 |  | 
 | ///////////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const | 
 | { | 
 |     return node->fAttrCount ? node->attrs() : nullptr; | 
 | } | 
 |  | 
 | const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const | 
 | { | 
 |     SkASSERT(node); | 
 |     if (attr == nullptr) | 
 |         return nullptr; | 
 |     return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nullptr; | 
 | } | 
 |  | 
 | const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const | 
 | { | 
 |     SkASSERT(node); | 
 |     SkASSERT(attr); | 
 |     return attr->fName; | 
 | } | 
 |  | 
 | const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const | 
 | { | 
 |     SkASSERT(node); | 
 |     SkASSERT(attr); | 
 |     return attr->fValue; | 
 | } | 
 |  | 
 | ///////////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node) | 
 | { | 
 |     SkASSERT(node); | 
 |     fAttr = node->attrs(); | 
 |     fStop = fAttr + node->fAttrCount; | 
 | } | 
 |  | 
 | const char* SkDOM::AttrIter::next(const char** value) | 
 | { | 
 |     const char* name = nullptr; | 
 |  | 
 |     if (fAttr < fStop) | 
 |     { | 
 |         name = fAttr->fName; | 
 |         if (value) | 
 |             *value = fAttr->fValue; | 
 |         fAttr += 1; | 
 |     } | 
 |     return name; | 
 | } | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #include "SkXMLParser.h" | 
 | #include "SkTDArray.h" | 
 |  | 
 | static char* dupstr(SkChunkAlloc* chunk, const char src[]) | 
 | { | 
 |     SkASSERT(chunk && src); | 
 |     size_t  len = strlen(src); | 
 |     char*   dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); | 
 |     memcpy(dst, src, len + 1); | 
 |     return dst; | 
 | } | 
 |  | 
 | class SkDOMParser : public SkXMLParser { | 
 | public: | 
 |     SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) | 
 |     { | 
 |         fAlloc->reset(); | 
 |         fRoot = nullptr; | 
 |         fLevel = 0; | 
 |         fNeedToFlush = true; | 
 |     } | 
 |     SkDOM::Node* getRoot() const { return fRoot; } | 
 |     SkXMLParserError fParserError; | 
 |  | 
 | protected: | 
 |     void flushAttributes() | 
 |     { | 
 |         SkASSERT(fLevel > 0); | 
 |  | 
 |         int attrCount = fAttrs.count(); | 
 |  | 
 |         SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr), | 
 |                                                         SkChunkAlloc::kThrow_AllocFailType); | 
 |  | 
 |         node->fName = fElemName; | 
 |         node->fFirstChild = nullptr; | 
 |         node->fAttrCount = SkToU16(attrCount); | 
 |         node->fType = fElemType; | 
 |  | 
 |         if (fRoot == nullptr) | 
 |         { | 
 |             node->fNextSibling = nullptr; | 
 |             fRoot = node; | 
 |         } | 
 |         else    // this adds siblings in reverse order. gets corrected in onEndElement() | 
 |         { | 
 |             SkDOM::Node* parent = fParentStack.top(); | 
 |             SkASSERT(fRoot && parent); | 
 |             node->fNextSibling = parent->fFirstChild; | 
 |             parent->fFirstChild = node; | 
 |         } | 
 |         *fParentStack.push() = node; | 
 |  | 
 |         sk_careful_memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr)); | 
 |         fAttrs.reset(); | 
 |  | 
 |     } | 
 |  | 
 |     bool onStartElement(const char elem[]) override { | 
 |         this->startCommon(elem, SkDOM::kElement_Type); | 
 |         return false; | 
 |     } | 
 |  | 
 |     bool onAddAttribute(const char name[], const char value[]) override { | 
 |         SkDOM::Attr* attr = fAttrs.append(); | 
 |         attr->fName = dupstr(fAlloc, name); | 
 |         attr->fValue = dupstr(fAlloc, value); | 
 |         return false; | 
 |     } | 
 |  | 
 |     bool onEndElement(const char elem[]) override { | 
 |         --fLevel; | 
 |         if (fNeedToFlush) | 
 |             this->flushAttributes(); | 
 |         fNeedToFlush = false; | 
 |  | 
 |         SkDOM::Node* parent; | 
 |  | 
 |         fParentStack.pop(&parent); | 
 |  | 
 |         SkDOM::Node* child = parent->fFirstChild; | 
 |         SkDOM::Node* prev = nullptr; | 
 |         while (child) | 
 |         { | 
 |             SkDOM::Node* next = child->fNextSibling; | 
 |             child->fNextSibling = prev; | 
 |             prev = child; | 
 |             child = next; | 
 |         } | 
 |         parent->fFirstChild = prev; | 
 |         return false; | 
 |     } | 
 |  | 
 |     bool onText(const char text[], int len) override { | 
 |         SkString str(text, len); | 
 |         this->startCommon(str.c_str(), SkDOM::kText_Type); | 
 |         this->SkDOMParser::onEndElement(str.c_str()); | 
 |  | 
 |         return false; | 
 |     } | 
 |  | 
 | private: | 
 |     void startCommon(const char elem[], SkDOM::Type type) { | 
 |         if (fLevel > 0 && fNeedToFlush) | 
 |             this->flushAttributes(); | 
 |  | 
 |         fNeedToFlush = true; | 
 |         fElemName = dupstr(fAlloc, elem); | 
 |         fElemType = type; | 
 |         ++fLevel; | 
 |     } | 
 |  | 
 |     SkTDArray<SkDOM::Node*> fParentStack; | 
 |     SkChunkAlloc*           fAlloc; | 
 |     SkDOM::Node*            fRoot; | 
 |     bool                    fNeedToFlush; | 
 |  | 
 |     // state needed for flushAttributes() | 
 |     SkTDArray<SkDOM::Attr>  fAttrs; | 
 |     char*                   fElemName; | 
 |     SkDOM::Type             fElemType; | 
 |     int                     fLevel; | 
 | }; | 
 |  | 
 | const SkDOM::Node* SkDOM::build(SkStream& docStream) { | 
 |     SkDOMParser parser(&fAlloc); | 
 |     if (!parser.parse(docStream)) | 
 |     { | 
 |         SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());) | 
 |         fRoot = nullptr; | 
 |         fAlloc.reset(); | 
 |         return nullptr; | 
 |     } | 
 |     fRoot = parser.getRoot(); | 
 |     return fRoot; | 
 | } | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser) | 
 | { | 
 |     const char* elem = dom.getName(node); | 
 |     if (dom.getType(node) == SkDOM::kText_Type) { | 
 |         SkASSERT(dom.countChildren(node) == 0); | 
 |         parser->text(elem, SkToInt(strlen(elem))); | 
 |         return; | 
 |     } | 
 |  | 
 |     parser->startElement(elem); | 
 |  | 
 |     SkDOM::AttrIter iter(dom, node); | 
 |     const char*     name; | 
 |     const char*     value; | 
 |     while ((name = iter.next(&value)) != nullptr) | 
 |         parser->addAttribute(name, value); | 
 |  | 
 |     node = dom.getFirstChild(node, nullptr); | 
 |     while (node) | 
 |     { | 
 |         walk_dom(dom, node, parser); | 
 |         node = dom.getNextSibling(node, nullptr); | 
 |     } | 
 |  | 
 |     parser->endElement(elem); | 
 | } | 
 |  | 
 | const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node) | 
 | { | 
 |     SkDOMParser parser(&fAlloc); | 
 |  | 
 |     walk_dom(dom, node, &parser); | 
 |  | 
 |     fRoot = parser.getRoot(); | 
 |     return fRoot; | 
 | } | 
 |  | 
 | SkXMLParser* SkDOM::beginParsing() { | 
 |     SkASSERT(!fParser); | 
 |     fParser.reset(new SkDOMParser(&fAlloc)); | 
 |  | 
 |     return fParser.get(); | 
 | } | 
 |  | 
 | const SkDOM::Node* SkDOM::finishParsing() { | 
 |     SkASSERT(fParser); | 
 |     fRoot = fParser->getRoot(); | 
 |     fParser.reset(); | 
 |  | 
 |     return fRoot; | 
 | } | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | int SkDOM::countChildren(const Node* node, const char elem[]) const | 
 | { | 
 |     int count = 0; | 
 |  | 
 |     node = this->getFirstChild(node, elem); | 
 |     while (node) | 
 |     { | 
 |         count += 1; | 
 |         node = this->getNextSibling(node, elem); | 
 |     } | 
 |     return count; | 
 | } | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #include "SkParse.h" | 
 |  | 
 | bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     return vstr && SkParse::FindS32(vstr, value); | 
 | } | 
 |  | 
 | bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     return vstr && SkParse::FindScalars(vstr, value, count); | 
 | } | 
 |  | 
 | bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     return vstr && SkParse::FindHex(vstr, value); | 
 | } | 
 |  | 
 | bool SkDOM::findBool(const Node* node, const char name[], bool* value) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     return vstr && SkParse::FindBool(vstr, value); | 
 | } | 
 |  | 
 | int SkDOM::findList(const Node* node, const char name[], const char list[]) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     return vstr ? SkParse::FindList(vstr, list) : -1; | 
 | } | 
 |  | 
 | bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     return vstr && !strcmp(vstr, value); | 
 | } | 
 |  | 
 | bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     int32_t     value; | 
 |     return vstr && SkParse::FindS32(vstr, &value) && value == target; | 
 | } | 
 |  | 
 | bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     SkScalar    value; | 
 |     return vstr && SkParse::FindScalar(vstr, &value) && value == target; | 
 | } | 
 |  | 
 | bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     uint32_t    value; | 
 |     return vstr && SkParse::FindHex(vstr, &value) && value == target; | 
 | } | 
 |  | 
 | bool SkDOM::hasBool(const Node* node, const char name[], bool target) const | 
 | { | 
 |     const char* vstr = this->findAttr(node, name); | 
 |     bool        value; | 
 |     return vstr && SkParse::FindBool(vstr, &value) && value == target; | 
 | } | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | #ifdef SK_DEBUG | 
 |  | 
 | void SkDOM::dump(const Node* node, int level) const | 
 | { | 
 |     if (node == nullptr) | 
 |         node = this->getRootNode(); | 
 |  | 
 |     SkDebugWStream debugStream; | 
 |     SkXMLStreamWriter xmlWriter(&debugStream); | 
 |     xmlWriter.writeDOM(*this, node, false); | 
 | } | 
 |  | 
 | #endif |