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"
parse(const SkDOM & dom,const SkDOMNode * node)17 bool 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)) != NULL)
28         if (this->addAttribute(name, value))
29             return false;
30 
31     if ((node = dom.getFirstChild(node)) != NULL)
32         do {
33             if (!this->parse(dom, node))
34                 return false;
35         } while ((node = dom.getNextSibling(node)) != NULL);
36 
37     return !this->endElement(elemName);
38 }
39 
40 /////////////////////////////////////////////////////////////////////////
41 
42 struct SkDOMAttr {
43     const char* fName;
44     const char* fValue;
45 };
46 
47 struct SkDOMNode {
48     const char* fName;
49     SkDOMNode*  fFirstChild;
50     SkDOMNode*  fNextSibling;
51     uint16_t    fAttrCount;
52     uint8_t     fType;
53     uint8_t     fPad;
54 
attrsSkDOMNode55     const SkDOMAttr* attrs() const
56     {
57         return (const SkDOMAttr*)(this + 1);
58     }
attrsSkDOMNode59     SkDOMAttr* attrs()
60     {
61         return (SkDOMAttr*)(this + 1);
62     }
63 };
64 
65 /////////////////////////////////////////////////////////////////////////
66 
67 #define kMinChunkSize   512
68 
SkDOM()69 SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL)
70 {
71 }
72 
~SkDOM()73 SkDOM::~SkDOM()
74 {
75 }
76 
getRootNode() const77 const SkDOM::Node* SkDOM::getRootNode() const
78 {
79     return fRoot;
80 }
81 
getFirstChild(const Node * node,const char name[]) const82 const 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 != NULL; child = child->fNextSibling)
90             if (!strcmp(name, child->fName))
91                 break;
92     }
93     return child;
94 }
95 
getNextSibling(const Node * node,const char name[]) const96 const 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 != NULL; sibling = sibling->fNextSibling)
103             if (!strcmp(name, sibling->fName))
104                 break;
105     }
106     return sibling;
107 }
108 
getType(const Node * node) const109 SkDOM::Type SkDOM::getType(const Node* node) const
110 {
111     SkASSERT(node);
112     return (Type)node->fType;
113 }
114 
getName(const Node * node) const115 const char* SkDOM::getName(const Node* node) const
116 {
117     SkASSERT(node);
118     return node->fName;
119 }
120 
findAttr(const Node * node,const char name[]) const121 const 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 NULL;
134 }
135 
136 /////////////////////////////////////////////////////////////////////////////////////
137 
getFirstAttr(const Node * node) const138 const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
139 {
140     return node->fAttrCount ? node->attrs() : NULL;
141 }
142 
getNextAttr(const Node * node,const Attr * attr) const143 const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
144 {
145     SkASSERT(node);
146     if (attr == NULL)
147         return NULL;
148     return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL;
149 }
150 
getAttrName(const Node * node,const Attr * attr) const151 const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const
152 {
153     SkASSERT(node);
154     SkASSERT(attr);
155     return attr->fName;
156 }
157 
getAttrValue(const Node * node,const Attr * attr) const158 const 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 
AttrIter(const SkDOM &,const SkDOM::Node * node)167 SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node)
168 {
169     SkASSERT(node);
170     fAttr = node->attrs();
171     fStop = fAttr + node->fAttrCount;
172 }
173 
next(const char ** value)174 const char* SkDOM::AttrIter::next(const char** value)
175 {
176     const char* name = NULL;
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 
dupstr(SkChunkAlloc * chunk,const char src[])193 static 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 
202 class SkDOMParser : public SkXMLParser {
203 public:
SkDOMParser(SkChunkAlloc * chunk)204     SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
205     {
206         fAlloc->reset();
207         fRoot = NULL;
208         fLevel = 0;
209         fNeedToFlush = true;
210     }
getRoot() const211     SkDOM::Node* getRoot() const { return fRoot; }
212     SkXMLParserError fParserError;
213 
214 protected:
flushAttributes()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 = NULL;
226         node->fAttrCount = SkToU16(attrCount);
227         node->fType = fElemType;
228 
229         if (fRoot == NULL)
230         {
231             node->fNextSibling = NULL;
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         memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
244         fAttrs.reset();
245 
246     }
247 
onStartElement(const char elem[])248     bool onStartElement(const char elem[]) override {
249         this->startCommon(elem, SkDOM::kElement_Type);
250         return false;
251     }
252 
onAddAttribute(const char name[],const char value[])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 
onEndElement(const char elem[])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 = 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     }
282 
onText(const char text[],int len)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 
291 private:
startCommon(const char elem[],SkDOM::Type type)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 
build(const char doc[],size_t len)314 const 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 = NULL;
321         fAlloc.reset();
322         return NULL;
323     }
324     fRoot = parser.getRoot();
325     return fRoot;
326 }
327 
328 ///////////////////////////////////////////////////////////////////////////
329 
walk_dom(const SkDOM & dom,const SkDOM::Node * node,SkXMLParser * parser)330 static 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)) != NULL)
345         parser->addAttribute(name, value);
346 
347     node = dom.getFirstChild(node, NULL);
348     while (node)
349     {
350         walk_dom(dom, node, parser);
351         node = dom.getNextSibling(node, NULL);
352     }
353 
354     parser->endElement(elem);
355 }
356 
copy(const SkDOM & dom,const SkDOM::Node * node)357 const 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 
beginParsing()367 SkXMLParser* SkDOM::beginParsing() {
368     SkASSERT(!fParser);
369     fParser.reset(SkNEW_ARGS(SkDOMParser, (&fAlloc)));
370 
371     return fParser.get();
372 }
373 
finishParsing()374 const SkDOM::Node* SkDOM::finishParsing() {
375     SkASSERT(fParser);
376     fRoot = fParser->getRoot();
377     fParser.free();
378 
379     return fRoot;
380 }
381 
382 //////////////////////////////////////////////////////////////////////////
383 
countChildren(const Node * node,const char elem[]) const384 int 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 
findS32(const Node * node,const char name[],int32_t * value) const401 bool 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 
findScalars(const Node * node,const char name[],SkScalar value[],int count) const407 bool 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 
findHex(const Node * node,const char name[],uint32_t * value) const413 bool 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 
findBool(const Node * node,const char name[],bool * value) const419 bool 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 
findList(const Node * node,const char name[],const char list[]) const425 int 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 
hasAttr(const Node * node,const char name[],const char value[]) const431 bool 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 
hasS32(const Node * node,const char name[],int32_t target) const437 bool 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 
hasScalar(const Node * node,const char name[],SkScalar target) const444 bool 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 
hasHex(const Node * node,const char name[],uint32_t target) const451 bool 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 
hasBool(const Node * node,const char name[],bool target) const458 bool 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 
dump(const Node * node,int level) const469 void SkDOM::dump(const Node* node, int level) const
470 {
471     if (node == NULL)
472         node = this->getRootNode();
473 
474     SkDebugWStream debugStream;
475     SkXMLStreamWriter xmlWriter(&debugStream);
476     xmlWriter.writeDOM(*this, node, false);
477 }
478 
UnitTest()479 void 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() == NULL);
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 == NULL);
507 
508     SkASSERT(dom.getFirstChild(root, "elem1"));
509     SkASSERT(!dom.getFirstChild(root, "subelem1"));
510 
511     dom.dump();
512 #endif
513 }
514 
515 #endif
516