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" 17 bool 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)) != nullptr) 28 if (this->addAttribute(name, value)) 29 return false; 30 31 if ((node = dom.getFirstChild(node)) != nullptr) 32 do { 33 if (!this->parse(dom, node)) 34 return false; 35 } while ((node = dom.getNextSibling(node)) != nullptr); 36 37 return !this->endElement(elemName); 38 } 39 40 ///////////////////////////////////////////////////////////////////////// 41 42 struct SkDOMAttr { 43 const char* fName; 44 const char* fValue; 45 }; 46 47 struct 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 69 SkDOM::SkDOM() : fAlloc(kMinChunkSize), fRoot(nullptr) 70 { 71 } 72 73 SkDOM::~SkDOM() 74 { 75 } 76 77 const SkDOM::Node* SkDOM::getRootNode() const 78 { 79 return fRoot; 80 } 81 82 const 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 != nullptr; child = child->fNextSibling) 90 if (!strcmp(name, child->fName)) 91 break; 92 } 93 return child; 94 } 95 96 const 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 != nullptr; sibling = sibling->fNextSibling) 103 if (!strcmp(name, sibling->fName)) 104 break; 105 } 106 return sibling; 107 } 108 109 SkDOM::Type SkDOM::getType(const Node* node) const 110 { 111 SkASSERT(node); 112 return (Type)node->fType; 113 } 114 115 const char* SkDOM::getName(const Node* node) const 116 { 117 SkASSERT(node); 118 return node->fName; 119 } 120 121 const 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 nullptr; 134 } 135 136 ///////////////////////////////////////////////////////////////////////////////////// 137 138 const SkDOM::Attr* SkDOM::getFirstAttr(const Node* node) const 139 { 140 return node->fAttrCount ? node->attrs() : nullptr; 141 } 142 143 const SkDOM::Attr* SkDOM::getNextAttr(const Node* node, const Attr* attr) const 144 { 145 SkASSERT(node); 146 if (attr == nullptr) 147 return nullptr; 148 return (attr - node->attrs() + 1) < node->fAttrCount ? attr + 1 : nullptr; 149 } 150 151 const char* SkDOM::getAttrName(const Node* node, const Attr* attr) const 152 { 153 SkASSERT(node); 154 SkASSERT(attr); 155 return attr->fName; 156 } 157 158 const 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 167 SkDOM::AttrIter::AttrIter(const SkDOM&, const SkDOM::Node* node) 168 { 169 SkASSERT(node); 170 fAttr = node->attrs(); 171 fStop = fAttr + node->fAttrCount; 172 } 173 174 const char* SkDOM::AttrIter::next(const char** value) 175 { 176 const char* name = nullptr; 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 193 static 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 202 class SkDOMParser : public SkXMLParser { 203 public: 204 SkDOMParser(SkChunkAlloc* chunk) : SkXMLParser(&fParserError), fAlloc(chunk) 205 { 206 fAlloc->reset(); 207 fRoot = nullptr; 208 fLevel = 0; 209 fNeedToFlush = true; 210 } 211 SkDOM::Node* getRoot() const { return fRoot; } 212 SkXMLParserError fParserError; 213 214 protected: 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 = nullptr; 226 node->fAttrCount = SkToU16(attrCount); 227 node->fType = fElemType; 228 229 if (fRoot == nullptr) 230 { 231 node->fNextSibling = nullptr; 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 sk_careful_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 = nullptr; 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 291 private: 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 314 const 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 = nullptr; 321 fAlloc.reset(); 322 return nullptr; 323 } 324 fRoot = parser.getRoot(); 325 return fRoot; 326 } 327 328 /////////////////////////////////////////////////////////////////////////// 329 330 static 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)) != nullptr) 345 parser->addAttribute(name, value); 346 347 node = dom.getFirstChild(node, nullptr); 348 while (node) 349 { 350 walk_dom(dom, node, parser); 351 node = dom.getNextSibling(node, nullptr); 352 } 353 354 parser->endElement(elem); 355 } 356 357 const 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 367 SkXMLParser* SkDOM::beginParsing() { 368 SkASSERT(!fParser); 369 fParser.reset(new SkDOMParser(&fAlloc)); 370 371 return fParser.get(); 372 } 373 374 const SkDOM::Node* SkDOM::finishParsing() { 375 SkASSERT(fParser); 376 fRoot = fParser->getRoot(); 377 fParser.free(); 378 379 return fRoot; 380 } 381 382 ////////////////////////////////////////////////////////////////////////// 383 384 int 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 401 bool 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 407 bool 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 413 bool 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 419 bool 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 425 int 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 431 bool 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 437 bool 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 444 bool 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 451 bool 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 458 bool 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 469 void SkDOM::dump(const Node* node, int level) const 470 { 471 if (node == nullptr) 472 node = this->getRootNode(); 473 474 SkDebugWStream debugStream; 475 SkXMLStreamWriter xmlWriter(&debugStream); 476 xmlWriter.writeDOM(*this, node, false); 477 } 478 479 void 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() == nullptr); 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 == nullptr); 507 508 SkASSERT(dom.getFirstChild(root, "elem1")); 509 SkASSERT(!dom.getFirstChild(root, "subelem1")); 510 511 dom.dump(); 512 #endif 513 } 514 515 #endif 516