2 // Copyright 2006 The Android Open Source Project 
   4 // Build resource files from raw assets. 
   8 #include "ResourceTable.h" 
  10 #include <host/pseudolocalize.h> 
  11 #include <utils/ByteOrder.h> 
  15 #ifndef HAVE_MS_C_RUNTIME 
  20 #define NOISY_PARSE(x) //x 
  22 const char* const RESOURCES_ROOT_NAMESPACE 
= "http://schemas.android.com/apk/res/"; 
  23 const char* const RESOURCES_ANDROID_NAMESPACE 
= "http://schemas.android.com/apk/res/android"; 
  24 const char* const RESOURCES_ROOT_PRV_NAMESPACE 
= "http://schemas.android.com/apk/prv/res/"; 
  26 const char* const XLIFF_XMLNS 
= "urn:oasis:names:tc:xliff:document:1.2"; 
  27 const char* const ALLOWED_XLIFF_ELEMENTS
[] = { 
  38 bool isWhitespace(const char16_t* str
) 
  40     while (*str 
!= 0 && *str 
< 128 && isspace(*str
)) { 
  46 static const String16 
RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE
); 
  47 static const String16 
RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE
); 
  49 String16 
getNamespaceResourcePackage(String16 namespaceUri
, bool* outIsPublic
) 
  51     //printf("%s starts with %s?\n", String8(namespaceUri).string(), 
  52     //       String8(RESOURCES_PREFIX).string()); 
  55     if (namespaceUri
.startsWith(RESOURCES_PREFIX
)) { 
  56         prefixSize 
= RESOURCES_PREFIX
.size(); 
  57     } else if (namespaceUri
.startsWith(RESOURCES_PRV_PREFIX
)) { 
  59         prefixSize 
= RESOURCES_PRV_PREFIX
.size(); 
  61         if (outIsPublic
) *outIsPublic 
= isPublic
; // = true 
  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
); 
  71 status_t 
hasSubstitutionErrors(const char* fileName
, 
  75     const char16_t* str 
= str16
.string(); 
  76     const char16_t* p 
= str
; 
  77     const char16_t* end 
= str 
+ str16
.size(); 
  79     bool nonpositional 
= false; 
  84          * Look for the start of a Java-style substitution sequence. 
  86         if (*p 
== '%' && p 
+ 1 < end
) { 
  89             // A literal percent sign represented by %% 
  97             if (*p 
>= '0' && *p 
<= '9') { 
 100                 } while (*p 
>= '0' && *p 
<= '9'); 
 102                     // This must be a size specification instead of position. 
 103                     nonpositional 
= true; 
 105             } else if (*p 
== '<') { 
 106                 // Reusing last argument; bad idea since it can be re-arranged. 
 107                 nonpositional 
= true; 
 110                 // Optionally '$' can be specified at the end. 
 111                 if (p 
< end 
&& *p 
== '$') { 
 115                 nonpositional 
= true; 
 118             // Ignore flags and widths 
 119             while (p 
< end 
&& (*p 
== '-' || 
 125                     (*p 
>= '0' && *p 
<= '9'))) { 
 130              * This is a shortcut to detect strings that are going to Time.format() 
 131              * instead of String.format() 
 133              * Comparison of String.format() and Time.format() args: 
 135              * String: ABC E GH  ST X abcdefgh  nost x 
 136              *   Time:    DEFGHKMS W Za  d   hkm  s w yz 
 138              * Therefore we know it's definitely Time if we have: 
 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. 
 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=\"false\" attribute?\n"); 
 170         return NOT_ENOUGH_DATA
; 
 176 status_t 
parseStyledString(Bundle
* bundle
, 
 177                            const char* fileName
, 
 179                            const String16
& endTag
, 
 181                            Vector
<StringPool::entry_style_span
>* outSpans
, 
 185     Vector
<StringPool::entry_style_span
> spanStack
; 
 188     const char* errorMsg
; 
 190     bool firstTime 
= true; 
 193     ResXMLTree::event_code_t code
; 
 194     while ((code
=inXml
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 196         if (code 
== ResXMLTree::TEXT
) { 
 197             String16 
text(inXml
->getText(&len
)); 
 198             if (firstTime 
&& text
.size() > 0) { 
 200                 if (text
.string()[0] == '@') { 
 201                     // If this is a resource reference, don't do the pseudoloc. 
 202                     pseudolocalize 
= false; 
 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()))); 
 214                 if (isFormatted 
&& hasSubstitutionErrors(fileName
, inXml
, text
) != NO_ERROR
) { 
 215                     return UNKNOWN_ERROR
; 
 217                     curString
.append(text
); 
 220         } else if (code 
== ResXMLTree::START_TAG
) { 
 221             const String16 
element16(inXml
->getElementName(&len
)); 
 222             const String8 
element8(element16
); 
 225             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 227                 ns 
= (const uint16_t*)"\0\0"; 
 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
]) { 
 236                         // in this case, treat it like it was just text, in other words, do nothing 
 237                         // here and silently drop this element 
 242                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 243                             "Found unsupported XLIFF tag <%s>\n", 
 245                     return UNKNOWN_ERROR
; 
 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
; 
 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
; 
 263             rawString
.append(curString
); 
 264             curString 
= String16(); 
 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
); 
 276             //printf("Span: %s\n", String8(span.name).string()); 
 277             span
.span
.firstChar 
= span
.span
.lastChar 
= outString
->size(); 
 278             spanStack
.push(span
); 
 280         } else if (code 
== ResXMLTree::END_TAG
) { 
 282             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 284                 ns 
= (const uint16_t*)"\0\0"; 
 287             const String8 
nspace(String16(ns
, nslen
)); 
 288             if (nspace 
== XLIFF_XMLNS
) { 
 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
; 
 298             rawString
.append(curString
); 
 299             curString 
= String16(); 
 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
; 
 311             StringPool::entry_style_span span 
= spanStack
.top(); 
 313             ssize_t semi 
= span
.name
.findFirst(';'); 
 315                 spanTag
.setTo(span
.name
.string(), semi
); 
 317                 spanTag
.setTo(span
.name
); 
 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
; 
 327             if (outString
->size() > 0) { 
 328                 span
.span
.lastChar 
= outString
->size()-1; 
 329                 if (span
.span
.lastChar 
>= span
.span
.firstChar
) { 
 337              * This warning seems to be just an irritation to most people, 
 338              * since it is typically introduced by translators who then never 
 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()); 
 347         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 352     if (code 
== ResXMLTree::BAD_DOCUMENT
) { 
 353             SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 354                     "Error parsing XML\n"); 
 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( 
 363                         errorMsg
, String8(curString
).string()); 
 364                 return UNKNOWN_ERROR
; 
 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
); 
 378 struct namespace_entry 
{ 
 383 static String8 
make_prefix(int depth
) 
 387     for (i
=0; i
<depth
; i
++) { 
 393 static String8 
build_namespace(const Vector
<namespace_entry
>& namespaces
, 
 399         const size_t N 
= namespaces
.size(); 
 400         for (size_t i
=0; i
<N
; i
++) { 
 401             const namespace_entry
& ne 
= namespaces
.itemAt(i
); 
 412 void printXMLBlock(ResXMLTree
* block
) 
 416     Vector
<namespace_entry
> namespaces
; 
 418     ResXMLTree::event_code_t code
; 
 420     while ((code
=block
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 421         String8 prefix 
= make_prefix(depth
); 
 423         if (code 
== ResXMLTree::START_TAG
) { 
 425             const uint16_t* ns16 
= block
->getElementNamespace(&len
); 
 426             String8 elemNs 
= build_namespace(namespaces
, ns16
); 
 427             const uint16_t* com16 
= block
->getComment(&len
); 
 429                 printf("%s <!-- %s -->\n", prefix
.string(), String8(com16
).string()); 
 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(); 
 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()); 
 444                     printf("%s%s(0x%08x)", ns
.string(), name
.string(), res
); 
 446                     printf("%s%s", ns
.string(), name
.string()); 
 449                 block
->getAttributeValue(i
, &value
); 
 450                 if (value
.dataType 
== Res_value::TYPE_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
) { 
 458                            String8(block
->getAttributeStringValue(i
, &len
)).string()); 
 460                     printf("=(type 0x%x)0x%x", (int)value
.dataType
, (int)value
.data
); 
 462                 const char16_t* val 
= block
->getAttributeStringValue(i
, &len
); 
 464                     printf(" (Raw: \"%s\")", String8(val
).string()); 
 468         } else if (code 
== ResXMLTree::END_TAG
) { 
 470         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 473             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 475                 ns
.prefix 
= String8(prefix16
); 
 479             ns
.uri 
= String8(block
->getNamespaceUri(&len
)); 
 481             printf("%sN: %s=%s\n", prefix
.string(), ns
.prefix
.string(), 
 484         } else if (code 
== ResXMLTree::END_NAMESPACE
) { 
 486             const namespace_entry
& ns 
= namespaces
.top(); 
 488             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 491                 pr 
= String8(prefix16
); 
 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()); 
 500             String8 uri 
= String8(block
->getNamespaceUri(&len
)); 
 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()); 
 507         } else if (code 
== ResXMLTree::TEXT
) { 
 509             printf("%sC: \"%s\"\n", prefix
.string(), String8(block
->getText(&len
)).string()); 
 516 status_t 
parseXMLResource(const sp
<AaptFile
>& file
, ResXMLTree
* outTree
, 
 517                           bool stripAll
, bool keepComments
, 
 518                           const char** cDataTags
) 
 520     sp
<XMLNode
> root 
= XMLNode::parse(file
); 
 522         return UNKNOWN_ERROR
; 
 524     root
->removeWhitespace(stripAll
, cDataTags
); 
 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
) { 
 533     err 
= outTree
->setTo(rsc
->getData(), rsc
->getSize(), true); 
 534     if (err 
!= NO_ERROR
) { 
 538     NOISY(printf("Output XML:\n")); 
 539     NOISY(printXMLBlock(outTree
)); 
 544 sp
<XMLNode
> XMLNode::parse(const sp
<AaptFile
>& file
) 
 547     int fd 
= open(file
->getSourceFile().string(), O_RDONLY 
| O_BINARY
); 
 549         SourcePos(file
->getSourceFile(), -1).error("Unable to open file for read: %s", 
 554     XML_Parser parser 
= XML_ParserCreateNS(NULL
, 1); 
 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
); 
 567         len 
= read(fd
, buf
, sizeof(buf
)); 
 568         done 
= len 
< (ssize_t
)sizeof(buf
); 
 570             SourcePos(file
->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno
)); 
 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
))); 
 582     XML_ParserFree(parser
); 
 583     if (state
.root 
== NULL
) { 
 584         SourcePos(file
->getSourceFile(), -1).error("No XML data generated when parsing"); 
 590 XMLNode::XMLNode(const String8
& filename
, const String16
& s1
, const String16
& s2
, bool isNamespace
) 
 591     : mNextAttributeIndex(0x80000000) 
 592     , mFilename(filename
) 
 593     , mStartLineNumber(0) 
 598         mNamespacePrefix 
= s1
; 
 606 XMLNode::XMLNode(const String8
& filename
) 
 607     : mFilename(filename
) 
 609     memset(&mCharsValue
, 0, sizeof(mCharsValue
)); 
 612 XMLNode::type 
XMLNode::getType() const 
 614     if (mElementName
.size() != 0) { 
 617     if (mNamespaceUri
.size() != 0) { 
 618         return TYPE_NAMESPACE
; 
 623 const String16
& XMLNode::getNamespacePrefix() const 
 625     return mNamespacePrefix
; 
 628 const String16
& XMLNode::getNamespaceUri() const 
 630     return mNamespaceUri
; 
 633 const String16
& XMLNode::getElementNamespace() const 
 635     return mNamespaceUri
; 
 638 const String16
& XMLNode::getElementName() const 
 643 const Vector
<sp
<XMLNode
> >& XMLNode::getChildren() const 
 648 const String8
& XMLNode::getFilename() const 
 653 const Vector
<XMLNode::attribute_entry
>& 
 654     XMLNode::getAttributes() const 
 659 const XMLNode::attribute_entry
* XMLNode::getAttribute(const String16
& ns
, 
 660         const String16
& name
) const 
 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
) { 
 672 XMLNode::attribute_entry
* XMLNode::editAttribute(const String16
& ns
, 
 673         const String16
& name
) 
 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
) { 
 685 const String16
& XMLNode::getCData() const 
 690 const String16
& XMLNode::getComment() const 
 695 int32_t XMLNode::getStartLineNumber() const 
 697     return mStartLineNumber
; 
 700 int32_t XMLNode::getEndLineNumber() const 
 702     return mEndLineNumber
; 
 705 sp
<XMLNode
> XMLNode::searchElement(const String16
& tagNamespace
, const String16
& tagName
) 
 707     if (getType() == XMLNode::TYPE_ELEMENT
 
 708             && mNamespaceUri 
== tagNamespace
 
 709             && mElementName 
== tagName
) { 
 713     for (size_t i
=0; i
<mChildren
.size(); i
++) { 
 714         sp
<XMLNode
> found 
= mChildren
.itemAt(i
)->searchElement(tagNamespace
, tagName
); 
 723 sp
<XMLNode
> XMLNode::getChildElement(const String16
& tagNamespace
, const String16
& tagName
) 
 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
) { 
 737 status_t 
XMLNode::addChild(const sp
<XMLNode
>& child
) 
 739     if (getType() == TYPE_CDATA
) { 
 740         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 741         return UNKNOWN_ERROR
; 
 743     //printf("Adding child %p to parent %p\n", child.get(), this); 
 744     mChildren
.add(child
); 
 748 status_t 
XMLNode::insertChildAt(const sp
<XMLNode
>& child
, size_t index
) 
 750     if (getType() == TYPE_CDATA
) { 
 751         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 752         return UNKNOWN_ERROR
; 
 754     //printf("Adding child %p to parent %p\n", child.get(), this); 
 755     mChildren
.insertAt(child
, index
); 
 759 status_t 
XMLNode::addAttribute(const String16
& ns
, const String16
& name
, 
 760                                const String16
& value
) 
 762     if (getType() == TYPE_CDATA
) { 
 763         SourcePos(mFilename
, getStartLineNumber()).error("Child to CDATA node."); 
 764         return UNKNOWN_ERROR
; 
 767     e
.index 
= mNextAttributeIndex
++; 
 772     mAttributeOrder
.add(e
.index
, mAttributes
.size()-1); 
 776 void XMLNode::setAttributeResID(size_t attrIdx
, uint32_t resId
) 
 778     attribute_entry
& e 
= mAttributes
.editItemAt(attrIdx
); 
 780         mAttributeOrder
.removeItem(e
.nameResId
); 
 782         mAttributeOrder
.removeItem(e
.index
); 
 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(), 
 789     mAttributes
.editItemAt(attrIdx
).nameResId 
= resId
; 
 790     mAttributeOrder
.add(resId
, attrIdx
); 
 793 status_t 
XMLNode::appendChars(const String16
& chars
) 
 795     if (getType() != TYPE_CDATA
) { 
 796         SourcePos(mFilename
, getStartLineNumber()).error("Adding characters to element node."); 
 797         return UNKNOWN_ERROR
; 
 799     mChars
.append(chars
); 
 803 status_t 
XMLNode::appendComment(const String16
& comment
) 
 805     if (mComment
.size() > 0) { 
 806         mComment
.append(String16("\n")); 
 808     mComment
.append(comment
); 
 812 void XMLNode::setStartLineNumber(int32_t line
) 
 814     mStartLineNumber 
= line
; 
 817 void XMLNode::setEndLineNumber(int32_t line
) 
 819     mEndLineNumber 
= line
; 
 822 void XMLNode::removeWhitespace(bool stripAll
, const char** cDataTags
) 
 824     //printf("Removing whitespace in %s\n", String8(mElementName).string()); 
 825     size_t N 
= mChildren
.size(); 
 827         String8 
tag(mElementName
); 
 828         const char** p 
= cDataTags
; 
 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
)) { 
 844             //printf("Space ends at %d in \"%s\"\n", 
 845             //       (int)(p-node->mChars.string()), 
 846             //       String8(node->mChars).string()); 
 850                     mChildren
.removeAt(i
); 
 854                     node
->mChars 
= String16(" "); 
 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
)) { 
 862                 if (p 
> node
->mChars
.string()) { 
 865                 if (e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 868                 if (p 
> node
->mChars
.string() || 
 869                     e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 870                     String16 
tmp(p
, e
-p
+1); 
 875             node
->removeWhitespace(stripAll
, cDataTags
); 
 880 status_t 
XMLNode::parseValues(const sp
<AaptAssets
>& assets
, 
 881                               ResourceTable
* table
) 
 883     bool hasErrors 
= false; 
 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
), 
 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
)) { 
 899             NOISY(printf("Attr %s: type=0x%x, str=%s\n", 
 900                    String8(e
.name
).string(), e
.value
.dataType
, 
 901                    String8(e
.string
).string())); 
 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
) { 
 911     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 914 status_t 
XMLNode::assignResourceIds(const sp
<AaptAssets
>& assets
, 
 915                                     const ResourceTable
* table
) 
 917     bool hasErrors 
= false; 
 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; 
 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()); 
 943                 NOISY(printf("XML attribute name %s: resid=0x%08x\n", 
 944                              String8(e
.name
).string(), res
)); 
 945                 setAttributeResID(i
, res
); 
 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()); 
 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
) { 
 962     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 965 status_t 
XMLNode::flatten(const sp
<AaptFile
>& dest
, 
 966         bool stripComments
, bool stripRawValues
) const 
 968     StringPool strings 
= StringPool(false, mUTF8
); 
 969     Vector
<uint32_t> resids
; 
 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
); 
 977     // Next collect all remainibng strings. 
 978     collect_strings(&strings
, &resids
, stripComments
, stripRawValues
); 
 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()); 
 989     sp
<AaptFile
> stringPool 
= strings
.createStringBlock(); 
 990     NOISY(aout 
<< "String pool:" 
 991           << HexDump(stringPool
->getData(), stringPool
->getSize()) << endl
); 
 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
)); 
 998     const size_t basePos 
= dest
->getSize(); 
 999     dest
->writeData(&header
, sizeof(header
)); 
1000     dest
->writeData(stringPool
->getData(), stringPool
->getSize()); 
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
]); 
1018     flatten_node(strings
, dest
, stripComments
, stripRawValues
); 
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
); 
1025     NOISY(aout 
<< "XML resource:" 
1026           << HexDump(dest
->getData(), dest
->getSize()) << endl
); 
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()); 
1037 void XMLNode::print(int indent
) 
1041     for (i
=0; i
<indent
; i
++) { 
1044     if (getType() == TYPE_ELEMENT
) { 
1045         String8 
elemNs(getNamespaceUri()); 
1046         if (elemNs
.size() > 0) { 
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
); 
1059             const attribute_entry
& attr 
= mAttributes
.itemAt(idx
); 
1060             String8 
attrNs(attr
.ns
); 
1061             if (attrNs
.size() > 0) { 
1064             if (attr
.nameResId
) { 
1065                 printf("%s%s(0x%08x)", attrNs
.string(), 
1066                        String8(attr
.name
).string(), attr
.nameResId
); 
1068                 printf("%s%s", attrNs
.string(), String8(attr
.name
).string()); 
1070             printf("=%s", String8(attr
.string
).string()); 
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()); 
1079         printf("%s C: \"%s\"\n", prefix
.string(), String8(getCData()).string()); 
1081     int N 
= mChildren
.size(); 
1082     for (i
=0; i
<N
; i
++) { 
1083         mChildren
.itemAt(i
)->print(indent
+1); 
1087 static void splitName(const char* name
, String16
* outNs
, String16
* outName
) 
1089     const char* p 
= name
; 
1090     while (*p 
!= 0 && *p 
!= 1) { 
1094         *outNs 
= String16(); 
1095         *outName 
= String16(name
); 
1097         *outNs 
= String16(name
, (p
-name
)); 
1098         *outName 
= String16(p
+1); 
1103 XMLNode::startNamespace(void *userData
, const char *prefix
, const char *uri
) 
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
); 
1115     st
->stack
.push(node
); 
1119 XMLNode::startElement(void *userData
, const char *name
, const char **atts
) 
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(); 
1131     if (st
->stack
.size() > 0) { 
1132         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
1136     st
->stack
.push(node
); 
1138     for (int i 
= 0; atts
[i
]; i 
+= 2) { 
1139         splitName(atts
[i
], &ns16
, &name16
); 
1140         node
->addAttribute(ns16
, name16
, String16(atts
[i
+1])); 
1145 XMLNode::characterData(void *userData
, const XML_Char 
*s
, int len
) 
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) { 
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. 
1163         node 
= XMLNode::newCData(st
->filename
); 
1164         node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1165         parent
->addChild(node
); 
1168     node
->appendChars(String16(s
, len
)); 
1172 XMLNode::endElement(void *userData
, const char *name
) 
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(); 
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
); 
1191 XMLNode::endNamespace(void *userData
, const char *prefix
) 
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
); 
1204 XMLNode::commentData(void *userData
, const char *comment
) 
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")); 
1211     st
->pendingComment
.append(String16(comment
)); 
1214 status_t 
XMLNode::collect_strings(StringPool
* dest
, Vector
<uint32_t>* outResIds
, 
1215         bool stripComments
, bool stripRawValues
) const 
1217     collect_attr_strings(dest
, outResIds
, true); 
1220     if (mNamespacePrefix
.size() > 0) { 
1221         dest
->add(mNamespacePrefix
, true); 
1223     if (mNamespaceUri
.size() > 0) { 
1224         dest
->add(mNamespaceUri
, true); 
1226     if (mElementName
.size() > 0) { 
1227         dest
->add(mElementName
, true); 
1230     if (!stripComments 
&& mComment
.size() > 0) { 
1231         dest
->add(mComment
, true); 
1234     const int NA 
= mAttributes
.size(); 
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); 
1241         if (!stripRawValues 
|| ae
.needStringValue()) { 
1242             dest
->add(ae
.string
, true); 
1245         if (ae.value.dataType == Res_value::TYPE_NULL 
1246                 || ae.value.dataType == Res_value::TYPE_STRING) { 
1247             dest->add(ae.string, true); 
1252     if (mElementName
.size() == 0) { 
1253         // If not an element, include the CDATA, even if it is empty. 
1254         dest
->add(mChars
, true); 
1257     const int NC 
= mChildren
.size(); 
1259     for (i
=0; i
<NC
; i
++) { 
1260         mChildren
.itemAt(i
)->collect_strings(dest
, outResIds
, 
1261                 stripComments
, stripRawValues
); 
1267 status_t 
XMLNode::collect_attr_strings(StringPool
* outPool
, 
1268         Vector
<uint32_t>* outResIds
, bool allAttrs
) const { 
1269     const int NA 
= mAttributes
.size(); 
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 
1277             const Vector
<size_t>* indices 
= outPool
->offsetsForString(attr
.name
); 
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
); 
1286                             // We don't need to assign a resource ID for this one. 
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
) { 
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
)); 
1304                     while ((ssize_t
)outResIds
->size() <= idx
) { 
1307                     outResIds
->replaceAt(id
, idx
); 
1310             attr
.namePoolIdx 
= idx
; 
1311             NOISY(printf("String %s offset=0x%08x\n", 
1312                          String8(attr
.name
).string(), idx
)); 
1319 status_t 
XMLNode::collect_resid_strings(StringPool
* outPool
, 
1320         Vector
<uint32_t>* outResIds
) const 
1322     collect_attr_strings(outPool
, outResIds
, false); 
1324     const int NC 
= mChildren
.size(); 
1326     for (int i
=0; i
<NC
; i
++) { 
1327         mChildren
.itemAt(i
)->collect_resid_strings(outPool
, outResIds
); 
1333 status_t 
XMLNode::flatten_node(const StringPool
& strings
, const sp
<AaptFile
>& dest
, 
1334         bool stripComments
, bool stripRawValues
) const 
1336     ResXMLTree_node node
; 
1337     ResXMLTree_cdataExt cdataExt
; 
1338     ResXMLTree_namespaceExt namespaceExt
; 
1339     ResXMLTree_attrExt attrExt
; 
1340     const void* extData 
= NULL
; 
1342     ResXMLTree_attribute attr
; 
1344     const size_t NA 
= mAttributes
.size(); 
1345     const size_t NC 
= mChildren
.size(); 
1348     LOG_ALWAYS_FATAL_IF(NA 
!= mAttributeOrder
.size(), "Attributes messed up!"); 
1350     const String16 
id16("id"); 
1351     const String16 
class16("class"); 
1352     const String16 
style16("style"); 
1354     const type type 
= getType(); 
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()); 
1367         node
.comment
.index 
= htodl((uint32_t)-1); 
1369     if (type 
== TYPE_ELEMENT
) { 
1370         node
.header
.type 
= htods(RES_XML_START_ELEMENT_TYPE
); 
1372         extSize 
= sizeof(attrExt
); 
1373         memset(&attrExt
, 0, sizeof(attrExt
)); 
1374         if (mNamespaceUri
.size() > 0) { 
1375             attrExt
.ns
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1377             attrExt
.ns
.index 
= htodl((uint32_t)-1); 
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); 
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
)); 
1407             namespaceExt
.prefix
.index 
= htodl((uint32_t)-1); 
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!"); 
1425     node
.header
.size 
= htodl(sizeof(node
) + extSize 
+ (sizeof(attr
)*NA
)); 
1427     dest
->writeData(&node
, sizeof(node
)); 
1429         dest
->writeData(extData
, extSize
); 
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
)); 
1438             attr
.ns
.index 
= htodl((uint32_t)-1); 
1440         attr
.name
.index 
= htodl(ae
.namePoolIdx
); 
1442         if (!stripRawValues 
|| ae
.needStringValue()) { 
1443             attr
.rawValue
.index 
= htodl(strings
.offsetForString(ae
.string
)); 
1445             attr
.rawValue
.index 
= htodl((uint32_t)-1); 
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
)); 
1454             attr
.typedValue
.res0 
= 0; 
1455             attr
.typedValue
.dataType 
= ae
.value
.dataType
; 
1456             attr
.typedValue
.data 
= htodl(ae
.value
.data
); 
1458         dest
->writeData(&attr
, sizeof(attr
)); 
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
) { 
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
);