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#include "SkStream.h" 12#include "SkXMLWriter.h" 13 14///////////////////////////////////////////////////////////////////////// 15 16#include "SkXMLParser.h" 17bool 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; 23 24 SkDOM::AttrIter iter(dom, node); 25 const char* name, *value; 26 27 while ((name = iter.next(&value)) != NULL) 28 if (this->addAttribute(name, value)) 29 return false; 30 31 if ((node = dom.getFirstChild(node)) != NULL) 32 do { 33 if (!this->parse(dom, node)) 34 return false; 35 } while ((node = dom.getNextSibling(node)) != NULL); 36 37 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 69SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(NULL) 70{ 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 { 89 for (; child != NULL; child = child->fNextSibling) 90 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 { 102 for (; sibling != NULL; sibling = sibling->fNextSibling) 103 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 } 133 return NULL; 134} 135 136///////////////////////////////////////////////////////////////////////////////////// 137 138const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const 139{ 140 return node->fAttrCount ? node->attrs() : NULL; 141} 142 143const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const 144{ 145 SkASSERT(node); 146 if (attr == NULL) 147 return NULL; 148 return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : NULL; 149} 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{ 176 const char* name = NULL; 177 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 { 203public: 204 SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) 205 { 206 fAlloc->reset(); 207 fRoot = NULL; 208 fLevel = 0; 209 fNeedToFlush = true; 210 } 211 SkDOM::Node* getRoot() const { return fRoot; } 212 SkXMLParserError fParserError; 213 214protected: 215 void flushAttributes() 216 { 217 SkASSERT(fLevel > 0); 218 219 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; 225 node->fFirstChild = NULL; 226 node->fAttrCount = SkToU16(attrCount); 227 node->fType = fElemType; 228 229 if (fRoot == NULL) 230 { 231 node->fNextSibling = NULL; 232 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 243 memcpy(node->attrs(), fAttrs.begin(), attrCount * sizeof(SkDOM::Attr)); 244 fAttrs.reset(); 245 246 } 247 248 bool onStartElement(const char elem[]) override { 249 this->startCommon(elem, SkDOM::kElement_Type); 250 return false; 251 } 252 253 bool onAddAttribute(const char name[], const char value[]) override { 254 SkDOM::Attr* attr = fAttrs.append(); 255 attr->fName = dupstr(fAlloc, name); 256 attr->fValue = dupstr(fAlloc, value); 257 return false; 258 } 259 260 bool onEndElement(const char elem[]) override { 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 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 291private: 292 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 302 SkTDArray<SkDOM::Node*> fParentStack; 303 SkChunkAlloc* fAlloc; 304 SkDOM::Node* fRoot; 305 bool fNeedToFlush; 306 307 // state needed for flushAttributes() 308 SkTDArray<SkDOM::Attr> fAttrs; 309 char* fElemName; 310 SkDOM::Type fElemType; 311 int fLevel; 312}; 313 314const SkDOM::Node* SkDOM::build(const char doc[], size_t len) 315{ 316 SkDOMParser parser(&fAlloc); 317 if (!parser.parse(doc, len)) 318 { 319 SkDEBUGCODE(SkDebugf("xml parse error, line %d\n", parser.fParserError.getLineNumber());) 320 fRoot = NULL; 321 fAlloc.reset(); 322 return NULL; 323 } 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); 333 if (dom.getType(node) == SkDOM::kText_Type) { 334 SkASSERT(dom.countChildren(node) == 0); 335 parser->text(elem, SkToInt(strlen(elem))); 336 return; 337 } 338 339 parser->startElement(elem); 340 341 SkDOM::AttrIter iter(dom, node); 342 const char* name; 343 const char* value; 344 while ((name = iter.next(&value)) != NULL) 345 parser->addAttribute(name, value); 346 347 node = dom.getFirstChild(node, NULL); 348 while (node) 349 { 350 walk_dom(dom, node, parser); 351 node = dom.getNextSibling(node, NULL); 352 } 353 354 parser->endElement(elem); 355} 356 357const SkDOM::Node* SkDOM::copy(const SkDOM& dom, const SkDOM::Node* node) 358{ 359 SkDOMParser parser(&fAlloc); 360 361 walk_dom(dom, node, &parser); 362 363 fRoot = parser.getRoot(); 364 return fRoot; 365} 366 367SkXMLParser* SkDOM::beginParsing() { 368 SkASSERT(!fParser); 369 fParser.reset(SkNEW_ARGS(SkDOMParser, (&fAlloc))); 370 371 return fParser.get(); 372} 373 374const SkDOM::Node* SkDOM::finishParsing() { 375 SkASSERT(fParser); 376 fRoot = fParser->getRoot(); 377 fParser.free(); 378 379 return fRoot; 380} 381 382////////////////////////////////////////////////////////////////////////// 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 469void SkDOM::dump(const Node* node, int level) const 470{ 471 if (node == NULL) 472 node = this->getRootNode(); 473 474 SkDebugWStream debugStream; 475 SkXMLStreamWriter xmlWriter(&debugStream); 476 xmlWriter.writeDOM(*this, node, false); 477} 478 479void SkDOM::UnitTest() 480{ 481#ifdef SK_SUPPORT_UNITTEST 482 static const char gDoc[] = 483 "<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 496 SkASSERT(dom.getRootNode() == NULL); 497 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"); 506 SkASSERT(v == NULL); 507 508 SkASSERT(dom.getFirstChild(root, "elem1")); 509 SkASSERT(!dom.getFirstChild(root, "subelem1")); 510 511 dom.dump(); 512#endif 513} 514 515#endif 516