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