blob: b1cf1d53606147febf38878b13c897601a7418f7 [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"
Mike Reedc3063e52017-01-07 16:16:02 -050011#include "SkXMLParser.h"
fmalita7a048692015-02-20 13:54:40 -080012#include "SkXMLWriter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013
Mike Reedc3063e52017-01-07 16:16:02 -050014bool SkXMLParser::parse(const SkDOM& dom, const SkDOMNode* node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000015 const char* elemName = dom.getName(node);
16
Mike Reedc3063e52017-01-07 16:16:02 -050017 if (this->startElement(elemName)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000018 return false;
Mike Reedc3063e52017-01-07 16:16:02 -050019 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000020
reed@android.com8a1c16f2008-12-17 15:59:43 +000021 SkDOM::AttrIter iter(dom, node);
22 const char* name, *value;
rmistry@google.comd6176b02012-08-23 18:14:13 +000023
Mike Reedc3063e52017-01-07 16:16:02 -050024 while ((name = iter.next(&value)) != nullptr) {
25 if (this->addAttribute(name, value)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000026 return false;
Mike Reedc3063e52017-01-07 16:16:02 -050027 }
28 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
Mike Reedc3063e52017-01-07 16:16:02 -050030 if ((node = dom.getFirstChild(node)) != nullptr) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 do {
Mike Reedc3063e52017-01-07 16:16:02 -050032 if (!this->parse(dom, node)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000033 return false;
Mike Reedc3063e52017-01-07 16:16:02 -050034 }
halcanary96fcdcc2015-08-27 07:41:13 -070035 } while ((node = dom.getNextSibling(node)) != nullptr);
Mike Reedc3063e52017-01-07 16:16:02 -050036 }
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
Mike Reedc3063e52017-01-07 16:16:02 -050055 const SkDOMAttr* attrs() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 return (const SkDOMAttr*)(this + 1);
57 }
Mike Reedc3063e52017-01-07 16:16:02 -050058
59 SkDOMAttr* attrs() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 return (SkDOMAttr*)(this + 1);
61 }
62};
63
64/////////////////////////////////////////////////////////////////////////
65
66#define kMinChunkSize 512
67
Mike Reedc3063e52017-01-07 16:16:02 -050068SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nullptr) {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000069
Mike Reedc3063e52017-01-07 16:16:02 -050070SkDOM::~SkDOM() {}
reed@android.com8a1c16f2008-12-17 15:59:43 +000071
Mike Reedc3063e52017-01-07 16:16:02 -050072const SkDOM::Node* SkDOM::getRootNode() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 return fRoot;
74}
75
Mike Reedc3063e52017-01-07 16:16:02 -050076const SkDOM::Node* SkDOM::getFirstChild(const Node* node, const char name[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 SkASSERT(node);
78 const Node* child = node->fFirstChild;
79
Mike Reedc3063e52017-01-07 16:16:02 -050080 if (name) {
81 for (; child != nullptr; child = child->fNextSibling) {
82 if (!strcmp(name, child->fName)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 break;
Mike Reedc3063e52017-01-07 16:16:02 -050084 }
85 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 }
87 return child;
88}
89
Mike Reedc3063e52017-01-07 16:16:02 -050090const SkDOM::Node* SkDOM::getNextSibling(const Node* node, const char name[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 SkASSERT(node);
92 const Node* sibling = node->fNextSibling;
Mike Reedc3063e52017-01-07 16:16:02 -050093 if (name) {
94 for (; sibling != nullptr; sibling = sibling->fNextSibling) {
95 if (!strcmp(name, sibling->fName)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 break;
Mike Reedc3063e52017-01-07 16:16:02 -050097 }
98 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 }
100 return sibling;
101}
102
Mike Reedc3063e52017-01-07 16:16:02 -0500103SkDOM::Type SkDOM::getType(const Node* node) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 SkASSERT(node);
105 return (Type)node->fType;
106}
107
Mike Reedc3063e52017-01-07 16:16:02 -0500108const char* SkDOM::getName(const Node* node) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 SkASSERT(node);
110 return node->fName;
111}
112
Mike Reedc3063e52017-01-07 16:16:02 -0500113const char* SkDOM::findAttr(const Node* node, const char name[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 SkASSERT(node);
115 const Attr* attr = node->attrs();
116 const Attr* stop = attr + node->fAttrCount;
117
Mike Reedc3063e52017-01-07 16:16:02 -0500118 while (attr < stop) {
119 if (!strcmp(attr->fName, name)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 return attr->fValue;
Mike Reedc3063e52017-01-07 16:16:02 -0500121 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 attr += 1;
123 }
halcanary96fcdcc2015-08-27 07:41:13 -0700124 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125}
126
127/////////////////////////////////////////////////////////////////////////////////////
128
Mike Reedc3063e52017-01-07 16:16:02 -0500129const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700130 return node->fAttrCount ? node->attrs() : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131}
132
Mike Reedc3063e52017-01-07 16:16:02 -0500133const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 SkASSERT(node);
Mike Reedc3063e52017-01-07 16:16:02 -0500135 if (attr == nullptr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700136 return nullptr;
Mike Reedc3063e52017-01-07 16:16:02 -0500137 }
halcanary96fcdcc2015-08-27 07:41:13 -0700138 return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139}
140
Mike Reedc3063e52017-01-07 16:16:02 -0500141const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 SkASSERT(node);
143 SkASSERT(attr);
144 return attr->fName;
145}
146
Mike Reedc3063e52017-01-07 16:16:02 -0500147const char* SkDOM::getAttrValue(const Node* node, const Attr* attr) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 SkASSERT(node);
149 SkASSERT(attr);
150 return attr->fValue;
151}
152
153/////////////////////////////////////////////////////////////////////////////////////
154
Mike Reedc3063e52017-01-07 16:16:02 -0500155SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 SkASSERT(node);
157 fAttr = node->attrs();
158 fStop = fAttr + node->fAttrCount;
159}
160
Mike Reedc3063e52017-01-07 16:16:02 -0500161const char* SkDOM::AttrIter::next(const char** value) {
halcanary96fcdcc2015-08-27 07:41:13 -0700162 const char* name = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163
Mike Reedc3063e52017-01-07 16:16:02 -0500164 if (fAttr < fStop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 name = fAttr->fName;
166 if (value)
167 *value = fAttr->fValue;
168 fAttr += 1;
169 }
170 return name;
171}
172
173//////////////////////////////////////////////////////////////////////////////
174
175#include "SkXMLParser.h"
176#include "SkTDArray.h"
177
Mike Reedc3063e52017-01-07 16:16:02 -0500178static char* dupstr(SkChunkAlloc* chunk, const char src[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 SkASSERT(chunk && src);
180 size_t len = strlen(src);
181 char* dst = (char*)chunk->alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
182 memcpy(dst, src, len + 1);
183 return dst;
184}
185
186class SkDOMParser : public SkXMLParser {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187public:
Mike Reedc3063e52017-01-07 16:16:02 -0500188 SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) {
fmalita7a048692015-02-20 13:54:40 -0800189 fAlloc->reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700190 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 fLevel = 0;
192 fNeedToFlush = true;
193 }
194 SkDOM::Node* getRoot() const { return fRoot; }
195 SkXMLParserError fParserError;
fmalita7a048692015-02-20 13:54:40 -0800196
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197protected:
Mike Reedc3063e52017-01-07 16:16:02 -0500198 void flushAttributes() {
fmalita7a048692015-02-20 13:54:40 -0800199 SkASSERT(fLevel > 0);
200
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 int attrCount = fAttrs.count();
202
203 SkDOM::Node* node = (SkDOM::Node*)fAlloc->alloc(sizeof(SkDOM::Node) + attrCount * sizeof(SkDOM::Attr),
204 SkChunkAlloc::kThrow_AllocFailType);
205
206 node->fName = fElemName;
halcanary96fcdcc2015-08-27 07:41:13 -0700207 node->fFirstChild = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 node->fAttrCount = SkToU16(attrCount);
fmalita7a048692015-02-20 13:54:40 -0800209 node->fType = fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210
Mike Reedc3063e52017-01-07 16:16:02 -0500211 if (fRoot == nullptr) {
halcanary96fcdcc2015-08-27 07:41:13 -0700212 node->fNextSibling = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 fRoot = node;
Mike Reedc3063e52017-01-07 16:16:02 -0500214 } else { // this adds siblings in reverse order. gets corrected in onEndElement()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 SkDOM::Node* parent = fParentStack.top();
216 SkASSERT(fRoot && parent);
217 node->fNextSibling = parent->fFirstChild;
218 parent->fFirstChild = node;
219 }
220 *fParentStack.push() = node;
221
mtklein067e90e2015-12-10 07:42:47 -0800222 sk_careful_memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 fAttrs.reset();
224
225 }
fmalita7a048692015-02-20 13:54:40 -0800226
227 bool onStartElement(const char elem[]) override {
228 this->startCommon(elem, SkDOM::kElement_Type);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 return false;
230 }
fmalita7a048692015-02-20 13:54:40 -0800231
232 bool onAddAttribute(const char name[], const char value[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 SkDOM::Attr* attr = fAttrs.append();
234 attr->fName = dupstr(fAlloc, name);
235 attr->fValue = dupstr(fAlloc, value);
236 return false;
237 }
fmalita7a048692015-02-20 13:54:40 -0800238
239 bool onEndElement(const char elem[]) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 --fLevel;
241 if (fNeedToFlush)
242 this->flushAttributes();
243 fNeedToFlush = false;
244
245 SkDOM::Node* parent;
246
247 fParentStack.pop(&parent);
248
249 SkDOM::Node* child = parent->fFirstChild;
halcanary96fcdcc2015-08-27 07:41:13 -0700250 SkDOM::Node* prev = nullptr;
Mike Reedc3063e52017-01-07 16:16:02 -0500251 while (child) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SkDOM::Node* next = child->fNextSibling;
253 child->fNextSibling = prev;
254 prev = child;
255 child = next;
256 }
257 parent->fFirstChild = prev;
258 return false;
259 }
fmalita7a048692015-02-20 13:54:40 -0800260
261 bool onText(const char text[], int len) override {
262 SkString str(text, len);
263 this->startCommon(str.c_str(), SkDOM::kText_Type);
264 this->SkDOMParser::onEndElement(str.c_str());
265
266 return false;
267 }
268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
fmalita7a048692015-02-20 13:54:40 -0800270 void startCommon(const char elem[], SkDOM::Type type) {
Mike Reedc3063e52017-01-07 16:16:02 -0500271 if (fLevel > 0 && fNeedToFlush) {
fmalita7a048692015-02-20 13:54:40 -0800272 this->flushAttributes();
Mike Reedc3063e52017-01-07 16:16:02 -0500273 }
fmalita7a048692015-02-20 13:54:40 -0800274 fNeedToFlush = true;
275 fElemName = dupstr(fAlloc, elem);
276 fElemType = type;
277 ++fLevel;
278 }
279
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 SkTDArray<SkDOM::Node*> fParentStack;
fmalita7a048692015-02-20 13:54:40 -0800281 SkChunkAlloc* fAlloc;
282 SkDOM::Node* fRoot;
283 bool fNeedToFlush;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
285 // state needed for flushAttributes()
286 SkTDArray<SkDOM::Attr> fAttrs;
287 char* fElemName;
fmalita7a048692015-02-20 13:54:40 -0800288 SkDOM::Type fElemType;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 int fLevel;
290};
291
fmalita7445e862016-07-14 19:14:06 -0700292const SkDOM::Node* SkDOM::build(SkStream& docStream) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 SkDOMParser parser(&fAlloc);
fmalita7445e862016-07-14 19:14:06 -0700294 if (!parser.parse(docStream))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 {
296 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());)
halcanary96fcdcc2015-08-27 07:41:13 -0700297 fRoot = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 fAlloc.reset();
halcanary96fcdcc2015-08-27 07:41:13 -0700299 return nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
301 fRoot = parser.getRoot();
302 return fRoot;
303}
304
305///////////////////////////////////////////////////////////////////////////
306
Mike Reedc3063e52017-01-07 16:16:02 -0500307static void walk_dom(const SkDOM& dom, const SkDOM::Node* node, SkXMLParser* parser) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 const char* elem = dom.getName(node);
fmalita7a048692015-02-20 13:54:40 -0800309 if (dom.getType(node) == SkDOM::kText_Type) {
310 SkASSERT(dom.countChildren(node) == 0);
311 parser->text(elem, SkToInt(strlen(elem)));
312 return;
313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314
315 parser->startElement(elem);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 SkDOM::AttrIter iter(dom, node);
318 const char* name;
319 const char* value;
halcanary96fcdcc2015-08-27 07:41:13 -0700320 while ((name = iter.next(&value)) != nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 parser->addAttribute(name, value);
322
halcanary96fcdcc2015-08-27 07:41:13 -0700323 node = dom.getFirstChild(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 while (node)
325 {
326 walk_dom(dom, node, parser);
halcanary96fcdcc2015-08-27 07:41:13 -0700327 node = dom.getNextSibling(node, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 }
329
330 parser->endElement(elem);
331}
332
Mike Reedc3063e52017-01-07 16:16:02 -0500333const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 SkDOMParser parser(&fAlloc);
335
336 walk_dom(dom, node, &parser);
337
338 fRoot = parser.getRoot();
339 return fRoot;
340}
341
fmalita7a048692015-02-20 13:54:40 -0800342SkXMLParser* SkDOM::beginParsing() {
343 SkASSERT(!fParser);
halcanary385fe4d2015-08-26 13:07:48 -0700344 fParser.reset(new SkDOMParser(&fAlloc));
fmalita7a048692015-02-20 13:54:40 -0800345
346 return fParser.get();
347}
348
349const SkDOM::Node* SkDOM::finishParsing() {
350 SkASSERT(fParser);
351 fRoot = fParser->getRoot();
mtklein852f15d2016-03-17 10:51:27 -0700352 fParser.reset();
fmalita7a048692015-02-20 13:54:40 -0800353
354 return fRoot;
355}
356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357//////////////////////////////////////////////////////////////////////////
358
Mike Reedc3063e52017-01-07 16:16:02 -0500359int SkDOM::countChildren(const Node* node, const char elem[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 int count = 0;
361
362 node = this->getFirstChild(node, elem);
Mike Reedc3063e52017-01-07 16:16:02 -0500363 while (node) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 count += 1;
365 node = this->getNextSibling(node, elem);
366 }
367 return count;
368}
369
370//////////////////////////////////////////////////////////////////////////
371
372#include "SkParse.h"
373
Mike Reedc3063e52017-01-07 16:16:02 -0500374bool SkDOM::findS32(const Node* node, const char name[], int32_t* value) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 const char* vstr = this->findAttr(node, name);
376 return vstr && SkParse::FindS32(vstr, value);
377}
378
Mike Reedc3063e52017-01-07 16:16:02 -0500379bool SkDOM::findScalars(const Node* node, const char name[], SkScalar value[], int count) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 const char* vstr = this->findAttr(node, name);
381 return vstr && SkParse::FindScalars(vstr, value, count);
382}
383
Mike Reedc3063e52017-01-07 16:16:02 -0500384bool SkDOM::findHex(const Node* node, const char name[], uint32_t* value) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 const char* vstr = this->findAttr(node, name);
386 return vstr && SkParse::FindHex(vstr, value);
387}
388
Mike Reedc3063e52017-01-07 16:16:02 -0500389bool SkDOM::findBool(const Node* node, const char name[], bool* value) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 const char* vstr = this->findAttr(node, name);
391 return vstr && SkParse::FindBool(vstr, value);
392}
393
Mike Reedc3063e52017-01-07 16:16:02 -0500394int SkDOM::findList(const Node* node, const char name[], const char list[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 const char* vstr = this->findAttr(node, name);
396 return vstr ? SkParse::FindList(vstr, list) : -1;
397}
398
Mike Reedc3063e52017-01-07 16:16:02 -0500399bool SkDOM::hasAttr(const Node* node, const char name[], const char value[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 const char* vstr = this->findAttr(node, name);
401 return vstr && !strcmp(vstr, value);
402}
403
Mike Reedc3063e52017-01-07 16:16:02 -0500404bool SkDOM::hasS32(const Node* node, const char name[], int32_t target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 const char* vstr = this->findAttr(node, name);
406 int32_t value;
407 return vstr && SkParse::FindS32(vstr, &value) && value == target;
408}
409
Mike Reedc3063e52017-01-07 16:16:02 -0500410bool SkDOM::hasScalar(const Node* node, const char name[], SkScalar target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 const char* vstr = this->findAttr(node, name);
412 SkScalar value;
413 return vstr && SkParse::FindScalar(vstr, &value) && value == target;
414}
415
Mike Reedc3063e52017-01-07 16:16:02 -0500416bool SkDOM::hasHex(const Node* node, const char name[], uint32_t target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 const char* vstr = this->findAttr(node, name);
418 uint32_t value;
419 return vstr && SkParse::FindHex(vstr, &value) && value == target;
420}
421
Mike Reedc3063e52017-01-07 16:16:02 -0500422bool SkDOM::hasBool(const Node* node, const char name[], bool target) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 const char* vstr = this->findAttr(node, name);
424 bool value;
425 return vstr && SkParse::FindBool(vstr, &value) && value == target;
426}