2 // Copyright 2006 The Android Open Source Project 
   4 // Build resource files from raw assets. 
   8 #include "ResourceTable.h" 
  10 #include <host/pseudolocalize.h> 
  11 #include <utils/ByteOrder.h> 
  15 #ifndef HAVE_MS_C_RUNTIME 
  20 #define NOISY_PARSE(x) //x 
  22 const char* const RESOURCES_ROOT_NAMESPACE 
= "http://schemas.android.com/apk/res/"; 
  23 const char* const RESOURCES_ANDROID_NAMESPACE 
= "http://schemas.android.com/apk/res/android"; 
  24 const char* const RESOURCES_ROOT_PRV_NAMESPACE 
= "http://schemas.android.com/apk/prv/res/"; 
  26 const char* const XLIFF_XMLNS 
= "urn:oasis:names:tc:xliff:document:1.2"; 
  27 const char* const ALLOWED_XLIFF_ELEMENTS
[] = { 
  38 bool isWhitespace(const char16_t* str
) 
  40     while (*str 
!= 0 && *str 
< 128 && isspace(*str
)) { 
  46 static const String16 
RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE
); 
  47 static const String16 
RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE
); 
  49 String16 
getNamespaceResourcePackage(String16 namespaceUri
, bool* outIsPublic
) 
  51     //printf("%s starts with %s?\n", String8(namespaceUri).string(), 
  52     //       String8(RESOURCES_PREFIX).string()); 
  55     if (namespaceUri
.startsWith(RESOURCES_PREFIX
)) { 
  56         prefixSize 
= RESOURCES_PREFIX
.size(); 
  57     } else if (namespaceUri
.startsWith(RESOURCES_PRV_PREFIX
)) { 
  59         prefixSize 
= RESOURCES_PRV_PREFIX
.size(); 
  61         if (outIsPublic
) *outIsPublic 
= isPublic
; // = true 
  66     //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string()); 
  67     if (outIsPublic
) *outIsPublic 
= isPublic
; 
  68     return String16(namespaceUri
, namespaceUri
.size()-prefixSize
, prefixSize
); 
  71 status_t 
parseStyledString(Bundle
* bundle
, 
  74                            const String16
& endTag
, 
  76                            Vector
<StringPool::entry_style_span
>* outSpans
, 
  79     Vector
<StringPool::entry_style_span
> spanStack
; 
  84     bool firstTime 
= true; 
  87     ResXMLTree::event_code_t code
; 
  88     while ((code
=inXml
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
  90         if (code 
== ResXMLTree::TEXT
) { 
  91             String16 
text(inXml
->getText(&len
)); 
  92             if (firstTime 
&& text
.size() > 0) { 
  94                 if (text
.string()[0] == '@') { 
  95                     // If this is a resource reference, don't do the pseudoloc. 
  96                     pseudolocalize 
= false; 
  99             if (xliffDepth 
== 0 && pseudolocalize
) { 
 100                 std::string 
orig(String8(text
).string()); 
 101                 std::string pseudo 
= pseudolocalize_string(orig
); 
 102                 curString
.append(String16(String8(pseudo
.c_str()))); 
 104                 curString
.append(text
); 
 106         } else if (code 
== ResXMLTree::START_TAG
) { 
 107             const String16 
element16(inXml
->getElementName(&len
)); 
 108             const String8 
element8(element16
); 
 111             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 113                 ns 
= (const uint16_t*)"\0\0"; 
 116             const String8 
nspace(String16(ns
, nslen
)); 
 117             if (nspace 
== XLIFF_XMLNS
) { 
 118                 const int N 
= sizeof(ALLOWED_XLIFF_ELEMENTS
)/sizeof(ALLOWED_XLIFF_ELEMENTS
[0]); 
 119                 for (int i
=0; i
<N
; i
++) { 
 120                     if (element8 
== ALLOWED_XLIFF_ELEMENTS
[i
]) { 
 122                         // in this case, treat it like it was just text, in other words, do nothing 
 123                         // here and silently drop this element 
 128                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 129                             "Found unsupported XLIFF tag <%s>\n", 
 131                     return UNKNOWN_ERROR
; 
 137             if (outSpans 
== NULL
) { 
 138                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 139                         "Found style tag <%s> where styles are not allowed\n", element8
.string()); 
 140                 return UNKNOWN_ERROR
; 
 143             if (!ResTable::collectString(outString
, curString
.string(), 
 144                                          curString
.size(), false, &errorMsg
, true)) { 
 145                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error("%s (in %s)\n", 
 146                         errorMsg
, String8(curString
).string()); 
 147                 return UNKNOWN_ERROR
; 
 149             rawString
.append(curString
); 
 150             curString 
= String16(); 
 152             StringPool::entry_style_span span
; 
 153             span
.name 
= element16
; 
 154             for (size_t ai
=0; ai
<inXml
->getAttributeCount(); ai
++) { 
 155                 span
.name
.append(String16(";")); 
 156                 const char16_t* str 
= inXml
->getAttributeName(ai
, &len
); 
 157                 span
.name
.append(str
, len
); 
 158                 span
.name
.append(String16("=")); 
 159                 str 
= inXml
->getAttributeStringValue(ai
, &len
); 
 160                 span
.name
.append(str
, len
); 
 162             //printf("Span: %s\n", String8(span.name).string()); 
 163             span
.span
.firstChar 
= span
.span
.lastChar 
= outString
->size(); 
 164             spanStack
.push(span
); 
 166         } else if (code 
== ResXMLTree::END_TAG
) { 
 168             const uint16_t* ns 
= inXml
->getElementNamespace(&nslen
); 
 170                 ns 
= (const uint16_t*)"\0\0"; 
 173             const String8 
nspace(String16(ns
, nslen
)); 
 174             if (nspace 
== XLIFF_XMLNS
) { 
 178             if (!ResTable::collectString(outString
, curString
.string(), 
 179                                          curString
.size(), false, &errorMsg
, true)) { 
 180                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error("%s (in %s)\n", 
 181                         errorMsg
, String8(curString
).string()); 
 182                 return UNKNOWN_ERROR
; 
 184             rawString
.append(curString
); 
 185             curString 
= String16(); 
 187             if (spanStack
.size() == 0) { 
 188                 if (strcmp16(inXml
->getElementName(&len
), endTag
.string()) != 0) { 
 189                     SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 190                             "Found tag %s where <%s> close is expected\n", 
 191                             String8(inXml
->getElementName(&len
)).string(), 
 192                             String8(endTag
).string()); 
 193                     return UNKNOWN_ERROR
; 
 197             StringPool::entry_style_span span 
= spanStack
.top(); 
 199             ssize_t semi 
= span
.name
.findFirst(';'); 
 201                 spanTag
.setTo(span
.name
.string(), semi
); 
 203                 spanTag
.setTo(span
.name
); 
 205             if (strcmp16(inXml
->getElementName(&len
), spanTag
.string()) != 0) { 
 206                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 207                         "Found close tag %s where close tag %s is expected\n", 
 208                         String8(inXml
->getElementName(&len
)).string(), 
 209                         String8(spanTag
).string()); 
 210                 return UNKNOWN_ERROR
; 
 213             if (outString
->size() > 0) { 
 214                 span
.span
.lastChar 
= outString
->size()-1; 
 215                 if (span
.span
.lastChar 
>= span
.span
.firstChar
) { 
 223                 fprintf(stderr
, "%s:%d: WARNING: empty '%s' span found in text '%s'\n", 
 224                         fileName
, inXml
->getLineNumber(), 
 225                         String8(spanTag
).string(), String8(*outString
).string()); 
 228         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 233     if (code 
== ResXMLTree::BAD_DOCUMENT
) { 
 234             SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 235                     "Error parsing XML\n"); 
 238     if (outSpans 
!= NULL 
&& outSpans
->size() > 0) { 
 239         if (curString
.size() > 0) { 
 240             if (!ResTable::collectString(outString
, curString
.string(), 
 241                                          curString
.size(), false, &errorMsg
, true)) { 
 242                 SourcePos(String8(fileName
), inXml
->getLineNumber()).error( 
 244                         errorMsg
, String8(curString
).string()); 
 245                 return UNKNOWN_ERROR
; 
 249         // There is no style information, so string processing will happen 
 250         // later as part of the overall type conversion.  Return to the 
 251         // client the raw unprocessed text. 
 252         rawString
.append(curString
); 
 253         outString
->setTo(rawString
); 
 259 struct namespace_entry 
{ 
 264 static String8 
make_prefix(int depth
) 
 268     for (i
=0; i
<depth
; i
++) { 
 274 static String8 
build_namespace(const Vector
<namespace_entry
>& namespaces
, 
 280         const size_t N 
= namespaces
.size(); 
 281         for (size_t i
=0; i
<N
; i
++) { 
 282             const namespace_entry
& ne 
= namespaces
.itemAt(i
); 
 293 void printXMLBlock(ResXMLTree
* block
) 
 297     Vector
<namespace_entry
> namespaces
; 
 299     ResXMLTree::event_code_t code
; 
 301     while ((code
=block
->next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 302         String8 prefix 
= make_prefix(depth
); 
 304         if (code 
== ResXMLTree::START_TAG
) { 
 306             const uint16_t* ns16 
= block
->getElementNamespace(&len
); 
 307             String8 elemNs 
= build_namespace(namespaces
, ns16
); 
 308             const uint16_t* com16 
= block
->getComment(&len
); 
 310                 printf("%s <!-- %s -->\n", prefix
.string(), String8(com16
).string()); 
 312             printf("%sE: %s%s (line=%d)\n", prefix
.string(), elemNs
.string(), 
 313                    String8(block
->getElementName(&len
)).string(), 
 314                    block
->getLineNumber()); 
 315             int N 
= block
->getAttributeCount(); 
 317             prefix 
= make_prefix(depth
); 
 318             for (i
=0; i
<N
; i
++) { 
 319                 uint32_t res 
= block
->getAttributeNameResID(i
); 
 320                 ns16 
= block
->getAttributeNamespace(i
, &len
); 
 321                 String8 ns 
= build_namespace(namespaces
, ns16
); 
 322                 String8 
name(block
->getAttributeName(i
, &len
)); 
 323                 printf("%sA: ", prefix
.string()); 
 325                     printf("%s%s(0x%08x)", ns
.string(), name
.string(), res
); 
 327                     printf("%s%s", ns
.string(), name
.string()); 
 330                 block
->getAttributeValue(i
, &value
); 
 331                 if (value
.dataType 
== Res_value::TYPE_NULL
) { 
 333                 } else if (value
.dataType 
== Res_value::TYPE_REFERENCE
) { 
 334                     printf("=@0x%x", (int)value
.data
); 
 335                 } else if (value
.dataType 
== Res_value::TYPE_ATTRIBUTE
) { 
 336                     printf("=?0x%x", (int)value
.data
); 
 337                 } else if (value
.dataType 
== Res_value::TYPE_STRING
) { 
 339                            String8(block
->getAttributeStringValue(i
, &len
)).string()); 
 341                     printf("=(type 0x%x)0x%x", (int)value
.dataType
, (int)value
.data
); 
 343                 const char16_t* val 
= block
->getAttributeStringValue(i
, &len
); 
 345                     printf(" (Raw: \"%s\")", String8(val
).string()); 
 349         } else if (code 
== ResXMLTree::END_TAG
) { 
 351         } else if (code 
== ResXMLTree::START_NAMESPACE
) { 
 354             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 356                 ns
.prefix 
= String8(prefix16
); 
 360             ns
.uri 
= String8(block
->getNamespaceUri(&len
)); 
 362             printf("%sN: %s=%s\n", prefix
.string(), ns
.prefix
.string(), 
 365         } else if (code 
== ResXMLTree::END_NAMESPACE
) { 
 367             const namespace_entry
& ns 
= namespaces
.top(); 
 369             const uint16_t* prefix16 
= block
->getNamespacePrefix(&len
); 
 372                 pr 
= String8(prefix16
); 
 376             if (ns
.prefix 
!= pr
) { 
 377                 prefix 
= make_prefix(depth
); 
 378                 printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n", 
 379                         prefix
.string(), pr
.string(), ns
.prefix
.string()); 
 381             String8 uri 
= String8(block
->getNamespaceUri(&len
)); 
 383                 prefix 
= make_prefix(depth
); 
 384                 printf("%s *** BAD END NS URI: found=%s, expected=%s\n", 
 385                         prefix
.string(), uri
.string(), ns
.uri
.string()); 
 388         } else if (code 
== ResXMLTree::TEXT
) { 
 390             printf("%sC: \"%s\"\n", prefix
.string(), String8(block
->getText(&len
)).string()); 
 397 status_t 
parseXMLResource(const sp
<AaptFile
>& file
, ResXMLTree
* outTree
, 
 398                           bool stripAll
, bool keepComments
, 
 399                           const char** cDataTags
) 
 401     sp
<XMLNode
> root 
= XMLNode::parse(file
); 
 403         return UNKNOWN_ERROR
; 
 405     root
->removeWhitespace(stripAll
, cDataTags
); 
 407     NOISY(printf("Input XML from %s:\n", (const char*)file
->getPrintableSource())); 
 408     NOISY(root
->print()); 
 409     sp
<AaptFile
> rsc 
= new AaptFile(String8(), AaptGroupEntry(), String8()); 
 410     status_t err 
= root
->flatten(rsc
, !keepComments
, false); 
 411     if (err 
!= NO_ERROR
) { 
 414     err 
= outTree
->setTo(rsc
->getData(), rsc
->getSize(), true); 
 415     if (err 
!= NO_ERROR
) { 
 419     NOISY(printf("Output XML:\n")); 
 420     NOISY(printXMLBlock(outTree
)); 
 425 sp
<XMLNode
> XMLNode::parse(const sp
<AaptFile
>& file
) 
 428     int fd 
= open(file
->getSourceFile().string(), O_RDONLY 
| O_BINARY
); 
 430         SourcePos(file
->getSourceFile(), -1).error("Unable to open file for read: %s", 
 435     XML_Parser parser 
= XML_ParserCreateNS(NULL
, 1); 
 437     state
.filename 
= file
->getPrintableSource(); 
 438     state
.parser 
= parser
; 
 439     XML_SetUserData(parser
, &state
); 
 440     XML_SetElementHandler(parser
, startElement
, endElement
); 
 441     XML_SetNamespaceDeclHandler(parser
, startNamespace
, endNamespace
); 
 442     XML_SetCharacterDataHandler(parser
, characterData
); 
 443     XML_SetCommentHandler(parser
, commentData
); 
 448         len 
= read(fd
, buf
, sizeof(buf
)); 
 449         done 
= len 
< (ssize_t
)sizeof(buf
); 
 451             SourcePos(file
->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno
)); 
 455         if (XML_Parse(parser
, buf
, len
, done
) == XML_STATUS_ERROR
) { 
 456             SourcePos(file
->getSourceFile(), (int)XML_GetCurrentLineNumber(parser
)).error( 
 457                     "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser
))); 
 463     XML_ParserFree(parser
); 
 464     if (state
.root 
== NULL
) { 
 465         SourcePos(file
->getSourceFile(), -1).error("No XML data generated when parsing"); 
 471 XMLNode::XMLNode(const String8
& filename
, const String16
& s1
, const String16
& s2
, bool isNamespace
) 
 472     : mNextAttributeIndex(0x80000000) 
 473     , mFilename(filename
) 
 474     , mStartLineNumber(0) 
 478         mNamespacePrefix 
= s1
; 
 486 XMLNode::XMLNode(const String8
& filename
) 
 487     : mFilename(filename
) 
 491 XMLNode::type 
XMLNode::getType() const 
 493     if (mElementName
.size() != 0) { 
 496     if (mNamespaceUri
.size() != 0) { 
 497         return TYPE_NAMESPACE
; 
 502 const String16
& XMLNode::getNamespacePrefix() const 
 504     return mNamespacePrefix
; 
 507 const String16
& XMLNode::getNamespaceUri() const 
 509     return mNamespaceUri
; 
 512 const String16
& XMLNode::getElementNamespace() const 
 514     return mNamespaceUri
; 
 517 const String16
& XMLNode::getElementName() const 
 522 const Vector
<sp
<XMLNode
> >& XMLNode::getChildren() const 
 527 const Vector
<XMLNode::attribute_entry
>& 
 528     XMLNode::getAttributes() const 
 533 const String16
& XMLNode::getCData() const 
 538 const String16
& XMLNode::getComment() const 
 543 int32_t XMLNode::getStartLineNumber() const 
 545     return mStartLineNumber
; 
 548 int32_t XMLNode::getEndLineNumber() const 
 550     return mEndLineNumber
; 
 553 status_t 
XMLNode::addChild(const sp
<XMLNode
>& child
) 
 555     if (getType() == TYPE_CDATA
) { 
 556         SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node."); 
 557         return UNKNOWN_ERROR
; 
 559     //printf("Adding child %p to parent %p\n", child.get(), this); 
 560     mChildren
.add(child
); 
 564 status_t 
XMLNode::addAttribute(const String16
& ns
, const String16
& name
, 
 565                                const String16
& value
) 
 567     if (getType() == TYPE_CDATA
) { 
 568         SourcePos(mFilename
, getStartLineNumber()).error("Child to CDATA node."); 
 569         return UNKNOWN_ERROR
; 
 572     e
.index 
= mNextAttributeIndex
++; 
 577     mAttributeOrder
.add(e
.index
, mAttributes
.size()-1); 
 581 void XMLNode::setAttributeResID(size_t attrIdx
, uint32_t resId
) 
 583     attribute_entry
& e 
= mAttributes
.editItemAt(attrIdx
); 
 585         mAttributeOrder
.removeItem(e
.nameResId
); 
 587         mAttributeOrder
.removeItem(e
.index
); 
 589     NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n", 
 590             String8(getElementName()).string(), 
 591             String8(mAttributes
.itemAt(attrIdx
).name
).string(), 
 592             String8(mAttributes
.itemAt(attrIdx
).string
).string(), 
 594     mAttributes
.editItemAt(attrIdx
).nameResId 
= resId
; 
 595     mAttributeOrder
.add(resId
, attrIdx
); 
 598 status_t 
XMLNode::appendChars(const String16
& chars
) 
 600     if (getType() != TYPE_CDATA
) { 
 601         SourcePos(mFilename
, getStartLineNumber()).error("Adding characters to element node."); 
 602         return UNKNOWN_ERROR
; 
 604     mChars
.append(chars
); 
 608 status_t 
XMLNode::appendComment(const String16
& comment
) 
 610     if (mComment
.size() > 0) { 
 611         mComment
.append(String16("\n")); 
 613     mComment
.append(comment
); 
 617 void XMLNode::setStartLineNumber(int32_t line
) 
 619     mStartLineNumber 
= line
; 
 622 void XMLNode::setEndLineNumber(int32_t line
) 
 624     mEndLineNumber 
= line
; 
 627 void XMLNode::removeWhitespace(bool stripAll
, const char** cDataTags
) 
 629     //printf("Removing whitespace in %s\n", String8(mElementName).string()); 
 630     size_t N 
= mChildren
.size(); 
 632         String8 
tag(mElementName
); 
 633         const char** p 
= cDataTags
; 
 641     for (size_t i
=0; i
<N
; i
++) { 
 642         sp
<XMLNode
> node 
= mChildren
.itemAt(i
); 
 643         if (node
->getType() == TYPE_CDATA
) { 
 644             // This is a CDATA node... 
 645             const char16_t* p 
= node
->mChars
.string(); 
 646             while (*p 
!= 0 && *p 
< 128 && isspace(*p
)) { 
 649             //printf("Space ends at %d in \"%s\"\n", 
 650             //       (int)(p-node->mChars.string()), 
 651             //       String8(node->mChars).string()); 
 655                     mChildren
.removeAt(i
); 
 659                     node
->mChars 
= String16(" "); 
 662                 // Compact leading/trailing whitespace. 
 663                 const char16_t* e 
= node
->mChars
.string()+node
->mChars
.size()-1; 
 664                 while (e 
> p 
&& *e 
< 128 && isspace(*e
)) { 
 667                 if (p 
> node
->mChars
.string()) { 
 670                 if (e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 673                 if (p 
> node
->mChars
.string() || 
 674                     e 
< (node
->mChars
.string()+node
->mChars
.size()-1)) { 
 675                     String16 
tmp(p
, e
-p
+1); 
 680             node
->removeWhitespace(stripAll
, cDataTags
); 
 685 status_t 
XMLNode::parseValues(const sp
<AaptAssets
>& assets
, 
 686                               ResourceTable
* table
) 
 688     bool hasErrors 
= false; 
 690     if (getType() == TYPE_ELEMENT
) { 
 691         const size_t N 
= mAttributes
.size(); 
 692         String16 
defPackage(assets
->getPackage()); 
 693         for (size_t i
=0; i
<N
; i
++) { 
 694             attribute_entry
& e 
= mAttributes
.editItemAt(i
); 
 695             AccessorCookie 
ac(SourcePos(mFilename
, getStartLineNumber()), String8(e
.name
), 
 697             table
->setCurrentXmlPos(SourcePos(mFilename
, getStartLineNumber())); 
 698             if (!assets
->getIncludedResources() 
 699                     .stringToValue(&e
.value
, &e
.string
, 
 700                                   e
.string
.string(), e
.string
.size(), true, true, 
 701                                   e
.nameResId
, NULL
, &defPackage
, table
, &ac
)) { 
 704             NOISY(printf("Attr %s: type=0x%x, str=%s\n", 
 705                    String8(e
.name
).string(), e
.value
.dataType
, 
 706                    String8(e
.string
).string())); 
 709     const size_t N 
= mChildren
.size(); 
 710     for (size_t i
=0; i
<N
; i
++) { 
 711         status_t err 
= mChildren
.itemAt(i
)->parseValues(assets
, table
); 
 712         if (err 
!= NO_ERROR
) { 
 716     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 719 status_t 
XMLNode::assignResourceIds(const sp
<AaptAssets
>& assets
, 
 720                                     const ResourceTable
* table
) 
 722     bool hasErrors 
= false; 
 724     if (getType() == TYPE_ELEMENT
) { 
 725         String16 
attr("attr"); 
 726         const char* errorMsg
; 
 727         const size_t N 
= mAttributes
.size(); 
 728         for (size_t i
=0; i
<N
; i
++) { 
 729             const attribute_entry
& e 
= mAttributes
.itemAt(i
); 
 730             if (e
.ns
.size() <= 0) continue; 
 732             String16 
pkg(getNamespaceResourcePackage(e
.ns
, &nsIsPublic
)); 
 733             NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n", 
 734                     String8(getElementName()).string(), 
 735                     String8(e
.name
).string(), 
 736                     String8(e
.string
).string(), 
 737                     String8(e
.ns
).string(), 
 738                     (nsIsPublic
) ? "public" : "private", 
 739                     String8(pkg
).string())); 
 740             if (pkg
.size() <= 0) continue; 
 741             uint32_t res 
= table 
!= NULL
 
 742                 ? table
->getResId(e
.name
, &attr
, &pkg
, &errorMsg
, nsIsPublic
) 
 743                 : assets
->getIncludedResources(). 
 744                     identifierForName(e
.name
.string(), e
.name
.size(), 
 745                                       attr
.string(), attr
.size(), 
 746                                       pkg
.string(), pkg
.size()); 
 748                 NOISY(printf("XML attribute name %s: resid=0x%08x\n", 
 749                              String8(e
.name
).string(), res
)); 
 750                 setAttributeResID(i
, res
); 
 752                 SourcePos(mFilename
, getStartLineNumber()).error( 
 753                         "No resource identifier found for attribute '%s' in package '%s'\n", 
 754                         String8(e
.name
).string(), String8(pkg
).string()); 
 759     const size_t N 
= mChildren
.size(); 
 760     for (size_t i
=0; i
<N
; i
++) { 
 761         status_t err 
= mChildren
.itemAt(i
)->assignResourceIds(assets
, table
); 
 762         if (err 
< NO_ERROR
) { 
 767     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
 770 status_t 
XMLNode::flatten(const sp
<AaptFile
>& dest
, 
 771         bool stripComments
, bool stripRawValues
) const 
 774     Vector
<uint32_t> resids
; 
 776     // First collect just the strings for attribute names that have a 
 777     // resource ID assigned to them.  This ensures that the resource ID 
 778     // array is compact, and makes it easier to deal with attribute names 
 779     // in different namespaces (and thus with different resource IDs). 
 780     collect_resid_strings(&strings
, &resids
); 
 782     // Next collect all remainibng strings. 
 783     collect_strings(&strings
, &resids
, stripComments
, stripRawValues
); 
 785 #if 0  // No longer compiles 
 786     NOISY(printf("Found strings:\n"); 
 787         const size_t N 
= strings
.size(); 
 788         for (size_t i
=0; i
<N
; i
++) { 
 789             printf("%s\n", String8(strings
.entryAt(i
).string
).string()); 
 794     sp
<AaptFile
> stringPool 
= strings
.createStringBlock(); 
 795     NOISY(aout 
<< "String pool:" 
 796           << HexDump(stringPool
->getData(), stringPool
->getSize()) << endl
); 
 798     ResXMLTree_header header
; 
 799     memset(&header
, 0, sizeof(header
)); 
 800     header
.header
.type 
= htods(RES_XML_TYPE
); 
 801     header
.header
.headerSize 
= htods(sizeof(header
)); 
 803     const size_t basePos 
= dest
->getSize(); 
 804     dest
->writeData(&header
, sizeof(header
)); 
 805     dest
->writeData(stringPool
->getData(), stringPool
->getSize()); 
 807     // If we have resource IDs, write them. 
 808     if (resids
.size() > 0) { 
 809         const size_t resIdsPos 
= dest
->getSize(); 
 810         const size_t resIdsSize 
= 
 811             sizeof(ResChunk_header
)+(sizeof(uint32_t)*resids
.size()); 
 812         ResChunk_header
* idsHeader 
= (ResChunk_header
*) 
 813             (((const uint8_t*)dest
->editData(resIdsPos
+resIdsSize
))+resIdsPos
); 
 814         idsHeader
->type 
= htods(RES_XML_RESOURCE_MAP_TYPE
); 
 815         idsHeader
->headerSize 
= htods(sizeof(*idsHeader
)); 
 816         idsHeader
->size 
= htodl(resIdsSize
); 
 817         uint32_t* ids 
= (uint32_t*)(idsHeader
+1); 
 818         for (size_t i
=0; i
<resids
.size(); i
++) { 
 819             *ids
++ = htodl(resids
[i
]); 
 823     flatten_node(strings
, dest
, stripComments
, stripRawValues
); 
 825     void* data 
= dest
->editData(); 
 826     ResXMLTree_header
* hd 
= (ResXMLTree_header
*)(((uint8_t*)data
)+basePos
); 
 827     size_t size 
= dest
->getSize()-basePos
; 
 828     hd
->header
.size 
= htodl(dest
->getSize()-basePos
); 
 830     NOISY(aout 
<< "XML resource:" 
 831           << HexDump(dest
->getData(), dest
->getSize()) << endl
); 
 833     #if PRINT_STRING_METRICS 
 834     fprintf(stderr
, "**** total xml size: %d / %d%% strings (in %s)\n", 
 835         dest
->getSize(), (stringPool
->getSize()*100)/dest
->getSize(), 
 836         dest
->getPath().string()); 
 842 void XMLNode::print(int indent
) 
 846     for (i
=0; i
<indent
; i
++) { 
 849     if (getType() == TYPE_ELEMENT
) { 
 850         String8 
elemNs(getNamespaceUri()); 
 851         if (elemNs
.size() > 0) { 
 854         printf("%s E: %s%s", prefix
.string(), 
 855                elemNs
.string(), String8(getElementName()).string()); 
 856         int N 
= mAttributes
.size(); 
 857         for (i
=0; i
<N
; i
++) { 
 858             ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
 864             const attribute_entry
& attr 
= mAttributes
.itemAt(idx
); 
 865             String8 
attrNs(attr
.ns
); 
 866             if (attrNs
.size() > 0) { 
 869             if (attr
.nameResId
) { 
 870                 printf("%s%s(0x%08x)", attrNs
.string(), 
 871                        String8(attr
.name
).string(), attr
.nameResId
); 
 873                 printf("%s%s", attrNs
.string(), String8(attr
.name
).string()); 
 875             printf("=%s", String8(attr
.string
).string()); 
 878     } else if (getType() == TYPE_NAMESPACE
) { 
 879         printf("%s N: %s=%s\n", prefix
.string(), 
 880                getNamespacePrefix().size() > 0 
 881                     ? String8(getNamespacePrefix()).string() : "<DEF>", 
 882                String8(getNamespaceUri()).string()); 
 884         printf("%s C: \"%s\"\n", prefix
.string(), String8(getCData()).string()); 
 886     int N 
= mChildren
.size(); 
 887     for (i
=0; i
<N
; i
++) { 
 888         mChildren
.itemAt(i
)->print(indent
+1); 
 892 static void splitName(const char* name
, String16
* outNs
, String16
* outName
) 
 894     const char* p 
= name
; 
 895     while (*p 
!= 0 && *p 
!= 1) { 
 900         *outName 
= String16(name
); 
 902         *outNs 
= String16(name
, (p
-name
)); 
 903         *outName 
= String16(p
+1); 
 908 XMLNode::startNamespace(void *userData
, const char *prefix
, const char *uri
) 
 910     NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix
, uri
)); 
 911     ParseState
* st 
= (ParseState
*)userData
; 
 912     sp
<XMLNode
> node 
= XMLNode::newNamespace(st
->filename
,  
 913             String16(prefix 
!= NULL 
? prefix 
: ""), String16(uri
)); 
 914     node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
 915     if (st
->stack
.size() > 0) { 
 916         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
 920     st
->stack
.push(node
); 
 924 XMLNode::startElement(void *userData
, const char *name
, const char **atts
) 
 926     NOISY_PARSE(printf("Start Element: %s\n", name
)); 
 927     ParseState
* st 
= (ParseState
*)userData
; 
 928     String16 ns16
, name16
; 
 929     splitName(name
, &ns16
, &name16
); 
 930     sp
<XMLNode
> node 
= XMLNode::newElement(st
->filename
, ns16
, name16
); 
 931     node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
 932     if (st
->pendingComment
.size() > 0) { 
 933         node
->appendComment(st
->pendingComment
); 
 934         st
->pendingComment 
= String16(); 
 936     if (st
->stack
.size() > 0) { 
 937         st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
); 
 941     st
->stack
.push(node
); 
 943     for (int i 
= 0; atts
[i
]; i 
+= 2) { 
 944         splitName(atts
[i
], &ns16
, &name16
); 
 945         node
->addAttribute(ns16
, name16
, String16(atts
[i
+1])); 
 950 XMLNode::characterData(void *userData
, const XML_Char 
*s
, int len
) 
 952     NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s
, len
).string())); 
 953     ParseState
* st 
= (ParseState
*)userData
; 
 954     sp
<XMLNode
> node 
= NULL
; 
 955     if (st
->stack
.size() == 0) { 
 958     sp
<XMLNode
> parent 
= st
->stack
.itemAt(st
->stack
.size()-1); 
 959     if (parent 
!= NULL 
&& parent
->getChildren().size() > 0) { 
 960         node 
= parent
->getChildren()[parent
->getChildren().size()-1]; 
 961         if (node
->getType() != TYPE_CDATA
) { 
 962             // Last node is not CDATA, need to make a new node. 
 968         node 
= XMLNode::newCData(st
->filename
); 
 969         node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
 970         parent
->addChild(node
); 
 973     node
->appendChars(String16(s
, len
)); 
 977 XMLNode::endElement(void *userData
, const char *name
) 
 979     NOISY_PARSE(printf("End Element: %s\n", name
)); 
 980     ParseState
* st 
= (ParseState
*)userData
; 
 981     sp
<XMLNode
> node 
= st
->stack
.itemAt(st
->stack
.size()-1); 
 982     node
->setEndLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
 983     if (st
->pendingComment
.size() > 0) { 
 984         node
->appendComment(st
->pendingComment
); 
 985         st
->pendingComment 
= String16(); 
 987     String16 ns16
, name16
; 
 988     splitName(name
, &ns16
, &name16
); 
 989     LOG_ALWAYS_FATAL_IF(node
->getElementNamespace() != ns16
 
 990                         || node
->getElementName() != name16
, 
 991                         "Bad end element %s", name
); 
 996 XMLNode::endNamespace(void *userData
, const char *prefix
) 
 998     const char* nonNullPrefix 
= prefix 
!= NULL 
? prefix 
: ""; 
 999     NOISY_PARSE(printf("End Namespace: %s\n", prefix
)); 
1000     ParseState
* st 
= (ParseState
*)userData
; 
1001     sp
<XMLNode
> node 
= st
->stack
.itemAt(st
->stack
.size()-1); 
1002     node
->setEndLineNumber(XML_GetCurrentLineNumber(st
->parser
)); 
1003     LOG_ALWAYS_FATAL_IF(node
->getNamespacePrefix() != String16(nonNullPrefix
), 
1004                         "Bad end namespace %s", prefix
); 
1009 XMLNode::commentData(void *userData
, const char *comment
) 
1011     NOISY_PARSE(printf("Comment: %s\n", comment
)); 
1012     ParseState
* st 
= (ParseState
*)userData
; 
1013     if (st
->pendingComment
.size() > 0) { 
1014         st
->pendingComment
.append(String16("\n")); 
1016     st
->pendingComment
.append(String16(comment
)); 
1019 status_t 
XMLNode::collect_strings(StringPool
* dest
, Vector
<uint32_t>* outResIds
, 
1020         bool stripComments
, bool stripRawValues
) const 
1022     collect_attr_strings(dest
, outResIds
, true); 
1025     if (mNamespacePrefix
.size() > 0) { 
1026         dest
->add(mNamespacePrefix
, true); 
1028     if (mNamespaceUri
.size() > 0) { 
1029         dest
->add(mNamespaceUri
, true); 
1031     if (mElementName
.size() > 0) { 
1032         dest
->add(mElementName
, true); 
1035     if (!stripComments 
&& mComment
.size() > 0) { 
1036         dest
->add(mComment
, true); 
1039     const int NA 
= mAttributes
.size(); 
1041     for (i
=0; i
<NA
; i
++) { 
1042         const attribute_entry
& ae 
= mAttributes
.itemAt(i
); 
1043         if (ae
.ns
.size() > 0) { 
1044             dest
->add(ae
.ns
, true); 
1046         if (!stripRawValues 
|| ae
.needStringValue()) { 
1047             dest
->add(ae
.string
, true); 
1050         if (ae.value.dataType == Res_value::TYPE_NULL 
1051                 || ae.value.dataType == Res_value::TYPE_STRING) { 
1052             dest->add(ae.string, true); 
1057     if (mElementName
.size() == 0) { 
1058         // If not an element, include the CDATA, even if it is empty. 
1059         dest
->add(mChars
, true); 
1062     const int NC 
= mChildren
.size(); 
1064     for (i
=0; i
<NC
; i
++) { 
1065         mChildren
.itemAt(i
)->collect_strings(dest
, outResIds
, 
1066                 stripComments
, stripRawValues
); 
1072 status_t 
XMLNode::collect_attr_strings(StringPool
* outPool
, 
1073         Vector
<uint32_t>* outResIds
, bool allAttrs
) const { 
1074     const int NA 
= mAttributes
.size(); 
1076     for (int i
=0; i
<NA
; i
++) { 
1077         const attribute_entry
& attr 
= mAttributes
.itemAt(i
); 
1078         uint32_t id 
= attr
.nameResId
; 
1079         if (id 
|| allAttrs
) { 
1080             // See if we have already assigned this resource ID to a pooled 
1082             const Vector
<size_t>* indices 
= outPool
->offsetsForString(attr
.name
); 
1084             if (indices 
!= NULL
) { 
1085                 const int NJ 
= indices
->size(); 
1086                 const size_t NR 
= outResIds
->size(); 
1087                 for (int j
=0; j
<NJ
; j
++) { 
1088                     size_t strIdx 
= indices
->itemAt(j
); 
1091                             // We don't need to assign a resource ID for this one. 
1095                         // Just ignore strings that are out of range of 
1096                         // the currently assigned resource IDs...  we add 
1097                         // strings as we assign the first ID. 
1098                     } else if (outResIds
->itemAt(strIdx
) == id
) { 
1105                 idx 
= outPool
->add(attr
.name
); 
1106                 NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n", 
1107                         String8(attr
.name
).string(), id
, idx
)); 
1109                     while ((ssize_t
)outResIds
->size() <= idx
) { 
1112                     outResIds
->replaceAt(id
, idx
); 
1115             attr
.namePoolIdx 
= idx
; 
1116             NOISY(printf("String %s offset=0x%08x\n", 
1117                          String8(attr
.name
).string(), idx
)); 
1124 status_t 
XMLNode::collect_resid_strings(StringPool
* outPool
, 
1125         Vector
<uint32_t>* outResIds
) const 
1127     collect_attr_strings(outPool
, outResIds
, false); 
1129     const int NC 
= mChildren
.size(); 
1131     for (int i
=0; i
<NC
; i
++) { 
1132         mChildren
.itemAt(i
)->collect_resid_strings(outPool
, outResIds
); 
1138 status_t 
XMLNode::flatten_node(const StringPool
& strings
, const sp
<AaptFile
>& dest
, 
1139         bool stripComments
, bool stripRawValues
) const 
1141     ResXMLTree_node node
; 
1142     ResXMLTree_cdataExt cdataExt
; 
1143     ResXMLTree_namespaceExt namespaceExt
; 
1144     ResXMLTree_attrExt attrExt
; 
1145     const void* extData 
= NULL
; 
1147     ResXMLTree_attribute attr
; 
1149     const size_t NA 
= mAttributes
.size(); 
1150     const size_t NC 
= mChildren
.size(); 
1153     LOG_ALWAYS_FATAL_IF(NA 
!= mAttributeOrder
.size(), "Attributes messed up!"); 
1155     const String16 
id16("id"); 
1156     const String16 
class16("class"); 
1157     const String16 
style16("style"); 
1159     const type type 
= getType(); 
1161     memset(&node
, 0, sizeof(node
)); 
1162     memset(&attr
, 0, sizeof(attr
)); 
1163     node
.header
.headerSize 
= htods(sizeof(node
)); 
1164     node
.lineNumber 
= htodl(getStartLineNumber()); 
1165     if (!stripComments
) { 
1166         node
.comment
.index 
= htodl( 
1167             mComment
.size() > 0 ? strings
.offsetForString(mComment
) : -1); 
1168         //if (mComment.size() > 0) { 
1169         //  printf("Flattening comment: %s\n", String8(mComment).string()); 
1172         node
.comment
.index 
= htodl((uint32_t)-1); 
1174     if (type 
== TYPE_ELEMENT
) { 
1175         node
.header
.type 
= htods(RES_XML_START_ELEMENT_TYPE
); 
1177         extSize 
= sizeof(attrExt
); 
1178         memset(&attrExt
, 0, sizeof(attrExt
)); 
1179         if (mNamespaceUri
.size() > 0) { 
1180             attrExt
.ns
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1182             attrExt
.ns
.index 
= htodl((uint32_t)-1); 
1184         attrExt
.name
.index 
= htodl(strings
.offsetForString(mElementName
)); 
1185         attrExt
.attributeStart 
= htods(sizeof(attrExt
)); 
1186         attrExt
.attributeSize 
= htods(sizeof(attr
)); 
1187         attrExt
.attributeCount 
= htods(NA
); 
1188         attrExt
.idIndex 
= htods(0); 
1189         attrExt
.classIndex 
= htods(0); 
1190         attrExt
.styleIndex 
= htods(0); 
1191         for (i
=0; i
<NA
; i
++) { 
1192             ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1193             const attribute_entry
& ae 
= mAttributes
.itemAt(idx
); 
1194             if (ae
.ns
.size() == 0) { 
1195                 if (ae
.name 
== id16
) { 
1196                     attrExt
.idIndex 
= htods(i
+1); 
1197                 } else if (ae
.name 
== class16
) { 
1198                     attrExt
.classIndex 
= htods(i
+1); 
1199                 } else if (ae
.name 
== style16
) { 
1200                     attrExt
.styleIndex 
= htods(i
+1); 
1204     } else if (type 
== TYPE_NAMESPACE
) { 
1205         node
.header
.type 
= htods(RES_XML_START_NAMESPACE_TYPE
); 
1206         extData 
= &namespaceExt
; 
1207         extSize 
= sizeof(namespaceExt
); 
1208         memset(&namespaceExt
, 0, sizeof(namespaceExt
)); 
1209         if (mNamespacePrefix
.size() > 0) { 
1210             namespaceExt
.prefix
.index 
= htodl(strings
.offsetForString(mNamespacePrefix
)); 
1212             namespaceExt
.prefix
.index 
= htodl((uint32_t)-1); 
1214         namespaceExt
.prefix
.index 
= htodl(strings
.offsetForString(mNamespacePrefix
)); 
1215         namespaceExt
.uri
.index 
= htodl(strings
.offsetForString(mNamespaceUri
)); 
1216         LOG_ALWAYS_FATAL_IF(NA 
!= 0, "Namespace nodes can't have attributes!"); 
1217     } else if (type 
== TYPE_CDATA
) { 
1218         node
.header
.type 
= htods(RES_XML_CDATA_TYPE
); 
1219         extData 
= &cdataExt
; 
1220         extSize 
= sizeof(cdataExt
); 
1221         memset(&cdataExt
, 0, sizeof(cdataExt
)); 
1222         cdataExt
.data
.index 
= htodl(strings
.offsetForString(mChars
)); 
1223         cdataExt
.typedData
.size 
= htods(sizeof(cdataExt
.typedData
)); 
1224         cdataExt
.typedData
.res0 
= 0; 
1225         cdataExt
.typedData
.dataType 
= mCharsValue
.dataType
; 
1226         cdataExt
.typedData
.data 
= htodl(mCharsValue
.data
); 
1227         LOG_ALWAYS_FATAL_IF(NA 
!= 0, "CDATA nodes can't have attributes!"); 
1230     node
.header
.size 
= htodl(sizeof(node
) + extSize 
+ (sizeof(attr
)*NA
)); 
1232     dest
->writeData(&node
, sizeof(node
)); 
1234         dest
->writeData(extData
, extSize
); 
1237     for (i
=0; i
<NA
; i
++) { 
1238         ssize_t idx 
= mAttributeOrder
.valueAt(i
); 
1239         const attribute_entry
& ae 
= mAttributes
.itemAt(idx
); 
1240         if (ae
.ns
.size() > 0) { 
1241             attr
.ns
.index 
= htodl(strings
.offsetForString(ae
.ns
)); 
1243             attr
.ns
.index 
= htodl((uint32_t)-1); 
1245         attr
.name
.index 
= htodl(ae
.namePoolIdx
); 
1247         if (!stripRawValues 
|| ae
.needStringValue()) { 
1248             attr
.rawValue
.index 
= htodl(strings
.offsetForString(ae
.string
)); 
1250             attr
.rawValue
.index 
= htodl((uint32_t)-1); 
1252         attr
.typedValue
.size 
= htods(sizeof(attr
.typedValue
)); 
1253         if (ae
.value
.dataType 
== Res_value::TYPE_NULL
 
1254                 || ae
.value
.dataType 
== Res_value::TYPE_STRING
) { 
1255             attr
.typedValue
.res0 
= 0; 
1256             attr
.typedValue
.dataType 
= Res_value::TYPE_STRING
; 
1257             attr
.typedValue
.data 
= htodl(strings
.offsetForString(ae
.string
)); 
1259             attr
.typedValue
.res0 
= 0; 
1260             attr
.typedValue
.dataType 
= ae
.value
.dataType
; 
1261             attr
.typedValue
.data 
= htodl(ae
.value
.data
); 
1263         dest
->writeData(&attr
, sizeof(attr
)); 
1266     for (i
=0; i
<NC
; i
++) { 
1267         status_t err 
= mChildren
.itemAt(i
)->flatten_node(strings
, dest
, 
1268                 stripComments
, stripRawValues
); 
1269         if (err 
!= NO_ERROR
) { 
1274     if (type 
== TYPE_ELEMENT
) { 
1275         ResXMLTree_endElementExt endElementExt
; 
1276         memset(&endElementExt
, 0, sizeof(endElementExt
)); 
1277         node
.header
.type 
= htods(RES_XML_END_ELEMENT_TYPE
); 
1278         node
.header
.size 
= htodl(sizeof(node
)+sizeof(endElementExt
)); 
1279         node
.lineNumber 
= htodl(getEndLineNumber()); 
1280         node
.comment
.index 
= htodl((uint32_t)-1); 
1281         endElementExt
.ns
.index 
= attrExt
.ns
.index
; 
1282         endElementExt
.name
.index 
= attrExt
.name
.index
; 
1283         dest
->writeData(&node
, sizeof(node
)); 
1284         dest
->writeData(&endElementExt
, sizeof(endElementExt
)); 
1285     } else if (type 
== TYPE_NAMESPACE
) { 
1286         node
.header
.type 
= htods(RES_XML_END_NAMESPACE_TYPE
); 
1287         node
.lineNumber 
= htodl(getEndLineNumber()); 
1288         node
.comment
.index 
= htodl((uint32_t)-1); 
1289         node
.header
.size 
= htodl(sizeof(node
)+extSize
); 
1290         dest
->writeData(&node
, sizeof(node
)); 
1291         dest
->writeData(extData
, extSize
);