|  |  | 
|  | /* | 
|  | * 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 "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)) != NULL) | 
|  | if (this->addAttribute(name, value)) | 
|  | return false; | 
|  |  | 
|  | if ((node = dom.getFirstChild(node)) != NULL) | 
|  | do { | 
|  | if (!this->parse(dom, node)) | 
|  | return false; | 
|  | } while ((node = dom.getNextSibling(node)) != NULL); | 
|  |  | 
|  | 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(NULL) | 
|  | { | 
|  | } | 
|  |  | 
|  | 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 != NULL; 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 != NULL; 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 NULL; | 
|  | } | 
|  |  | 
|  | ///////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const | 
|  | { | 
|  | return node->fAttrCount ? node->attrs() : NULL; | 
|  | } | 
|  |  | 
|  | const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const | 
|  | { | 
|  | SkASSERT(node); | 
|  | if (attr == NULL) | 
|  | return NULL; | 
|  | return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL; | 
|  | } | 
|  |  | 
|  | 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 = NULL; | 
|  |  | 
|  | 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 { | 
|  | bool fNeedToFlush; | 
|  | public: | 
|  | SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) | 
|  | { | 
|  | fRoot = NULL; | 
|  | fLevel = 0; | 
|  | fNeedToFlush = true; | 
|  | } | 
|  | SkDOM::Node* getRoot() const { return fRoot; } | 
|  | SkXMLParserError fParserError; | 
|  | protected: | 
|  | void flushAttributes() | 
|  | { | 
|  | 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 = NULL; | 
|  | node->fAttrCount = SkToU16(attrCount); | 
|  | node->fType = SkDOM::kElement_Type; | 
|  |  | 
|  | if (fRoot == NULL) | 
|  | { | 
|  | node->fNextSibling = NULL; | 
|  | 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; | 
|  |  | 
|  | memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr)); | 
|  | fAttrs.reset(); | 
|  |  | 
|  | } | 
|  | virtual bool onStartElement(const char elem[]) | 
|  | { | 
|  | if (fLevel > 0 && fNeedToFlush) | 
|  | this->flushAttributes(); | 
|  | fNeedToFlush = true; | 
|  | fElemName = dupstr(fAlloc, elem); | 
|  | ++fLevel; | 
|  | return false; | 
|  | } | 
|  | virtual bool onAddAttribute(const char name[], const char value[]) | 
|  | { | 
|  | SkDOM::Attr* attr = fAttrs.append(); | 
|  | attr->fName = dupstr(fAlloc, name); | 
|  | attr->fValue = dupstr(fAlloc, value); | 
|  | return false; | 
|  | } | 
|  | virtual bool onEndElement(const char elem[]) | 
|  | { | 
|  | --fLevel; | 
|  | if (fNeedToFlush) | 
|  | this->flushAttributes(); | 
|  | fNeedToFlush = false; | 
|  |  | 
|  | SkDOM::Node* parent; | 
|  |  | 
|  | fParentStack.pop(&parent); | 
|  |  | 
|  | SkDOM::Node* child = parent->fFirstChild; | 
|  | SkDOM::Node* prev = NULL; | 
|  | while (child) | 
|  | { | 
|  | SkDOM::Node* next = child->fNextSibling; | 
|  | child->fNextSibling = prev; | 
|  | prev = child; | 
|  | child = next; | 
|  | } | 
|  | parent->fFirstChild = prev; | 
|  | return false; | 
|  | } | 
|  | private: | 
|  | SkTDArray<SkDOM::Node*> fParentStack; | 
|  | SkChunkAlloc*   fAlloc; | 
|  | SkDOM::Node*    fRoot; | 
|  |  | 
|  | // state needed for flushAttributes() | 
|  | SkTDArray<SkDOM::Attr>  fAttrs; | 
|  | char*                   fElemName; | 
|  | int                     fLevel; | 
|  | }; | 
|  |  | 
|  | const SkDOM::Node* SkDOM::build(const char doc[], size_t len) | 
|  | { | 
|  | fAlloc.reset(); | 
|  | SkDOMParser parser(&fAlloc); | 
|  | if (!parser.parse(doc, len)) | 
|  | { | 
|  | SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());) | 
|  | fRoot = NULL; | 
|  | fAlloc.reset(); | 
|  | return NULL; | 
|  | } | 
|  | fRoot = parser.getRoot(); | 
|  | return fRoot; | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser) | 
|  | { | 
|  | const char* elem = dom.getName(node); | 
|  |  | 
|  | parser->startElement(elem); | 
|  |  | 
|  | SkDOM::AttrIter iter(dom, node); | 
|  | const char*     name; | 
|  | const char*     value; | 
|  | while ((name = iter.next(&value)) != NULL) | 
|  | parser->addAttribute(name, value); | 
|  |  | 
|  | node = dom.getFirstChild(node, NULL); | 
|  | while (node) | 
|  | { | 
|  | walk_dom(dom, node, parser); | 
|  | node = dom.getNextSibling(node, NULL); | 
|  | } | 
|  |  | 
|  | parser->endElement(elem); | 
|  | } | 
|  |  | 
|  | const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node) | 
|  | { | 
|  | fAlloc.reset(); | 
|  | SkDOMParser parser(&fAlloc); | 
|  |  | 
|  | walk_dom(dom, node, &parser); | 
|  |  | 
|  | fRoot = parser.getRoot(); | 
|  | 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 | 
|  |  | 
|  | static void tab(int level) | 
|  | { | 
|  | while (--level >= 0) | 
|  | SkDebugf("\t"); | 
|  | } | 
|  |  | 
|  | void SkDOM::dump(const Node* node, int level) const | 
|  | { | 
|  | if (node == NULL) | 
|  | node = this->getRootNode(); | 
|  | if (node) | 
|  | { | 
|  | tab(level); | 
|  | SkDebugf("<%s", this->getName(node)); | 
|  |  | 
|  | const Attr* attr = node->attrs(); | 
|  | const Attr* stop = attr + node->fAttrCount; | 
|  | for (; attr < stop; attr++) | 
|  | SkDebugf(" %s=\"%s\"", attr->fName, attr->fValue); | 
|  |  | 
|  | const Node* child = this->getFirstChild(node); | 
|  | if (child) | 
|  | { | 
|  | SkDebugf(">\n"); | 
|  | while (child) | 
|  | { | 
|  | this->dump(child, level+1); | 
|  | child = this->getNextSibling(child); | 
|  | } | 
|  | tab(level); | 
|  | SkDebugf("</%s>\n", node->fName); | 
|  | } | 
|  | else | 
|  | SkDebugf("/>\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkDOM::UnitTest() | 
|  | { | 
|  | #ifdef SK_SUPPORT_UNITTEST | 
|  | static const char gDoc[] = | 
|  | "<root a='1' b='2'>" | 
|  | "<elem1 c='3' />" | 
|  | "<elem2 d='4' />" | 
|  | "<elem3 e='5'>" | 
|  | "<subelem1/>" | 
|  | "<subelem2 f='6' g='7'/>" | 
|  | "</elem3>" | 
|  | "<elem4 h='8'/>" | 
|  | "</root>" | 
|  | ; | 
|  |  | 
|  | SkDOM   dom; | 
|  |  | 
|  | SkASSERT(dom.getRootNode() == NULL); | 
|  |  | 
|  | const Node* root = dom.build(gDoc, sizeof(gDoc) - 1); | 
|  | SkASSERT(root && dom.getRootNode() == root); | 
|  |  | 
|  | const char* v = dom.findAttr(root, "a"); | 
|  | SkASSERT(v && !strcmp(v, "1")); | 
|  | v = dom.findAttr(root, "b"); | 
|  | SkASSERT(v && !strcmp(v, "2")); | 
|  | v = dom.findAttr(root, "c"); | 
|  | SkASSERT(v == NULL); | 
|  |  | 
|  | SkASSERT(dom.getFirstChild(root, "elem1")); | 
|  | SkASSERT(!dom.getFirstChild(root, "subelem1")); | 
|  |  | 
|  | dom.dump(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #endif |