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