XMLNode.cpp revision dd931864209eac0b4182d7a0d1ca965fcc3b8c03
1// 2// Copyright 2006 The Android Open Source Project 3// 4// Build resource files from raw assets. 5// 6 7#include "XMLNode.h" 8#include "ResourceTable.h" 9 10#include <host/pseudolocalize.h> 11#include <utils/ByteOrder.h> 12#include <errno.h> 13#include <string.h> 14 15#ifndef HAVE_MS_C_RUNTIME 16#define O_BINARY 0 17#endif 18 19#define NOISY(x) //x 20#define NOISY_PARSE(x) //x 21 22const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/"; 23const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android"; 24const char* const RESOURCES_ROOT_PRV_NAMESPACE = "http://schemas.android.com/apk/prv/res/"; 25 26const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; 27const char* const ALLOWED_XLIFF_ELEMENTS[] = { 28 "bpt", 29 "ept", 30 "it", 31 "ph", 32 "g", 33 "bx", 34 "ex", 35 "x" 36 }; 37 38bool isWhitespace(const char16_t* str) 39{ 40 while (*str != 0 && *str < 128 && isspace(*str)) { 41 str++; 42 } 43 return *str == 0; 44} 45 46static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE); 47static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE); 48 49String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic) 50{ 51 //printf("%s starts with %s?\n", String8(namespaceUri).string(), 52 // String8(RESOURCES_PREFIX).string()); 53 size_t prefixSize; 54 bool isPublic = true; 55 if (namespaceUri.startsWith(RESOURCES_PREFIX)) { 56 prefixSize = RESOURCES_PREFIX.size(); 57 } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) { 58 isPublic = false; 59 prefixSize = RESOURCES_PRV_PREFIX.size(); 60 } else { 61 if (outIsPublic) *outIsPublic = isPublic; // = true 62 return String16(); 63 } 64 65 //printf("YES!\n"); 66 //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string()); 67 if (outIsPublic) *outIsPublic = isPublic; 68 return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize); 69} 70 71status_t parseStyledString(Bundle* bundle, 72 const char* fileName, 73 ResXMLTree* inXml, 74 const String16& endTag, 75 String16* outString, 76 Vector<StringPool::entry_style_span>* outSpans, 77 bool pseudolocalize) 78{ 79 Vector<StringPool::entry_style_span> spanStack; 80 String16 curString; 81 String16 rawString; 82 const char* errorMsg; 83 int xliffDepth = 0; 84 bool firstTime = true; 85 86 size_t len; 87 ResXMLTree::event_code_t code; 88 while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 89 90 if (code == ResXMLTree::TEXT) { 91 String16 text(inXml->getText(&len)); 92 if (firstTime && text.size() > 0) { 93 firstTime = false; 94 if (text.string()[0] == '@') { 95 // If this is a resource reference, don't do the pseudoloc. 96 pseudolocalize = false; 97 } 98 } 99 if (xliffDepth == 0 && pseudolocalize) { 100 std::string orig(String8(text).string()); 101 std::string pseudo = pseudolocalize_string(orig); 102 curString.append(String16(String8(pseudo.c_str()))); 103 } else { 104 curString.append(text); 105 } 106 } else if (code == ResXMLTree::START_TAG) { 107 const String16 element16(inXml->getElementName(&len)); 108 const String8 element8(element16); 109 110 size_t nslen; 111 const uint16_t* ns = inXml->getElementNamespace(&nslen); 112 if (ns == NULL) { 113 ns = (const uint16_t*)"\0\0"; 114 nslen = 0; 115 } 116 const String8 nspace(String16(ns, nslen)); 117 if (nspace == XLIFF_XMLNS) { 118 const int N = sizeof(ALLOWED_XLIFF_ELEMENTS)/sizeof(ALLOWED_XLIFF_ELEMENTS[0]); 119 for (int i=0; i<N; i++) { 120 if (element8 == ALLOWED_XLIFF_ELEMENTS[i]) { 121 xliffDepth++; 122 // in this case, treat it like it was just text, in other words, do nothing 123 // here and silently drop this element 124 goto moveon; 125 } 126 } 127 { 128 SourcePos(String8(fileName), inXml->getLineNumber()).error( 129 "Found unsupported XLIFF tag <%s>\n", 130 element8.string()); 131 return UNKNOWN_ERROR; 132 } 133moveon: 134 continue; 135 } 136 137 if (outSpans == NULL) { 138 SourcePos(String8(fileName), inXml->getLineNumber()).error( 139 "Found style tag <%s> where styles are not allowed\n", element8.string()); 140 return UNKNOWN_ERROR; 141 } 142 143 if (!ResTable::collectString(outString, curString.string(), 144 curString.size(), false, &errorMsg, true)) { 145 SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n", 146 errorMsg, String8(curString).string()); 147 return UNKNOWN_ERROR; 148 } 149 rawString.append(curString); 150 curString = String16(); 151 152 StringPool::entry_style_span span; 153 span.name = element16; 154 for (size_t ai=0; ai<inXml->getAttributeCount(); ai++) { 155 span.name.append(String16(";")); 156 const char16_t* str = inXml->getAttributeName(ai, &len); 157 span.name.append(str, len); 158 span.name.append(String16("=")); 159 str = inXml->getAttributeStringValue(ai, &len); 160 span.name.append(str, len); 161 } 162 //printf("Span: %s\n", String8(span.name).string()); 163 span.span.firstChar = span.span.lastChar = outString->size(); 164 spanStack.push(span); 165 166 } else if (code == ResXMLTree::END_TAG) { 167 size_t nslen; 168 const uint16_t* ns = inXml->getElementNamespace(&nslen); 169 if (ns == NULL) { 170 ns = (const uint16_t*)"\0\0"; 171 nslen = 0; 172 } 173 const String8 nspace(String16(ns, nslen)); 174 if (nspace == XLIFF_XMLNS) { 175 xliffDepth--; 176 continue; 177 } 178 if (!ResTable::collectString(outString, curString.string(), 179 curString.size(), false, &errorMsg, true)) { 180 SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n", 181 errorMsg, String8(curString).string()); 182 return UNKNOWN_ERROR; 183 } 184 rawString.append(curString); 185 curString = String16(); 186 187 if (spanStack.size() == 0) { 188 if (strcmp16(inXml->getElementName(&len), endTag.string()) != 0) { 189 SourcePos(String8(fileName), inXml->getLineNumber()).error( 190 "Found tag %s where <%s> close is expected\n", 191 String8(inXml->getElementName(&len)).string(), 192 String8(endTag).string()); 193 return UNKNOWN_ERROR; 194 } 195 break; 196 } 197 StringPool::entry_style_span span = spanStack.top(); 198 String16 spanTag; 199 ssize_t semi = span.name.findFirst(';'); 200 if (semi >= 0) { 201 spanTag.setTo(span.name.string(), semi); 202 } else { 203 spanTag.setTo(span.name); 204 } 205 if (strcmp16(inXml->getElementName(&len), spanTag.string()) != 0) { 206 SourcePos(String8(fileName), inXml->getLineNumber()).error( 207 "Found close tag %s where close tag %s is expected\n", 208 String8(inXml->getElementName(&len)).string(), 209 String8(spanTag).string()); 210 return UNKNOWN_ERROR; 211 } 212 bool empty = true; 213 if (outString->size() > 0) { 214 span.span.lastChar = outString->size()-1; 215 if (span.span.lastChar >= span.span.firstChar) { 216 empty = false; 217 outSpans->add(span); 218 } 219 } 220 spanStack.pop(); 221 222 if (empty) { 223 fprintf(stderr, "%s:%d: warning: empty '%s' span found in text '%s'\n", 224 fileName, inXml->getLineNumber(), 225 String8(spanTag).string(), String8(*outString).string()); 226 227 } 228 } else if (code == ResXMLTree::START_NAMESPACE) { 229 // nothing 230 } 231 } 232 233 if (code == ResXMLTree::BAD_DOCUMENT) { 234 SourcePos(String8(fileName), inXml->getLineNumber()).error( 235 "Error parsing XML\n"); 236 } 237 238 if (outSpans != NULL && outSpans->size() > 0) { 239 if (curString.size() > 0) { 240 if (!ResTable::collectString(outString, curString.string(), 241 curString.size(), false, &errorMsg, true)) { 242 SourcePos(String8(fileName), inXml->getLineNumber()).error( 243 "%s (in %s)\n", 244 errorMsg, String8(curString).string()); 245 return UNKNOWN_ERROR; 246 } 247 } 248 } else { 249 // There is no style information, so string processing will happen 250 // later as part of the overall type conversion. Return to the 251 // client the raw unprocessed text. 252 rawString.append(curString); 253 outString->setTo(rawString); 254 } 255 256 return NO_ERROR; 257} 258 259struct namespace_entry { 260 String8 prefix; 261 String8 uri; 262}; 263 264static String8 make_prefix(int depth) 265{ 266 String8 prefix; 267 int i; 268 for (i=0; i<depth; i++) { 269 prefix.append(" "); 270 } 271 return prefix; 272} 273 274static String8 build_namespace(const Vector<namespace_entry>& namespaces, 275 const uint16_t* ns) 276{ 277 String8 str; 278 if (ns != NULL) { 279 str = String8(ns); 280 const size_t N = namespaces.size(); 281 for (size_t i=0; i<N; i++) { 282 const namespace_entry& ne = namespaces.itemAt(i); 283 if (ne.uri == str) { 284 str = ne.prefix; 285 break; 286 } 287 } 288 str.append(":"); 289 } 290 return str; 291} 292 293void printXMLBlock(ResXMLTree* block) 294{ 295 block->restart(); 296 297 Vector<namespace_entry> namespaces; 298 299 ResXMLTree::event_code_t code; 300 int depth = 0; 301 while ((code=block->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 302 String8 prefix = make_prefix(depth); 303 int i; 304 if (code == ResXMLTree::START_TAG) { 305 size_t len; 306 const uint16_t* ns16 = block->getElementNamespace(&len); 307 String8 elemNs = build_namespace(namespaces, ns16); 308 const uint16_t* com16 = block->getComment(&len); 309 if (com16) { 310 printf("%s <!-- %s -->\n", prefix.string(), String8(com16).string()); 311 } 312 printf("%sE: %s%s (line=%d)\n", prefix.string(), elemNs.string(), 313 String8(block->getElementName(&len)).string(), 314 block->getLineNumber()); 315 int N = block->getAttributeCount(); 316 depth++; 317 prefix = make_prefix(depth); 318 for (i=0; i<N; i++) { 319 uint32_t res = block->getAttributeNameResID(i); 320 ns16 = block->getAttributeNamespace(i, &len); 321 String8 ns = build_namespace(namespaces, ns16); 322 String8 name(block->getAttributeName(i, &len)); 323 printf("%sA: ", prefix.string()); 324 if (res) { 325 printf("%s%s(0x%08x)", ns.string(), name.string(), res); 326 } else { 327 printf("%s%s", ns.string(), name.string()); 328 } 329 Res_value value; 330 block->getAttributeValue(i, &value); 331 if (value.dataType == Res_value::TYPE_NULL) { 332 printf("=(null)"); 333 } else if (value.dataType == Res_value::TYPE_REFERENCE) { 334 printf("=@0x%x", (int)value.data); 335 } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) { 336 printf("=?0x%x", (int)value.data); 337 } else if (value.dataType == Res_value::TYPE_STRING) { 338 printf("=\"%s\"", 339 String8(block->getAttributeStringValue(i, &len)).string()); 340 } else { 341 printf("=(type 0x%x)0x%x", (int)value.dataType, (int)value.data); 342 } 343 const char16_t* val = block->getAttributeStringValue(i, &len); 344 if (val != NULL) { 345 printf(" (Raw: \"%s\")", String8(val).string()); 346 } 347 printf("\n"); 348 } 349 } else if (code == ResXMLTree::END_TAG) { 350 depth--; 351 } else if (code == ResXMLTree::START_NAMESPACE) { 352 namespace_entry ns; 353 size_t len; 354 const uint16_t* prefix16 = block->getNamespacePrefix(&len); 355 if (prefix16) { 356 ns.prefix = String8(prefix16); 357 } else { 358 ns.prefix = "<DEF>"; 359 } 360 ns.uri = String8(block->getNamespaceUri(&len)); 361 namespaces.push(ns); 362 printf("%sN: %s=%s\n", prefix.string(), ns.prefix.string(), 363 ns.uri.string()); 364 depth++; 365 } else if (code == ResXMLTree::END_NAMESPACE) { 366 depth--; 367 const namespace_entry& ns = namespaces.top(); 368 size_t len; 369 const uint16_t* prefix16 = block->getNamespacePrefix(&len); 370 String8 pr; 371 if (prefix16) { 372 pr = String8(prefix16); 373 } else { 374 pr = "<DEF>"; 375 } 376 if (ns.prefix != pr) { 377 prefix = make_prefix(depth); 378 printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n", 379 prefix.string(), pr.string(), ns.prefix.string()); 380 } 381 String8 uri = String8(block->getNamespaceUri(&len)); 382 if (ns.uri != uri) { 383 prefix = make_prefix(depth); 384 printf("%s *** BAD END NS URI: found=%s, expected=%s\n", 385 prefix.string(), uri.string(), ns.uri.string()); 386 } 387 namespaces.pop(); 388 } else if (code == ResXMLTree::TEXT) { 389 size_t len; 390 printf("%sC: \"%s\"\n", prefix.string(), String8(block->getText(&len)).string()); 391 } 392 } 393 394 block->restart(); 395} 396 397status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree, 398 bool stripAll, bool keepComments, 399 const char** cDataTags) 400{ 401 sp<XMLNode> root = XMLNode::parse(file); 402 if (root == NULL) { 403 return UNKNOWN_ERROR; 404 } 405 root->removeWhitespace(stripAll, cDataTags); 406 407 NOISY(printf("Input XML from %s:\n", (const char*)file->getPrintableSource())); 408 NOISY(root->print()); 409 sp<AaptFile> rsc = new AaptFile(String8(), AaptGroupEntry(), String8()); 410 status_t err = root->flatten(rsc, !keepComments, false); 411 if (err != NO_ERROR) { 412 return err; 413 } 414 err = outTree->setTo(rsc->getData(), rsc->getSize(), true); 415 if (err != NO_ERROR) { 416 return err; 417 } 418 419 NOISY(printf("Output XML:\n")); 420 NOISY(printXMLBlock(outTree)); 421 422 return NO_ERROR; 423} 424 425sp<XMLNode> XMLNode::parse(const sp<AaptFile>& file) 426{ 427 char buf[16384]; 428 int fd = open(file->getSourceFile().string(), O_RDONLY | O_BINARY); 429 if (fd < 0) { 430 SourcePos(file->getSourceFile(), -1).error("Unable to open file for read: %s", 431 strerror(errno)); 432 return NULL; 433 } 434 435 XML_Parser parser = XML_ParserCreateNS(NULL, 1); 436 ParseState state; 437 state.filename = file->getPrintableSource(); 438 state.parser = parser; 439 XML_SetUserData(parser, &state); 440 XML_SetElementHandler(parser, startElement, endElement); 441 XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace); 442 XML_SetCharacterDataHandler(parser, characterData); 443 XML_SetCommentHandler(parser, commentData); 444 445 ssize_t len; 446 bool done; 447 do { 448 len = read(fd, buf, sizeof(buf)); 449 done = len < (ssize_t)sizeof(buf); 450 if (len < 0) { 451 SourcePos(file->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno)); 452 close(fd); 453 return NULL; 454 } 455 if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) { 456 SourcePos(file->getSourceFile(), (int)XML_GetCurrentLineNumber(parser)).error( 457 "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser))); 458 close(fd); 459 return NULL; 460 } 461 } while (!done); 462 463 XML_ParserFree(parser); 464 if (state.root == NULL) { 465 SourcePos(file->getSourceFile(), -1).error("No XML data generated when parsing"); 466 } 467 close(fd); 468 return state.root; 469} 470 471XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace) 472 : mNextAttributeIndex(0x80000000) 473 , mFilename(filename) 474 , mStartLineNumber(0) 475 , mEndLineNumber(0) 476{ 477 if (isNamespace) { 478 mNamespacePrefix = s1; 479 mNamespaceUri = s2; 480 } else { 481 mNamespaceUri = s1; 482 mElementName = s2; 483 } 484} 485 486XMLNode::XMLNode(const String8& filename) 487 : mFilename(filename) 488{ 489 memset(&mCharsValue, 0, sizeof(mCharsValue)); 490} 491 492XMLNode::type XMLNode::getType() const 493{ 494 if (mElementName.size() != 0) { 495 return TYPE_ELEMENT; 496 } 497 if (mNamespaceUri.size() != 0) { 498 return TYPE_NAMESPACE; 499 } 500 return TYPE_CDATA; 501} 502 503const String16& XMLNode::getNamespacePrefix() const 504{ 505 return mNamespacePrefix; 506} 507 508const String16& XMLNode::getNamespaceUri() const 509{ 510 return mNamespaceUri; 511} 512 513const String16& XMLNode::getElementNamespace() const 514{ 515 return mNamespaceUri; 516} 517 518const String16& XMLNode::getElementName() const 519{ 520 return mElementName; 521} 522 523const Vector<sp<XMLNode> >& XMLNode::getChildren() const 524{ 525 return mChildren; 526} 527 528const String8& XMLNode::getFilename() const 529{ 530 return mFilename; 531} 532 533const Vector<XMLNode::attribute_entry>& 534 XMLNode::getAttributes() const 535{ 536 return mAttributes; 537} 538 539const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns, 540 const String16& name) const 541{ 542 for (size_t i=0; i<mAttributes.size(); i++) { 543 const attribute_entry& ae(mAttributes.itemAt(i)); 544 if (ae.ns == ns && ae.name == name) { 545 return &ae; 546 } 547 } 548 549 return NULL; 550} 551 552const String16& XMLNode::getCData() const 553{ 554 return mChars; 555} 556 557const String16& XMLNode::getComment() const 558{ 559 return mComment; 560} 561 562int32_t XMLNode::getStartLineNumber() const 563{ 564 return mStartLineNumber; 565} 566 567int32_t XMLNode::getEndLineNumber() const 568{ 569 return mEndLineNumber; 570} 571 572sp<XMLNode> XMLNode::searchElement(const String16& tagNamespace, const String16& tagName) 573{ 574 if (getType() == XMLNode::TYPE_ELEMENT 575 && mNamespaceUri == tagNamespace 576 && mElementName == tagName) { 577 return this; 578 } 579 580 for (size_t i=0; i<mChildren.size(); i++) { 581 sp<XMLNode> found = mChildren.itemAt(i)->searchElement(tagNamespace, tagName); 582 if (found != NULL) { 583 return found; 584 } 585 } 586 587 return NULL; 588} 589 590sp<XMLNode> XMLNode::getChildElement(const String16& tagNamespace, const String16& tagName) 591{ 592 for (size_t i=0; i<mChildren.size(); i++) { 593 sp<XMLNode> child = mChildren.itemAt(i); 594 if (child->getType() == XMLNode::TYPE_ELEMENT 595 && child->mNamespaceUri == tagNamespace 596 && child->mElementName == tagName) { 597 return child; 598 } 599 } 600 601 return NULL; 602} 603 604status_t XMLNode::addChild(const sp<XMLNode>& child) 605{ 606 if (getType() == TYPE_CDATA) { 607 SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node."); 608 return UNKNOWN_ERROR; 609 } 610 //printf("Adding child %p to parent %p\n", child.get(), this); 611 mChildren.add(child); 612 return NO_ERROR; 613} 614 615status_t XMLNode::insertChildAt(const sp<XMLNode>& child, size_t index) 616{ 617 if (getType() == TYPE_CDATA) { 618 SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node."); 619 return UNKNOWN_ERROR; 620 } 621 //printf("Adding child %p to parent %p\n", child.get(), this); 622 mChildren.insertAt(child, index); 623 return NO_ERROR; 624} 625 626status_t XMLNode::addAttribute(const String16& ns, const String16& name, 627 const String16& value) 628{ 629 if (getType() == TYPE_CDATA) { 630 SourcePos(mFilename, getStartLineNumber()).error("Child to CDATA node."); 631 return UNKNOWN_ERROR; 632 } 633 attribute_entry e; 634 e.index = mNextAttributeIndex++; 635 e.ns = ns; 636 e.name = name; 637 e.string = value; 638 mAttributes.add(e); 639 mAttributeOrder.add(e.index, mAttributes.size()-1); 640 return NO_ERROR; 641} 642 643void XMLNode::setAttributeResID(size_t attrIdx, uint32_t resId) 644{ 645 attribute_entry& e = mAttributes.editItemAt(attrIdx); 646 if (e.nameResId) { 647 mAttributeOrder.removeItem(e.nameResId); 648 } else { 649 mAttributeOrder.removeItem(e.index); 650 } 651 NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n", 652 String8(getElementName()).string(), 653 String8(mAttributes.itemAt(attrIdx).name).string(), 654 String8(mAttributes.itemAt(attrIdx).string).string(), 655 resId)); 656 mAttributes.editItemAt(attrIdx).nameResId = resId; 657 mAttributeOrder.add(resId, attrIdx); 658} 659 660status_t XMLNode::appendChars(const String16& chars) 661{ 662 if (getType() != TYPE_CDATA) { 663 SourcePos(mFilename, getStartLineNumber()).error("Adding characters to element node."); 664 return UNKNOWN_ERROR; 665 } 666 mChars.append(chars); 667 return NO_ERROR; 668} 669 670status_t XMLNode::appendComment(const String16& comment) 671{ 672 if (mComment.size() > 0) { 673 mComment.append(String16("\n")); 674 } 675 mComment.append(comment); 676 return NO_ERROR; 677} 678 679void XMLNode::setStartLineNumber(int32_t line) 680{ 681 mStartLineNumber = line; 682} 683 684void XMLNode::setEndLineNumber(int32_t line) 685{ 686 mEndLineNumber = line; 687} 688 689void XMLNode::removeWhitespace(bool stripAll, const char** cDataTags) 690{ 691 //printf("Removing whitespace in %s\n", String8(mElementName).string()); 692 size_t N = mChildren.size(); 693 if (cDataTags) { 694 String8 tag(mElementName); 695 const char** p = cDataTags; 696 while (*p) { 697 if (tag == *p) { 698 stripAll = false; 699 break; 700 } 701 } 702 } 703 for (size_t i=0; i<N; i++) { 704 sp<XMLNode> node = mChildren.itemAt(i); 705 if (node->getType() == TYPE_CDATA) { 706 // This is a CDATA node... 707 const char16_t* p = node->mChars.string(); 708 while (*p != 0 && *p < 128 && isspace(*p)) { 709 p++; 710 } 711 //printf("Space ends at %d in \"%s\"\n", 712 // (int)(p-node->mChars.string()), 713 // String8(node->mChars).string()); 714 if (*p == 0) { 715 if (stripAll) { 716 // Remove this node! 717 mChildren.removeAt(i); 718 N--; 719 i--; 720 } else { 721 node->mChars = String16(" "); 722 } 723 } else { 724 // Compact leading/trailing whitespace. 725 const char16_t* e = node->mChars.string()+node->mChars.size()-1; 726 while (e > p && *e < 128 && isspace(*e)) { 727 e--; 728 } 729 if (p > node->mChars.string()) { 730 p--; 731 } 732 if (e < (node->mChars.string()+node->mChars.size()-1)) { 733 e++; 734 } 735 if (p > node->mChars.string() || 736 e < (node->mChars.string()+node->mChars.size()-1)) { 737 String16 tmp(p, e-p+1); 738 node->mChars = tmp; 739 } 740 } 741 } else { 742 node->removeWhitespace(stripAll, cDataTags); 743 } 744 } 745} 746 747status_t XMLNode::parseValues(const sp<AaptAssets>& assets, 748 ResourceTable* table) 749{ 750 bool hasErrors = false; 751 752 if (getType() == TYPE_ELEMENT) { 753 const size_t N = mAttributes.size(); 754 String16 defPackage(assets->getPackage()); 755 for (size_t i=0; i<N; i++) { 756 attribute_entry& e = mAttributes.editItemAt(i); 757 AccessorCookie ac(SourcePos(mFilename, getStartLineNumber()), String8(e.name), 758 String8(e.string)); 759 table->setCurrentXmlPos(SourcePos(mFilename, getStartLineNumber())); 760 if (!assets->getIncludedResources() 761 .stringToValue(&e.value, &e.string, 762 e.string.string(), e.string.size(), true, true, 763 e.nameResId, NULL, &defPackage, table, &ac)) { 764 hasErrors = true; 765 } 766 NOISY(printf("Attr %s: type=0x%x, str=%s\n", 767 String8(e.name).string(), e.value.dataType, 768 String8(e.string).string())); 769 } 770 } 771 const size_t N = mChildren.size(); 772 for (size_t i=0; i<N; i++) { 773 status_t err = mChildren.itemAt(i)->parseValues(assets, table); 774 if (err != NO_ERROR) { 775 hasErrors = true; 776 } 777 } 778 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 779} 780 781status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, 782 const ResourceTable* table) 783{ 784 bool hasErrors = false; 785 786 if (getType() == TYPE_ELEMENT) { 787 String16 attr("attr"); 788 const char* errorMsg; 789 const size_t N = mAttributes.size(); 790 for (size_t i=0; i<N; i++) { 791 const attribute_entry& e = mAttributes.itemAt(i); 792 if (e.ns.size() <= 0) continue; 793 bool nsIsPublic; 794 String16 pkg(getNamespaceResourcePackage(e.ns, &nsIsPublic)); 795 NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", 796 String8(getElementName()).string(), 797 String8(e.name).string(), 798 String8(e.string).string(), 799 String8(e.ns).string(), 800 (nsIsPublic) ? "public" : "private", 801 String8(pkg).string())); 802 if (pkg.size() <= 0) continue; 803 uint32_t res = table != NULL 804 ? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic) 805 : assets->getIncludedResources(). 806 identifierForName(e.name.string(), e.name.size(), 807 attr.string(), attr.size(), 808 pkg.string(), pkg.size()); 809 if (res != 0) { 810 NOISY(printf("XML attribute name %s: resid=0x%08x\n", 811 String8(e.name).string(), res)); 812 setAttributeResID(i, res); 813 } else { 814 SourcePos(mFilename, getStartLineNumber()).error( 815 "No resource identifier found for attribute '%s' in package '%s'\n", 816 String8(e.name).string(), String8(pkg).string()); 817 hasErrors = true; 818 } 819 } 820 } 821 const size_t N = mChildren.size(); 822 for (size_t i=0; i<N; i++) { 823 status_t err = mChildren.itemAt(i)->assignResourceIds(assets, table); 824 if (err < NO_ERROR) { 825 hasErrors = true; 826 } 827 } 828 829 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 830} 831 832status_t XMLNode::flatten(const sp<AaptFile>& dest, 833 bool stripComments, bool stripRawValues) const 834{ 835 StringPool strings; 836 Vector<uint32_t> resids; 837 838 // First collect just the strings for attribute names that have a 839 // resource ID assigned to them. This ensures that the resource ID 840 // array is compact, and makes it easier to deal with attribute names 841 // in different namespaces (and thus with different resource IDs). 842 collect_resid_strings(&strings, &resids); 843 844 // Next collect all remainibng strings. 845 collect_strings(&strings, &resids, stripComments, stripRawValues); 846 847#if 0 // No longer compiles 848 NOISY(printf("Found strings:\n"); 849 const size_t N = strings.size(); 850 for (size_t i=0; i<N; i++) { 851 printf("%s\n", String8(strings.entryAt(i).string).string()); 852 } 853 ); 854#endif 855 856 sp<AaptFile> stringPool = strings.createStringBlock(); 857 NOISY(aout << "String pool:" 858 << HexDump(stringPool->getData(), stringPool->getSize()) << endl); 859 860 ResXMLTree_header header; 861 memset(&header, 0, sizeof(header)); 862 header.header.type = htods(RES_XML_TYPE); 863 header.header.headerSize = htods(sizeof(header)); 864 865 const size_t basePos = dest->getSize(); 866 dest->writeData(&header, sizeof(header)); 867 dest->writeData(stringPool->getData(), stringPool->getSize()); 868 869 // If we have resource IDs, write them. 870 if (resids.size() > 0) { 871 const size_t resIdsPos = dest->getSize(); 872 const size_t resIdsSize = 873 sizeof(ResChunk_header)+(sizeof(uint32_t)*resids.size()); 874 ResChunk_header* idsHeader = (ResChunk_header*) 875 (((const uint8_t*)dest->editData(resIdsPos+resIdsSize))+resIdsPos); 876 idsHeader->type = htods(RES_XML_RESOURCE_MAP_TYPE); 877 idsHeader->headerSize = htods(sizeof(*idsHeader)); 878 idsHeader->size = htodl(resIdsSize); 879 uint32_t* ids = (uint32_t*)(idsHeader+1); 880 for (size_t i=0; i<resids.size(); i++) { 881 *ids++ = htodl(resids[i]); 882 } 883 } 884 885 flatten_node(strings, dest, stripComments, stripRawValues); 886 887 void* data = dest->editData(); 888 ResXMLTree_header* hd = (ResXMLTree_header*)(((uint8_t*)data)+basePos); 889 size_t size = dest->getSize()-basePos; 890 hd->header.size = htodl(dest->getSize()-basePos); 891 892 NOISY(aout << "XML resource:" 893 << HexDump(dest->getData(), dest->getSize()) << endl); 894 895 #if PRINT_STRING_METRICS 896 fprintf(stderr, "**** total xml size: %d / %d%% strings (in %s)\n", 897 dest->getSize(), (stringPool->getSize()*100)/dest->getSize(), 898 dest->getPath().string()); 899 #endif 900 901 return NO_ERROR; 902} 903 904void XMLNode::print(int indent) 905{ 906 String8 prefix; 907 int i; 908 for (i=0; i<indent; i++) { 909 prefix.append(" "); 910 } 911 if (getType() == TYPE_ELEMENT) { 912 String8 elemNs(getNamespaceUri()); 913 if (elemNs.size() > 0) { 914 elemNs.append(":"); 915 } 916 printf("%s E: %s%s", prefix.string(), 917 elemNs.string(), String8(getElementName()).string()); 918 int N = mAttributes.size(); 919 for (i=0; i<N; i++) { 920 ssize_t idx = mAttributeOrder.valueAt(i); 921 if (i == 0) { 922 printf(" / "); 923 } else { 924 printf(", "); 925 } 926 const attribute_entry& attr = mAttributes.itemAt(idx); 927 String8 attrNs(attr.ns); 928 if (attrNs.size() > 0) { 929 attrNs.append(":"); 930 } 931 if (attr.nameResId) { 932 printf("%s%s(0x%08x)", attrNs.string(), 933 String8(attr.name).string(), attr.nameResId); 934 } else { 935 printf("%s%s", attrNs.string(), String8(attr.name).string()); 936 } 937 printf("=%s", String8(attr.string).string()); 938 } 939 printf("\n"); 940 } else if (getType() == TYPE_NAMESPACE) { 941 printf("%s N: %s=%s\n", prefix.string(), 942 getNamespacePrefix().size() > 0 943 ? String8(getNamespacePrefix()).string() : "<DEF>", 944 String8(getNamespaceUri()).string()); 945 } else { 946 printf("%s C: \"%s\"\n", prefix.string(), String8(getCData()).string()); 947 } 948 int N = mChildren.size(); 949 for (i=0; i<N; i++) { 950 mChildren.itemAt(i)->print(indent+1); 951 } 952} 953 954static void splitName(const char* name, String16* outNs, String16* outName) 955{ 956 const char* p = name; 957 while (*p != 0 && *p != 1) { 958 p++; 959 } 960 if (*p == 0) { 961 *outNs = String16(); 962 *outName = String16(name); 963 } else { 964 *outNs = String16(name, (p-name)); 965 *outName = String16(p+1); 966 } 967} 968 969void XMLCALL 970XMLNode::startNamespace(void *userData, const char *prefix, const char *uri) 971{ 972 NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix, uri)); 973 ParseState* st = (ParseState*)userData; 974 sp<XMLNode> node = XMLNode::newNamespace(st->filename, 975 String16(prefix != NULL ? prefix : ""), String16(uri)); 976 node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser)); 977 if (st->stack.size() > 0) { 978 st->stack.itemAt(st->stack.size()-1)->addChild(node); 979 } else { 980 st->root = node; 981 } 982 st->stack.push(node); 983} 984 985void XMLCALL 986XMLNode::startElement(void *userData, const char *name, const char **atts) 987{ 988 NOISY_PARSE(printf("Start Element: %s\n", name)); 989 ParseState* st = (ParseState*)userData; 990 String16 ns16, name16; 991 splitName(name, &ns16, &name16); 992 sp<XMLNode> node = XMLNode::newElement(st->filename, ns16, name16); 993 node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser)); 994 if (st->pendingComment.size() > 0) { 995 node->appendComment(st->pendingComment); 996 st->pendingComment = String16(); 997 } 998 if (st->stack.size() > 0) { 999 st->stack.itemAt(st->stack.size()-1)->addChild(node); 1000 } else { 1001 st->root = node; 1002 } 1003 st->stack.push(node); 1004 1005 for (int i = 0; atts[i]; i += 2) { 1006 splitName(atts[i], &ns16, &name16); 1007 node->addAttribute(ns16, name16, String16(atts[i+1])); 1008 } 1009} 1010 1011void XMLCALL 1012XMLNode::characterData(void *userData, const XML_Char *s, int len) 1013{ 1014 NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s, len).string())); 1015 ParseState* st = (ParseState*)userData; 1016 sp<XMLNode> node = NULL; 1017 if (st->stack.size() == 0) { 1018 return; 1019 } 1020 sp<XMLNode> parent = st->stack.itemAt(st->stack.size()-1); 1021 if (parent != NULL && parent->getChildren().size() > 0) { 1022 node = parent->getChildren()[parent->getChildren().size()-1]; 1023 if (node->getType() != TYPE_CDATA) { 1024 // Last node is not CDATA, need to make a new node. 1025 node = NULL; 1026 } 1027 } 1028 1029 if (node == NULL) { 1030 node = XMLNode::newCData(st->filename); 1031 node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser)); 1032 parent->addChild(node); 1033 } 1034 1035 node->appendChars(String16(s, len)); 1036} 1037 1038void XMLCALL 1039XMLNode::endElement(void *userData, const char *name) 1040{ 1041 NOISY_PARSE(printf("End Element: %s\n", name)); 1042 ParseState* st = (ParseState*)userData; 1043 sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1); 1044 node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser)); 1045 if (st->pendingComment.size() > 0) { 1046 node->appendComment(st->pendingComment); 1047 st->pendingComment = String16(); 1048 } 1049 String16 ns16, name16; 1050 splitName(name, &ns16, &name16); 1051 LOG_ALWAYS_FATAL_IF(node->getElementNamespace() != ns16 1052 || node->getElementName() != name16, 1053 "Bad end element %s", name); 1054 st->stack.pop(); 1055} 1056 1057void XMLCALL 1058XMLNode::endNamespace(void *userData, const char *prefix) 1059{ 1060 const char* nonNullPrefix = prefix != NULL ? prefix : ""; 1061 NOISY_PARSE(printf("End Namespace: %s\n", prefix)); 1062 ParseState* st = (ParseState*)userData; 1063 sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1); 1064 node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser)); 1065 LOG_ALWAYS_FATAL_IF(node->getNamespacePrefix() != String16(nonNullPrefix), 1066 "Bad end namespace %s", prefix); 1067 st->stack.pop(); 1068} 1069 1070void XMLCALL 1071XMLNode::commentData(void *userData, const char *comment) 1072{ 1073 NOISY_PARSE(printf("Comment: %s\n", comment)); 1074 ParseState* st = (ParseState*)userData; 1075 if (st->pendingComment.size() > 0) { 1076 st->pendingComment.append(String16("\n")); 1077 } 1078 st->pendingComment.append(String16(comment)); 1079} 1080 1081status_t XMLNode::collect_strings(StringPool* dest, Vector<uint32_t>* outResIds, 1082 bool stripComments, bool stripRawValues) const 1083{ 1084 collect_attr_strings(dest, outResIds, true); 1085 1086 int i; 1087 if (mNamespacePrefix.size() > 0) { 1088 dest->add(mNamespacePrefix, true); 1089 } 1090 if (mNamespaceUri.size() > 0) { 1091 dest->add(mNamespaceUri, true); 1092 } 1093 if (mElementName.size() > 0) { 1094 dest->add(mElementName, true); 1095 } 1096 1097 if (!stripComments && mComment.size() > 0) { 1098 dest->add(mComment, true); 1099 } 1100 1101 const int NA = mAttributes.size(); 1102 1103 for (i=0; i<NA; i++) { 1104 const attribute_entry& ae = mAttributes.itemAt(i); 1105 if (ae.ns.size() > 0) { 1106 dest->add(ae.ns, true); 1107 } 1108 if (!stripRawValues || ae.needStringValue()) { 1109 dest->add(ae.string, true); 1110 } 1111 /* 1112 if (ae.value.dataType == Res_value::TYPE_NULL 1113 || ae.value.dataType == Res_value::TYPE_STRING) { 1114 dest->add(ae.string, true); 1115 } 1116 */ 1117 } 1118 1119 if (mElementName.size() == 0) { 1120 // If not an element, include the CDATA, even if it is empty. 1121 dest->add(mChars, true); 1122 } 1123 1124 const int NC = mChildren.size(); 1125 1126 for (i=0; i<NC; i++) { 1127 mChildren.itemAt(i)->collect_strings(dest, outResIds, 1128 stripComments, stripRawValues); 1129 } 1130 1131 return NO_ERROR; 1132} 1133 1134status_t XMLNode::collect_attr_strings(StringPool* outPool, 1135 Vector<uint32_t>* outResIds, bool allAttrs) const { 1136 const int NA = mAttributes.size(); 1137 1138 for (int i=0; i<NA; i++) { 1139 const attribute_entry& attr = mAttributes.itemAt(i); 1140 uint32_t id = attr.nameResId; 1141 if (id || allAttrs) { 1142 // See if we have already assigned this resource ID to a pooled 1143 // string... 1144 const Vector<size_t>* indices = outPool->offsetsForString(attr.name); 1145 ssize_t idx = -1; 1146 if (indices != NULL) { 1147 const int NJ = indices->size(); 1148 const size_t NR = outResIds->size(); 1149 for (int j=0; j<NJ; j++) { 1150 size_t strIdx = indices->itemAt(j); 1151 if (strIdx >= NR) { 1152 if (id == 0) { 1153 // We don't need to assign a resource ID for this one. 1154 idx = strIdx; 1155 break; 1156 } 1157 // Just ignore strings that are out of range of 1158 // the currently assigned resource IDs... we add 1159 // strings as we assign the first ID. 1160 } else if (outResIds->itemAt(strIdx) == id) { 1161 idx = strIdx; 1162 break; 1163 } 1164 } 1165 } 1166 if (idx < 0) { 1167 idx = outPool->add(attr.name); 1168 NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n", 1169 String8(attr.name).string(), id, idx)); 1170 if (id != 0) { 1171 while ((ssize_t)outResIds->size() <= idx) { 1172 outResIds->add(0); 1173 } 1174 outResIds->replaceAt(id, idx); 1175 } 1176 } 1177 attr.namePoolIdx = idx; 1178 NOISY(printf("String %s offset=0x%08x\n", 1179 String8(attr.name).string(), idx)); 1180 } 1181 } 1182 1183 return NO_ERROR; 1184} 1185 1186status_t XMLNode::collect_resid_strings(StringPool* outPool, 1187 Vector<uint32_t>* outResIds) const 1188{ 1189 collect_attr_strings(outPool, outResIds, false); 1190 1191 const int NC = mChildren.size(); 1192 1193 for (int i=0; i<NC; i++) { 1194 mChildren.itemAt(i)->collect_resid_strings(outPool, outResIds); 1195 } 1196 1197 return NO_ERROR; 1198} 1199 1200status_t XMLNode::flatten_node(const StringPool& strings, const sp<AaptFile>& dest, 1201 bool stripComments, bool stripRawValues) const 1202{ 1203 ResXMLTree_node node; 1204 ResXMLTree_cdataExt cdataExt; 1205 ResXMLTree_namespaceExt namespaceExt; 1206 ResXMLTree_attrExt attrExt; 1207 const void* extData = NULL; 1208 size_t extSize = 0; 1209 ResXMLTree_attribute attr; 1210 1211 const size_t NA = mAttributes.size(); 1212 const size_t NC = mChildren.size(); 1213 size_t i; 1214 1215 LOG_ALWAYS_FATAL_IF(NA != mAttributeOrder.size(), "Attributes messed up!"); 1216 1217 const String16 id16("id"); 1218 const String16 class16("class"); 1219 const String16 style16("style"); 1220 1221 const type type = getType(); 1222 1223 memset(&node, 0, sizeof(node)); 1224 memset(&attr, 0, sizeof(attr)); 1225 node.header.headerSize = htods(sizeof(node)); 1226 node.lineNumber = htodl(getStartLineNumber()); 1227 if (!stripComments) { 1228 node.comment.index = htodl( 1229 mComment.size() > 0 ? strings.offsetForString(mComment) : -1); 1230 //if (mComment.size() > 0) { 1231 // printf("Flattening comment: %s\n", String8(mComment).string()); 1232 //} 1233 } else { 1234 node.comment.index = htodl((uint32_t)-1); 1235 } 1236 if (type == TYPE_ELEMENT) { 1237 node.header.type = htods(RES_XML_START_ELEMENT_TYPE); 1238 extData = &attrExt; 1239 extSize = sizeof(attrExt); 1240 memset(&attrExt, 0, sizeof(attrExt)); 1241 if (mNamespaceUri.size() > 0) { 1242 attrExt.ns.index = htodl(strings.offsetForString(mNamespaceUri)); 1243 } else { 1244 attrExt.ns.index = htodl((uint32_t)-1); 1245 } 1246 attrExt.name.index = htodl(strings.offsetForString(mElementName)); 1247 attrExt.attributeStart = htods(sizeof(attrExt)); 1248 attrExt.attributeSize = htods(sizeof(attr)); 1249 attrExt.attributeCount = htods(NA); 1250 attrExt.idIndex = htods(0); 1251 attrExt.classIndex = htods(0); 1252 attrExt.styleIndex = htods(0); 1253 for (i=0; i<NA; i++) { 1254 ssize_t idx = mAttributeOrder.valueAt(i); 1255 const attribute_entry& ae = mAttributes.itemAt(idx); 1256 if (ae.ns.size() == 0) { 1257 if (ae.name == id16) { 1258 attrExt.idIndex = htods(i+1); 1259 } else if (ae.name == class16) { 1260 attrExt.classIndex = htods(i+1); 1261 } else if (ae.name == style16) { 1262 attrExt.styleIndex = htods(i+1); 1263 } 1264 } 1265 } 1266 } else if (type == TYPE_NAMESPACE) { 1267 node.header.type = htods(RES_XML_START_NAMESPACE_TYPE); 1268 extData = &namespaceExt; 1269 extSize = sizeof(namespaceExt); 1270 memset(&namespaceExt, 0, sizeof(namespaceExt)); 1271 if (mNamespacePrefix.size() > 0) { 1272 namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix)); 1273 } else { 1274 namespaceExt.prefix.index = htodl((uint32_t)-1); 1275 } 1276 namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix)); 1277 namespaceExt.uri.index = htodl(strings.offsetForString(mNamespaceUri)); 1278 LOG_ALWAYS_FATAL_IF(NA != 0, "Namespace nodes can't have attributes!"); 1279 } else if (type == TYPE_CDATA) { 1280 node.header.type = htods(RES_XML_CDATA_TYPE); 1281 extData = &cdataExt; 1282 extSize = sizeof(cdataExt); 1283 memset(&cdataExt, 0, sizeof(cdataExt)); 1284 cdataExt.data.index = htodl(strings.offsetForString(mChars)); 1285 cdataExt.typedData.size = htods(sizeof(cdataExt.typedData)); 1286 cdataExt.typedData.res0 = 0; 1287 cdataExt.typedData.dataType = mCharsValue.dataType; 1288 cdataExt.typedData.data = htodl(mCharsValue.data); 1289 LOG_ALWAYS_FATAL_IF(NA != 0, "CDATA nodes can't have attributes!"); 1290 } 1291 1292 node.header.size = htodl(sizeof(node) + extSize + (sizeof(attr)*NA)); 1293 1294 dest->writeData(&node, sizeof(node)); 1295 if (extSize > 0) { 1296 dest->writeData(extData, extSize); 1297 } 1298 1299 for (i=0; i<NA; i++) { 1300 ssize_t idx = mAttributeOrder.valueAt(i); 1301 const attribute_entry& ae = mAttributes.itemAt(idx); 1302 if (ae.ns.size() > 0) { 1303 attr.ns.index = htodl(strings.offsetForString(ae.ns)); 1304 } else { 1305 attr.ns.index = htodl((uint32_t)-1); 1306 } 1307 attr.name.index = htodl(ae.namePoolIdx); 1308 1309 if (!stripRawValues || ae.needStringValue()) { 1310 attr.rawValue.index = htodl(strings.offsetForString(ae.string)); 1311 } else { 1312 attr.rawValue.index = htodl((uint32_t)-1); 1313 } 1314 attr.typedValue.size = htods(sizeof(attr.typedValue)); 1315 if (ae.value.dataType == Res_value::TYPE_NULL 1316 || ae.value.dataType == Res_value::TYPE_STRING) { 1317 attr.typedValue.res0 = 0; 1318 attr.typedValue.dataType = Res_value::TYPE_STRING; 1319 attr.typedValue.data = htodl(strings.offsetForString(ae.string)); 1320 } else { 1321 attr.typedValue.res0 = 0; 1322 attr.typedValue.dataType = ae.value.dataType; 1323 attr.typedValue.data = htodl(ae.value.data); 1324 } 1325 dest->writeData(&attr, sizeof(attr)); 1326 } 1327 1328 for (i=0; i<NC; i++) { 1329 status_t err = mChildren.itemAt(i)->flatten_node(strings, dest, 1330 stripComments, stripRawValues); 1331 if (err != NO_ERROR) { 1332 return err; 1333 } 1334 } 1335 1336 if (type == TYPE_ELEMENT) { 1337 ResXMLTree_endElementExt endElementExt; 1338 memset(&endElementExt, 0, sizeof(endElementExt)); 1339 node.header.type = htods(RES_XML_END_ELEMENT_TYPE); 1340 node.header.size = htodl(sizeof(node)+sizeof(endElementExt)); 1341 node.lineNumber = htodl(getEndLineNumber()); 1342 node.comment.index = htodl((uint32_t)-1); 1343 endElementExt.ns.index = attrExt.ns.index; 1344 endElementExt.name.index = attrExt.name.index; 1345 dest->writeData(&node, sizeof(node)); 1346 dest->writeData(&endElementExt, sizeof(endElementExt)); 1347 } else if (type == TYPE_NAMESPACE) { 1348 node.header.type = htods(RES_XML_END_NAMESPACE_TYPE); 1349 node.lineNumber = htodl(getEndLineNumber()); 1350 node.comment.index = htodl((uint32_t)-1); 1351 node.header.size = htodl(sizeof(node)+extSize); 1352 dest->writeData(&node, sizeof(node)); 1353 dest->writeData(extData, extSize); 1354 } 1355 1356 return NO_ERROR; 1357} 1358