blob: 15b458e4fbc3821edf3df69d11b6ff5c2eda331a [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008
9#include "SkDOM.h"
fmalita7a048692015-02-20 13:54:40 -080010#include "SkStream.h"
11#include "SkXMLWriter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012
13/////////////////////////////////////////////////////////////////////////
14
15#include "SkXMLParser.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016bool 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;
rmistry@google.comd6176b02012-08-23 18:14:13 +000022
reed@android.com8a1c16f2008-12-17 15:59:43 +000023 SkDOM::AttrIter iter(dom, node);
24 const char* name, *value;
rmistry@google.comd6176b02012-08-23 18:14:13 +000025
halcanary96fcdcc2015-08-27 07:41:13 -070026 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +000027 if (this->addAttribute(name, value))
28 return false;
29
halcanary96fcdcc2015-08-27 07:41:13 -070030 if ((node = dom.getFirstChild(node)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 do {
32 if (!this->parse(dom, node))
33 return false;
halcanary96fcdcc2015-08-27 07:41:13 -070034 } while ((node = dom.getNextSibling(node)) != nullptr);
rmistry@google.comd6176b02012-08-23 18:14:13 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 return !this->endElement(elemName);
37}
38
39/////////////////////////////////////////////////////////////////////////
40
41struct SkDOMAttr {
42 const char* fName;
43 const char* fValue;
44};
45
46struct 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
halcanary96fcdcc2015-08-27 07:41:13 -070068SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +000069{
70}
71
72SkDOM::~SkDOM()
73{
74}
75
76const SkDOM::Node* SkDOM::getRootNode() const
77{
78 return fRoot;
79}
80
81const 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 {
halcanary96fcdcc2015-08-27 07:41:13 -070088 for (; child != nullptr; child = child->fNextSibling)
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 if (!strcmp(name, child->fName))
90 break;
91 }
92 return child;
93}
94
95const 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 {
halcanary96fcdcc2015-08-27 07:41:13 -0700101 for (; sibling != nullptr; sibling = sibling->fNextSibling)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 if (!strcmp(name, sibling->fName))
103 break;
104 }
105 return sibling;
106}
107
108SkDOM::Type SkDOM::getType(const Node* node) const
109{
110 SkASSERT(node);
111 return (Type)node->fType;
112}
113
114const char* SkDOM::getName(const Node* node) const
115{
116 SkASSERT(node);
117 return node->fName;
118}
119
120const 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 }
halcanary96fcdcc2015-08-27 07:41:13 -0700132 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133}
134
135/////////////////////////////////////////////////////////////////////////////////////
136
137const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const
138{
halcanary96fcdcc2015-08-27 07:41:13 -0700139 return node->fAttrCount ? node->attrs() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140}
141
142const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const
143{
144 SkASSERT(node);
halcanary96fcdcc2015-08-27 07:41:13 -0700145 if (attr == nullptr)
146 return nullptr;
147 return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148}
149
150const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const
151{
152 SkASSERT(node);
153 SkASSERT(attr);
154 return attr->fName;
155}
156
157const 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
166SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node)
167{
168 SkASSERT(node);
169 fAttr = node->attrs();
170 fStop = fAttr + node->fAttrCount;
171}
172
173const char* SkDOM::AttrIter::next(const char** value)
174{
halcanary96fcdcc2015-08-27 07:41:13 -0700175 const char* name = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
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
192static 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
201class SkDOMParser : public SkXMLParser {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202public:
203 SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk)
204 {
fmalita7a048692015-02-20 13:54:40 -0800205 fAlloc->reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700206 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 fLevel = 0;
208 fNeedToFlush = true;
209 }
210 SkDOM::Node* getRoot() const { return fRoot; }
211 SkXMLParserError fParserError;
fmalita7a048692015-02-20 13:54:40 -0800212
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213protected:
214 void flushAttributes()
215 {
fmalita7a048692015-02-20 13:54:40 -0800216 SkASSERT(fLevel > 0);
217
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 int attrCount = fAttrs.count();
219
220 SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr),
221 SkChunkAlloc::kThrow_AllocFailType);
222
223 node->fName = fElemName;
halcanary96fcdcc2015-08-27 07:41:13 -0700224 node->fFirstChild = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 node->fAttrCount = SkToU16(attrCount);
fmalita7a048692015-02-20 13:54:40 -0800226 node->fType = fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227
halcanary96fcdcc2015-08-27 07:41:13 -0700228 if (fRoot == nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 {
halcanary96fcdcc2015-08-27 07:41:13 -0700230 node->fNextSibling = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 fRoot = node;
232 }
233 else // this adds siblings in reverse order. gets corrected in onEndElement()
234 {
235 SkDOM::Node* parent = fParentStack.top();
236 SkASSERT(fRoot && parent);
237 node->fNextSibling = parent->fFirstChild;
238 parent->fFirstChild = node;
239 }
240 *fParentStack.push() = node;
241
mtklein067e90e2015-12-10 07:42:47 -0800242 sk_careful_memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 fAttrs.reset();
244
245 }
fmalita7a048692015-02-20 13:54:40 -0800246
247 bool onStartElement(const char elem[]) override {
248 this->startCommon(elem, SkDOM::kElement_Type);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 return false;
250 }
fmalita7a048692015-02-20 13:54:40 -0800251
252 bool onAddAttribute(const char name[], const char value[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkDOM::Attr* attr = fAttrs.append();
254 attr->fName = dupstr(fAlloc, name);
255 attr->fValue = dupstr(fAlloc, value);
256 return false;
257 }
fmalita7a048692015-02-20 13:54:40 -0800258
259 bool onEndElement(const char elem[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 --fLevel;
261 if (fNeedToFlush)
262 this->flushAttributes();
263 fNeedToFlush = false;
264
265 SkDOM::Node* parent;
266
267 fParentStack.pop(&parent);
268
269 SkDOM::Node* child = parent->fFirstChild;
halcanary96fcdcc2015-08-27 07:41:13 -0700270 SkDOM::Node* prev = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 while (child)
272 {
273 SkDOM::Node* next = child->fNextSibling;
274 child->fNextSibling = prev;
275 prev = child;
276 child = next;
277 }
278 parent->fFirstChild = prev;
279 return false;
280 }
fmalita7a048692015-02-20 13:54:40 -0800281
282 bool onText(const char text[], int len) override {
283 SkString str(text, len);
284 this->startCommon(str.c_str(), SkDOM::kText_Type);
285 this->SkDOMParser::onEndElement(str.c_str());
286
287 return false;
288 }
289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290private:
fmalita7a048692015-02-20 13:54:40 -0800291 void startCommon(const char elem[], SkDOM::Type type) {
292 if (fLevel > 0 && fNeedToFlush)
293 this->flushAttributes();
294
295 fNeedToFlush = true;
296 fElemName = dupstr(fAlloc, elem);
297 fElemType = type;
298 ++fLevel;
299 }
300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 SkTDArray<SkDOM::Node*> fParentStack;
fmalita7a048692015-02-20 13:54:40 -0800302 SkChunkAlloc* fAlloc;
303 SkDOM::Node* fRoot;
304 bool fNeedToFlush;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 // state needed for flushAttributes()
307 SkTDArray<SkDOM::Attr> fAttrs;
308 char* fElemName;
fmalita7a048692015-02-20 13:54:40 -0800309 SkDOM::Type fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 int fLevel;
311};
312
313const SkDOM::Node* SkDOM::build(const char doc[], size_t len)
314{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 SkDOMParser parser(&fAlloc);
316 if (!parser.parse(doc, len))
317 {
318 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
halcanary96fcdcc2015-08-27 07:41:13 -0700319 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 fAlloc.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700321 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 }
323 fRoot = parser.getRoot();
324 return fRoot;
325}
326
327///////////////////////////////////////////////////////////////////////////
328
329static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser)
330{
331 const char* elem = dom.getName(node);
fmalita7a048692015-02-20 13:54:40 -0800332 if (dom.getType(node) == SkDOM::kText_Type) {
333 SkASSERT(dom.countChildren(node) == 0);
334 parser->text(elem, SkToInt(strlen(elem)));
335 return;
336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337
338 parser->startElement(elem);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 SkDOM::AttrIter iter(dom, node);
341 const char* name;
342 const char* value;
halcanary96fcdcc2015-08-27 07:41:13 -0700343 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 parser->addAttribute(name, value);
345
halcanary96fcdcc2015-08-27 07:41:13 -0700346 node = dom.getFirstChild(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 while (node)
348 {
349 walk_dom(dom, node, parser);
halcanary96fcdcc2015-08-27 07:41:13 -0700350 node = dom.getNextSibling(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 }
352
353 parser->endElement(elem);
354}
355
356const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
357{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 SkDOMParser parser(&fAlloc);
359
360 walk_dom(dom, node, &parser);
361
362 fRoot = parser.getRoot();
363 return fRoot;
364}
365
fmalita7a048692015-02-20 13:54:40 -0800366SkXMLParser* SkDOM::beginParsing() {
367 SkASSERT(!fParser);
halcanary385fe4d2015-08-26 13:07:48 -0700368 fParser.reset(new SkDOMParser(&fAlloc));
fmalita7a048692015-02-20 13:54:40 -0800369
370 return fParser.get();
371}
372
373const SkDOM::Node* SkDOM::finishParsing() {
374 SkASSERT(fParser);
375 fRoot = fParser->getRoot();
mtklein852f15d2016-03-17 10:51:27 -0700376 fParser.reset();
fmalita7a048692015-02-20 13:54:40 -0800377
378 return fRoot;
379}
380
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381//////////////////////////////////////////////////////////////////////////
382
383int SkDOM::countChildren(const Node* node, const char elem[]) const
384{
385 int count = 0;
386
387 node = this->getFirstChild(node, elem);
388 while (node)
389 {
390 count += 1;
391 node = this->getNextSibling(node, elem);
392 }
393 return count;
394}
395
396//////////////////////////////////////////////////////////////////////////
397
398#include "SkParse.h"
399
400bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const
401{
402 const char* vstr = this->findAttr(node, name);
403 return vstr && SkParse::FindS32(vstr, value);
404}
405
406bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const
407{
408 const char* vstr = this->findAttr(node, name);
409 return vstr && SkParse::FindScalars(vstr, value, count);
410}
411
412bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const
413{
414 const char* vstr = this->findAttr(node, name);
415 return vstr && SkParse::FindHex(vstr, value);
416}
417
418bool SkDOM::findBool(const Node* node, const char name[], bool* value) const
419{
420 const char* vstr = this->findAttr(node, name);
421 return vstr && SkParse::FindBool(vstr, value);
422}
423
424int SkDOM::findList(const Node* node, const char name[], const char list[]) const
425{
426 const char* vstr = this->findAttr(node, name);
427 return vstr ? SkParse::FindList(vstr, list) : -1;
428}
429
430bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const
431{
432 const char* vstr = this->findAttr(node, name);
433 return vstr && !strcmp(vstr, value);
434}
435
436bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const
437{
438 const char* vstr = this->findAttr(node, name);
439 int32_t value;
440 return vstr && SkParse::FindS32(vstr, &value) && value == target;
441}
442
443bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const
444{
445 const char* vstr = this->findAttr(node, name);
446 SkScalar value;
447 return vstr && SkParse::FindScalar(vstr, &value) && value == target;
448}
449
450bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const
451{
452 const char* vstr = this->findAttr(node, name);
453 uint32_t value;
454 return vstr && SkParse::FindHex(vstr, &value) && value == target;
455}
456
457bool SkDOM::hasBool(const Node* node, const char name[], bool target) const
458{
459 const char* vstr = this->findAttr(node, name);
460 bool value;
461 return vstr && SkParse::FindBool(vstr, &value) && value == target;
462}
463
464//////////////////////////////////////////////////////////////////////////
465
466#ifdef SK_DEBUG
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468void SkDOM::dump(const Node* node, int level) const
469{
halcanary96fcdcc2015-08-27 07:41:13 -0700470 if (node == nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 node = this->getRootNode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472
fmalita7a048692015-02-20 13:54:40 -0800473 SkDebugWStream debugStream;
474 SkXMLStreamWriter xmlWriter(&debugStream);
475 xmlWriter.writeDOM(*this, node, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476}
477
478void SkDOM::UnitTest()
479{
480#ifdef SK_SUPPORT_UNITTEST
rmistry@google.comd6176b02012-08-23 18:14:13 +0000481 static const char gDoc[] =
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 "<root a='1' b='2'>"
483 "<elem1 c='3' />"
484 "<elem2 d='4' />"
485 "<elem3 e='5'>"
486 "<subelem1/>"
487 "<subelem2 f='6' g='7'/>"
488 "</elem3>"
489 "<elem4 h='8'/>"
490 "</root>"
491 ;
492
493 SkDOM dom;
494
halcanary96fcdcc2015-08-27 07:41:13 -0700495 SkASSERT(dom.getRootNode() == nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496
497 const Node* root = dom.build(gDoc, sizeof(gDoc) - 1);
498 SkASSERT(root && dom.getRootNode() == root);
499
500 const char* v = dom.findAttr(root, "a");
501 SkASSERT(v && !strcmp(v, "1"));
502 v = dom.findAttr(root, "b");
503 SkASSERT(v && !strcmp(v, "2"));
504 v = dom.findAttr(root, "c");
halcanary96fcdcc2015-08-27 07:41:13 -0700505 SkASSERT(v == nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506
507 SkASSERT(dom.getFirstChild(root, "elem1"));
508 SkASSERT(!dom.getFirstChild(root, "subelem1"));
509
510 dom.dump();
511#endif
512}
513
514#endif