blob: 38ba669bb71327fb94e5a04b230328522fab0f56 [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
fmalita7445e862016-07-14 19:14:06 -0700313const SkDOM::Node* SkDOM::build(SkStream& docStream) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 SkDOMParser parser(&fAlloc);
fmalita7445e862016-07-14 19:14:06 -0700315 if (!parser.parse(docStream))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 {
317 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
halcanary96fcdcc2015-08-27 07:41:13 -0700318 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 fAlloc.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700320 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 }
322 fRoot = parser.getRoot();
323 return fRoot;
324}
325
326///////////////////////////////////////////////////////////////////////////
327
328static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser)
329{
330 const char* elem = dom.getName(node);
fmalita7a048692015-02-20 13:54:40 -0800331 if (dom.getType(node) == SkDOM::kText_Type) {
332 SkASSERT(dom.countChildren(node) == 0);
333 parser->text(elem, SkToInt(strlen(elem)));
334 return;
335 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336
337 parser->startElement(elem);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000338
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 SkDOM::AttrIter iter(dom, node);
340 const char* name;
341 const char* value;
halcanary96fcdcc2015-08-27 07:41:13 -0700342 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 parser->addAttribute(name, value);
344
halcanary96fcdcc2015-08-27 07:41:13 -0700345 node = dom.getFirstChild(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 while (node)
347 {
348 walk_dom(dom, node, parser);
halcanary96fcdcc2015-08-27 07:41:13 -0700349 node = dom.getNextSibling(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 }
351
352 parser->endElement(elem);
353}
354
355const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node)
356{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 SkDOMParser parser(&fAlloc);
358
359 walk_dom(dom, node, &parser);
360
361 fRoot = parser.getRoot();
362 return fRoot;
363}
364
fmalita7a048692015-02-20 13:54:40 -0800365SkXMLParser* SkDOM::beginParsing() {
366 SkASSERT(!fParser);
halcanary385fe4d2015-08-26 13:07:48 -0700367 fParser.reset(new SkDOMParser(&fAlloc));
fmalita7a048692015-02-20 13:54:40 -0800368
369 return fParser.get();
370}
371
372const SkDOM::Node* SkDOM::finishParsing() {
373 SkASSERT(fParser);
374 fRoot = fParser->getRoot();
mtklein852f15d2016-03-17 10:51:27 -0700375 fParser.reset();
fmalita7a048692015-02-20 13:54:40 -0800376
377 return fRoot;
378}
379
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380//////////////////////////////////////////////////////////////////////////
381
382int SkDOM::countChildren(const Node* node, const char elem[]) const
383{
384 int count = 0;
385
386 node = this->getFirstChild(node, elem);
387 while (node)
388 {
389 count += 1;
390 node = this->getNextSibling(node, elem);
391 }
392 return count;
393}
394
395//////////////////////////////////////////////////////////////////////////
396
397#include "SkParse.h"
398
399bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const
400{
401 const char* vstr = this->findAttr(node, name);
402 return vstr && SkParse::FindS32(vstr, value);
403}
404
405bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const
406{
407 const char* vstr = this->findAttr(node, name);
408 return vstr && SkParse::FindScalars(vstr, value, count);
409}
410
411bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const
412{
413 const char* vstr = this->findAttr(node, name);
414 return vstr && SkParse::FindHex(vstr, value);
415}
416
417bool SkDOM::findBool(const Node* node, const char name[], bool* value) const
418{
419 const char* vstr = this->findAttr(node, name);
420 return vstr && SkParse::FindBool(vstr, value);
421}
422
423int SkDOM::findList(const Node* node, const char name[], const char list[]) const
424{
425 const char* vstr = this->findAttr(node, name);
426 return vstr ? SkParse::FindList(vstr, list) : -1;
427}
428
429bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const
430{
431 const char* vstr = this->findAttr(node, name);
432 return vstr && !strcmp(vstr, value);
433}
434
435bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const
436{
437 const char* vstr = this->findAttr(node, name);
438 int32_t value;
439 return vstr && SkParse::FindS32(vstr, &value) && value == target;
440}
441
442bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const
443{
444 const char* vstr = this->findAttr(node, name);
445 SkScalar value;
446 return vstr && SkParse::FindScalar(vstr, &value) && value == target;
447}
448
449bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const
450{
451 const char* vstr = this->findAttr(node, name);
452 uint32_t value;
453 return vstr && SkParse::FindHex(vstr, &value) && value == target;
454}
455
456bool SkDOM::hasBool(const Node* node, const char name[], bool target) const
457{
458 const char* vstr = this->findAttr(node, name);
459 bool value;
460 return vstr && SkParse::FindBool(vstr, &value) && value == target;
461}
462
463//////////////////////////////////////////////////////////////////////////
464
465#ifdef SK_DEBUG
466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467void SkDOM::dump(const Node* node, int level) const
468{
halcanary96fcdcc2015-08-27 07:41:13 -0700469 if (node == nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 node = this->getRootNode();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471
fmalita7a048692015-02-20 13:54:40 -0800472 SkDebugWStream debugStream;
473 SkXMLStreamWriter xmlWriter(&debugStream);
474 xmlWriter.writeDOM(*this, node, false);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475}
476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477#endif