|
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 |
|
16 bool 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 |
|
41 struct SkDOMAttr { |
|
42 const char* fName; |
|
43 const char* fValue; |
|
44 }; |
|
45 |
|
46 struct 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 |
|
68 SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL) |
|
69 { |
|
70 } |
|
71 |
|
72 SkDOM::~SkDOM() |
|
73 { |
|
74 } |
|
75 |
|
76 const SkDOM::Node* SkDOM::getRootNode() const |
|
77 { |
|
78 return fRoot; |
|
79 } |
|
80 |
|
81 const 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 |
|
95 const 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 |
|
108 SkDOM::Type SkDOM::getType(const Node* node) const |
|
109 { |
|
110 SkASSERT(node); |
|
111 return (Type)node->fType; |
|
112 } |
|
113 |
|
114 const char* SkDOM::getName(const Node* node) const |
|
115 { |
|
116 SkASSERT(node); |
|
117 return node->fName; |
|
118 } |
|
119 |
|
120 const 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 |
|
137 const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const |
|
138 { |
|
139 return node->fAttrCount ? node->attrs() : NULL; |
|
140 } |
|
141 |
|
142 const 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 |
|
150 const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const |
|
151 { |
|
152 SkASSERT(node); |
|
153 SkASSERT(attr); |
|
154 return attr->fName; |
|
155 } |
|
156 |
|
157 const 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 |
|
166 SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node) |
|
167 { |
|
168 SkASSERT(node); |
|
169 fAttr = node->attrs(); |
|
170 fStop = fAttr + node->fAttrCount; |
|
171 } |
|
172 |
|
173 const 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 |
|
192 static 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 |
|
201 class SkDOMParser : public SkXMLParser { |
|
202 bool fNeedToFlush; |
|
203 public: |
|
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; |
|
212 protected: |
|
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 } |
|
282 private: |
|
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 |
|
293 const 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 |
|
310 static 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 |
|
332 const 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 |
|
345 int 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 |
|
362 bool 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 |
|
368 bool 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 |
|
374 bool 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 |
|
380 bool 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 |
|
386 int 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 |
|
392 bool 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 |
|
398 bool 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 |
|
405 bool 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 |
|
412 bool 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 |
|
419 bool 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 |
|
430 static void tab(int level) |
|
431 { |
|
432 while (--level >= 0) |
|
433 SkDebugf("\t"); |
|
434 } |
|
435 |
|
436 void 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 |
|
467 void 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 |