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