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
); 
  48 static const String16 
RESOURCES_TOOLS_NAMESPACE("http://schemas.android.com/tools"); 
  50 String16 
getNamespaceResourcePackage(String16 namespaceUri
, bool* outIsPublic
) 
  52     //printf("%s starts with %s?\n", String8(namespaceUri).string(), 
  53     //       String8(RESOURCES_PREFIX).string()); 
  56     if (namespaceUri
.startsWith(RESOURCES_PREFIX
)) { 
  57         prefixSize 
= RESOURCES_PREFIX
.size(); 
  58     } else if (namespaceUri
.startsWith(RESOURCES_PRV_PREFIX
)) { 
  60         prefixSize 
= RESOURCES_PRV_PREFIX
.size(); 
  62         if (outIsPublic
) *outIsPublic 
= isPublic
; // = true 
  67     //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string()); 
  68     if (outIsPublic
) *outIsPublic 
= isPublic
; 
  69     return String16(namespaceUri
, namespaceUri
.size()-prefixSize
, prefixSize
); 
  72 status_t 
hasSubstitutionErrors(const char* fileName
, 
  76     const char16_t* str 
= str16
.string(); 
  77     const char16_t* p 
= str
; 
  78     const char16_t* end 
= str 
+ str16
.size(); 
  80     bool nonpositional 
= false; 
  85          * Look for the start of a Java-style substitution sequence. 
  87         if (*p 
== '%' && p 
+ 1 < end
) { 
  90             // A literal percent sign represented by %% 
  98             if (*p 
>= '0' && *p 
<= '9') { 
 101                 } while (*p 
>= '0' && *p 
<= '9'); 
 103                     // This must be a size specification instead of position. 
 104                     nonpositional 
= true; 
 106             } else if (*p 
== '<') { 
 107                 // Reusing last argument; bad idea since it can be re-arranged. 
 108                 nonpositional 
= true; 
 111                 // Optionally '$' can be specified at the end. 
 112                 if (p 
< end 
&& *p 
== '$') { 
 116                 nonpositional 
= true; 
 119             // Ignore flags and widths 
 120             while (p 
< end 
&& (*p 
== '-' || 
 126                     (*p 
>= '0' && *p 
<= '9'))) { 
 131              * This is a shortcut to detect strings that are going to Time.format() 
 132              * instead of String.format() 
 134              * Comparison of String.format() and Time.format() args: 
 136              * String: ABC E GH  ST X abcdefgh  nost x 
 137              *   Time:    DEFGHKMS W Za  d   hkm  s w yz 
 139              * Therefore we know it's definitely Time if we have: 
 164      * If we have more than one substitution in this string and any of them 
 165      * are not in positional form, give the user an error. 
 167     if (argCount 
> 1 && nonpositional
) { 
 168         SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 169                 "Multiple substitutions specified in non-positional format; " 
 170                 "did you mean to add the formatted=\"false\" attribute?\n"); 
 171         return NOT_ENOUGH_DATA
; 
 177 status_t 
parseStyledString(Bundle
* bundle
, 
 178                            const char* fileName
, 
 180                            const String16
& endTag
, 
 182                            Vector
<StringPool::entry_style_span
>* outSpans
, 
 186     Vector
<StringPool::entry_style_span
> spanStack
; 
 189     const char* errorMsg
; 
 191     bool firstTime 
= true; 
 194     ResXMLTree::event_code_t code
; 
 195     while ((code
=inXml
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 197         if (code 
== ResXMLTree::TEXT
) { 
 198             String16 
text(inXml
->getText(&len
)); 
 199             if (firstTime 
&& text
.size() > 0) { 
 201                 if (text
.string()[0] == '@') { 
 202                     // If this is a resource reference, don't do the pseudoloc. 
 203                     pseudolocalize 
= false; 
 206             if (xliffDepth 
== 0 && pseudolocalize
) { 
 207                 std::string 
orig(String8(text
).string()); 
 208                 std::string pseudo 
= pseudolocalize_string(orig
); 
 209                 curString
.append(String16(String8(pseudo
.c_str()))); 
 211                 if (isFormatted 
&& hasSubstitutionErrors(fileName
, inXml
, text
) != NO_ERROR
) { 
 212                     return UNKNOWN_ERROR
; 
 214                     curString
.append(text
); 
 217         } else if (code 
== ResXMLTree::START_TAG
) { 
 218             const String16 
element16(inXml
->getElementName(&len
)); 
 219             const String8 
element8(element16
); 
 222             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 224                 ns 
= (const uint16_t*)"\0\0"; 
 227             const String8 
nspace(String16(ns
, nslen
)); 
 228             if (nspace 
== XLIFF_XMLNS
) { 
 229                 const int N 
= sizeof(ALLOWED_XLIFF_ELEMENTS
)/sizeof(ALLOWED_XLIFF_ELEMENTS
[0]); 
 230                 for (int i
=0; i
<N
; i
++) { 
 231                     if (element8 
== ALLOWED_XLIFF_ELEMENTS
[i
]) { 
 233                         // in this case, treat it like it was just text, in other words, do nothing 
 234                         // here and silently drop this element 
 239                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 240                             "Found unsupported XLIFF tag <%s>\n", 
 242                     return UNKNOWN_ERROR
; 
 248             if (outSpans 
== NULL
) { 
 249                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 250                         "Found style tag <%s> where styles are not allowed\n", element8
.string()); 
 251                 return UNKNOWN_ERROR
; 
 254             if (!ResTable::collectString(outString
, curString
.string(), 
 255                                          curString
.size(), false, &errorMsg
, true)) { 
 256                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error("%s (in %s)\n", 
 257                         errorMsg
, String8(curString
).string()); 
 258                 return UNKNOWN_ERROR
; 
 260             rawString
.append(curString
); 
 261             curString 
= String16(); 
 263             StringPool::entry_style_span span
; 
 264             span
.name 
= element16
; 
 265             for (size_t ai
=0; ai
<inXml
->getAttributeCount(); ai
++) { 
 266                 span
.name
.append(String16(";")); 
 267                 const char16_t* str 
= inXml
->getAttributeName(ai
, &len
); 
 268                 span
.name
.append(str
, len
); 
 269                 span
.name
.append(String16("=")); 
 270                 str 
= inXml
->getAttributeStringValue(ai
, &len
); 
 271                 span
.name
.append(str
, len
); 
 273             //printf("Span: %s\n", String8(span.name).string()); 
 274             span
.span
.firstChar 
= span
.span
.lastChar 
= outString
->size(); 
 275             spanStack
.push(span
); 
 277         } else if (code 
== ResXMLTree::END_TAG
) { 
 279             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 281                 ns 
= (const uint16_t*)"\0\0"; 
 284             const String8 
nspace(String16(ns
, nslen
)); 
 285             if (nspace 
== XLIFF_XMLNS
) { 
 289             if (!ResTable::collectString(outString
, curString
.string(), 
 290                                          curString
.size(), false, &errorMsg
, true)) { 
 291                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error("%s (in %s)\n", 
 292                         errorMsg
, String8(curString
).string()); 
 293                 return UNKNOWN_ERROR
; 
 295             rawString
.append(curString
); 
 296             curString 
= String16(); 
 298             if (spanStack
.size() == 0) { 
 299                 if (strcmp16(inXml
->getElementName(&len
), endTag
.string()) != 0) { 
 300                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 301                             "Found tag %s where <%s> close is expected\n", 
 302                             String8(inXml
->getElementName(&len
)).string(), 
 303                             String8(endTag
).string()); 
 304                     return UNKNOWN_ERROR
; 
 308             StringPool::entry_style_span span 
= spanStack
.top(); 
 310             ssize_t semi 
= span
.name
.findFirst(';'); 
 312                 spanTag
.setTo(span
.name
.string(), semi
); 
 314                 spanTag
.setTo(span
.name
); 
 316             if (strcmp16(inXml
->getElementName(&len
), spanTag
.string()) != 0) { 
 317                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 318                         "Found close tag %s where close tag %s is expected\n", 
 319                         String8(inXml
->getElementName(&len
)).string(), 
 320                         String8(spanTag
).string()); 
 321                 return UNKNOWN_ERROR
; 
 324             if (outString
->size() > 0) { 
 325                 span
.span
.lastChar 
= outString
->size()-1; 
 326                 if (span
.span
.lastChar 
>= span
.span
.firstChar
) { 
 334              * This warning seems to be just an irritation to most people, 
 335              * since it is typically introduced by translators who then never 
 339                 fprintf(stderr
, "%s:%d: warning: empty '%s' span found in text '%s'\n", 
 340                         fileName
, inXml
->getLineNumber(), 
 341                         String8(spanTag
).string(), String8(*outString
).string()); 
 344         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 349     if (code 
== ResXMLTree::BAD_DOCUMENT
) { 
 350             SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 351                     "Error parsing XML\n"); 
 354     if (outSpans 
!= NULL 
&& outSpans
->size() > 0) { 
 355         if (curString
.size() > 0) { 
 356             if (!ResTable::collectString(outString
, curString
.string(), 
 357                                          curString
.size(), false, &errorMsg
, true)) { 
 358                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 360                         errorMsg
, String8(curString
).string()); 
 361                 return UNKNOWN_ERROR
; 
 365         // There is no style information, so string processing will happen 
 366         // later as part of the overall type conversion.  Return to the 
 367         // client the raw unprocessed text. 
 368         rawString
.append(curString
); 
 369         outString
->setTo(rawString
); 
 375 struct namespace_entry 
{ 
 380 static String8 
make_prefix(int depth
) 
 384     for (i
=0; i
<depth
; i
++) { 
 390 static String8 
build_namespace(const Vector
<namespace_entry
>& namespaces
, 
 396         const size_t N 
= namespaces
.size(); 
 397         for (size_t i
=0; i
<N
; i
++) { 
 398             const namespace_entry
& ne 
= namespaces
.itemAt(i
); 
 409 void printXMLBlock(ResXMLTree
* block
) 
 413     Vector
<namespace_entry
> namespaces
; 
 415     ResXMLTree::event_code_t code
; 
 417     while ((code
=block
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 418         String8 prefix 
= make_prefix(depth
); 
 420         if (code 
== ResXMLTree::START_TAG
) { 
 422             const uint16_t* ns16 
= block
->getElementNamespace(&len
); 
 423             String8 elemNs 
= build_namespace(namespaces
, ns16
); 
 424             const uint16_t* com16 
= block
->getComment(&len
); 
 426                 printf("%s <!-- %s -->\n", prefix
.string(), String8(com16
).string()); 
 428             printf("%sE: %s%s (line=%d)\n", prefix
.string(), elemNs
.string(), 
 429                    String8(block
->getElementName(&len
)).string(), 
 430                    block
->getLineNumber()); 
 431             int N 
= block
->getAttributeCount(); 
 433             prefix 
= make_prefix(depth
); 
 434             for (i
=0; i
<N
; i
++) { 
 435                 uint32_t res 
= block
->getAttributeNameResID(i
); 
 436                 ns16 
= block
->getAttributeNamespace(i
, &len
); 
 437                 String8 ns 
= build_namespace(namespaces
, ns16
); 
 438                 String8 
name(block
->getAttributeName(i
, &len
)); 
 439                 printf("%sA: ", prefix
.string()); 
 441                     printf("%s%s(0x%08x)", ns
.string(), name
.string(), res
); 
 443                     printf("%s%s", ns
.string(), name
.string()); 
 446                 block
->getAttributeValue(i
, &value
); 
 447                 if (value
.dataType 
== Res_value::TYPE_NULL
) { 
 449                 } else if (value
.dataType 
== Res_value::TYPE_REFERENCE
) { 
 450                     printf("=@0x%x", (int)value
.data
); 
 451                 } else if (value
.dataType 
== Res_value::TYPE_ATTRIBUTE
) { 
 452                     printf("=?0x%x", (int)value
.data
); 
 453                 } else if (value
.dataType 
== Res_value::TYPE_STRING
) { 
 455                             ResTable::normalizeForOutput(String8(block
->getAttributeStringValue(i
, 
 456                                         &len
)).string()).string()); 
 458                     printf("=(type 0x%x)0x%x", (int)value
.dataType
, (int)value
.data
); 
 460                 const char16_t* val 
= block
->getAttributeStringValue(i
, &len
); 
 462                     printf(" (Raw: \"%s\")", ResTable::normalizeForOutput(String8(val
).string()). 
 467         } else if (code 
== ResXMLTree::END_TAG
) { 
 469         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 472             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 474                 ns
.prefix 
= String8(prefix16
); 
 478             ns
.uri 
= String8(block
->getNamespaceUri(&len
)); 
 480             printf("%sN: %s=%s\n", prefix
.string(), ns
.prefix
.string(), 
 483         } else if (code 
== ResXMLTree::END_NAMESPACE
) { 
 485             const namespace_entry
& ns 
= namespaces
.top(); 
 487             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 490                 pr 
= String8(prefix16
); 
 494             if (ns
.prefix 
!= pr
) { 
 495                 prefix 
= make_prefix(depth
); 
 496                 printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n", 
 497                         prefix
.string(), pr
.string(), ns
.prefix
.string()); 
 499             String8 uri 
= String8(block
->getNamespaceUri(&len
)); 
 501                 prefix 
= make_prefix(depth
); 
 502                 printf("%s *** BAD END NS URI: found=%s, expected=%s\n", 
 503                         prefix
.string(), uri
.string(), ns
.uri
.string()); 
 506         } else if (code 
== ResXMLTree::TEXT
) { 
 508             printf("%sC: \"%s\"\n", prefix
.string(), String8(block
->getText(&len
)).string()); 
 515 status_t 
parseXMLResource(const sp
<AaptFile
>& file
, ResXMLTree
* outTree
, 
 516                           bool stripAll
, bool keepComments
, 
 517                           const char** cDataTags
) 
 519     sp
<XMLNode
> root 
= XMLNode::parse(file
); 
 521         return UNKNOWN_ERROR
; 
 523     root
->removeWhitespace(stripAll
, cDataTags
); 
 525     NOISY(printf("Input XML from %s:\n", (const char*)file
->getPrintableSource())); 
 526     NOISY(root
->print()); 
 527     sp
<AaptFile
> rsc 
= new AaptFile(String8(), AaptGroupEntry(), String8()); 
 528     status_t err 
= root
->flatten(rsc
, !keepComments
, false); 
 529     if (err 
!= NO_ERROR
) { 
 532     err 
= outTree
->setTo(rsc
->getData(), rsc
->getSize(), true); 
 533     if (err 
!= NO_ERROR
) { 
 537     NOISY(printf("Output XML:\n")); 
 538     NOISY(printXMLBlock(outTree
)); 
 543 sp
<XMLNode
> XMLNode::parse(const sp
<AaptFile
>& file
) 
 546     int fd 
= open(file
->getSourceFile().string(), O_RDONLY 
| O_BINARY
); 
 548         SourcePos(file
->getSourceFile(), -1).error("Unable to open file for read: %s", 
 553     XML_Parser parser 
= XML_ParserCreateNS(NULL
, 1); 
 555     state
.filename 
= file
->getPrintableSource(); 
 556     state
.parser 
= parser
; 
 557     XML_SetUserData(parser
, &state
); 
 558     XML_SetElementHandler(parser
, startElement
, endElement
); 
 559     XML_SetNamespaceDeclHandler(parser
, startNamespace
, endNamespace
); 
 560     XML_SetCharacterDataHandler(parser
, characterData
); 
 561     XML_SetCommentHandler(parser
, commentData
); 
 566         len 
= read(fd
, buf
, sizeof(buf
)); 
 567         done 
= len 
< (ssize_t
)sizeof(buf
); 
 569             SourcePos(file
->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno
)); 
 573         if (XML_Parse(parser
, buf
, len
, done
) == XML_STATUS_ERROR
) { 
 574             SourcePos(file
->getSourceFile(), (int)XML_GetCurrentLineNumber(parser
)).error( 
 575                     "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser
))); 
 581     XML_ParserFree(parser
); 
 582     if (state
.root 
== NULL
) { 
 583         SourcePos(file
->getSourceFile(), -1).error("No XML data generated when parsing"); 
 589 XMLNode::XMLNode(const String8
& filename
, const String16
& s1
, const String16
& s2
, bool isNamespace
) 
 590     : mNextAttributeIndex(0x80000000) 
 591     , mFilename(filename
) 
 592     , mStartLineNumber(0) 
 597         mNamespacePrefix 
= s1
; 
 605 XMLNode::XMLNode(const String8
& filename
) 
 606     : mFilename(filename
) 
 608     memset(&mCharsValue
, 0, sizeof(mCharsValue
)); 
 611 XMLNode::type 
XMLNode::getType() const 
 613     if (mElementName
.size() != 0) { 
 616     if (mNamespaceUri
.size() != 0) { 
 617         return TYPE_NAMESPACE
; 
 622 const String16
& XMLNode::getNamespacePrefix() const 
 624     return mNamespacePrefix
; 
 627 const String16
& XMLNode::getNamespaceUri() const 
 629     return mNamespaceUri
; 
 632 const String16
& XMLNode::getElementNamespace() const 
 634     return mNamespaceUri
; 
 637 const String16
& XMLNode::getElementName() const 
 642 const Vector
<sp
<XMLNode
> >& XMLNode::getChildren() const 
 647 const String8
& XMLNode::getFilename() const 
 652 const Vector
<XMLNode::attribute_entry
>& 
 653     XMLNode::getAttributes() const 
 658 const XMLNode::attribute_entry
* XMLNode::getAttribute(const String16
& ns
, 
 659         const String16
& name
) const 
 661     for (size_t i
=0; i
<mAttributes
.size(); i
++) { 
 662         const attribute_entry
& ae(mAttributes
.itemAt(i
)); 
 663         if (ae
.ns 
== ns 
&& ae
.name 
== name
) { 
 671 XMLNode::attribute_entry
* XMLNode::editAttribute(const String16
& ns
, 
 672         const String16
& name
) 
 674     for (size_t i
=0; i
<mAttributes
.size(); i
++) { 
 675         attribute_entry 
* ae 
= &mAttributes
.editItemAt(i
); 
 676         if (ae
->ns 
== ns 
&& ae
->name 
== name
) { 
 684 const String16
& XMLNode::getCData() const 
 689 const String16
& XMLNode::getComment() const 
 694 int32_t XMLNode::getStartLineNumber() const 
 696     return mStartLineNumber
; 
 699 int32_t XMLNode::getEndLineNumber() const 
 701     return mEndLineNumber
; 
 704 sp
<XMLNode
> XMLNode::searchElement(const String16
& tagNamespace
, const String16
& tagName
) 
 706     if (getType() == XMLNode::TYPE_ELEMENT
 
 707             && mNamespaceUri 
== tagNamespace
 
 708             && mElementName 
== tagName
) { 
 712     for (size_t i
=0; i
<mChildren
.size(); i
++) { 
 713         sp
<XMLNode
> found 
= mChildren
.itemAt(i
)->searchElement(tagNamespace
, tagName
); 
 722 sp
<XMLNode
> XMLNode::getChildElement(const String16
& tagNamespace
, const String16
& tagName
) 
 724     for (size_t i
=0; i
<mChildren
.size(); i
++) { 
 725         sp
<XMLNode
> child 
= mChildren
.itemAt(i
); 
 726         if (child
->getType() == XMLNode::TYPE_ELEMENT
 
 727                 && child
->mNamespaceUri 
== tagNamespace
 
 728                 && child
->mElementName 
== tagName
) { 
 736 status_t 
XMLNode::addChild(const sp
<XMLNode
>& child
) 
 738     if (getType() == TYPE_CDATA
) { 
 739         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 740         return UNKNOWN_ERROR
; 
 742     //printf("Adding child %p to parent %p\n", child.get(), this); 
 743     mChildren
.add(child
); 
 747 status_t 
XMLNode::insertChildAt(const sp
<XMLNode
>& child
, size_t index
) 
 749     if (getType() == TYPE_CDATA
) { 
 750         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 751         return UNKNOWN_ERROR
; 
 753     //printf("Adding child %p to parent %p\n", child.get(), this); 
 754     mChildren
.insertAt(child
, index
); 
 758 status_t 
XMLNode::addAttribute(const String16
& ns
, const String16
& name
, 
 759                                const String16
& value
) 
 761     if (getType() == TYPE_CDATA
) { 
 762         SourcePos(mFilename
, getStartLineNumber()).error("Child to CDATA node."); 
 763         return UNKNOWN_ERROR
; 
 766     if (ns 
!= RESOURCES_TOOLS_NAMESPACE
) { 
 768         e
.index 
= mNextAttributeIndex
++; 
 773         mAttributeOrder
.add(e
.index
, mAttributes
.size()-1); 
 778 void XMLNode::setAttributeResID(size_t attrIdx
, uint32_t resId
) 
 780     attribute_entry
& e 
= mAttributes
.editItemAt(attrIdx
); 
 782         mAttributeOrder
.removeItem(e
.nameResId
); 
 784         mAttributeOrder
.removeItem(e
.index
); 
 786     NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n", 
 787             String8(getElementName()).string(), 
 788             String8(mAttributes
.itemAt(attrIdx
).name
).string(), 
 789             String8(mAttributes
.itemAt(attrIdx
).string
).string(), 
 791     mAttributes
.editItemAt(attrIdx
).nameResId 
= resId
; 
 792     mAttributeOrder
.add(resId
, attrIdx
); 
 795 status_t 
XMLNode::appendChars(const String16
& chars
) 
 797     if (getType() != TYPE_CDATA
) { 
 798         SourcePos(mFilename
, getStartLineNumber()).error("Adding characters to element node."); 
 799         return UNKNOWN_ERROR
; 
 801     mChars
.append(chars
); 
 805 status_t 
XMLNode::appendComment(const String16
& comment
) 
 807     if (mComment
.size() > 0) { 
 808         mComment
.append(String16("\n")); 
 810     mComment
.append(comment
); 
 814 void XMLNode::setStartLineNumber(int32_t line
) 
 816     mStartLineNumber 
= line
; 
 819 void XMLNode::setEndLineNumber(int32_t line
) 
 821     mEndLineNumber 
= line
; 
 824 void XMLNode::removeWhitespace(bool stripAll
, const char** cDataTags
) 
 826     //printf("Removing whitespace in %s\n", String8(mElementName).string()); 
 827     size_t N 
= mChildren
.size(); 
 829         String8 
tag(mElementName
); 
 830         const char** p 
= cDataTags
; 
 838     for (size_t i
=0; i
<N
; i
++) { 
 839         sp
<XMLNode
> node 
= mChildren
.itemAt(i
); 
 840         if (node
->getType() == TYPE_CDATA
) { 
 841             // This is a CDATA node... 
 842             const char16_t* p 
= node
->mChars
.string(); 
 843             while (*p 
!= 0 && *p 
< 128 && isspace(*p
)) { 
 846             //printf("Space ends at %d in \"%s\"\n", 
 847             //       (int)(p-node->mChars.string()), 
 848             //       String8(node->mChars).string()); 
 852                     mChildren
.removeAt(i
); 
 856                     node
->mChars 
= String16(" "); 
 859                 // Compact leading/trailing whitespace. 
 860                 const char16_t* e 
= node
->mChars
.string()+node
->mChars
.size()-1; 
 861                 while (e 
> p 
&& *e 
< 128 && isspace(*e
)) { 
 864                 if (p 
> node
->mChars
.string()) { 
 867                 if (e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 870                 if (p 
> node
->mChars
.string() || 
 871                     e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 872                     String16 
tmp(p
, e
-p
+1); 
 877             node
->removeWhitespace(stripAll
, cDataTags
); 
 882 status_t 
XMLNode::parseValues(const sp
<AaptAssets
>& assets
, 
 883                               ResourceTable
* table
) 
 885     bool hasErrors 
= false; 
 887     if (getType() == TYPE_ELEMENT
) { 
 888         const size_t N 
= mAttributes
.size(); 
 889         String16 
defPackage(assets
->getPackage()); 
 890         for (size_t i
=0; i
<N
; i
++) { 
 891             attribute_entry
& e 
= mAttributes
.editItemAt(i
); 
 892             AccessorCookie 
ac(SourcePos(mFilename
, getStartLineNumber()), String8(e
.name
), 
 894             table
->setCurrentXmlPos(SourcePos(mFilename
, getStartLineNumber())); 
 895             if (!assets
->getIncludedResources() 
 896                     .stringToValue(&e
.value
, &e
.string
, 
 897                                   e
.string
.string(), e
.string
.size(), true, true, 
 898                                   e
.nameResId
, NULL
, &defPackage
, table
, &ac
)) { 
 901             NOISY(printf("Attr %s: type=0x%x, str=%s\n", 
 902                    String8(e
.name
).string(), e
.value
.dataType
, 
 903                    String8(e
.string
).string())); 
 906     const size_t N 
= mChildren
.size(); 
 907     for (size_t i
=0; i
<N
; i
++) { 
 908         status_t err 
= mChildren
.itemAt(i
)->parseValues(assets
, table
); 
 909         if (err 
!= NO_ERROR
) { 
 913     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 916 status_t 
XMLNode::assignResourceIds(const sp
<AaptAssets
>& assets
, 
 917                                     const ResourceTable
* table
) 
 919     bool hasErrors 
= false; 
 921     if (getType() == TYPE_ELEMENT
) { 
 922         String16 
attr("attr"); 
 923         const char* errorMsg
; 
 924         const size_t N 
= mAttributes
.size(); 
 925         for (size_t i
=0; i
<N
; i
++) { 
 926             const attribute_entry
& e 
= mAttributes
.itemAt(i
); 
 927             if (e
.ns
.size() <= 0) continue; 
 929             String16 
pkg(getNamespaceResourcePackage(e
.ns
, &nsIsPublic
)); 
 930             NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", 
 931                     String8(getElementName()).string(), 
 932                     String8(e
.name
).string(), 
 933                     String8(e
.string
).string(), 
 934                     String8(e
.ns
).string(), 
 935                     (nsIsPublic
) ? "public" : "private", 
 936                     String8(pkg
).string())); 
 937             if (pkg
.size() <= 0) continue; 
 938             uint32_t res 
= table 
!= NULL
 
 939                 ? table
->getResId(e
.name
, &attr
, &pkg
, &errorMsg
, nsIsPublic
) 
 940                 : assets
->getIncludedResources(). 
 941                     identifierForName(e
.name
.string(), e
.name
.size(), 
 942                                       attr
.string(), attr
.size(), 
 943                                       pkg
.string(), pkg
.size()); 
 945                 NOISY(printf("XML attribute name %s: resid=0x%08x\n", 
 946                              String8(e
.name
).string(), res
)); 
 947                 setAttributeResID(i
, res
); 
 949                 SourcePos(mFilename
, getStartLineNumber()).error( 
 950                         "No resource identifier found for attribute '%s' in package '%s'\n", 
 951                         String8(e
.name
).string(), String8(pkg
).string()); 
 956     const size_t N 
= mChildren
.size(); 
 957     for (size_t i
=0; i
<N
; i
++) { 
 958         status_t err 
= mChildren
.itemAt(i
)->assignResourceIds(assets
, table
); 
 959         if (err 
< NO_ERROR
) { 
 964     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 967 status_t 
XMLNode::flatten(const sp
<AaptFile
>& dest
, 
 968         bool stripComments
, bool stripRawValues
) const 
 970     StringPool strings 
= StringPool(false, mUTF8
); 
 971     Vector
<uint32_t> resids
; 
 973     // First collect just the strings for attribute names that have a 
 974     // resource ID assigned to them.  This ensures that the resource ID 
 975     // array is compact, and makes it easier to deal with attribute names 
 976     // in different namespaces (and thus with different resource IDs). 
 977     collect_resid_strings(&strings
, &resids
); 
 979     // Next collect all remainibng strings. 
 980     collect_strings(&strings
, &resids
, stripComments
, stripRawValues
); 
 982 #if 0  // No longer compiles 
 983     NOISY(printf("Found strings:\n"); 
 984         const size_t N 
= strings
.size(); 
 985         for (size_t i
=0; i
<N
; i
++) { 
 986             printf("%s\n", String8(strings
.entryAt(i
).string
).string()); 
 991     sp
<AaptFile
> stringPool 
= strings
.createStringBlock(); 
 992     NOISY(aout 
<< "String pool:" 
 993           << HexDump(stringPool
->getData(), stringPool
->getSize()) << endl
); 
 995     ResXMLTree_header header
; 
 996     memset(&header
, 0, sizeof(header
)); 
 997     header
.header
.type 
= htods(RES_XML_TYPE
); 
 998     header
.header
.headerSize 
= htods(sizeof(header
)); 
1000     const size_t basePos 
= dest
->getSize(); 
1001     dest
->writeData(&header
, sizeof(header
)); 
1002     dest
->writeData(stringPool
->getData(), stringPool
->getSize()); 
1004     // If we have resource IDs, write them. 
1005     if (resids
.size() > 0) { 
1006         const size_t resIdsPos 
= dest
->getSize(); 
1007         const size_t resIdsSize 
= 
1008             sizeof(ResChunk_header
)+(sizeof(uint32_t)*resids
.size()); 
1009         ResChunk_header
* idsHeader 
= (ResChunk_header
*) 
1010             (((const uint8_t*)dest
->editData(resIdsPos
+resIdsSize
))+resIdsPos
); 
1011         idsHeader
->type 
= htods(RES_XML_RESOURCE_MAP_TYPE
); 
1012         idsHeader
->headerSize 
= htods(sizeof(*idsHeader
)); 
1013         idsHeader
->size 
= htodl(resIdsSize
); 
1014         uint32_t* ids 
= (uint32_t*)(idsHeader
+1); 
1015         for (size_t i
=0; i
<resids
.size(); i
++) { 
1016             *ids
++ = htodl(resids
[i
]); 
1020     flatten_node(strings
, dest
, stripComments
, stripRawValues
); 
1022     void* data 
= dest
->editData(); 
1023     ResXMLTree_header
* hd 
= (ResXMLTree_header
*)(((uint8_t*)data
)+basePos
); 
1024     size_t size 
= dest
->getSize()-basePos
; 
1025     hd
->header
.size 
= htodl(dest
->getSize()-basePos
); 
1027     NOISY(aout 
<< "XML resource:" 
1028           << HexDump(dest
->getData(), dest
->getSize()) << endl
); 
1030     #if PRINT_STRING_METRICS 
1031     fprintf(stderr
, "**** total xml size: %d / %d%% strings (in %s)\n", 
1032         dest
->getSize(), (stringPool
->getSize()*100)/dest
->getSize(), 
1033         dest
->getPath().string()); 
1039 void XMLNode::print(int indent
) 
1043     for (i
=0; i
<indent
; i
++) { 
1046     if (getType() == TYPE_ELEMENT
) { 
1047         String8 
elemNs(getNamespaceUri()); 
1048         if (elemNs
.size() > 0) { 
1051         printf("%s E: %s%s", prefix
.string(), 
1052                elemNs
.string(), String8(getElementName()).string()); 
1053         int N 
= mAttributes
.size(); 
1054         for (i
=0; i
<N
; i
++) { 
1055             ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1061             const attribute_entry
& attr 
= mAttributes
.itemAt(idx
); 
1062             String8 
attrNs(attr
.ns
); 
1063             if (attrNs
.size() > 0) { 
1066             if (attr
.nameResId
) { 
1067                 printf("%s%s(0x%08x)", attrNs
.string(), 
1068                        String8(attr
.name
).string(), attr
.nameResId
); 
1070                 printf("%s%s", attrNs
.string(), String8(attr
.name
).string()); 
1072             printf("=%s", String8(attr
.string
).string()); 
1075     } else if (getType() == TYPE_NAMESPACE
) { 
1076         printf("%s N: %s=%s\n", prefix
.string(), 
1077                getNamespacePrefix().size() > 0 
1078                     ? String8(getNamespacePrefix()).string() : "<DEF>", 
1079                String8(getNamespaceUri()).string()); 
1081         printf("%s C: \"%s\"\n", prefix
.string(), String8(getCData()).string()); 
1083     int N 
= mChildren
.size(); 
1084     for (i
=0; i
<N
; i
++) { 
1085         mChildren
.itemAt(i
)->print(indent
+1); 
1089 static void splitName(const char* name
, String16
* outNs
, String16
* outName
) 
1091     const char* p 
= name
; 
1092     while (*p 
!= 0 && *p 
!= 1) { 
1096         *outNs 
= String16(); 
1097         *outName 
= String16(name
); 
1099         *outNs 
= String16(name
, (p
-name
)); 
1100         *outName 
= String16(p
+1); 
1105 XMLNode::startNamespace(void *userData
, const char *prefix
, const char *uri
) 
1107     NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix
, uri
)); 
1108     ParseState
* st 
= (ParseState
*)userData
; 
1109     sp
<XMLNode
> node 
= XMLNode::newNamespace(st
->filename
,  
1110             String16(prefix 
!= NULL 
? prefix 
: ""), String16(uri
)); 
1111     node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1112     if (st
->stack
.size() > 0) { 
1113         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
1117     st
->stack
.push(node
); 
1121 XMLNode::startElement(void *userData
, const char *name
, const char **atts
) 
1123     NOISY_PARSE(printf("Start Element: %s\n", name
)); 
1124     ParseState
* st 
= (ParseState
*)userData
; 
1125     String16 ns16
, name16
; 
1126     splitName(name
, &ns16
, &name16
); 
1127     sp
<XMLNode
> node 
= XMLNode::newElement(st
->filename
, ns16
, name16
); 
1128     node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1129     if (st
->pendingComment
.size() > 0) { 
1130         node
->appendComment(st
->pendingComment
); 
1131         st
->pendingComment 
= String16(); 
1133     if (st
->stack
.size() > 0) { 
1134         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
1138     st
->stack
.push(node
); 
1140     for (int i 
= 0; atts
[i
]; i 
+= 2) { 
1141         splitName(atts
[i
], &ns16
, &name16
); 
1142         node
->addAttribute(ns16
, name16
, String16(atts
[i
+1])); 
1147 XMLNode::characterData(void *userData
, const XML_Char 
*s
, int len
) 
1149     NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s
, len
).string())); 
1150     ParseState
* st 
= (ParseState
*)userData
; 
1151     sp
<XMLNode
> node 
= NULL
; 
1152     if (st
->stack
.size() == 0) { 
1155     sp
<XMLNode
> parent 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1156     if (parent 
!= NULL 
&& parent
->getChildren().size() > 0) { 
1157         node 
= parent
->getChildren()[parent
->getChildren().size()-1]; 
1158         if (node
->getType() != TYPE_CDATA
) { 
1159             // Last node is not CDATA, need to make a new node. 
1165         node 
= XMLNode::newCData(st
->filename
); 
1166         node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1167         parent
->addChild(node
); 
1170     node
->appendChars(String16(s
, len
)); 
1174 XMLNode::endElement(void *userData
, const char *name
) 
1176     NOISY_PARSE(printf("End Element: %s\n", name
)); 
1177     ParseState
* st 
= (ParseState
*)userData
; 
1178     sp
<XMLNode
> node 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1179     node
->setEndLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1180     if (st
->pendingComment
.size() > 0) { 
1181         node
->appendComment(st
->pendingComment
); 
1182         st
->pendingComment 
= String16(); 
1184     String16 ns16
, name16
; 
1185     splitName(name
, &ns16
, &name16
); 
1186     LOG_ALWAYS_FATAL_IF(node
->getElementNamespace() != ns16
 
1187                         || node
->getElementName() != name16
, 
1188                         "Bad end element %s", name
); 
1193 XMLNode::endNamespace(void *userData
, const char *prefix
) 
1195     const char* nonNullPrefix 
= prefix 
!= NULL 
? prefix 
: ""; 
1196     NOISY_PARSE(printf("End Namespace: %s\n", prefix
)); 
1197     ParseState
* st 
= (ParseState
*)userData
; 
1198     sp
<XMLNode
> node 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1199     node
->setEndLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1200     LOG_ALWAYS_FATAL_IF(node
->getNamespacePrefix() != String16(nonNullPrefix
), 
1201                         "Bad end namespace %s", prefix
); 
1206 XMLNode::commentData(void *userData
, const char *comment
) 
1208     NOISY_PARSE(printf("Comment: %s\n", comment
)); 
1209     ParseState
* st 
= (ParseState
*)userData
; 
1210     if (st
->pendingComment
.size() > 0) { 
1211         st
->pendingComment
.append(String16("\n")); 
1213     st
->pendingComment
.append(String16(comment
)); 
1216 status_t 
XMLNode::collect_strings(StringPool
* dest
, Vector
<uint32_t>* outResIds
, 
1217         bool stripComments
, bool stripRawValues
) const 
1219     collect_attr_strings(dest
, outResIds
, true); 
1222     if (RESOURCES_TOOLS_NAMESPACE 
!= mNamespaceUri
) { 
1223         if (mNamespacePrefix
.size() > 0) { 
1224             dest
->add(mNamespacePrefix
, true); 
1226         if (mNamespaceUri
.size() > 0) { 
1227             dest
->add(mNamespaceUri
, true); 
1230     if (mElementName
.size() > 0) { 
1231         dest
->add(mElementName
, true); 
1234     if (!stripComments 
&& mComment
.size() > 0) { 
1235         dest
->add(mComment
, true); 
1238     const int NA 
= mAttributes
.size(); 
1240     for (i
=0; i
<NA
; i
++) { 
1241         const attribute_entry
& ae 
= mAttributes
.itemAt(i
); 
1242         if (ae
.ns
.size() > 0) { 
1243             dest
->add(ae
.ns
, true); 
1245         if (!stripRawValues 
|| ae
.needStringValue()) { 
1246             dest
->add(ae
.string
, true); 
1249         if (ae.value.dataType == Res_value::TYPE_NULL 
1250                 || ae.value.dataType == Res_value::TYPE_STRING) { 
1251             dest->add(ae.string, true); 
1256     if (mElementName
.size() == 0) { 
1257         // If not an element, include the CDATA, even if it is empty. 
1258         dest
->add(mChars
, true); 
1261     const int NC 
= mChildren
.size(); 
1263     for (i
=0; i
<NC
; i
++) { 
1264         mChildren
.itemAt(i
)->collect_strings(dest
, outResIds
, 
1265                 stripComments
, stripRawValues
); 
1271 status_t 
XMLNode::collect_attr_strings(StringPool
* outPool
, 
1272         Vector
<uint32_t>* outResIds
, bool allAttrs
) const { 
1273     const int NA 
= mAttributes
.size(); 
1275     for (int i
=0; i
<NA
; i
++) { 
1276         const attribute_entry
& attr 
= mAttributes
.itemAt(i
); 
1277         uint32_t id 
= attr
.nameResId
; 
1278         if (id 
|| allAttrs
) { 
1279             // See if we have already assigned this resource ID to a pooled 
1281             const Vector
<size_t>* indices 
= outPool
->offsetsForString(attr
.name
); 
1283             if (indices 
!= NULL
) { 
1284                 const int NJ 
= indices
->size(); 
1285                 const size_t NR 
= outResIds
->size(); 
1286                 for (int j
=0; j
<NJ
; j
++) { 
1287                     size_t strIdx 
= indices
->itemAt(j
); 
1290                             // We don't need to assign a resource ID for this one. 
1294                         // Just ignore strings that are out of range of 
1295                         // the currently assigned resource IDs...  we add 
1296                         // strings as we assign the first ID. 
1297                     } else if (outResIds
->itemAt(strIdx
) == id
) { 
1304                 idx 
= outPool
->add(attr
.name
); 
1305                 NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n", 
1306                         String8(attr
.name
).string(), id
, idx
)); 
1308                     while ((ssize_t
)outResIds
->size() <= idx
) { 
1311                     outResIds
->replaceAt(id
, idx
); 
1314             attr
.namePoolIdx 
= idx
; 
1315             NOISY(printf("String %s offset=0x%08x\n", 
1316                          String8(attr
.name
).string(), idx
)); 
1323 status_t 
XMLNode::collect_resid_strings(StringPool
* outPool
, 
1324         Vector
<uint32_t>* outResIds
) const 
1326     collect_attr_strings(outPool
, outResIds
, false); 
1328     const int NC 
= mChildren
.size(); 
1330     for (int i
=0; i
<NC
; i
++) { 
1331         mChildren
.itemAt(i
)->collect_resid_strings(outPool
, outResIds
); 
1337 status_t 
XMLNode::flatten_node(const StringPool
& strings
, const sp
<AaptFile
>& dest
, 
1338         bool stripComments
, bool stripRawValues
) const 
1340     ResXMLTree_node node
; 
1341     ResXMLTree_cdataExt cdataExt
; 
1342     ResXMLTree_namespaceExt namespaceExt
; 
1343     ResXMLTree_attrExt attrExt
; 
1344     const void* extData 
= NULL
; 
1346     ResXMLTree_attribute attr
; 
1347     bool writeCurrentNode 
= true; 
1349     const size_t NA 
= mAttributes
.size(); 
1350     const size_t NC 
= mChildren
.size(); 
1353     LOG_ALWAYS_FATAL_IF(NA 
!= mAttributeOrder
.size(), "Attributes messed up!"); 
1355     const String16 
id16("id"); 
1356     const String16 
class16("class"); 
1357     const String16 
style16("style"); 
1359     const type type 
= getType(); 
1361     memset(&node
, 0, sizeof(node
)); 
1362     memset(&attr
, 0, sizeof(attr
)); 
1363     node
.header
.headerSize 
= htods(sizeof(node
)); 
1364     node
.lineNumber 
= htodl(getStartLineNumber()); 
1365     if (!stripComments
) { 
1366         node
.comment
.index 
= htodl( 
1367             mComment
.size() > 0 ? strings
.offsetForString(mComment
) : -1); 
1368         //if (mComment.size() > 0) { 
1369         //  printf("Flattening comment: %s\n", String8(mComment).string()); 
1372         node
.comment
.index 
= htodl((uint32_t)-1); 
1374     if (type 
== TYPE_ELEMENT
) { 
1375         node
.header
.type 
= htods(RES_XML_START_ELEMENT_TYPE
); 
1377         extSize 
= sizeof(attrExt
); 
1378         memset(&attrExt
, 0, sizeof(attrExt
)); 
1379         if (mNamespaceUri
.size() > 0) { 
1380             attrExt
.ns
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1382             attrExt
.ns
.index 
= htodl((uint32_t)-1); 
1384         attrExt
.name
.index 
= htodl(strings
.offsetForString(mElementName
)); 
1385         attrExt
.attributeStart 
= htods(sizeof(attrExt
)); 
1386         attrExt
.attributeSize 
= htods(sizeof(attr
)); 
1387         attrExt
.attributeCount 
= htods(NA
); 
1388         attrExt
.idIndex 
= htods(0); 
1389         attrExt
.classIndex 
= htods(0); 
1390         attrExt
.styleIndex 
= htods(0); 
1391         for (i
=0; i
<NA
; i
++) { 
1392             ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1393             const attribute_entry
& ae 
= mAttributes
.itemAt(idx
); 
1394             if (ae
.ns
.size() == 0) { 
1395                 if (ae
.name 
== id16
) { 
1396                     attrExt
.idIndex 
= htods(i
+1); 
1397                 } else if (ae
.name 
== class16
) { 
1398                     attrExt
.classIndex 
= htods(i
+1); 
1399                 } else if (ae
.name 
== style16
) { 
1400                     attrExt
.styleIndex 
= htods(i
+1); 
1404     } else if (type 
== TYPE_NAMESPACE
) { 
1405         if (mNamespaceUri 
== RESOURCES_TOOLS_NAMESPACE
) { 
1406             writeCurrentNode 
= false; 
1408             node
.header
.type 
= htods(RES_XML_START_NAMESPACE_TYPE
); 
1409             extData 
= &namespaceExt
; 
1410             extSize 
= sizeof(namespaceExt
); 
1411             memset(&namespaceExt
, 0, sizeof(namespaceExt
)); 
1412             if (mNamespacePrefix
.size() > 0) { 
1413                 namespaceExt
.prefix
.index 
= htodl(strings
.offsetForString(mNamespacePrefix
)); 
1415                 namespaceExt
.prefix
.index 
= htodl((uint32_t)-1); 
1417             namespaceExt
.prefix
.index 
= htodl(strings
.offsetForString(mNamespacePrefix
)); 
1418             namespaceExt
.uri
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1420         LOG_ALWAYS_FATAL_IF(NA 
!= 0, "Namespace nodes can't have attributes!"); 
1421     } else if (type 
== TYPE_CDATA
) { 
1422         node
.header
.type 
= htods(RES_XML_CDATA_TYPE
); 
1423         extData 
= &cdataExt
; 
1424         extSize 
= sizeof(cdataExt
); 
1425         memset(&cdataExt
, 0, sizeof(cdataExt
)); 
1426         cdataExt
.data
.index 
= htodl(strings
.offsetForString(mChars
)); 
1427         cdataExt
.typedData
.size 
= htods(sizeof(cdataExt
.typedData
)); 
1428         cdataExt
.typedData
.res0 
= 0; 
1429         cdataExt
.typedData
.dataType 
= mCharsValue
.dataType
; 
1430         cdataExt
.typedData
.data 
= htodl(mCharsValue
.data
); 
1431         LOG_ALWAYS_FATAL_IF(NA 
!= 0, "CDATA nodes can't have attributes!"); 
1434     node
.header
.size 
= htodl(sizeof(node
) + extSize 
+ (sizeof(attr
)*NA
)); 
1436     if (writeCurrentNode
) { 
1437         dest
->writeData(&node
, sizeof(node
)); 
1439             dest
->writeData(extData
, extSize
); 
1443     for (i
=0; i
<NA
; i
++) { 
1444         ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1445         const attribute_entry
& ae 
= mAttributes
.itemAt(idx
); 
1446         if (ae
.ns
.size() > 0) { 
1447             attr
.ns
.index 
= htodl(strings
.offsetForString(ae
.ns
)); 
1449             attr
.ns
.index 
= htodl((uint32_t)-1); 
1451         attr
.name
.index 
= htodl(ae
.namePoolIdx
); 
1453         if (!stripRawValues 
|| ae
.needStringValue()) { 
1454             attr
.rawValue
.index 
= htodl(strings
.offsetForString(ae
.string
)); 
1456             attr
.rawValue
.index 
= htodl((uint32_t)-1); 
1458         attr
.typedValue
.size 
= htods(sizeof(attr
.typedValue
)); 
1459         if (ae
.value
.dataType 
== Res_value::TYPE_NULL
 
1460                 || ae
.value
.dataType 
== Res_value::TYPE_STRING
) { 
1461             attr
.typedValue
.res0 
= 0; 
1462             attr
.typedValue
.dataType 
= Res_value::TYPE_STRING
; 
1463             attr
.typedValue
.data 
= htodl(strings
.offsetForString(ae
.string
)); 
1465             attr
.typedValue
.res0 
= 0; 
1466             attr
.typedValue
.dataType 
= ae
.value
.dataType
; 
1467             attr
.typedValue
.data 
= htodl(ae
.value
.data
); 
1469         dest
->writeData(&attr
, sizeof(attr
)); 
1472     for (i
=0; i
<NC
; i
++) { 
1473         status_t err 
= mChildren
.itemAt(i
)->flatten_node(strings
, dest
, 
1474                 stripComments
, stripRawValues
); 
1475         if (err 
!= NO_ERROR
) { 
1480     if (type 
== TYPE_ELEMENT
) { 
1481         ResXMLTree_endElementExt endElementExt
; 
1482         memset(&endElementExt
, 0, sizeof(endElementExt
)); 
1483         node
.header
.type 
= htods(RES_XML_END_ELEMENT_TYPE
); 
1484         node
.header
.size 
= htodl(sizeof(node
)+sizeof(endElementExt
)); 
1485         node
.lineNumber 
= htodl(getEndLineNumber()); 
1486         node
.comment
.index 
= htodl((uint32_t)-1); 
1487         endElementExt
.ns
.index 
= attrExt
.ns
.index
; 
1488         endElementExt
.name
.index 
= attrExt
.name
.index
; 
1489         dest
->writeData(&node
, sizeof(node
)); 
1490         dest
->writeData(&endElementExt
, sizeof(endElementExt
)); 
1491     } else if (type 
== TYPE_NAMESPACE
) { 
1492         if (writeCurrentNode
) { 
1493             node
.header
.type 
= htods(RES_XML_END_NAMESPACE_TYPE
); 
1494             node
.lineNumber 
= htodl(getEndLineNumber()); 
1495             node
.comment
.index 
= htodl((uint32_t)-1); 
1496             node
.header
.size 
= htodl(sizeof(node
)+extSize
); 
1497             dest
->writeData(&node
, sizeof(node
)); 
1498             dest
->writeData(extData
, extSize
);