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