blob: 0f0b614f1116bed80ccf7cf5f4a1e6f9d9d43d34 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDOM.h"
fmalita7a048692015-02-20 13:54:40 -080011#include "SkStream.h"
12#include "SkXMLWriter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013
14/////////////////////////////////////////////////////////////////////////
15
16#include "SkXMLParser.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017bool 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;
rmistry@google.comd6176b02012-08-23 18:14:13 +000023
reed@android.com8a1c16f2008-12-17 15:59:43 +000024 SkDOM::AttrIter iter(dom, node);
25 const char* name, *value;
rmistry@google.comd6176b02012-08-23 18:14:13 +000026
halcanary96fcdcc2015-08-27 07:41:13 -070027 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +000028 if (this->addAttribute(name, value))
29 return false;
30
halcanary96fcdcc2015-08-27 07:41:13 -070031 if ((node = dom.getFirstChild(node)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 do {
33 if (!this->parse(dom, node))
34 return false;
halcanary96fcdcc2015-08-27 07:41:13 -070035 } while ((node = dom.getNextSibling(node)) != nullptr);
rmistry@google.comd6176b02012-08-23 18:14:13 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037 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
halcanary96fcdcc2015-08-27 07:41:13 -070069SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +000070{
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 {
halcanary96fcdcc2015-08-27 07:41:13 -070089 for (; child != nullptr; child = child->fNextSibling)
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 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 {
halcanary96fcdcc2015-08-27 07:41:13 -0700102 for (; sibling != nullptr; sibling = sibling->fNextSibling)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 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 }
halcanary96fcdcc2015-08-27 07:41:13 -0700133 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134}
135
136/////////////////////////////////////////////////////////////////////////////////////
137
138const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
139{
halcanary96fcdcc2015-08-27 07:41:13 -0700140 return node->fAttrCount ? node->attrs() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141}
142
143const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
144{
145 SkASSERT(node);
halcanary96fcdcc2015-08-27 07:41:13 -0700146 if (attr == nullptr)
147 return nullptr;
148 return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149}
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{
halcanary96fcdcc2015-08-27 07:41:13 -0700176 const char* name = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
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 {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203public:
204 SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
205 {
fmalita7a048692015-02-20 13:54:40 -0800206 fAlloc->reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700207 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 fLevel = 0;
209 fNeedToFlush = true;
210 }
211 SkDOM::Node* getRoot() const { return fRoot; }
212 SkXMLParserError fParserError;
fmalita7a048692015-02-20 13:54:40 -0800213
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214protected:
215 void flushAttributes()
216 {
fmalita7a048692015-02-20 13:54:40 -0800217 SkASSERT(fLevel > 0);
218
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 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;
halcanary96fcdcc2015-08-27 07:41:13 -0700225 node->fFirstChild = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 node->fAttrCount = SkToU16(attrCount);
fmalita7a048692015-02-20 13:54:40 -0800227 node->fType = fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228
halcanary96fcdcc2015-08-27 07:41:13 -0700229 if (fRoot == nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 {
halcanary96fcdcc2015-08-27 07:41:13 -0700231 node->fNextSibling = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 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
mtklein067e90e2015-12-10 07:42:47 -0800243 sk_careful_memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 fAttrs.reset();
245
246 }
fmalita7a048692015-02-20 13:54:40 -0800247
248 bool onStartElement(const char elem[]) override {
249 this->startCommon(elem, SkDOM::kElement_Type);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 return false;
251 }
fmalita7a048692015-02-20 13:54:40 -0800252
253 bool onAddAttribute(const char name[], const char value[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 SkDOM::Attr* attr = fAttrs.append();
255 attr->fName = dupstr(fAlloc, name);
256 attr->fValue = dupstr(fAlloc, value);
257 return false;
258 }
fmalita7a048692015-02-20 13:54:40 -0800259
260 bool onEndElement(const char elem[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 --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;
halcanary96fcdcc2015-08-27 07:41:13 -0700271 SkDOM::Node* prev = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 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 }
fmalita7a048692015-02-20 13:54:40 -0800282
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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291private:
fmalita7a048692015-02-20 13:54:40 -0800292 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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 SkTDArray<SkDOM::Node*> fParentStack;
fmalita7a048692015-02-20 13:54:40 -0800303 SkChunkAlloc* fAlloc;
304 SkDOM::Node* fRoot;
305 bool fNeedToFlush;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306
307 // state needed for flushAttributes()
308 SkTDArray<SkDOM::Attr> fAttrs;
309 char* fElemName;
fmalita7a048692015-02-20 13:54:40 -0800310 SkDOM::Type fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 int fLevel;
312};
313
314const SkDOM::Node* SkDOM::build(const char doc[], size_t len)
315{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 SkDOMParser parser(&fAlloc);
317 if (!parser.parse(doc, len))
318 {
319 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
halcanary96fcdcc2015-08-27 07:41:13 -0700320 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 fAlloc.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700322 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 }
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);
fmalita7a048692015-02-20 13:54:40 -0800333 if (dom.getType(node) == SkDOM::kText_Type) {
334 SkASSERT(dom.countChildren(node) == 0);
335 parser->text(elem, SkToInt(strlen(elem)));
336 return;
337 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338
339 parser->startElement(elem);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 SkDOM::AttrIter iter(dom, node);
342 const char* name;
343 const char* value;
halcanary96fcdcc2015-08-27 07:41:13 -0700344 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 parser->addAttribute(name, value);
346
halcanary96fcdcc2015-08-27 07:41:13 -0700347 node = dom.getFirstChild(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 while (node)
349 {
350 walk_dom(dom, node, parser);
halcanary96fcdcc2015-08-27 07:41:13 -0700351 node = dom.getNextSibling(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 }
353
354 parser->endElement(elem);
355}
356
357const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
358{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 SkDOMParser parser(&fAlloc);
360
361 walk_dom(dom, node, &parser);
362
363 fRoot = parser.getRoot();
364 return fRoot;
365}
366
fmalita7a048692015-02-20 13:54:40 -0800367SkXMLParser* SkDOM::beginParsing() {
368 SkASSERT(!fParser);
halcanary385fe4d2015-08-26 13:07:48 -0700369 fParser.reset(new SkDOMParser(&fAlloc));
fmalita7a048692015-02-20 13:54:40 -0800370
371 return fParser.get();
372}
373
374const SkDOM::Node* SkDOM::finishParsing() {
375 SkASSERT(fParser);
376 fRoot = fParser->getRoot();
bungeman43812e22016-03-17 09:53:58 -0700377 fParser.free();
fmalita7a048692015-02-20 13:54:40 -0800378
379 return fRoot;
380}
381
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382//////////////////////////////////////////////////////////////////////////
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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469void SkDOM::dump(const Node* node, int level) const
470{
halcanary96fcdcc2015-08-27 07:41:13 -0700471 if (node == nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 node = this->getRootNode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473
fmalita7a048692015-02-20 13:54:40 -0800474 SkDebugWStream debugStream;
475 SkXMLStreamWriter xmlWriter(&debugStream);
476 xmlWriter.writeDOM(*this, node, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477}
478
479void SkDOM::UnitTest()
480{
481#ifdef SK_SUPPORT_UNITTEST
rmistry@google.comd6176b02012-08-23 18:14:13 +0000482 static const char gDoc[] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 "<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
halcanary96fcdcc2015-08-27 07:41:13 -0700496 SkASSERT(dom.getRootNode() == nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497
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");
halcanary96fcdcc2015-08-27 07:41:13 -0700506 SkASSERT(v == nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507
508 SkASSERT(dom.getFirstChild(root, "elem1"));
509 SkASSERT(!dom.getFirstChild(root, "subelem1"));
510
511 dom.dump();
512#endif
513}
514
515#endif