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