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