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_AUTO_PACKAGE_NAMESPACE 
= "http://schemas.android.com/apk/res-auto"; 
  25 const char* const RESOURCES_ROOT_PRV_NAMESPACE 
= "http://schemas.android.com/apk/prv/res/"; 
  27 const char* const XLIFF_XMLNS 
= "urn:oasis:names:tc:xliff:document:1.2"; 
  28 const char* const ALLOWED_XLIFF_ELEMENTS
[] = { 
  39 bool isWhitespace(const char16_t* str
) 
  41     while (*str 
!= 0 && *str 
< 128 && isspace(*str
)) { 
  47 static const String16 
RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE
); 
  48 static const String16 
RESOURCES_PREFIX_AUTO_PACKAGE(RESOURCES_AUTO_PACKAGE_NAMESPACE
); 
  49 static const String16 
RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE
); 
  50 static const String16 
RESOURCES_TOOLS_NAMESPACE("http://schemas.android.com/tools"); 
  52 String16 
getNamespaceResourcePackage(String16 appPackage
, String16 namespaceUri
, bool* outIsPublic
) 
  54     //printf("%s starts with %s?\n", String8(namespaceUri).string(), 
  55     //       String8(RESOURCES_PREFIX).string()); 
  58     if(namespaceUri
.startsWith(RESOURCES_PREFIX_AUTO_PACKAGE
)) { 
  59         NOISY(printf("Using default application package: %s -> %s\n", String8(namespaceUri
).string(), String8(appPackage
).string())); 
  62     } else if (namespaceUri
.startsWith(RESOURCES_PREFIX
)) { 
  63         prefixSize 
= RESOURCES_PREFIX
.size(); 
  64     } else if (namespaceUri
.startsWith(RESOURCES_PRV_PREFIX
)) { 
  66         prefixSize 
= RESOURCES_PRV_PREFIX
.size(); 
  68         if (outIsPublic
) *outIsPublic 
= isPublic
; // = true 
  73     //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string()); 
  74     if (outIsPublic
) *outIsPublic 
= isPublic
; 
  75     return String16(namespaceUri
, namespaceUri
.size()-prefixSize
, prefixSize
); 
  78 status_t 
hasSubstitutionErrors(const char* fileName
, 
  82     const char16_t* str 
= str16
.string(); 
  83     const char16_t* p 
= str
; 
  84     const char16_t* end 
= str 
+ str16
.size(); 
  86     bool nonpositional 
= false; 
  91          * Look for the start of a Java-style substitution sequence. 
  93         if (*p 
== '%' && p 
+ 1 < end
) { 
  96             // A literal percent sign represented by %% 
 104             if (*p 
>= '0' && *p 
<= '9') { 
 107                 } while (*p 
>= '0' && *p 
<= '9'); 
 109                     // This must be a size specification instead of position. 
 110                     nonpositional 
= true; 
 112             } else if (*p 
== '<') { 
 113                 // Reusing last argument; bad idea since it can be re-arranged. 
 114                 nonpositional 
= true; 
 117                 // Optionally '$' can be specified at the end. 
 118                 if (p 
< end 
&& *p 
== '$') { 
 122                 nonpositional 
= true; 
 125             // Ignore flags and widths 
 126             while (p 
< end 
&& (*p 
== '-' || 
 132                     (*p 
>= '0' && *p 
<= '9'))) { 
 137              * This is a shortcut to detect strings that are going to Time.format() 
 138              * instead of String.format() 
 140              * Comparison of String.format() and Time.format() args: 
 142              * String: ABC E GH  ST X abcdefgh  nost x 
 143              *   Time:    DEFGHKMS W Za  d   hkm  s w yz 
 145              * Therefore we know it's definitely Time if we have: 
 170      * If we have more than one substitution in this string and any of them 
 171      * are not in positional form, give the user an error. 
 173     if (argCount 
> 1 && nonpositional
) { 
 174         SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 175                 "Multiple substitutions specified in non-positional format; " 
 176                 "did you mean to add the formatted=\"false\" attribute?\n"); 
 177         return NOT_ENOUGH_DATA
; 
 183 status_t 
parseStyledString(Bundle
* bundle
, 
 184                            const char* fileName
, 
 186                            const String16
& endTag
, 
 188                            Vector
<StringPool::entry_style_span
>* outSpans
, 
 192     Vector
<StringPool::entry_style_span
> spanStack
; 
 195     const char* errorMsg
; 
 197     bool firstTime 
= true; 
 200     ResXMLTree::event_code_t code
; 
 201     while ((code
=inXml
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 203         if (code 
== ResXMLTree::TEXT
) { 
 204             String16 
text(inXml
->getText(&len
)); 
 205             if (firstTime 
&& text
.size() > 0) { 
 207                 if (text
.string()[0] == '@') { 
 208                     // If this is a resource reference, don't do the pseudoloc. 
 209                     pseudolocalize 
= false; 
 212             if (xliffDepth 
== 0 && pseudolocalize
) { 
 213                 std::string 
orig(String8(text
).string()); 
 214                 std::string pseudo 
= pseudolocalize_string(orig
); 
 215                 curString
.append(String16(String8(pseudo
.c_str()))); 
 217                 if (isFormatted 
&& hasSubstitutionErrors(fileName
, inXml
, text
) != NO_ERROR
) { 
 218                     return UNKNOWN_ERROR
; 
 220                     curString
.append(text
); 
 223         } else if (code 
== ResXMLTree::START_TAG
) { 
 224             const String16 
element16(inXml
->getElementName(&len
)); 
 225             const String8 
element8(element16
); 
 228             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 230                 ns 
= (const uint16_t*)"\0\0"; 
 233             const String8 
nspace(String16(ns
, nslen
)); 
 234             if (nspace 
== XLIFF_XMLNS
) { 
 235                 const int N 
= sizeof(ALLOWED_XLIFF_ELEMENTS
)/sizeof(ALLOWED_XLIFF_ELEMENTS
[0]); 
 236                 for (int i
=0; i
<N
; i
++) { 
 237                     if (element8 
== ALLOWED_XLIFF_ELEMENTS
[i
]) { 
 239                         // in this case, treat it like it was just text, in other words, do nothing 
 240                         // here and silently drop this element 
 245                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 246                             "Found unsupported XLIFF tag <%s>\n", 
 248                     return UNKNOWN_ERROR
; 
 254             if (outSpans 
== NULL
) { 
 255                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 256                         "Found style tag <%s> where styles are not allowed\n", element8
.string()); 
 257                 return UNKNOWN_ERROR
; 
 260             if (!ResTable::collectString(outString
, curString
.string(), 
 261                                          curString
.size(), false, &errorMsg
, true)) { 
 262                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error("%s (in %s)\n", 
 263                         errorMsg
, String8(curString
).string()); 
 264                 return UNKNOWN_ERROR
; 
 266             rawString
.append(curString
); 
 267             curString 
= String16(); 
 269             StringPool::entry_style_span span
; 
 270             span
.name 
= element16
; 
 271             for (size_t ai
=0; ai
<inXml
->getAttributeCount(); ai
++) { 
 272                 span
.name
.append(String16(";")); 
 273                 const char16_t* str 
= inXml
->getAttributeName(ai
, &len
); 
 274                 span
.name
.append(str
, len
); 
 275                 span
.name
.append(String16("=")); 
 276                 str 
= inXml
->getAttributeStringValue(ai
, &len
); 
 277                 span
.name
.append(str
, len
); 
 279             //printf("Span: %s\n", String8(span.name).string()); 
 280             span
.span
.firstChar 
= span
.span
.lastChar 
= outString
->size(); 
 281             spanStack
.push(span
); 
 283         } else if (code 
== ResXMLTree::END_TAG
) { 
 285             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 287                 ns 
= (const uint16_t*)"\0\0"; 
 290             const String8 
nspace(String16(ns
, nslen
)); 
 291             if (nspace 
== XLIFF_XMLNS
) { 
 295             if (!ResTable::collectString(outString
, curString
.string(), 
 296                                          curString
.size(), false, &errorMsg
, true)) { 
 297                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error("%s (in %s)\n", 
 298                         errorMsg
, String8(curString
).string()); 
 299                 return UNKNOWN_ERROR
; 
 301             rawString
.append(curString
); 
 302             curString 
= String16(); 
 304             if (spanStack
.size() == 0) { 
 305                 if (strcmp16(inXml
->getElementName(&len
), endTag
.string()) != 0) { 
 306                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 307                             "Found tag %s where <%s> close is expected\n", 
 308                             String8(inXml
->getElementName(&len
)).string(), 
 309                             String8(endTag
).string()); 
 310                     return UNKNOWN_ERROR
; 
 314             StringPool::entry_style_span span 
= spanStack
.top(); 
 316             ssize_t semi 
= span
.name
.findFirst(';'); 
 318                 spanTag
.setTo(span
.name
.string(), semi
); 
 320                 spanTag
.setTo(span
.name
); 
 322             if (strcmp16(inXml
->getElementName(&len
), spanTag
.string()) != 0) { 
 323                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 324                         "Found close tag %s where close tag %s is expected\n", 
 325                         String8(inXml
->getElementName(&len
)).string(), 
 326                         String8(spanTag
).string()); 
 327                 return UNKNOWN_ERROR
; 
 330             if (outString
->size() > 0) { 
 331                 span
.span
.lastChar 
= outString
->size()-1; 
 332                 if (span
.span
.lastChar 
>= span
.span
.firstChar
) { 
 340              * This warning seems to be just an irritation to most people, 
 341              * since it is typically introduced by translators who then never 
 345                 fprintf(stderr
, "%s:%d: warning: empty '%s' span found in text '%s'\n", 
 346                         fileName
, inXml
->getLineNumber(), 
 347                         String8(spanTag
).string(), String8(*outString
).string()); 
 350         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 355     if (code 
== ResXMLTree::BAD_DOCUMENT
) { 
 356             SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 357                     "Error parsing XML\n"); 
 360     if (outSpans 
!= NULL 
&& outSpans
->size() > 0) { 
 361         if (curString
.size() > 0) { 
 362             if (!ResTable::collectString(outString
, curString
.string(), 
 363                                          curString
.size(), false, &errorMsg
, true)) { 
 364                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 366                         errorMsg
, String8(curString
).string()); 
 367                 return UNKNOWN_ERROR
; 
 371         // There is no style information, so string processing will happen 
 372         // later as part of the overall type conversion.  Return to the 
 373         // client the raw unprocessed text. 
 374         rawString
.append(curString
); 
 375         outString
->setTo(rawString
); 
 381 struct namespace_entry 
{ 
 386 static String8 
make_prefix(int depth
) 
 390     for (i
=0; i
<depth
; i
++) { 
 396 static String8 
build_namespace(const Vector
<namespace_entry
>& namespaces
, 
 402         const size_t N 
= namespaces
.size(); 
 403         for (size_t i
=0; i
<N
; i
++) { 
 404             const namespace_entry
& ne 
= namespaces
.itemAt(i
); 
 415 void printXMLBlock(ResXMLTree
* block
) 
 419     Vector
<namespace_entry
> namespaces
; 
 421     ResXMLTree::event_code_t code
; 
 423     while ((code
=block
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 424         String8 prefix 
= make_prefix(depth
); 
 426         if (code 
== ResXMLTree::START_TAG
) { 
 428             const uint16_t* ns16 
= block
->getElementNamespace(&len
); 
 429             String8 elemNs 
= build_namespace(namespaces
, ns16
); 
 430             const uint16_t* com16 
= block
->getComment(&len
); 
 432                 printf("%s <!-- %s -->\n", prefix
.string(), String8(com16
).string()); 
 434             printf("%sE: %s%s (line=%d)\n", prefix
.string(), elemNs
.string(), 
 435                    String8(block
->getElementName(&len
)).string(), 
 436                    block
->getLineNumber()); 
 437             int N 
= block
->getAttributeCount(); 
 439             prefix 
= make_prefix(depth
); 
 440             for (i
=0; i
<N
; i
++) { 
 441                 uint32_t res 
= block
->getAttributeNameResID(i
); 
 442                 ns16 
= block
->getAttributeNamespace(i
, &len
); 
 443                 String8 ns 
= build_namespace(namespaces
, ns16
); 
 444                 String8 
name(block
->getAttributeName(i
, &len
)); 
 445                 printf("%sA: ", prefix
.string()); 
 447                     printf("%s%s(0x%08x)", ns
.string(), name
.string(), res
); 
 449                     printf("%s%s", ns
.string(), name
.string()); 
 452                 block
->getAttributeValue(i
, &value
); 
 453                 if (value
.dataType 
== Res_value::TYPE_NULL
) { 
 455                 } else if (value
.dataType 
== Res_value::TYPE_REFERENCE
) { 
 456                     printf("=@0x%x", (int)value
.data
); 
 457                 } else if (value
.dataType 
== Res_value::TYPE_ATTRIBUTE
) { 
 458                     printf("=?0x%x", (int)value
.data
); 
 459                 } else if (value
.dataType 
== Res_value::TYPE_STRING
) { 
 461                             ResTable::normalizeForOutput(String8(block
->getAttributeStringValue(i
, 
 462                                         &len
)).string()).string()); 
 464                     printf("=(type 0x%x)0x%x", (int)value
.dataType
, (int)value
.data
); 
 466                 const char16_t* val 
= block
->getAttributeStringValue(i
, &len
); 
 468                     printf(" (Raw: \"%s\")", ResTable::normalizeForOutput(String8(val
).string()). 
 473         } else if (code 
== ResXMLTree::END_TAG
) { 
 475         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 478             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 480                 ns
.prefix 
= String8(prefix16
); 
 484             ns
.uri 
= String8(block
->getNamespaceUri(&len
)); 
 486             printf("%sN: %s=%s\n", prefix
.string(), ns
.prefix
.string(), 
 489         } else if (code 
== ResXMLTree::END_NAMESPACE
) { 
 491             const namespace_entry
& ns 
= namespaces
.top(); 
 493             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 496                 pr 
= String8(prefix16
); 
 500             if (ns
.prefix 
!= pr
) { 
 501                 prefix 
= make_prefix(depth
); 
 502                 printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n", 
 503                         prefix
.string(), pr
.string(), ns
.prefix
.string()); 
 505             String8 uri 
= String8(block
->getNamespaceUri(&len
)); 
 507                 prefix 
= make_prefix(depth
); 
 508                 printf("%s *** BAD END NS URI: found=%s, expected=%s\n", 
 509                         prefix
.string(), uri
.string(), ns
.uri
.string()); 
 512         } else if (code 
== ResXMLTree::TEXT
) { 
 514             printf("%sC: \"%s\"\n", prefix
.string(), String8(block
->getText(&len
)).string()); 
 521 status_t 
parseXMLResource(const sp
<AaptFile
>& file
, ResXMLTree
* outTree
, 
 522                           bool stripAll
, bool keepComments
, 
 523                           const char** cDataTags
) 
 525     sp
<XMLNode
> root 
= XMLNode::parse(file
); 
 527         return UNKNOWN_ERROR
; 
 529     root
->removeWhitespace(stripAll
, cDataTags
); 
 531     NOISY(printf("Input XML from %s:\n", (const char*)file
->getPrintableSource())); 
 532     NOISY(root
->print()); 
 533     sp
<AaptFile
> rsc 
= new AaptFile(String8(), AaptGroupEntry(), String8()); 
 534     status_t err 
= root
->flatten(rsc
, !keepComments
, false); 
 535     if (err 
!= NO_ERROR
) { 
 538     err 
= outTree
->setTo(rsc
->getData(), rsc
->getSize(), true); 
 539     if (err 
!= NO_ERROR
) { 
 543     NOISY(printf("Output XML:\n")); 
 544     NOISY(printXMLBlock(outTree
)); 
 549 sp
<XMLNode
> XMLNode::parse(const sp
<AaptFile
>& file
) 
 552     int fd 
= open(file
->getSourceFile().string(), O_RDONLY 
| O_BINARY
); 
 554         SourcePos(file
->getSourceFile(), -1).error("Unable to open file for read: %s", 
 559     XML_Parser parser 
= XML_ParserCreateNS(NULL
, 1); 
 561     state
.filename 
= file
->getPrintableSource(); 
 562     state
.parser 
= parser
; 
 563     XML_SetUserData(parser
, &state
); 
 564     XML_SetElementHandler(parser
, startElement
, endElement
); 
 565     XML_SetNamespaceDeclHandler(parser
, startNamespace
, endNamespace
); 
 566     XML_SetCharacterDataHandler(parser
, characterData
); 
 567     XML_SetCommentHandler(parser
, commentData
); 
 572         len 
= read(fd
, buf
, sizeof(buf
)); 
 573         done 
= len 
< (ssize_t
)sizeof(buf
); 
 575             SourcePos(file
->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno
)); 
 579         if (XML_Parse(parser
, buf
, len
, done
) == XML_STATUS_ERROR
) { 
 580             SourcePos(file
->getSourceFile(), (int)XML_GetCurrentLineNumber(parser
)).error( 
 581                     "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser
))); 
 587     XML_ParserFree(parser
); 
 588     if (state
.root 
== NULL
) { 
 589         SourcePos(file
->getSourceFile(), -1).error("No XML data generated when parsing"); 
 595 XMLNode::XMLNode(const String8
& filename
, const String16
& s1
, const String16
& s2
, bool isNamespace
) 
 596     : mNextAttributeIndex(0x80000000) 
 597     , mFilename(filename
) 
 598     , mStartLineNumber(0) 
 603         mNamespacePrefix 
= s1
; 
 611 XMLNode::XMLNode(const String8
& filename
) 
 612     : mFilename(filename
) 
 614     memset(&mCharsValue
, 0, sizeof(mCharsValue
)); 
 617 XMLNode::type 
XMLNode::getType() const 
 619     if (mElementName
.size() != 0) { 
 622     if (mNamespaceUri
.size() != 0) { 
 623         return TYPE_NAMESPACE
; 
 628 const String16
& XMLNode::getNamespacePrefix() const 
 630     return mNamespacePrefix
; 
 633 const String16
& XMLNode::getNamespaceUri() const 
 635     return mNamespaceUri
; 
 638 const String16
& XMLNode::getElementNamespace() const 
 640     return mNamespaceUri
; 
 643 const String16
& XMLNode::getElementName() const 
 648 const Vector
<sp
<XMLNode
> >& XMLNode::getChildren() const 
 653 const String8
& XMLNode::getFilename() const 
 658 const Vector
<XMLNode::attribute_entry
>& 
 659     XMLNode::getAttributes() const 
 664 const XMLNode::attribute_entry
* XMLNode::getAttribute(const String16
& ns
, 
 665         const String16
& name
) const 
 667     for (size_t i
=0; i
<mAttributes
.size(); i
++) { 
 668         const attribute_entry
& ae(mAttributes
.itemAt(i
)); 
 669         if (ae
.ns 
== ns 
&& ae
.name 
== name
) { 
 677 XMLNode::attribute_entry
* XMLNode::editAttribute(const String16
& ns
, 
 678         const String16
& name
) 
 680     for (size_t i
=0; i
<mAttributes
.size(); i
++) { 
 681         attribute_entry 
* ae 
= &mAttributes
.editItemAt(i
); 
 682         if (ae
->ns 
== ns 
&& ae
->name 
== name
) { 
 690 const String16
& XMLNode::getCData() const 
 695 const String16
& XMLNode::getComment() const 
 700 int32_t XMLNode::getStartLineNumber() const 
 702     return mStartLineNumber
; 
 705 int32_t XMLNode::getEndLineNumber() const 
 707     return mEndLineNumber
; 
 710 sp
<XMLNode
> XMLNode::searchElement(const String16
& tagNamespace
, const String16
& tagName
) 
 712     if (getType() == XMLNode::TYPE_ELEMENT
 
 713             && mNamespaceUri 
== tagNamespace
 
 714             && mElementName 
== tagName
) { 
 718     for (size_t i
=0; i
<mChildren
.size(); i
++) { 
 719         sp
<XMLNode
> found 
= mChildren
.itemAt(i
)->searchElement(tagNamespace
, tagName
); 
 728 sp
<XMLNode
> XMLNode::getChildElement(const String16
& tagNamespace
, const String16
& tagName
) 
 730     for (size_t i
=0; i
<mChildren
.size(); i
++) { 
 731         sp
<XMLNode
> child 
= mChildren
.itemAt(i
); 
 732         if (child
->getType() == XMLNode::TYPE_ELEMENT
 
 733                 && child
->mNamespaceUri 
== tagNamespace
 
 734                 && child
->mElementName 
== tagName
) { 
 742 status_t 
XMLNode::addChild(const sp
<XMLNode
>& child
) 
 744     if (getType() == TYPE_CDATA
) { 
 745         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 746         return UNKNOWN_ERROR
; 
 748     //printf("Adding child %p to parent %p\n", child.get(), this); 
 749     mChildren
.add(child
); 
 753 status_t 
XMLNode::insertChildAt(const sp
<XMLNode
>& child
, size_t index
) 
 755     if (getType() == TYPE_CDATA
) { 
 756         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 757         return UNKNOWN_ERROR
; 
 759     //printf("Adding child %p to parent %p\n", child.get(), this); 
 760     mChildren
.insertAt(child
, index
); 
 764 status_t 
XMLNode::addAttribute(const String16
& ns
, const String16
& name
, 
 765                                const String16
& value
) 
 767     if (getType() == TYPE_CDATA
) { 
 768         SourcePos(mFilename
, getStartLineNumber()).error("Child to CDATA node."); 
 769         return UNKNOWN_ERROR
; 
 772     if (ns 
!= RESOURCES_TOOLS_NAMESPACE
) { 
 774         e
.index 
= mNextAttributeIndex
++; 
 779         mAttributeOrder
.add(e
.index
, mAttributes
.size()-1); 
 784 void XMLNode::setAttributeResID(size_t attrIdx
, uint32_t resId
) 
 786     attribute_entry
& e 
= mAttributes
.editItemAt(attrIdx
); 
 788         mAttributeOrder
.removeItem(e
.nameResId
); 
 790         mAttributeOrder
.removeItem(e
.index
); 
 792     NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n", 
 793             String8(getElementName()).string(), 
 794             String8(mAttributes
.itemAt(attrIdx
).name
).string(), 
 795             String8(mAttributes
.itemAt(attrIdx
).string
).string(), 
 797     mAttributes
.editItemAt(attrIdx
).nameResId 
= resId
; 
 798     mAttributeOrder
.add(resId
, attrIdx
); 
 801 status_t 
XMLNode::appendChars(const String16
& chars
) 
 803     if (getType() != TYPE_CDATA
) { 
 804         SourcePos(mFilename
, getStartLineNumber()).error("Adding characters to element node."); 
 805         return UNKNOWN_ERROR
; 
 807     mChars
.append(chars
); 
 811 status_t 
XMLNode::appendComment(const String16
& comment
) 
 813     if (mComment
.size() > 0) { 
 814         mComment
.append(String16("\n")); 
 816     mComment
.append(comment
); 
 820 void XMLNode::setStartLineNumber(int32_t line
) 
 822     mStartLineNumber 
= line
; 
 825 void XMLNode::setEndLineNumber(int32_t line
) 
 827     mEndLineNumber 
= line
; 
 830 void XMLNode::removeWhitespace(bool stripAll
, const char** cDataTags
) 
 832     //printf("Removing whitespace in %s\n", String8(mElementName).string()); 
 833     size_t N 
= mChildren
.size(); 
 835         String8 
tag(mElementName
); 
 836         const char** p 
= cDataTags
; 
 844     for (size_t i
=0; i
<N
; i
++) { 
 845         sp
<XMLNode
> node 
= mChildren
.itemAt(i
); 
 846         if (node
->getType() == TYPE_CDATA
) { 
 847             // This is a CDATA node... 
 848             const char16_t* p 
= node
->mChars
.string(); 
 849             while (*p 
!= 0 && *p 
< 128 && isspace(*p
)) { 
 852             //printf("Space ends at %d in \"%s\"\n", 
 853             //       (int)(p-node->mChars.string()), 
 854             //       String8(node->mChars).string()); 
 858                     mChildren
.removeAt(i
); 
 862                     node
->mChars 
= String16(" "); 
 865                 // Compact leading/trailing whitespace. 
 866                 const char16_t* e 
= node
->mChars
.string()+node
->mChars
.size()-1; 
 867                 while (e 
> p 
&& *e 
< 128 && isspace(*e
)) { 
 870                 if (p 
> node
->mChars
.string()) { 
 873                 if (e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 876                 if (p 
> node
->mChars
.string() || 
 877                     e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 878                     String16 
tmp(p
, e
-p
+1); 
 883             node
->removeWhitespace(stripAll
, cDataTags
); 
 888 status_t 
XMLNode::parseValues(const sp
<AaptAssets
>& assets
, 
 889                               ResourceTable
* table
) 
 891     bool hasErrors 
= false; 
 893     if (getType() == TYPE_ELEMENT
) { 
 894         const size_t N 
= mAttributes
.size(); 
 895         String16 
defPackage(assets
->getPackage()); 
 896         for (size_t i
=0; i
<N
; i
++) { 
 897             attribute_entry
& e 
= mAttributes
.editItemAt(i
); 
 898             AccessorCookie 
ac(SourcePos(mFilename
, getStartLineNumber()), String8(e
.name
), 
 900             table
->setCurrentXmlPos(SourcePos(mFilename
, getStartLineNumber())); 
 901             if (!assets
->getIncludedResources() 
 902                     .stringToValue(&e
.value
, &e
.string
, 
 903                                   e
.string
.string(), e
.string
.size(), true, true, 
 904                                   e
.nameResId
, NULL
, &defPackage
, table
, &ac
)) { 
 907             NOISY(printf("Attr %s: type=0x%x, str=%s\n", 
 908                    String8(e
.name
).string(), e
.value
.dataType
, 
 909                    String8(e
.string
).string())); 
 912     const size_t N 
= mChildren
.size(); 
 913     for (size_t i
=0; i
<N
; i
++) { 
 914         status_t err 
= mChildren
.itemAt(i
)->parseValues(assets
, table
); 
 915         if (err 
!= NO_ERROR
) { 
 919     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 922 status_t 
XMLNode::assignResourceIds(const sp
<AaptAssets
>& assets
, 
 923                                     const ResourceTable
* table
) 
 925     bool hasErrors 
= false; 
 927     if (getType() == TYPE_ELEMENT
) { 
 928         String16 
attr("attr"); 
 929         const char* errorMsg
; 
 930         const size_t N 
= mAttributes
.size(); 
 931         for (size_t i
=0; i
<N
; i
++) { 
 932             const attribute_entry
& e 
= mAttributes
.itemAt(i
); 
 933             if (e
.ns
.size() <= 0) continue; 
 935             String16 
pkg(getNamespaceResourcePackage(String16(assets
->getPackage()), e
.ns
, &nsIsPublic
)); 
 936             NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", 
 937                     String8(getElementName()).string(), 
 938                     String8(e
.name
).string(), 
 939                     String8(e
.string
).string(), 
 940                     String8(e
.ns
).string(), 
 941                     (nsIsPublic
) ? "public" : "private", 
 942                     String8(pkg
).string())); 
 943             if (pkg
.size() <= 0) continue; 
 944             uint32_t res 
= table 
!= NULL
 
 945                 ? table
->getResId(e
.name
, &attr
, &pkg
, &errorMsg
, nsIsPublic
) 
 946                 : assets
->getIncludedResources(). 
 947                     identifierForName(e
.name
.string(), e
.name
.size(), 
 948                                       attr
.string(), attr
.size(), 
 949                                       pkg
.string(), pkg
.size()); 
 951                 NOISY(printf("XML attribute name %s: resid=0x%08x\n", 
 952                              String8(e
.name
).string(), res
)); 
 953                 setAttributeResID(i
, res
); 
 955                 SourcePos(mFilename
, getStartLineNumber()).error( 
 956                         "No resource identifier found for attribute '%s' in package '%s'\n", 
 957                         String8(e
.name
).string(), String8(pkg
).string()); 
 962     const size_t N 
= mChildren
.size(); 
 963     for (size_t i
=0; i
<N
; i
++) { 
 964         status_t err 
= mChildren
.itemAt(i
)->assignResourceIds(assets
, table
); 
 965         if (err 
< NO_ERROR
) { 
 970     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 973 status_t 
XMLNode::flatten(const sp
<AaptFile
>& dest
, 
 974         bool stripComments
, bool stripRawValues
) const 
 976     StringPool strings 
= StringPool(false, mUTF8
); 
 977     Vector
<uint32_t> resids
; 
 979     // First collect just the strings for attribute names that have a 
 980     // resource ID assigned to them.  This ensures that the resource ID 
 981     // array is compact, and makes it easier to deal with attribute names 
 982     // in different namespaces (and thus with different resource IDs). 
 983     collect_resid_strings(&strings
, &resids
); 
 985     // Next collect all remainibng strings. 
 986     collect_strings(&strings
, &resids
, stripComments
, stripRawValues
); 
 988 #if 0  // No longer compiles 
 989     NOISY(printf("Found strings:\n"); 
 990         const size_t N 
= strings
.size(); 
 991         for (size_t i
=0; i
<N
; i
++) { 
 992             printf("%s\n", String8(strings
.entryAt(i
).string
).string()); 
 997     sp
<AaptFile
> stringPool 
= strings
.createStringBlock(); 
 998     NOISY(aout 
<< "String pool:" 
 999           << HexDump(stringPool
->getData(), stringPool
->getSize()) << endl
); 
1001     ResXMLTree_header header
; 
1002     memset(&header
, 0, sizeof(header
)); 
1003     header
.header
.type 
= htods(RES_XML_TYPE
); 
1004     header
.header
.headerSize 
= htods(sizeof(header
)); 
1006     const size_t basePos 
= dest
->getSize(); 
1007     dest
->writeData(&header
, sizeof(header
)); 
1008     dest
->writeData(stringPool
->getData(), stringPool
->getSize()); 
1010     // If we have resource IDs, write them. 
1011     if (resids
.size() > 0) { 
1012         const size_t resIdsPos 
= dest
->getSize(); 
1013         const size_t resIdsSize 
= 
1014             sizeof(ResChunk_header
)+(sizeof(uint32_t)*resids
.size()); 
1015         ResChunk_header
* idsHeader 
= (ResChunk_header
*) 
1016             (((const uint8_t*)dest
->editData(resIdsPos
+resIdsSize
))+resIdsPos
); 
1017         idsHeader
->type 
= htods(RES_XML_RESOURCE_MAP_TYPE
); 
1018         idsHeader
->headerSize 
= htods(sizeof(*idsHeader
)); 
1019         idsHeader
->size 
= htodl(resIdsSize
); 
1020         uint32_t* ids 
= (uint32_t*)(idsHeader
+1); 
1021         for (size_t i
=0; i
<resids
.size(); i
++) { 
1022             *ids
++ = htodl(resids
[i
]); 
1026     flatten_node(strings
, dest
, stripComments
, stripRawValues
); 
1028     void* data 
= dest
->editData(); 
1029     ResXMLTree_header
* hd 
= (ResXMLTree_header
*)(((uint8_t*)data
)+basePos
); 
1030     size_t size 
= dest
->getSize()-basePos
; 
1031     hd
->header
.size 
= htodl(dest
->getSize()-basePos
); 
1033     NOISY(aout 
<< "XML resource:" 
1034           << HexDump(dest
->getData(), dest
->getSize()) << endl
); 
1036     #if PRINT_STRING_METRICS 
1037     fprintf(stderr
, "**** total xml size: %d / %d%% strings (in %s)\n", 
1038         dest
->getSize(), (stringPool
->getSize()*100)/dest
->getSize(), 
1039         dest
->getPath().string()); 
1045 void XMLNode::print(int indent
) 
1049     for (i
=0; i
<indent
; i
++) { 
1052     if (getType() == TYPE_ELEMENT
) { 
1053         String8 
elemNs(getNamespaceUri()); 
1054         if (elemNs
.size() > 0) { 
1057         printf("%s E: %s%s", prefix
.string(), 
1058                elemNs
.string(), String8(getElementName()).string()); 
1059         int N 
= mAttributes
.size(); 
1060         for (i
=0; i
<N
; i
++) { 
1061             ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1067             const attribute_entry
& attr 
= mAttributes
.itemAt(idx
); 
1068             String8 
attrNs(attr
.ns
); 
1069             if (attrNs
.size() > 0) { 
1072             if (attr
.nameResId
) { 
1073                 printf("%s%s(0x%08x)", attrNs
.string(), 
1074                        String8(attr
.name
).string(), attr
.nameResId
); 
1076                 printf("%s%s", attrNs
.string(), String8(attr
.name
).string()); 
1078             printf("=%s", String8(attr
.string
).string()); 
1081     } else if (getType() == TYPE_NAMESPACE
) { 
1082         printf("%s N: %s=%s\n", prefix
.string(), 
1083                getNamespacePrefix().size() > 0 
1084                     ? String8(getNamespacePrefix()).string() : "<DEF>", 
1085                String8(getNamespaceUri()).string()); 
1087         printf("%s C: \"%s\"\n", prefix
.string(), String8(getCData()).string()); 
1089     int N 
= mChildren
.size(); 
1090     for (i
=0; i
<N
; i
++) { 
1091         mChildren
.itemAt(i
)->print(indent
+1); 
1095 static void splitName(const char* name
, String16
* outNs
, String16
* outName
) 
1097     const char* p 
= name
; 
1098     while (*p 
!= 0 && *p 
!= 1) { 
1102         *outNs 
= String16(); 
1103         *outName 
= String16(name
); 
1105         *outNs 
= String16(name
, (p
-name
)); 
1106         *outName 
= String16(p
+1); 
1111 XMLNode::startNamespace(void *userData
, const char *prefix
, const char *uri
) 
1113     NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix
, uri
)); 
1114     ParseState
* st 
= (ParseState
*)userData
; 
1115     sp
<XMLNode
> node 
= XMLNode::newNamespace(st
->filename
,  
1116             String16(prefix 
!= NULL 
? prefix 
: ""), String16(uri
)); 
1117     node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1118     if (st
->stack
.size() > 0) { 
1119         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
1123     st
->stack
.push(node
); 
1127 XMLNode::startElement(void *userData
, const char *name
, const char **atts
) 
1129     NOISY_PARSE(printf("Start Element: %s\n", name
)); 
1130     ParseState
* st 
= (ParseState
*)userData
; 
1131     String16 ns16
, name16
; 
1132     splitName(name
, &ns16
, &name16
); 
1133     sp
<XMLNode
> node 
= XMLNode::newElement(st
->filename
, ns16
, name16
); 
1134     node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1135     if (st
->pendingComment
.size() > 0) { 
1136         node
->appendComment(st
->pendingComment
); 
1137         st
->pendingComment 
= String16(); 
1139     if (st
->stack
.size() > 0) { 
1140         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
1144     st
->stack
.push(node
); 
1146     for (int i 
= 0; atts
[i
]; i 
+= 2) { 
1147         splitName(atts
[i
], &ns16
, &name16
); 
1148         node
->addAttribute(ns16
, name16
, String16(atts
[i
+1])); 
1153 XMLNode::characterData(void *userData
, const XML_Char 
*s
, int len
) 
1155     NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s
, len
).string())); 
1156     ParseState
* st 
= (ParseState
*)userData
; 
1157     sp
<XMLNode
> node 
= NULL
; 
1158     if (st
->stack
.size() == 0) { 
1161     sp
<XMLNode
> parent 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1162     if (parent 
!= NULL 
&& parent
->getChildren().size() > 0) { 
1163         node 
= parent
->getChildren()[parent
->getChildren().size()-1]; 
1164         if (node
->getType() != TYPE_CDATA
) { 
1165             // Last node is not CDATA, need to make a new node. 
1171         node 
= XMLNode::newCData(st
->filename
); 
1172         node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1173         parent
->addChild(node
); 
1176     node
->appendChars(String16(s
, len
)); 
1180 XMLNode::endElement(void *userData
, const char *name
) 
1182     NOISY_PARSE(printf("End Element: %s\n", name
)); 
1183     ParseState
* st 
= (ParseState
*)userData
; 
1184     sp
<XMLNode
> node 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1185     node
->setEndLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1186     if (st
->pendingComment
.size() > 0) { 
1187         node
->appendComment(st
->pendingComment
); 
1188         st
->pendingComment 
= String16(); 
1190     String16 ns16
, name16
; 
1191     splitName(name
, &ns16
, &name16
); 
1192     LOG_ALWAYS_FATAL_IF(node
->getElementNamespace() != ns16
 
1193                         || node
->getElementName() != name16
, 
1194                         "Bad end element %s", name
); 
1199 XMLNode::endNamespace(void *userData
, const char *prefix
) 
1201     const char* nonNullPrefix 
= prefix 
!= NULL 
? prefix 
: ""; 
1202     NOISY_PARSE(printf("End Namespace: %s\n", prefix
)); 
1203     ParseState
* st 
= (ParseState
*)userData
; 
1204     sp
<XMLNode
> node 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1205     node
->setEndLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1206     LOG_ALWAYS_FATAL_IF(node
->getNamespacePrefix() != String16(nonNullPrefix
), 
1207                         "Bad end namespace %s", prefix
); 
1212 XMLNode::commentData(void *userData
, const char *comment
) 
1214     NOISY_PARSE(printf("Comment: %s\n", comment
)); 
1215     ParseState
* st 
= (ParseState
*)userData
; 
1216     if (st
->pendingComment
.size() > 0) { 
1217         st
->pendingComment
.append(String16("\n")); 
1219     st
->pendingComment
.append(String16(comment
)); 
1222 status_t 
XMLNode::collect_strings(StringPool
* dest
, Vector
<uint32_t>* outResIds
, 
1223         bool stripComments
, bool stripRawValues
) const 
1225     collect_attr_strings(dest
, outResIds
, true); 
1228     if (RESOURCES_TOOLS_NAMESPACE 
!= mNamespaceUri
) { 
1229         if (mNamespacePrefix
.size() > 0) { 
1230             dest
->add(mNamespacePrefix
, true); 
1232         if (mNamespaceUri
.size() > 0) { 
1233             dest
->add(mNamespaceUri
, true); 
1236     if (mElementName
.size() > 0) { 
1237         dest
->add(mElementName
, true); 
1240     if (!stripComments 
&& mComment
.size() > 0) { 
1241         dest
->add(mComment
, true); 
1244     const int NA 
= mAttributes
.size(); 
1246     for (i
=0; i
<NA
; i
++) { 
1247         const attribute_entry
& ae 
= mAttributes
.itemAt(i
); 
1248         if (ae
.ns
.size() > 0) { 
1249             dest
->add(ae
.ns
, true); 
1251         if (!stripRawValues 
|| ae
.needStringValue()) { 
1252             dest
->add(ae
.string
, true); 
1255         if (ae.value.dataType == Res_value::TYPE_NULL 
1256                 || ae.value.dataType == Res_value::TYPE_STRING) { 
1257             dest->add(ae.string, true); 
1262     if (mElementName
.size() == 0) { 
1263         // If not an element, include the CDATA, even if it is empty. 
1264         dest
->add(mChars
, true); 
1267     const int NC 
= mChildren
.size(); 
1269     for (i
=0; i
<NC
; i
++) { 
1270         mChildren
.itemAt(i
)->collect_strings(dest
, outResIds
, 
1271                 stripComments
, stripRawValues
); 
1277 status_t 
XMLNode::collect_attr_strings(StringPool
* outPool
, 
1278         Vector
<uint32_t>* outResIds
, bool allAttrs
) const { 
1279     const int NA 
= mAttributes
.size(); 
1281     for (int i
=0; i
<NA
; i
++) { 
1282         const attribute_entry
& attr 
= mAttributes
.itemAt(i
); 
1283         uint32_t id 
= attr
.nameResId
; 
1284         if (id 
|| allAttrs
) { 
1285             // See if we have already assigned this resource ID to a pooled 
1287             const Vector
<size_t>* indices 
= outPool
->offsetsForString(attr
.name
); 
1289             if (indices 
!= NULL
) { 
1290                 const int NJ 
= indices
->size(); 
1291                 const size_t NR 
= outResIds
->size(); 
1292                 for (int j
=0; j
<NJ
; j
++) { 
1293                     size_t strIdx 
= indices
->itemAt(j
); 
1296                             // We don't need to assign a resource ID for this one. 
1300                         // Just ignore strings that are out of range of 
1301                         // the currently assigned resource IDs...  we add 
1302                         // strings as we assign the first ID. 
1303                     } else if (outResIds
->itemAt(strIdx
) == id
) { 
1310                 idx 
= outPool
->add(attr
.name
); 
1311                 NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n", 
1312                         String8(attr
.name
).string(), id
, idx
)); 
1314                     while ((ssize_t
)outResIds
->size() <= idx
) { 
1317                     outResIds
->replaceAt(id
, idx
); 
1320             attr
.namePoolIdx 
= idx
; 
1321             NOISY(printf("String %s offset=0x%08x\n", 
1322                          String8(attr
.name
).string(), idx
)); 
1329 status_t 
XMLNode::collect_resid_strings(StringPool
* outPool
, 
1330         Vector
<uint32_t>* outResIds
) const 
1332     collect_attr_strings(outPool
, outResIds
, false); 
1334     const int NC 
= mChildren
.size(); 
1336     for (int i
=0; i
<NC
; i
++) { 
1337         mChildren
.itemAt(i
)->collect_resid_strings(outPool
, outResIds
); 
1343 status_t 
XMLNode::flatten_node(const StringPool
& strings
, const sp
<AaptFile
>& dest
, 
1344         bool stripComments
, bool stripRawValues
) const 
1346     ResXMLTree_node node
; 
1347     ResXMLTree_cdataExt cdataExt
; 
1348     ResXMLTree_namespaceExt namespaceExt
; 
1349     ResXMLTree_attrExt attrExt
; 
1350     const void* extData 
= NULL
; 
1352     ResXMLTree_attribute attr
; 
1353     bool writeCurrentNode 
= true; 
1355     const size_t NA 
= mAttributes
.size(); 
1356     const size_t NC 
= mChildren
.size(); 
1359     LOG_ALWAYS_FATAL_IF(NA 
!= mAttributeOrder
.size(), "Attributes messed up!"); 
1361     const String16 
id16("id"); 
1362     const String16 
class16("class"); 
1363     const String16 
style16("style"); 
1365     const type type 
= getType(); 
1367     memset(&node
, 0, sizeof(node
)); 
1368     memset(&attr
, 0, sizeof(attr
)); 
1369     node
.header
.headerSize 
= htods(sizeof(node
)); 
1370     node
.lineNumber 
= htodl(getStartLineNumber()); 
1371     if (!stripComments
) { 
1372         node
.comment
.index 
= htodl( 
1373             mComment
.size() > 0 ? strings
.offsetForString(mComment
) : -1); 
1374         //if (mComment.size() > 0) { 
1375         //  printf("Flattening comment: %s\n", String8(mComment).string()); 
1378         node
.comment
.index 
= htodl((uint32_t)-1); 
1380     if (type 
== TYPE_ELEMENT
) { 
1381         node
.header
.type 
= htods(RES_XML_START_ELEMENT_TYPE
); 
1383         extSize 
= sizeof(attrExt
); 
1384         memset(&attrExt
, 0, sizeof(attrExt
)); 
1385         if (mNamespaceUri
.size() > 0) { 
1386             attrExt
.ns
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1388             attrExt
.ns
.index 
= htodl((uint32_t)-1); 
1390         attrExt
.name
.index 
= htodl(strings
.offsetForString(mElementName
)); 
1391         attrExt
.attributeStart 
= htods(sizeof(attrExt
)); 
1392         attrExt
.attributeSize 
= htods(sizeof(attr
)); 
1393         attrExt
.attributeCount 
= htods(NA
); 
1394         attrExt
.idIndex 
= htods(0); 
1395         attrExt
.classIndex 
= htods(0); 
1396         attrExt
.styleIndex 
= htods(0); 
1397         for (i
=0; i
<NA
; i
++) { 
1398             ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1399             const attribute_entry
& ae 
= mAttributes
.itemAt(idx
); 
1400             if (ae
.ns
.size() == 0) { 
1401                 if (ae
.name 
== id16
) { 
1402                     attrExt
.idIndex 
= htods(i
+1); 
1403                 } else if (ae
.name 
== class16
) { 
1404                     attrExt
.classIndex 
= htods(i
+1); 
1405                 } else if (ae
.name 
== style16
) { 
1406                     attrExt
.styleIndex 
= htods(i
+1); 
1410     } else if (type 
== TYPE_NAMESPACE
) { 
1411         if (mNamespaceUri 
== RESOURCES_TOOLS_NAMESPACE
) { 
1412             writeCurrentNode 
= false; 
1414             node
.header
.type 
= htods(RES_XML_START_NAMESPACE_TYPE
); 
1415             extData 
= &namespaceExt
; 
1416             extSize 
= sizeof(namespaceExt
); 
1417             memset(&namespaceExt
, 0, sizeof(namespaceExt
)); 
1418             if (mNamespacePrefix
.size() > 0) { 
1419                 namespaceExt
.prefix
.index 
= htodl(strings
.offsetForString(mNamespacePrefix
)); 
1421                 namespaceExt
.prefix
.index 
= htodl((uint32_t)-1); 
1423             namespaceExt
.prefix
.index 
= htodl(strings
.offsetForString(mNamespacePrefix
)); 
1424             namespaceExt
.uri
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1426         LOG_ALWAYS_FATAL_IF(NA 
!= 0, "Namespace nodes can't have attributes!"); 
1427     } else if (type 
== TYPE_CDATA
) { 
1428         node
.header
.type 
= htods(RES_XML_CDATA_TYPE
); 
1429         extData 
= &cdataExt
; 
1430         extSize 
= sizeof(cdataExt
); 
1431         memset(&cdataExt
, 0, sizeof(cdataExt
)); 
1432         cdataExt
.data
.index 
= htodl(strings
.offsetForString(mChars
)); 
1433         cdataExt
.typedData
.size 
= htods(sizeof(cdataExt
.typedData
)); 
1434         cdataExt
.typedData
.res0 
= 0; 
1435         cdataExt
.typedData
.dataType 
= mCharsValue
.dataType
; 
1436         cdataExt
.typedData
.data 
= htodl(mCharsValue
.data
); 
1437         LOG_ALWAYS_FATAL_IF(NA 
!= 0, "CDATA nodes can't have attributes!"); 
1440     node
.header
.size 
= htodl(sizeof(node
) + extSize 
+ (sizeof(attr
)*NA
)); 
1442     if (writeCurrentNode
) { 
1443         dest
->writeData(&node
, sizeof(node
)); 
1445             dest
->writeData(extData
, extSize
); 
1449     for (i
=0; i
<NA
; i
++) { 
1450         ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1451         const attribute_entry
& ae 
= mAttributes
.itemAt(idx
); 
1452         if (ae
.ns
.size() > 0) { 
1453             attr
.ns
.index 
= htodl(strings
.offsetForString(ae
.ns
)); 
1455             attr
.ns
.index 
= htodl((uint32_t)-1); 
1457         attr
.name
.index 
= htodl(ae
.namePoolIdx
); 
1459         if (!stripRawValues 
|| ae
.needStringValue()) { 
1460             attr
.rawValue
.index 
= htodl(strings
.offsetForString(ae
.string
)); 
1462             attr
.rawValue
.index 
= htodl((uint32_t)-1); 
1464         attr
.typedValue
.size 
= htods(sizeof(attr
.typedValue
)); 
1465         if (ae
.value
.dataType 
== Res_value::TYPE_NULL
 
1466                 || ae
.value
.dataType 
== Res_value::TYPE_STRING
) { 
1467             attr
.typedValue
.res0 
= 0; 
1468             attr
.typedValue
.dataType 
= Res_value::TYPE_STRING
; 
1469             attr
.typedValue
.data 
= htodl(strings
.offsetForString(ae
.string
)); 
1471             attr
.typedValue
.res0 
= 0; 
1472             attr
.typedValue
.dataType 
= ae
.value
.dataType
; 
1473             attr
.typedValue
.data 
= htodl(ae
.value
.data
); 
1475         dest
->writeData(&attr
, sizeof(attr
)); 
1478     for (i
=0; i
<NC
; i
++) { 
1479         status_t err 
= mChildren
.itemAt(i
)->flatten_node(strings
, dest
, 
1480                 stripComments
, stripRawValues
); 
1481         if (err 
!= NO_ERROR
) { 
1486     if (type 
== TYPE_ELEMENT
) { 
1487         ResXMLTree_endElementExt endElementExt
; 
1488         memset(&endElementExt
, 0, sizeof(endElementExt
)); 
1489         node
.header
.type 
= htods(RES_XML_END_ELEMENT_TYPE
); 
1490         node
.header
.size 
= htodl(sizeof(node
)+sizeof(endElementExt
)); 
1491         node
.lineNumber 
= htodl(getEndLineNumber()); 
1492         node
.comment
.index 
= htodl((uint32_t)-1); 
1493         endElementExt
.ns
.index 
= attrExt
.ns
.index
; 
1494         endElementExt
.name
.index 
= attrExt
.name
.index
; 
1495         dest
->writeData(&node
, sizeof(node
)); 
1496         dest
->writeData(&endElementExt
, sizeof(endElementExt
)); 
1497     } else if (type 
== TYPE_NAMESPACE
) { 
1498         if (writeCurrentNode
) { 
1499             node
.header
.type 
= htods(RES_XML_END_NAMESPACE_TYPE
); 
1500             node
.lineNumber 
= htodl(getEndLineNumber()); 
1501             node
.comment
.index 
= htodl((uint32_t)-1); 
1502             node
.header
.size 
= htodl(sizeof(node
)+extSize
); 
1503             dest
->writeData(&node
, sizeof(node
)); 
1504             dest
->writeData(extData
, extSize
);