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