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