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