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