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