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