diff options
Diffstat (limited to 'src/xml/SkDOM.cpp')
-rw-r--r-- | src/xml/SkDOM.cpp | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp new file mode 100644 index 0000000..a9fc31e --- /dev/null +++ b/src/xml/SkDOM.cpp @@ -0,0 +1,512 @@ +/* libs/graphics/xml/SkDOM.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#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 + |