2 // Copyright 2006 The Android Open Source Project 
   4 // Build resource files from raw assets. 
   7 #include "ResourceTable.h" 
  11 #include <utils/ByteOrder.h> 
  12 #include <utils/ResourceTypes.h> 
  17 status_t 
compileXmlFile(const sp
<AaptAssets
>& assets
, 
  18                         const sp
<AaptFile
>& target
, 
  22     sp
<XMLNode
> root 
= XMLNode::parse(target
); 
  27     return compileXmlFile(assets
, root
, target
, table
, options
); 
  30 status_t 
compileXmlFile(const sp
<AaptAssets
>& assets
, 
  31                         const sp
<AaptFile
>& target
, 
  32                         const sp
<AaptFile
>& outTarget
, 
  36     sp
<XMLNode
> root 
= XMLNode::parse(target
); 
  41     return compileXmlFile(assets
, root
, outTarget
, table
, options
); 
  44 status_t 
compileXmlFile(const sp
<AaptAssets
>& assets
, 
  45                         const sp
<XMLNode
>& root
, 
  46                         const sp
<AaptFile
>& target
, 
  50     if ((options
&XML_COMPILE_STRIP_WHITESPACE
) != 0) { 
  51         root
->removeWhitespace(true, NULL
); 
  52     } else  if ((options
&XML_COMPILE_COMPACT_WHITESPACE
) != 0) { 
  53         root
->removeWhitespace(false, NULL
); 
  56     if ((options
&XML_COMPILE_UTF8
) != 0) { 
  60     bool hasErrors 
= false; 
  62     if ((options
&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
) != 0) { 
  63         status_t err 
= root
->assignResourceIds(assets
, table
); 
  64         if (err 
!= NO_ERROR
) { 
  69     status_t err 
= root
->parseValues(assets
, table
); 
  70     if (err 
!= NO_ERROR
) { 
  78     NOISY(printf("Input XML Resource:\n")); 
  80     err 
= root
->flatten(target
, 
  81             (options
&XML_COMPILE_STRIP_COMMENTS
) != 0, 
  82             (options
&XML_COMPILE_STRIP_RAW_VALUES
) != 0); 
  83     if (err 
!= NO_ERROR
) { 
  87     NOISY(printf("Output XML Resource:\n")); 
  88     NOISY(ResXMLTree tree
; 
  89         tree
.setTo(target
->getData(), target
->getSize()); 
  90         printXMLBlock(&tree
)); 
  92     target
->setCompressionMethod(ZipEntry::kCompressDeflated
); 
 102     const char16_t* name
; 
 105     const char* description
; 
 108 static const char16_t referenceArray
[] = 
 109     { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' }; 
 110 static const char16_t stringArray
[] = 
 111     { 's', 't', 'r', 'i', 'n', 'g' }; 
 112 static const char16_t integerArray
[] = 
 113     { 'i', 'n', 't', 'e', 'g', 'e', 'r' }; 
 114 static const char16_t booleanArray
[] = 
 115     { 'b', 'o', 'o', 'l', 'e', 'a', 'n' }; 
 116 static const char16_t colorArray
[] = 
 117     { 'c', 'o', 'l', 'o', 'r' }; 
 118 static const char16_t floatArray
[] = 
 119     { 'f', 'l', 'o', 'a', 't' }; 
 120 static const char16_t dimensionArray
[] = 
 121     { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' }; 
 122 static const char16_t fractionArray
[] = 
 123     { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' }; 
 124 static const char16_t enumArray
[] = 
 125     { 'e', 'n', 'u', 'm' }; 
 126 static const char16_t flagsArray
[] = 
 127     { 'f', 'l', 'a', 'g', 's' }; 
 129 static const flag_entry gFormatFlags
[] = { 
 130     { referenceArray
, sizeof(referenceArray
)/2, ResTable_map::TYPE_REFERENCE
, 
 131       "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n" 
 132       "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."}, 
 133     { stringArray
, sizeof(stringArray
)/2, ResTable_map::TYPE_STRING
, 
 134       "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." }, 
 135     { integerArray
, sizeof(integerArray
)/2, ResTable_map::TYPE_INTEGER
, 
 136       "an integer value, such as \"<code>100</code>\"." }, 
 137     { booleanArray
, sizeof(booleanArray
)/2, ResTable_map::TYPE_BOOLEAN
, 
 138       "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." }, 
 139     { colorArray
, sizeof(colorArray
)/2, ResTable_map::TYPE_COLOR
, 
 140       "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n" 
 141       "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." }, 
 142     { floatArray
, sizeof(floatArray
)/2, ResTable_map::TYPE_FLOAT
, 
 143       "a floating point value, such as \"<code>1.2</code>\"."}, 
 144     { dimensionArray
, sizeof(dimensionArray
)/2, ResTable_map::TYPE_DIMENSION
, 
 145       "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n" 
 146       "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n" 
 147       "in (inches), mm (millimeters)." }, 
 148     { fractionArray
, sizeof(fractionArray
)/2, ResTable_map::TYPE_FRACTION
, 
 149       "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n" 
 150       "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n" 
 151       "some parent container." }, 
 152     { enumArray
, sizeof(enumArray
)/2, ResTable_map::TYPE_ENUM
, NULL 
}, 
 153     { flagsArray
, sizeof(flagsArray
)/2, ResTable_map::TYPE_FLAGS
, NULL 
}, 
 157 static const char16_t suggestedArray
[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' }; 
 159 static const flag_entry l10nRequiredFlags
[] = { 
 160     { suggestedArray
, sizeof(suggestedArray
)/2, ResTable_map::L10N_SUGGESTED
, NULL 
}, 
 164 static const char16_t nulStr
[] = { 0 }; 
 166 static uint32_t parse_flags(const char16_t* str
, size_t len
, 
 167                              const flag_entry
* flags
, bool* outError 
= NULL
) 
 169     while (len 
> 0 && isspace(*str
)) { 
 173     while (len 
> 0 && isspace(str
[len
-1])) { 
 177     const char16_t* const end 
= str 
+ len
; 
 181         const char16_t* div 
= str
; 
 182         while (div 
< end 
&& *div 
!= '|') { 
 186         const flag_entry
* cur 
= flags
; 
 188             if (strzcmp16(cur
->name
, cur
->nameLen
, str
, div
-str
) == 0) { 
 196             if (outError
) *outError 
= true; 
 200         str 
= div 
< end 
? div
+1 : div
; 
 203     if (outError
) *outError 
= false; 
 207 static String16 
mayOrMust(int type
, int flags
) 
 209     if ((type
&(~flags
)) == 0) { 
 210         return String16("<p>Must"); 
 213     return String16("<p>May"); 
 216 static void appendTypeInfo(ResourceTable
* outTable
, const String16
& pkg
, 
 217         const String16
& typeName
, const String16
& ident
, int type
, 
 218         const flag_entry
* flags
) 
 220     bool hadType 
= false; 
 221     while (flags
->name
) { 
 222         if ((type
&flags
->value
) != 0 && flags
->description 
!= NULL
) { 
 223             String16 
fullMsg(mayOrMust(type
, flags
->value
)); 
 224             fullMsg
.append(String16(" be ")); 
 225             fullMsg
.append(String16(flags
->description
)); 
 226             outTable
->appendTypeComment(pkg
, typeName
, ident
, fullMsg
); 
 231     if (hadType 
&& (type
&ResTable_map::TYPE_REFERENCE
) == 0) { 
 232         outTable
->appendTypeComment(pkg
, typeName
, ident
, 
 233                 String16("<p>This may also be a reference to a resource (in the form\n" 
 234                          "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n" 
 235                          "theme attribute (in the form\n" 
 236                          "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n" 
 237                          "containing a value of this type.")); 
 241 struct PendingAttribute
 
 243     const String16 myPackage
; 
 244     const SourcePos sourcePos
; 
 245     const bool appendComment
; 
 252     PendingAttribute(String16 _package
, const sp
<AaptFile
>& in
, 
 253             ResXMLTree
& block
, bool _appendComment
) 
 254         : myPackage(_package
) 
 255         , sourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 256         , appendComment(_appendComment
) 
 257         , type(ResTable_map::TYPE_ANY
) 
 263     status_t 
createIfNeeded(ResourceTable
* outTable
) 
 265         if (added 
|| hasErrors
) { 
 270         String16 
attr16("attr"); 
 272         if (outTable
->hasBagOrEntry(myPackage
, attr16
, ident
)) { 
 273             sourcePos
.error("Attribute \"%s\" has already been defined\n", 
 274                     String8(ident
).string()); 
 276             return UNKNOWN_ERROR
; 
 280         sprintf(numberStr
, "%d", type
); 
 281         status_t err 
= outTable
->addBag(sourcePos
, myPackage
, 
 282                 attr16
, ident
, String16(""), 
 284                 String16(numberStr
), NULL
, NULL
); 
 285         if (err 
!= NO_ERROR
) { 
 289         outTable
->appendComment(myPackage
, attr16
, ident
, comment
, appendComment
); 
 290         //printf("Attribute %s comment: %s\n", String8(ident).string(), 
 291         //     String8(comment).string()); 
 296 static status_t 
compileAttribute(const sp
<AaptFile
>& in
, 
 298                                  const String16
& myPackage
, 
 299                                  ResourceTable
* outTable
, 
 300                                  String16
* outIdent 
= NULL
, 
 301                                  bool inStyleable 
= false) 
 303     PendingAttribute 
attr(myPackage
, in
, block
, inStyleable
); 
 305     const String16 
attr16("attr"); 
 306     const String16 
id16("id"); 
 308     // Attribute type constants. 
 309     const String16 
enum16("enum"); 
 310     const String16 
flag16("flag"); 
 312     ResXMLTree::event_code_t code
; 
 316     ssize_t identIdx 
= block
.indexOfAttribute(NULL
, "name"); 
 318         attr
.ident 
= String16(block
.getAttributeStringValue(identIdx
, &len
)); 
 320             *outIdent 
= attr
.ident
; 
 323         attr
.sourcePos
.error("A 'name' attribute is required for <attr>\n"); 
 324         attr
.hasErrors 
= true; 
 327     attr
.comment 
= String16( 
 328             block
.getComment(&len
) ? block
.getComment(&len
) : nulStr
); 
 330     ssize_t typeIdx 
= block
.indexOfAttribute(NULL
, "format"); 
 332         String16 typeStr 
= String16(block
.getAttributeStringValue(typeIdx
, &len
)); 
 333         attr
.type 
= parse_flags(typeStr
.string(), typeStr
.size(), gFormatFlags
); 
 334         if (attr
.type 
== 0) { 
 335             attr
.sourcePos
.error("Tag <attr> 'format' attribute value \"%s\" not valid\n", 
 336                     String8(typeStr
).string()); 
 337             attr
.hasErrors 
= true; 
 339         attr
.createIfNeeded(outTable
); 
 340     } else if (!inStyleable
) { 
 341         // Attribute definitions outside of styleables always define the 
 342         // attribute as a generic value. 
 343         attr
.createIfNeeded(outTable
); 
 346     //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type); 
 348     ssize_t minIdx 
= block
.indexOfAttribute(NULL
, "min"); 
 350         String16 val 
= String16(block
.getAttributeStringValue(minIdx
, &len
)); 
 351         if (!ResTable::stringToInt(val
.string(), val
.size(), NULL
)) { 
 352             attr
.sourcePos
.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n", 
 353                     String8(val
).string()); 
 354             attr
.hasErrors 
= true; 
 356         attr
.createIfNeeded(outTable
); 
 357         if (!attr
.hasErrors
) { 
 358             err 
= outTable
->addBag(attr
.sourcePos
, myPackage
, attr16
, attr
.ident
, 
 359                     String16(""), String16("^min"), String16(val
), NULL
, NULL
); 
 360             if (err 
!= NO_ERROR
) { 
 361                 attr
.hasErrors 
= true; 
 366     ssize_t maxIdx 
= block
.indexOfAttribute(NULL
, "max"); 
 368         String16 val 
= String16(block
.getAttributeStringValue(maxIdx
, &len
)); 
 369         if (!ResTable::stringToInt(val
.string(), val
.size(), NULL
)) { 
 370             attr
.sourcePos
.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n", 
 371                     String8(val
).string()); 
 372             attr
.hasErrors 
= true; 
 374         attr
.createIfNeeded(outTable
); 
 375         if (!attr
.hasErrors
) { 
 376             err 
= outTable
->addBag(attr
.sourcePos
, myPackage
, attr16
, attr
.ident
, 
 377                     String16(""), String16("^max"), String16(val
), NULL
, NULL
); 
 378             attr
.hasErrors 
= true; 
 382     if ((minIdx 
>= 0 || maxIdx 
>= 0) && (attr
.type
&ResTable_map::TYPE_INTEGER
) == 0) { 
 383         attr
.sourcePos
.error("Tag <attr> must have format=integer attribute if using max or min\n"); 
 384         attr
.hasErrors 
= true; 
 387     ssize_t l10nIdx 
= block
.indexOfAttribute(NULL
, "localization"); 
 389         const uint16_t* str 
= block
.getAttributeStringValue(l10nIdx
, &len
); 
 391         uint32_t l10n_required 
= parse_flags(str
, len
, l10nRequiredFlags
, &error
); 
 393             attr
.sourcePos
.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n", 
 394                     String8(str
).string()); 
 395             attr
.hasErrors 
= true; 
 397         attr
.createIfNeeded(outTable
); 
 398         if (!attr
.hasErrors
) { 
 400             sprintf(buf
, "%d", l10n_required
); 
 401             err 
= outTable
->addBag(attr
.sourcePos
, myPackage
, attr16
, attr
.ident
, 
 402                     String16(""), String16("^l10n"), String16(buf
), NULL
, NULL
); 
 403             if (err 
!= NO_ERROR
) { 
 404                 attr
.hasErrors 
= true; 
 409     String16 enumOrFlagsComment
; 
 411     while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 412         if (code 
== ResXMLTree::START_TAG
) { 
 413             uint32_t localType 
= 0; 
 414             if (strcmp16(block
.getElementName(&len
), enum16
.string()) == 0) { 
 415                 localType 
= ResTable_map::TYPE_ENUM
; 
 416             } else if (strcmp16(block
.getElementName(&len
), flag16
.string()) == 0) { 
 417                 localType 
= ResTable_map::TYPE_FLAGS
; 
 419                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 420                         .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n", 
 421                         String8(block
.getElementName(&len
)).string()); 
 422                 return UNKNOWN_ERROR
; 
 425             attr
.createIfNeeded(outTable
); 
 427             if (attr
.type 
== ResTable_map::TYPE_ANY
) { 
 428                 // No type was explicitly stated, so supplying enum tags 
 429                 // implicitly creates an enum or flag. 
 433             if ((attr
.type
&(ResTable_map::TYPE_ENUM
|ResTable_map::TYPE_FLAGS
)) == 0) { 
 434                 // Wasn't originally specified as an enum, so update its type. 
 435                 attr
.type 
|= localType
; 
 436                 if (!attr
.hasErrors
) { 
 438                     sprintf(numberStr
, "%d", attr
.type
); 
 439                     err 
= outTable
->addBag(SourcePos(in
->getPrintableSource(), block
.getLineNumber()), 
 440                             myPackage
, attr16
, attr
.ident
, String16(""), 
 441                             String16("^type"), String16(numberStr
), NULL
, NULL
, true); 
 442                     if (err 
!= NO_ERROR
) { 
 443                         attr
.hasErrors 
= true; 
 446             } else if ((uint32_t)(attr
.type
&(ResTable_map::TYPE_ENUM
|ResTable_map::TYPE_FLAGS
)) != localType
) { 
 447                 if (localType 
== ResTable_map::TYPE_ENUM
) { 
 448                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 449                             .error("<enum> attribute can not be used inside a flags format\n"); 
 450                     attr
.hasErrors 
= true; 
 452                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 453                             .error("<flag> attribute can not be used inside a enum format\n"); 
 454                     attr
.hasErrors 
= true; 
 459             ssize_t itemIdentIdx 
= block
.indexOfAttribute(NULL
, "name"); 
 460             if (itemIdentIdx 
>= 0) { 
 461                 itemIdent 
= String16(block
.getAttributeStringValue(itemIdentIdx
, &len
)); 
 463                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 464                         .error("A 'name' attribute is required for <enum> or <flag>\n"); 
 465                 attr
.hasErrors 
= true; 
 469             ssize_t valueIdx 
= block
.indexOfAttribute(NULL
, "value"); 
 471                 value 
= String16(block
.getAttributeStringValue(valueIdx
, &len
)); 
 473                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 474                         .error("A 'value' attribute is required for <enum> or <flag>\n"); 
 475                 attr
.hasErrors 
= true; 
 477             if (!attr
.hasErrors 
&& !ResTable::stringToInt(value
.string(), value
.size(), NULL
)) { 
 478                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 479                         .error("Tag <enum> or <flag> 'value' attribute must be a number," 
 481                         String8(value
).string()); 
 482                 attr
.hasErrors 
= true; 
 485             // Make sure an id is defined for this enum/flag identifier... 
 486             if (!attr
.hasErrors 
&& !outTable
->hasBagOrEntry(itemIdent
, &id16
, &myPackage
)) { 
 487                 err 
= outTable
->startBag(SourcePos(in
->getPrintableSource(), block
.getLineNumber()), 
 488                                          myPackage
, id16
, itemIdent
, String16(), NULL
); 
 489                 if (err 
!= NO_ERROR
) { 
 490                     attr
.hasErrors 
= true; 
 494             if (!attr
.hasErrors
) { 
 495                 if (enumOrFlagsComment
.size() == 0) { 
 496                     enumOrFlagsComment
.append(mayOrMust(attr
.type
, 
 497                             ResTable_map::TYPE_ENUM
|ResTable_map::TYPE_FLAGS
)); 
 498                     enumOrFlagsComment
.append((attr
.type
&ResTable_map::TYPE_ENUM
) 
 499                                        ? String16(" be one of the following constant values.") 
 500                                        : String16(" be one or more (separated by '|') of the following constant values.")); 
 501                     enumOrFlagsComment
.append(String16("</p>\n<table>\n" 
 502                                                 "<colgroup align=\"left\" />\n" 
 503                                                 "<colgroup align=\"left\" />\n" 
 504                                                 "<colgroup align=\"left\" />\n" 
 505                                                 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>")); 
 508                 enumOrFlagsComment
.append(String16("\n<tr><td><code>")); 
 509                 enumOrFlagsComment
.append(itemIdent
); 
 510                 enumOrFlagsComment
.append(String16("</code></td><td>")); 
 511                 enumOrFlagsComment
.append(value
); 
 512                 enumOrFlagsComment
.append(String16("</td><td>")); 
 513                 if (block
.getComment(&len
)) { 
 514                     enumOrFlagsComment
.append(String16(block
.getComment(&len
))); 
 516                 enumOrFlagsComment
.append(String16("</td></tr>")); 
 518                 err 
= outTable
->addBag(SourcePos(in
->getPrintableSource(), block
.getLineNumber()), 
 520                                        attr16
, attr
.ident
, String16(""), 
 521                                        itemIdent
, value
, NULL
, NULL
, false, true); 
 522                 if (err 
!= NO_ERROR
) { 
 523                     attr
.hasErrors 
= true; 
 526         } else if (code 
== ResXMLTree::END_TAG
) { 
 527             if (strcmp16(block
.getElementName(&len
), attr16
.string()) == 0) { 
 530             if ((attr
.type
&ResTable_map::TYPE_ENUM
) != 0) { 
 531                 if (strcmp16(block
.getElementName(&len
), enum16
.string()) != 0) { 
 532                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 533                             .error("Found tag </%s> where </enum> is expected\n", 
 534                             String8(block
.getElementName(&len
)).string()); 
 535                     return UNKNOWN_ERROR
; 
 538                 if (strcmp16(block
.getElementName(&len
), flag16
.string()) != 0) { 
 539                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()) 
 540                             .error("Found tag </%s> where </flag> is expected\n", 
 541                             String8(block
.getElementName(&len
)).string()); 
 542                     return UNKNOWN_ERROR
; 
 548     if (!attr
.hasErrors 
&& attr
.added
) { 
 549         appendTypeInfo(outTable
, myPackage
, attr16
, attr
.ident
, attr
.type
, gFormatFlags
); 
 552     if (!attr
.hasErrors 
&& enumOrFlagsComment
.size() > 0) { 
 553         enumOrFlagsComment
.append(String16("\n</table>")); 
 554         outTable
->appendTypeComment(myPackage
, attr16
, attr
.ident
, enumOrFlagsComment
); 
 561 bool localeIsDefined(const ResTable_config
& config
) 
 563     return config
.locale 
== 0; 
 566 status_t 
parseAndAddBag(Bundle
* bundle
, 
 567                         const sp
<AaptFile
>& in
, 
 569                         const ResTable_config
& config
, 
 570                         const String16
& myPackage
, 
 571                         const String16
& curType
, 
 572                         const String16
& ident
, 
 573                         const String16
& parentIdent
, 
 574                         const String16
& itemIdent
, 
 577                         const String16
& product
, 
 579                         const bool overwrite
, 
 580                         ResourceTable
* outTable
) 
 583     const String16 
item16("item"); 
 586     Vector
<StringPool::entry_style_span
> spans
; 
 587     err 
= parseStyledString(bundle
, in
->getPrintableSource().string(), 
 588                             block
, item16
, &str
, &spans
, isFormatted
, 
 590     if (err 
!= NO_ERROR
) { 
 594     NOISY(printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d " 
 595                  " pid=%s, bag=%s, id=%s: %s\n", 
 596                  config
.language
[0], config
.language
[1], 
 597                  config
.country
[0], config
.country
[1], 
 598                  config
.orientation
, config
.density
, 
 599                  String8(parentIdent
).string(), 
 600                  String8(ident
).string(), 
 601                  String8(itemIdent
).string(), 
 602                  String8(str
).string())); 
 604     err 
= outTable
->addBag(SourcePos(in
->getPrintableSource(), block
->getLineNumber()), 
 605                            myPackage
, curType
, ident
, parentIdent
, itemIdent
, str
, 
 606                            &spans
, &config
, overwrite
, false, curFormat
); 
 611  * Returns true if needle is one of the elements in the comma-separated list 
 612  * haystack, false otherwise. 
 614 bool isInProductList(const String16
& needle
, const String16
& haystack
) { 
 615     const char16_t *needle2 
= needle
.string(); 
 616     const char16_t *haystack2 
= haystack
.string(); 
 617     size_t needlesize 
= needle
.size(); 
 619     while (*haystack2 
!= '\0') { 
 620         if (strncmp16(haystack2
, needle2
, needlesize
) == 0) { 
 621             if (haystack2
[needlesize
] == '\0' || haystack2
[needlesize
] == ',') { 
 626         while (*haystack2 
!= '\0' && *haystack2 
!= ',') { 
 629         if (*haystack2 
== ',') { 
 637 status_t 
parseAndAddEntry(Bundle
* bundle
, 
 638                         const sp
<AaptFile
>& in
, 
 640                         const ResTable_config
& config
, 
 641                         const String16
& myPackage
, 
 642                         const String16
& curType
, 
 643                         const String16
& ident
, 
 644                         const String16
& curTag
, 
 648                         const String16
& product
, 
 650                         const bool overwrite
, 
 651                         ResourceTable
* outTable
) 
 656     Vector
<StringPool::entry_style_span
> spans
; 
 657     err 
= parseStyledString(bundle
, in
->getPrintableSource().string(), block
, 
 658                             curTag
, &str
, curIsStyled 
? &spans 
: NULL
, 
 659                             isFormatted
, pseudolocalize
); 
 661     if (err 
< NO_ERROR
) {  
 666      * If a product type was specified on the command line 
 667      * and also in the string, and the two are not the same, 
 668      * return without adding the string. 
 671     const char *bundleProduct 
= bundle
->getProduct(); 
 672     if (bundleProduct 
== NULL
) { 
 676     if (product
.size() != 0) { 
 678          * If the command-line-specified product is empty, only "default" 
 679          * matches.  Other variants are skipped.  This is so generation 
 680          * of the R.java file when the product is not known is predictable. 
 683         if (bundleProduct
[0] == '\0') { 
 684             if (strcmp16(String16("default").string(), product
.string()) != 0) { 
 689              * The command-line product is not empty. 
 690              * If the product for this string is on the command-line list, 
 691              * it matches.  "default" also matches, but only if nothing 
 692              * else has matched already. 
 695             if (isInProductList(product
, String16(bundleProduct
))) { 
 697             } else if (strcmp16(String16("default").string(), product
.string()) == 0 && 
 698                        !outTable
->hasBagOrEntry(myPackage
, curType
, ident
)) { 
 706     NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", 
 707                  config
.language
[0], config
.language
[1], 
 708                  config
.country
[0], config
.country
[1], 
 709                  config
.orientation
, config
.density
, 
 710                  String8(ident
).string(), String8(str
).string())); 
 712     err 
= outTable
->addEntry(SourcePos(in
->getPrintableSource(), block
->getLineNumber()), 
 713                              myPackage
, curType
, ident
, str
, &spans
, &config
, 
 714                              false, curFormat
, overwrite
); 
 719 status_t 
compileResourceFile(Bundle
* bundle
, 
 720                              const sp
<AaptAssets
>& assets
, 
 721                              const sp
<AaptFile
>& in
, 
 722                              const ResTable_config
& defParams
, 
 723                              const bool overwrite
, 
 724                              ResourceTable
* outTable
) 
 727     status_t err 
= parseXMLResource(in
, &block
, false, true); 
 728     if (err 
!= NO_ERROR
) { 
 733     const String16 
resources16("resources"); 
 735     // Identifier declaration tags. 
 736     const String16 
declare_styleable16("declare-styleable"); 
 737     const String16 
attr16("attr"); 
 739     // Data creation organizational tags. 
 740     const String16 
string16("string"); 
 741     const String16 
drawable16("drawable"); 
 742     const String16 
color16("color"); 
 743     const String16 
bool16("bool"); 
 744     const String16 
integer16("integer"); 
 745     const String16 
dimen16("dimen"); 
 746     const String16 
fraction16("fraction"); 
 747     const String16 
style16("style"); 
 748     const String16 
plurals16("plurals"); 
 749     const String16 
array16("array"); 
 750     const String16 
string_array16("string-array"); 
 751     const String16 
integer_array16("integer-array"); 
 752     const String16 
public16("public"); 
 753     const String16 
public_padding16("public-padding"); 
 754     const String16 
private_symbols16("private-symbols"); 
 755     const String16 
add_resource16("add-resource"); 
 756     const String16 
skip16("skip"); 
 757     const String16 
eat_comment16("eat-comment"); 
 759     // Data creation tags. 
 760     const String16 
bag16("bag"); 
 761     const String16 
item16("item"); 
 763     // Attribute type constants. 
 764     const String16 
enum16("enum"); 
 767     const String16 
other16("other"); 
 768     const String16 
quantityOther16("^other"); 
 769     const String16 
zero16("zero"); 
 770     const String16 
quantityZero16("^zero"); 
 771     const String16 
one16("one"); 
 772     const String16 
quantityOne16("^one"); 
 773     const String16 
two16("two"); 
 774     const String16 
quantityTwo16("^two"); 
 775     const String16 
few16("few"); 
 776     const String16 
quantityFew16("^few"); 
 777     const String16 
many16("many"); 
 778     const String16 
quantityMany16("^many"); 
 780     // useful attribute names and special values 
 781     const String16 
name16("name"); 
 782     const String16 
translatable16("translatable"); 
 783     const String16 
formatted16("formatted"); 
 784     const String16 
false16("false"); 
 785     const String16 
product16("product"); 
 787     const String16 
myPackage(assets
->getPackage()); 
 789     bool hasErrors 
= false; 
 791     bool fileIsTranslatable 
= true; 
 792     if (strstr(in
->getPrintableSource().string(), "donottranslate") != NULL
) { 
 793         fileIsTranslatable 
= false; 
 796     DefaultKeyedVector
<String16
, uint32_t> nextPublicId(0); 
 798     ResXMLTree::event_code_t code
; 
 801     } while (code 
== ResXMLTree::START_NAMESPACE
); 
 804     if (code 
!= ResXMLTree::START_TAG
) { 
 805         SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
 806                 "No start tag found\n"); 
 807         return UNKNOWN_ERROR
; 
 809     if (strcmp16(block
.getElementName(&len
), resources16
.string()) != 0) { 
 810         SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
 811                 "Invalid start tag %s\n", String8(block
.getElementName(&len
)).string()); 
 812         return UNKNOWN_ERROR
; 
 815     ResTable_config 
curParams(defParams
); 
 817     ResTable_config 
pseudoParams(curParams
); 
 818         pseudoParams
.language
[0] = 'z'; 
 819         pseudoParams
.language
[1] = 'z'; 
 820         pseudoParams
.country
[0] = 'Z'; 
 821         pseudoParams
.country
[1] = 'Z'; 
 823     while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 824         if (code 
== ResXMLTree::START_TAG
) { 
 825             const String16
* curTag 
= NULL
; 
 827             int32_t curFormat 
= ResTable_map::TYPE_ANY
; 
 828             bool curIsBag 
= false; 
 829             bool curIsBagReplaceOnOverwrite 
= false; 
 830             bool curIsStyled 
= false; 
 831             bool curIsPseudolocalizable 
= false; 
 832             bool curIsFormatted 
= fileIsTranslatable
; 
 834             bool localHasErrors 
= false; 
 836             if (strcmp16(block
.getElementName(&len
), skip16
.string()) == 0) { 
 837                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT
 
 838                         && code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 839                     if (code 
== ResXMLTree::END_TAG
) { 
 840                         if (strcmp16(block
.getElementName(&len
), skip16
.string()) == 0) { 
 847             } else if (strcmp16(block
.getElementName(&len
), eat_comment16
.string()) == 0) { 
 848                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT
 
 849                         && code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 850                     if (code 
== ResXMLTree::END_TAG
) { 
 851                         if (strcmp16(block
.getElementName(&len
), eat_comment16
.string()) == 0) { 
 858             } else if (strcmp16(block
.getElementName(&len
), public16
.string()) == 0) { 
 859                 SourcePos 
srcPos(in
->getPrintableSource(), block
.getLineNumber()); 
 862                 ssize_t typeIdx 
= block
.indexOfAttribute(NULL
, "type"); 
 864                     srcPos
.error("A 'type' attribute is required for <public>\n"); 
 865                     hasErrors 
= localHasErrors 
= true; 
 867                 type 
= String16(block
.getAttributeStringValue(typeIdx
, &len
)); 
 870                 ssize_t nameIdx 
= block
.indexOfAttribute(NULL
, "name"); 
 872                     srcPos
.error("A 'name' attribute is required for <public>\n"); 
 873                     hasErrors 
= localHasErrors 
= true; 
 875                 name 
= String16(block
.getAttributeStringValue(nameIdx
, &len
)); 
 878                 ssize_t identIdx 
= block
.indexOfAttribute(NULL
, "id"); 
 880                     const char16_t* identStr 
= block
.getAttributeStringValue(identIdx
, &len
); 
 881                     Res_value identValue
; 
 882                     if (!ResTable::stringToInt(identStr
, len
, &identValue
)) { 
 883                         srcPos
.error("Given 'id' attribute is not an integer: %s\n", 
 884                                 String8(block
.getAttributeStringValue(identIdx
, &len
)).string()); 
 885                         hasErrors 
= localHasErrors 
= true; 
 887                         ident 
= identValue
.data
; 
 888                         nextPublicId
.replaceValueFor(type
, ident
+1); 
 890                 } else if (nextPublicId
.indexOfKey(type
) < 0) { 
 891                     srcPos
.error("No 'id' attribute supplied <public>," 
 892                             " and no previous id defined in this file.\n"); 
 893                     hasErrors 
= localHasErrors 
= true; 
 894                 } else if (!localHasErrors
) { 
 895                     ident 
= nextPublicId
.valueFor(type
); 
 896                     nextPublicId
.replaceValueFor(type
, ident
+1); 
 899                 if (!localHasErrors
) { 
 900                     err 
= outTable
->addPublic(srcPos
, myPackage
, type
, name
, ident
); 
 901                     if (err 
< NO_ERROR
) { 
 902                         hasErrors 
= localHasErrors 
= true; 
 905                 if (!localHasErrors
) { 
 906                     sp
<AaptSymbols
> symbols 
= assets
->getSymbolsFor(String8("R")); 
 907                     if (symbols 
!= NULL
) { 
 908                         symbols 
= symbols
->addNestedSymbol(String8(type
), srcPos
); 
 910                     if (symbols 
!= NULL
) { 
 911                         symbols
->makeSymbolPublic(String8(name
), srcPos
); 
 913                             block
.getComment(&len
) ? block
.getComment(&len
) : nulStr
); 
 914                         symbols
->appendComment(String8(name
), comment
, srcPos
); 
 916                         srcPos
.error("Unable to create symbols!\n"); 
 917                         hasErrors 
= localHasErrors 
= true; 
 921                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
 922                     if (code 
== ResXMLTree::END_TAG
) { 
 923                         if (strcmp16(block
.getElementName(&len
), public16
.string()) == 0) { 
 930             } else if (strcmp16(block
.getElementName(&len
), public_padding16
.string()) == 0) { 
 931                 SourcePos 
srcPos(in
->getPrintableSource(), block
.getLineNumber()); 
 934                 ssize_t typeIdx 
= block
.indexOfAttribute(NULL
, "type"); 
 936                     srcPos
.error("A 'type' attribute is required for <public-padding>\n"); 
 937                     hasErrors 
= localHasErrors 
= true; 
 939                 type 
= String16(block
.getAttributeStringValue(typeIdx
, &len
)); 
 942                 ssize_t nameIdx 
= block
.indexOfAttribute(NULL
, "name"); 
 944                     srcPos
.error("A 'name' attribute is required for <public-padding>\n"); 
 945                     hasErrors 
= localHasErrors 
= true; 
 947                 name 
= String16(block
.getAttributeStringValue(nameIdx
, &len
)); 
 950                 ssize_t startIdx 
= block
.indexOfAttribute(NULL
, "start"); 
 952                     const char16_t* startStr 
= block
.getAttributeStringValue(startIdx
, &len
); 
 953                     Res_value startValue
; 
 954                     if (!ResTable::stringToInt(startStr
, len
, &startValue
)) { 
 955                         srcPos
.error("Given 'start' attribute is not an integer: %s\n", 
 956                                 String8(block
.getAttributeStringValue(startIdx
, &len
)).string()); 
 957                         hasErrors 
= localHasErrors 
= true; 
 959                         start 
= startValue
.data
; 
 961                 } else if (nextPublicId
.indexOfKey(type
) < 0) { 
 962                     srcPos
.error("No 'start' attribute supplied <public-padding>," 
 963                             " and no previous id defined in this file.\n"); 
 964                     hasErrors 
= localHasErrors 
= true; 
 965                 } else if (!localHasErrors
) { 
 966                     start 
= nextPublicId
.valueFor(type
); 
 970                 ssize_t endIdx 
= block
.indexOfAttribute(NULL
, "end"); 
 972                     const char16_t* endStr 
= block
.getAttributeStringValue(endIdx
, &len
); 
 974                     if (!ResTable::stringToInt(endStr
, len
, &endValue
)) { 
 975                         srcPos
.error("Given 'end' attribute is not an integer: %s\n", 
 976                                 String8(block
.getAttributeStringValue(endIdx
, &len
)).string()); 
 977                         hasErrors 
= localHasErrors 
= true; 
 982                     srcPos
.error("No 'end' attribute supplied <public-padding>\n"); 
 983                     hasErrors 
= localHasErrors 
= true; 
 987                     nextPublicId
.replaceValueFor(type
, end
+1); 
 989                     srcPos
.error("Padding start '%ul' is after end '%ul'\n", 
 991                     hasErrors 
= localHasErrors 
= true; 
 995                     block
.getComment(&len
) ? block
.getComment(&len
) : nulStr
); 
 996                 for (uint32_t curIdent
=start
; curIdent
<=end
; curIdent
++) { 
 997                     if (localHasErrors
) { 
1000                     String16 
curName(name
); 
1002                     sprintf(buf
, "%d", (int)(end
-curIdent
+1)); 
1003                     curName
.append(String16(buf
)); 
1005                     err 
= outTable
->addEntry(srcPos
, myPackage
, type
, curName
, 
1006                                              String16("padding"), NULL
, &curParams
, false, 
1007                                              ResTable_map::TYPE_STRING
, overwrite
); 
1008                     if (err 
< NO_ERROR
) { 
1009                         hasErrors 
= localHasErrors 
= true; 
1012                     err 
= outTable
->addPublic(srcPos
, myPackage
, type
, 
1014                     if (err 
< NO_ERROR
) { 
1015                         hasErrors 
= localHasErrors 
= true; 
1018                     sp
<AaptSymbols
> symbols 
= assets
->getSymbolsFor(String8("R")); 
1019                     if (symbols 
!= NULL
) { 
1020                         symbols 
= symbols
->addNestedSymbol(String8(type
), srcPos
); 
1022                     if (symbols 
!= NULL
) { 
1023                         symbols
->makeSymbolPublic(String8(curName
), srcPos
); 
1024                         symbols
->appendComment(String8(curName
), comment
, srcPos
); 
1026                         srcPos
.error("Unable to create symbols!\n"); 
1027                         hasErrors 
= localHasErrors 
= true; 
1031                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1032                     if (code 
== ResXMLTree::END_TAG
) { 
1033                         if (strcmp16(block
.getElementName(&len
), public_padding16
.string()) == 0) { 
1040             } else if (strcmp16(block
.getElementName(&len
), private_symbols16
.string()) == 0) { 
1042                 ssize_t pkgIdx 
= block
.indexOfAttribute(NULL
, "package"); 
1044                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1045                             "A 'package' attribute is required for <private-symbols>\n"); 
1046                     hasErrors 
= localHasErrors 
= true; 
1048                 pkg 
= String16(block
.getAttributeStringValue(pkgIdx
, &len
)); 
1049                 if (!localHasErrors
) { 
1050                     assets
->setSymbolsPrivatePackage(String8(pkg
)); 
1053                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1054                     if (code 
== ResXMLTree::END_TAG
) { 
1055                         if (strcmp16(block
.getElementName(&len
), private_symbols16
.string()) == 0) { 
1062             } else if (strcmp16(block
.getElementName(&len
), add_resource16
.string()) == 0) { 
1063                 SourcePos 
srcPos(in
->getPrintableSource(), block
.getLineNumber()); 
1066                 ssize_t typeIdx 
= block
.indexOfAttribute(NULL
, "type"); 
1068                     srcPos
.error("A 'type' attribute is required for <add-resource>\n"); 
1069                     hasErrors 
= localHasErrors 
= true; 
1071                 typeName 
= String16(block
.getAttributeStringValue(typeIdx
, &len
)); 
1074                 ssize_t nameIdx 
= block
.indexOfAttribute(NULL
, "name"); 
1076                     srcPos
.error("A 'name' attribute is required for <add-resource>\n"); 
1077                     hasErrors 
= localHasErrors 
= true; 
1079                 name 
= String16(block
.getAttributeStringValue(nameIdx
, &len
)); 
1081                 outTable
->canAddEntry(srcPos
, myPackage
, typeName
, name
); 
1083                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1084                     if (code 
== ResXMLTree::END_TAG
) { 
1085                         if (strcmp16(block
.getElementName(&len
), add_resource16
.string()) == 0) { 
1092             } else if (strcmp16(block
.getElementName(&len
), declare_styleable16
.string()) == 0) { 
1093                 SourcePos 
srcPos(in
->getPrintableSource(), block
.getLineNumber()); 
1096                 ssize_t identIdx 
= block
.indexOfAttribute(NULL
, "name"); 
1098                     srcPos
.error("A 'name' attribute is required for <declare-styleable>\n"); 
1099                     hasErrors 
= localHasErrors 
= true; 
1101                 ident 
= String16(block
.getAttributeStringValue(identIdx
, &len
)); 
1103                 sp
<AaptSymbols
> symbols 
= assets
->getSymbolsFor(String8("R")); 
1104                 if (!localHasErrors
) { 
1105                     if (symbols 
!= NULL
) { 
1106                         symbols 
= symbols
->addNestedSymbol(String8("styleable"), srcPos
); 
1108                     sp
<AaptSymbols
> styleSymbols 
= symbols
; 
1109                     if (symbols 
!= NULL
) { 
1110                         symbols 
= symbols
->addNestedSymbol(String8(ident
), srcPos
); 
1112                     if (symbols 
== NULL
) { 
1113                         srcPos
.error("Unable to create symbols!\n"); 
1114                         return UNKNOWN_ERROR
; 
1118                         block
.getComment(&len
) ? block
.getComment(&len
) : nulStr
); 
1119                     styleSymbols
->appendComment(String8(ident
), comment
, srcPos
); 
1124                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT 
&& code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1125                     if (code 
== ResXMLTree::START_TAG
) { 
1126                         if (strcmp16(block
.getElementName(&len
), skip16
.string()) == 0) { 
1127                             while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT
 
1128                                    && code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1129                                 if (code 
== ResXMLTree::END_TAG
) { 
1130                                     if (strcmp16(block
.getElementName(&len
), skip16
.string()) == 0) { 
1136                         } else if (strcmp16(block
.getElementName(&len
), eat_comment16
.string()) == 0) { 
1137                             while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT
 
1138                                    && code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1139                                 if (code 
== ResXMLTree::END_TAG
) { 
1140                                     if (strcmp16(block
.getElementName(&len
), eat_comment16
.string()) == 0) { 
1146                         } else if (strcmp16(block
.getElementName(&len
), attr16
.string()) != 0) { 
1147                             SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1148                                     "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n", 
1149                                     String8(block
.getElementName(&len
)).string()); 
1150                             return UNKNOWN_ERROR
; 
1154                             block
.getComment(&len
) ? block
.getComment(&len
) : nulStr
); 
1156                         err 
= compileAttribute(in
, block
, myPackage
, outTable
, &itemIdent
, true); 
1157                         if (err 
!= NO_ERROR
) { 
1158                             hasErrors 
= localHasErrors 
= true; 
1161                         if (symbols 
!= NULL
) { 
1162                             SourcePos 
srcPos(String8(in
->getPrintableSource()), block
.getLineNumber()); 
1163                             symbols
->addSymbol(String8(itemIdent
), 0, srcPos
); 
1164                             symbols
->appendComment(String8(itemIdent
), comment
, srcPos
); 
1165                             //printf("Attribute %s comment: %s\n", String8(itemIdent).string(), 
1166                             //     String8(comment).string()); 
1168                     } else if (code 
== ResXMLTree::END_TAG
) { 
1169                         if (strcmp16(block
.getElementName(&len
), declare_styleable16
.string()) == 0) { 
1173                         SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1174                                 "Found tag </%s> where </attr> is expected\n", 
1175                                 String8(block
.getElementName(&len
)).string()); 
1176                         return UNKNOWN_ERROR
; 
1181             } else if (strcmp16(block
.getElementName(&len
), attr16
.string()) == 0) { 
1182                 err 
= compileAttribute(in
, block
, myPackage
, outTable
, NULL
); 
1183                 if (err 
!= NO_ERROR
) { 
1188             } else if (strcmp16(block
.getElementName(&len
), item16
.string()) == 0) { 
1190                 ssize_t attri 
= block
.indexOfAttribute(NULL
, "type"); 
1192                     curType 
= String16(block
.getAttributeStringValue(attri
, &len
)); 
1193                     ssize_t formatIdx 
= block
.indexOfAttribute(NULL
, "format"); 
1194                     if (formatIdx 
>= 0) { 
1195                         String16 formatStr 
= String16(block
.getAttributeStringValue( 
1197                         curFormat 
= parse_flags(formatStr
.string(), formatStr
.size(), 
1199                         if (curFormat 
== 0) { 
1200                             SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1201                                     "Tag <item> 'format' attribute value \"%s\" not valid\n", 
1202                                     String8(formatStr
).string()); 
1203                             hasErrors 
= localHasErrors 
= true; 
1207                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1208                             "A 'type' attribute is required for <item>\n"); 
1209                     hasErrors 
= localHasErrors 
= true; 
1212             } else if (strcmp16(block
.getElementName(&len
), string16
.string()) == 0) { 
1213                 // Note the existence and locale of every string we process 
1215                 curParams
.getLocale(rawLocale
); 
1216                 String8 
locale(rawLocale
); 
1218                 String16 translatable
; 
1221                 size_t n 
= block
.getAttributeCount(); 
1222                 for (size_t i 
= 0; i 
< n
; i
++) { 
1224                     const uint16_t* attr 
= block
.getAttributeName(i
, &length
); 
1225                     if (strcmp16(attr
, name16
.string()) == 0) { 
1226                         name
.setTo(block
.getAttributeStringValue(i
, &length
)); 
1227                     } else if (strcmp16(attr
, translatable16
.string()) == 0) { 
1228                         translatable
.setTo(block
.getAttributeStringValue(i
, &length
)); 
1229                     } else if (strcmp16(attr
, formatted16
.string()) == 0) { 
1230                         formatted
.setTo(block
.getAttributeStringValue(i
, &length
)); 
1231                     } else if (strcmp16(attr
, product16
.string()) == 0) { 
1232                         curProduct
.setTo(block
.getAttributeStringValue(i
, &length
)); 
1236                 if (name
.size() > 0) { 
1237                     if (translatable 
== false16
) { 
1238                         curIsFormatted 
= false; 
1239                         // Untranslatable strings must only exist in the default [empty] locale 
1240                         if (locale
.size() > 0) { 
1241                             fprintf(stderr
, "aapt: warning: string '%s' in %s marked untranslatable but exists" 
1242                                     " in locale '%s'\n", String8(name
).string(), 
1243                                     bundle
->getResourceSourceDirs()[0], 
1245                             // hasErrors = localHasErrors = true; 
1247                             // Intentionally empty block: 
1249                             // Don't add untranslatable strings to the localization table; that 
1250                             // way if we later see localizations of them, they'll be flagged as 
1251                             // having no default translation. 
1254                         outTable
->addLocalization(name
, locale
); 
1257                     if (formatted 
== false16
) { 
1258                         curIsFormatted 
= false; 
1264                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_STRING
; 
1266                 curIsPseudolocalizable 
= true; 
1267             } else if (strcmp16(block
.getElementName(&len
), drawable16
.string()) == 0) { 
1268                 curTag 
= &drawable16
; 
1269                 curType 
= drawable16
; 
1270                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_COLOR
; 
1271             } else if (strcmp16(block
.getElementName(&len
), color16
.string()) == 0) { 
1274                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_COLOR
; 
1275             } else if (strcmp16(block
.getElementName(&len
), bool16
.string()) == 0) { 
1278                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_BOOLEAN
; 
1279             } else if (strcmp16(block
.getElementName(&len
), integer16
.string()) == 0) { 
1280                 curTag 
= &integer16
; 
1281                 curType 
= integer16
; 
1282                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_INTEGER
; 
1283             } else if (strcmp16(block
.getElementName(&len
), dimen16
.string()) == 0) { 
1286                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_DIMENSION
; 
1287             } else if (strcmp16(block
.getElementName(&len
), fraction16
.string()) == 0) { 
1288                 curTag 
= &fraction16
; 
1289                 curType 
= fraction16
; 
1290                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_FRACTION
; 
1291             } else if (strcmp16(block
.getElementName(&len
), bag16
.string()) == 0) { 
1294                 ssize_t attri 
= block
.indexOfAttribute(NULL
, "type"); 
1296                     curType 
= String16(block
.getAttributeStringValue(attri
, &len
)); 
1298                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1299                             "A 'type' attribute is required for <bag>\n"); 
1300                     hasErrors 
= localHasErrors 
= true; 
1302             } else if (strcmp16(block
.getElementName(&len
), style16
.string()) == 0) { 
1306             } else if (strcmp16(block
.getElementName(&len
), plurals16
.string()) == 0) { 
1307                 curTag 
= &plurals16
; 
1308                 curType 
= plurals16
; 
1310             } else if (strcmp16(block
.getElementName(&len
), array16
.string()) == 0) { 
1314                 curIsBagReplaceOnOverwrite 
= true; 
1315                 ssize_t formatIdx 
= block
.indexOfAttribute(NULL
, "format"); 
1316                 if (formatIdx 
>= 0) { 
1317                     String16 formatStr 
= String16(block
.getAttributeStringValue( 
1319                     curFormat 
= parse_flags(formatStr
.string(), formatStr
.size(), 
1321                     if (curFormat 
== 0) { 
1322                         SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1323                                 "Tag <array> 'format' attribute value \"%s\" not valid\n", 
1324                                 String8(formatStr
).string()); 
1325                         hasErrors 
= localHasErrors 
= true; 
1328             } else if (strcmp16(block
.getElementName(&len
), string_array16
.string()) == 0) { 
1329                 curTag 
= &string_array16
; 
1331                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_STRING
; 
1333                 curIsBagReplaceOnOverwrite 
= true; 
1334                 curIsPseudolocalizable 
= true; 
1335             } else if (strcmp16(block
.getElementName(&len
), integer_array16
.string()) == 0) { 
1336                 curTag 
= &integer_array16
; 
1338                 curFormat 
= ResTable_map::TYPE_REFERENCE
|ResTable_map::TYPE_INTEGER
; 
1340                 curIsBagReplaceOnOverwrite 
= true; 
1342                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1343                         "Found tag %s where item is expected\n", 
1344                         String8(block
.getElementName(&len
)).string()); 
1345                 return UNKNOWN_ERROR
; 
1349             ssize_t identIdx 
= block
.indexOfAttribute(NULL
, "name"); 
1350             if (identIdx 
>= 0) { 
1351                 ident 
= String16(block
.getAttributeStringValue(identIdx
, &len
)); 
1353                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1354                         "A 'name' attribute is required for <%s>\n", 
1355                         String8(*curTag
).string()); 
1356                 hasErrors 
= localHasErrors 
= true; 
1359             String16 
comment(block
.getComment(&len
) ? block
.getComment(&len
) : nulStr
); 
1362                 // Figure out the parent of this bag... 
1363                 String16 parentIdent
; 
1364                 ssize_t parentIdentIdx 
= block
.indexOfAttribute(NULL
, "parent"); 
1365                 if (parentIdentIdx 
>= 0) { 
1366                     parentIdent 
= String16(block
.getAttributeStringValue(parentIdentIdx
, &len
)); 
1368                     ssize_t sep 
= ident
.findLast('.'); 
1370                         parentIdent
.setTo(ident
, sep
); 
1374                 if (!localHasErrors
) { 
1375                     err 
= outTable
->startBag(SourcePos(in
->getPrintableSource(), 
1376                             block
.getLineNumber()), myPackage
, curType
, ident
, 
1377                             parentIdent
, &curParams
, 
1378                             overwrite
, curIsBagReplaceOnOverwrite
); 
1379                     if (err 
!= NO_ERROR
) { 
1380                         hasErrors 
= localHasErrors 
= true; 
1384                 ssize_t elmIndex 
= 0; 
1385                 char elmIndexStr
[14]; 
1386                 while ((code
=block
.next()) != ResXMLTree::END_DOCUMENT
 
1387                         && code 
!= ResXMLTree::BAD_DOCUMENT
) { 
1389                     if (code 
== ResXMLTree::START_TAG
) { 
1390                         if (strcmp16(block
.getElementName(&len
), item16
.string()) != 0) { 
1391                             SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1392                                     "Tag <%s> can not appear inside <%s>, only <item>\n", 
1393                                     String8(block
.getElementName(&len
)).string(), 
1394                                     String8(*curTag
).string()); 
1395                             return UNKNOWN_ERROR
; 
1399                         if (curType 
== array16
) { 
1400                             sprintf(elmIndexStr
, "^index_%d", (int)elmIndex
++); 
1401                             itemIdent 
= String16(elmIndexStr
); 
1402                         } else if (curType 
== plurals16
) { 
1403                             ssize_t itemIdentIdx 
= block
.indexOfAttribute(NULL
, "quantity"); 
1404                             if (itemIdentIdx 
>= 0) { 
1405                                 String16 
quantity16(block
.getAttributeStringValue(itemIdentIdx
, &len
)); 
1406                                 if (quantity16 
== other16
) { 
1407                                     itemIdent 
= quantityOther16
; 
1409                                 else if (quantity16 
== zero16
) { 
1410                                     itemIdent 
= quantityZero16
; 
1412                                 else if (quantity16 
== one16
) { 
1413                                     itemIdent 
= quantityOne16
; 
1415                                 else if (quantity16 
== two16
) { 
1416                                     itemIdent 
= quantityTwo16
; 
1418                                 else if (quantity16 
== few16
) { 
1419                                     itemIdent 
= quantityFew16
; 
1421                                 else if (quantity16 
== many16
) { 
1422                                     itemIdent 
= quantityMany16
; 
1425                                     SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1426                                             "Illegal 'quantity' attribute is <item> inside <plurals>\n"); 
1427                                     hasErrors 
= localHasErrors 
= true; 
1430                                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1431                                         "A 'quantity' attribute is required for <item> inside <plurals>\n"); 
1432                                 hasErrors 
= localHasErrors 
= true; 
1435                             ssize_t itemIdentIdx 
= block
.indexOfAttribute(NULL
, "name"); 
1436                             if (itemIdentIdx 
>= 0) { 
1437                                 itemIdent 
= String16(block
.getAttributeStringValue(itemIdentIdx
, &len
)); 
1439                                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1440                                         "A 'name' attribute is required for <item>\n"); 
1441                                 hasErrors 
= localHasErrors 
= true; 
1445                         ResXMLParser::ResXMLPosition parserPosition
; 
1446                         block
.getPosition(&parserPosition
); 
1448                         err 
= parseAndAddBag(bundle
, in
, &block
, curParams
, myPackage
, curType
, 
1449                                 ident
, parentIdent
, itemIdent
, curFormat
, curIsFormatted
, 
1450                                 curProduct
, false, overwrite
, outTable
); 
1451                         if (err 
== NO_ERROR
) { 
1452                             if (curIsPseudolocalizable 
&& localeIsDefined(curParams
) 
1453                                     && bundle
->getPseudolocalize()) { 
1454                                 // pseudolocalize here 
1456                                 block
.setPosition(parserPosition
); 
1457                                 err 
= parseAndAddBag(bundle
, in
, &block
, pseudoParams
, myPackage
, 
1458                                         curType
, ident
, parentIdent
, itemIdent
, curFormat
, 
1459                                         curIsFormatted
, curProduct
, true, overwrite
, outTable
); 
1463                         if (err 
!= NO_ERROR
) { 
1464                             hasErrors 
= localHasErrors 
= true; 
1466                     } else if (code 
== ResXMLTree::END_TAG
) { 
1467                         if (strcmp16(block
.getElementName(&len
), curTag
->string()) != 0) { 
1468                             SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1469                                     "Found tag </%s> where </%s> is expected\n", 
1470                                     String8(block
.getElementName(&len
)).string(), 
1471                                     String8(*curTag
).string()); 
1472                             return UNKNOWN_ERROR
; 
1478                 ResXMLParser::ResXMLPosition parserPosition
; 
1479                 block
.getPosition(&parserPosition
); 
1481                 err 
= parseAndAddEntry(bundle
, in
, &block
, curParams
, myPackage
, curType
, ident
, 
1482                         *curTag
, curIsStyled
, curFormat
, curIsFormatted
, 
1483                         curProduct
, false, overwrite
, outTable
); 
1485                 if (err 
< NO_ERROR
) { // Why err < NO_ERROR instead of err != NO_ERROR? 
1486                     hasErrors 
= localHasErrors 
= true; 
1488                 else if (err 
== NO_ERROR
) { 
1489                     if (curIsPseudolocalizable 
&& localeIsDefined(curParams
) 
1490                             && bundle
->getPseudolocalize()) { 
1491                         // pseudolocalize here 
1492                         block
.setPosition(parserPosition
); 
1493                         err 
= parseAndAddEntry(bundle
, in
, &block
, pseudoParams
, myPackage
, curType
, 
1494                                 ident
, *curTag
, curIsStyled
, curFormat
, 
1495                                 curIsFormatted
, curProduct
, 
1496                                 true, overwrite
, outTable
); 
1497                         if (err 
!= NO_ERROR
) { 
1498                             hasErrors 
= localHasErrors 
= true; 
1505             if (comment
.size() > 0) { 
1506                 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage
).string(), 
1507                        String8(curType
).string(), String8(ident
).string(), 
1508                        String8(comment
).string()); 
1511             if (!localHasErrors
) { 
1512                 outTable
->appendComment(myPackage
, curType
, ident
, comment
, false); 
1515         else if (code 
== ResXMLTree::END_TAG
) { 
1516             if (strcmp16(block
.getElementName(&len
), resources16
.string()) != 0) { 
1517                 SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1518                         "Unexpected end tag %s\n", String8(block
.getElementName(&len
)).string()); 
1519                 return UNKNOWN_ERROR
; 
1522         else if (code 
== ResXMLTree::START_NAMESPACE 
|| code 
== ResXMLTree::END_NAMESPACE
) { 
1524         else if (code 
== ResXMLTree::TEXT
) { 
1525             if (isWhitespace(block
.getText(&len
))) { 
1528             SourcePos(in
->getPrintableSource(), block
.getLineNumber()).error( 
1529                     "Found text \"%s\" where item tag is expected\n", 
1530                     String8(block
.getText(&len
)).string()); 
1531             return UNKNOWN_ERROR
; 
1535     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
1538 ResourceTable::ResourceTable(Bundle
* bundle
, const String16
& assetsPackage
) 
1539     : mAssetsPackage(assetsPackage
), mNextPackageId(1), mHaveAppPackage(false), 
1540       mIsAppPackage(!bundle
->getExtending()), 
1546 status_t 
ResourceTable::addIncludedResources(Bundle
* bundle
, const sp
<AaptAssets
>& assets
) 
1548     status_t err 
= assets
->buildIncludedResources(bundle
); 
1549     if (err 
!= NO_ERROR
) { 
1553     // For future reference to included resources. 
1556     const ResTable
& incl 
= assets
->getIncludedResources(); 
1558     // Retrieve all the packages. 
1559     const size_t N 
= incl
.getBasePackageCount(); 
1560     for (size_t phase
=0; phase
<2; phase
++) { 
1561         for (size_t i
=0; i
<N
; i
++) { 
1562             String16 
name(incl
.getBasePackageName(i
)); 
1563             uint32_t id 
= incl
.getBasePackageId(i
); 
1564             // First time through: only add base packages (id 
1565             // is not 0); second time through add the other 
1569                     // Skip base packages -- already one. 
1572                     // Assign a dynamic id. 
1573                     id 
= mNextPackageId
; 
1575             } else if (id 
!= 0) { 
1577                     if (mHaveAppPackage
) { 
1578                         fprintf(stderr
, "Included resources have two application packages!\n"); 
1579                         return UNKNOWN_ERROR
; 
1581                     mHaveAppPackage 
= true; 
1583                 if (mNextPackageId 
> id
) { 
1584                     fprintf(stderr
, "Included base package ID %d already in use!\n", id
); 
1585                     return UNKNOWN_ERROR
; 
1589                 NOISY(printf("Including package %s with ID=%d\n", 
1590                              String8(name
).string(), id
)); 
1591                 sp
<Package
> p 
= new Package(name
, id
); 
1592                 mPackages
.add(name
, p
); 
1593                 mOrderedPackages
.add(p
); 
1595                 if (id 
>= mNextPackageId
) { 
1596                     mNextPackageId 
= id
+1; 
1602     // Every resource table always has one first entry, the bag attributes. 
1603     const SourcePos 
unknown(String8("????"), 0); 
1604     sp
<Type
> attr 
= getType(mAssetsPackage
, String16("attr"), unknown
); 
1609 status_t 
ResourceTable::addPublic(const SourcePos
& sourcePos
, 
1610                                   const String16
& package
, 
1611                                   const String16
& type
, 
1612                                   const String16
& name
, 
1613                                   const uint32_t ident
) 
1615     uint32_t rid 
= mAssets
->getIncludedResources() 
1616         .identifierForName(name
.string(), name
.size(), 
1617                            type
.string(), type
.size(), 
1618                            package
.string(), package
.size()); 
1620         sourcePos
.error("Error declaring public resource %s/%s for included package %s\n", 
1621                 String8(type
).string(), String8(name
).string(), 
1622                 String8(package
).string()); 
1623         return UNKNOWN_ERROR
; 
1626     sp
<Type
> t 
= getType(package
, type
, sourcePos
); 
1628         return UNKNOWN_ERROR
; 
1630     return t
->addPublic(sourcePos
, name
, ident
); 
1633 status_t 
ResourceTable::addEntry(const SourcePos
& sourcePos
, 
1634                                  const String16
& package
, 
1635                                  const String16
& type
, 
1636                                  const String16
& name
, 
1637                                  const String16
& value
, 
1638                                  const Vector
<StringPool::entry_style_span
>* style
, 
1639                                  const ResTable_config
* params
, 
1640                                  const bool doSetIndex
, 
1641                                  const int32_t format
, 
1642                                  const bool overwrite
) 
1644     // Check for adding entries in other packages...  for now we do 
1645     // nothing.  We need to do the right thing here to support skinning. 
1646     uint32_t rid 
= mAssets
->getIncludedResources() 
1647         .identifierForName(name
.string(), name
.size(), 
1648                            type
.string(), type
.size(), 
1649                            package
.string(), package
.size()); 
1655     if (name 
== String16("left")) { 
1656         printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n", 
1657                sourcePos
.file
.string(), sourcePos
.line
, String8(type
).string(), 
1658                String8(value
).string()); 
1662     sp
<Entry
> e 
= getEntry(package
, type
, name
, sourcePos
, overwrite
, 
1663                            params
, doSetIndex
); 
1665         return UNKNOWN_ERROR
; 
1667     status_t err 
= e
->setItem(sourcePos
, value
, style
, format
, overwrite
); 
1668     if (err 
== NO_ERROR
) { 
1674 status_t 
ResourceTable::startBag(const SourcePos
& sourcePos
, 
1675                                  const String16
& package
, 
1676                                  const String16
& type
, 
1677                                  const String16
& name
, 
1678                                  const String16
& bagParent
, 
1679                                  const ResTable_config
* params
, 
1681                                  bool replace
, bool isId
) 
1683     status_t result 
= NO_ERROR
; 
1685     // Check for adding entries in other packages...  for now we do 
1686     // nothing.  We need to do the right thing here to support skinning. 
1687     uint32_t rid 
= mAssets
->getIncludedResources() 
1688     .identifierForName(name
.string(), name
.size(), 
1689                        type
.string(), type
.size(), 
1690                        package
.string(), package
.size()); 
1696     if (name 
== String16("left")) { 
1697         printf("Adding bag left: file=%s, line=%d, type=%s\n", 
1698                sourcePos
.file
.striing(), sourcePos
.line
, String8(type
).string()); 
1701     if (overlay 
&& !mBundle
->getAutoAddOverlay() && !hasBagOrEntry(package
, type
, name
)) { 
1702         bool canAdd 
= false; 
1703         sp
<Package
> p 
= mPackages
.valueFor(package
); 
1705             sp
<Type
> t 
= p
->getTypes().valueFor(type
); 
1707                 if (t
->getCanAddEntries().indexOf(name
) >= 0) { 
1713             sourcePos
.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n", 
1714                             String8(name
).string()); 
1715             return UNKNOWN_ERROR
; 
1718     sp
<Entry
> e 
= getEntry(package
, type
, name
, sourcePos
, overlay
, params
); 
1720         return UNKNOWN_ERROR
; 
1723     // If a parent is explicitly specified, set it. 
1724     if (bagParent
.size() > 0) { 
1725         String16 curPar 
= e
->getParent(); 
1726         if (curPar
.size() > 0 && curPar 
!= bagParent
) { 
1727             sourcePos
.error("Conflicting parents specified, was '%s', now '%s'\n", 
1728                             String8(e
->getParent()).string(), 
1729                             String8(bagParent
).string()); 
1730             return UNKNOWN_ERROR
; 
1732         e
->setParent(bagParent
); 
1735     if ((result 
= e
->makeItABag(sourcePos
)) != NO_ERROR
) { 
1739     if (overlay 
&& replace
) {  
1740         return e
->emptyBag(sourcePos
); 
1745 status_t 
ResourceTable::addBag(const SourcePos
& sourcePos
, 
1746                                const String16
& package
, 
1747                                const String16
& type
, 
1748                                const String16
& name
, 
1749                                const String16
& bagParent
, 
1750                                const String16
& bagKey
, 
1751                                const String16
& value
, 
1752                                const Vector
<StringPool::entry_style_span
>* style
, 
1753                                const ResTable_config
* params
, 
1754                                bool replace
, bool isId
, const int32_t format
) 
1756     // Check for adding entries in other packages...  for now we do 
1757     // nothing.  We need to do the right thing here to support skinning. 
1758     uint32_t rid 
= mAssets
->getIncludedResources() 
1759         .identifierForName(name
.string(), name
.size(), 
1760                            type
.string(), type
.size(), 
1761                            package
.string(), package
.size()); 
1767     if (name 
== String16("left")) { 
1768         printf("Adding bag left: file=%s, line=%d, type=%s\n", 
1769                sourcePos
.file
.striing(), sourcePos
.line
, String8(type
).string()); 
1772     sp
<Entry
> e 
= getEntry(package
, type
, name
, sourcePos
, replace
, params
); 
1774         return UNKNOWN_ERROR
; 
1777     // If a parent is explicitly specified, set it. 
1778     if (bagParent
.size() > 0) { 
1779         String16 curPar 
= e
->getParent(); 
1780         if (curPar
.size() > 0 && curPar 
!= bagParent
) { 
1781             sourcePos
.error("Conflicting parents specified, was '%s', now '%s'\n", 
1782                     String8(e
->getParent()).string(), 
1783                     String8(bagParent
).string()); 
1784             return UNKNOWN_ERROR
; 
1786         e
->setParent(bagParent
); 
1789     const bool first 
= e
->getBag().indexOfKey(bagKey
) < 0; 
1790     status_t err 
= e
->addToBag(sourcePos
, bagKey
, value
, style
, replace
, isId
, format
); 
1791     if (err 
== NO_ERROR 
&& first
) { 
1797 bool ResourceTable::hasBagOrEntry(const String16
& package
, 
1798                                   const String16
& type
, 
1799                                   const String16
& name
) const 
1801     // First look for this in the included resources... 
1802     uint32_t rid 
= mAssets
->getIncludedResources() 
1803         .identifierForName(name
.string(), name
.size(), 
1804                            type
.string(), type
.size(), 
1805                            package
.string(), package
.size()); 
1810     sp
<Package
> p 
= mPackages
.valueFor(package
); 
1812         sp
<Type
> t 
= p
->getTypes().valueFor(type
); 
1814             sp
<ConfigList
> c 
=  t
->getConfigs().valueFor(name
); 
1815             if (c 
!= NULL
) return true; 
1822 bool ResourceTable::hasBagOrEntry(const String16
& ref
, 
1823                                   const String16
* defType
, 
1824                                   const String16
* defPackage
) 
1826     String16 package
, type
, name
; 
1827     if (!ResTable::expandResourceRef(ref
.string(), ref
.size(), &package
, &type
, &name
, 
1828                 defType
, defPackage 
? defPackage
:&mAssetsPackage
, NULL
)) { 
1831     return hasBagOrEntry(package
, type
, name
); 
1834 bool ResourceTable::appendComment(const String16
& package
, 
1835                                   const String16
& type
, 
1836                                   const String16
& name
, 
1837                                   const String16
& comment
, 
1840     if (comment
.size() <= 0) { 
1844     sp
<Package
> p 
= mPackages
.valueFor(package
); 
1846         sp
<Type
> t 
= p
->getTypes().valueFor(type
); 
1848             sp
<ConfigList
> c 
=  t
->getConfigs().valueFor(name
); 
1850                 c
->appendComment(comment
, onlyIfEmpty
); 
1858 bool ResourceTable::appendTypeComment(const String16
& package
, 
1859                                       const String16
& type
, 
1860                                       const String16
& name
, 
1861                                       const String16
& comment
) 
1863     if (comment
.size() <= 0) { 
1867     sp
<Package
> p 
= mPackages
.valueFor(package
); 
1869         sp
<Type
> t 
= p
->getTypes().valueFor(type
); 
1871             sp
<ConfigList
> c 
=  t
->getConfigs().valueFor(name
); 
1873                 c
->appendTypeComment(comment
); 
1881 void ResourceTable::canAddEntry(const SourcePos
& pos
, 
1882         const String16
& package
, const String16
& type
, const String16
& name
) 
1884     sp
<Type
> t 
= getType(package
, type
, pos
); 
1886         t
->canAddEntry(name
); 
1890 size_t ResourceTable::size() const { 
1891     return mPackages
.size(); 
1894 size_t ResourceTable::numLocalResources() const { 
1898 bool ResourceTable::hasResources() const { 
1899     return mNumLocal 
> 0; 
1902 sp
<AaptFile
> ResourceTable::flatten(Bundle
* bundle
) 
1904     sp
<AaptFile
> data 
= new AaptFile(String8(), AaptGroupEntry(), String8()); 
1905     status_t err 
= flatten(bundle
, data
); 
1906     return err 
== NO_ERROR 
? data 
: NULL
; 
1909 inline uint32_t ResourceTable::getResId(const sp
<Package
>& p
, 
1913     return makeResId(p
->getAssignedId(), t
->getIndex(), nameId
); 
1916 uint32_t ResourceTable::getResId(const String16
& package
, 
1917                                  const String16
& type
, 
1918                                  const String16
& name
, 
1919                                  bool onlyPublic
) const 
1921     sp
<Package
> p 
= mPackages
.valueFor(package
); 
1922     if (p 
== NULL
) return 0; 
1924     // First look for this in the included resources... 
1925     uint32_t specFlags 
= 0; 
1926     uint32_t rid 
= mAssets
->getIncludedResources() 
1927         .identifierForName(name
.string(), name
.size(), 
1928                            type
.string(), type
.size(), 
1929                            package
.string(), package
.size(), 
1933             if ((specFlags 
& ResTable_typeSpec::SPEC_PUBLIC
) == 0) { 
1938         if (Res_INTERNALID(rid
)) { 
1941         return Res_MAKEID(p
->getAssignedId()-1, 
1946     sp
<Type
> t 
= p
->getTypes().valueFor(type
); 
1947     if (t 
== NULL
) return 0; 
1948     sp
<ConfigList
> c 
=  t
->getConfigs().valueFor(name
); 
1949     if (c 
== NULL
) return 0; 
1950     int32_t ei 
= c
->getEntryIndex(); 
1951     if (ei 
< 0) return 0; 
1952     return getResId(p
, t
, ei
); 
1955 uint32_t ResourceTable::getResId(const String16
& ref
, 
1956                                  const String16
* defType
, 
1957                                  const String16
* defPackage
, 
1958                                  const char** outErrorMsg
, 
1959                                  bool onlyPublic
) const 
1961     String16 package
, type
, name
; 
1962     if (!ResTable::expandResourceRef( 
1963         ref
.string(), ref
.size(), &package
, &type
, &name
, 
1964         defType
, defPackage 
? defPackage
:&mAssetsPackage
, 
1966         NOISY(printf("Expanding resource: ref=%s\n", 
1967                      String8(ref
).string())); 
1968         NOISY(printf("Expanding resource: defType=%s\n", 
1969                      defType 
? String8(*defType
).string() : "NULL")); 
1970         NOISY(printf("Expanding resource: defPackage=%s\n", 
1971                      defPackage 
? String8(*defPackage
).string() : "NULL")); 
1972         NOISY(printf("Expanding resource: ref=%s\n", String8(ref
).string())); 
1973         NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n", 
1974                      String8(package
).string(), String8(type
).string(), 
1975                      String8(name
).string())); 
1978     uint32_t res 
= getResId(package
, type
, name
, onlyPublic
); 
1979     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", 
1980                  String8(package
).string(), String8(type
).string(), 
1981                  String8(name
).string(), res
)); 
1984             *outErrorMsg 
= "No resource found that matches the given name"; 
1989 bool ResourceTable::isValidResourceName(const String16
& s
) 
1991     const char16_t* p 
= s
.string(); 
1994         if ((*p 
>= 'a' && *p 
<= 'z') 
1995             || (*p 
>= 'A' && *p 
<= 'Z') 
1997             || (!first 
&& *p 
>= '0' && *p 
<= '9')) { 
2007 bool ResourceTable::stringToValue(Res_value
* outValue
, StringPool
* pool
, 
2008                                   const String16
& str
, 
2009                                   bool preserveSpaces
, bool coerceType
, 
2011                                   const Vector
<StringPool::entry_style_span
>* style
, 
2012                                   String16
* outStr
, void* accessorCookie
, 
2018     if (style 
== NULL 
|| style
->size() == 0) { 
2019         // Text is not styled so it can be any type...  let's figure it out. 
2020         res 
= mAssets
->getIncludedResources() 
2021             .stringToValue(outValue
, &finalStr
, str
.string(), str
.size(), preserveSpaces
, 
2022                             coerceType
, attrID
, NULL
, &mAssetsPackage
, this, 
2023                            accessorCookie
, attrType
); 
2025         // Styled text can only be a string, and while collecting the style 
2026         // information we have already processed that string! 
2027         outValue
->size 
= sizeof(Res_value
); 
2029         outValue
->dataType 
= outValue
->TYPE_STRING
; 
2038     if (outValue
->dataType 
== outValue
->TYPE_STRING
) { 
2039         // Should do better merging styles. 
2041             if (style 
!= NULL 
&& style
->size() > 0) { 
2042                 outValue
->data 
= pool
->add(finalStr
, *style
); 
2044                 outValue
->data 
= pool
->add(finalStr
, true); 
2047             // Caller will fill this in later. 
2060 uint32_t ResourceTable::getCustomResource( 
2061     const String16
& package
, const String16
& type
, const String16
& name
) const 
2063     //printf("getCustomResource: %s %s %s\n", String8(package).string(), 
2064     //       String8(type).string(), String8(name).string()); 
2065     sp
<Package
> p 
= mPackages
.valueFor(package
); 
2066     if (p 
== NULL
) return 0; 
2067     sp
<Type
> t 
= p
->getTypes().valueFor(type
); 
2068     if (t 
== NULL
) return 0; 
2069     sp
<ConfigList
> c 
=  t
->getConfigs().valueFor(name
); 
2070     if (c 
== NULL
) return 0; 
2071     int32_t ei 
= c
->getEntryIndex(); 
2072     if (ei 
< 0) return 0; 
2073     return getResId(p
, t
, ei
); 
2076 uint32_t ResourceTable::getCustomResourceWithCreation( 
2077         const String16
& package
, const String16
& type
, const String16
& name
, 
2078         const bool createIfNotFound
) 
2080     uint32_t resId 
= getCustomResource(package
, type
, name
); 
2081     if (resId 
!= 0 || !createIfNotFound
) { 
2084     String16 
value("false"); 
2086     status_t status 
= addEntry(mCurrentXmlPos
, package
, type
, name
, value
, NULL
, NULL
, true); 
2087     if (status 
== NO_ERROR
) { 
2088         resId 
= getResId(package
, type
, name
); 
2094 uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage
) const 
2099 bool ResourceTable::getAttributeType(uint32_t attrID
, uint32_t* outType
) 
2101     //printf("getAttributeType #%08x\n", attrID); 
2103     if (getItemValue(attrID
, ResTable_map::ATTR_TYPE
, &value
)) { 
2104         //printf("getAttributeType #%08x (%s): #%08x\n", attrID, 
2105         //       String8(getEntry(attrID)->getName()).string(), value.data); 
2106         *outType 
= value
.data
; 
2112 bool ResourceTable::getAttributeMin(uint32_t attrID
, uint32_t* outMin
) 
2114     //printf("getAttributeMin #%08x\n", attrID); 
2116     if (getItemValue(attrID
, ResTable_map::ATTR_MIN
, &value
)) { 
2117         *outMin 
= value
.data
; 
2123 bool ResourceTable::getAttributeMax(uint32_t attrID
, uint32_t* outMax
) 
2125     //printf("getAttributeMax #%08x\n", attrID); 
2127     if (getItemValue(attrID
, ResTable_map::ATTR_MAX
, &value
)) { 
2128         *outMax 
= value
.data
; 
2134 uint32_t ResourceTable::getAttributeL10N(uint32_t attrID
) 
2136     //printf("getAttributeL10N #%08x\n", attrID); 
2138     if (getItemValue(attrID
, ResTable_map::ATTR_L10N
, &value
)) { 
2141     return ResTable_map::L10N_NOT_REQUIRED
; 
2144 bool ResourceTable::getLocalizationSetting() 
2146     return mBundle
->getRequireLocalization(); 
2149 void ResourceTable::reportError(void* accessorCookie
, const char* fmt
, ...) 
2151     if (accessorCookie 
!= NULL 
&& fmt 
!= NULL
) { 
2152         AccessorCookie
* ac 
= (AccessorCookie
*)accessorCookie
; 
2157         retval 
= vsnprintf(buf
, sizeof(buf
), fmt
, ap
); 
2159         ac
->sourcePos
.error("Error: %s (at '%s' with value '%s').\n", 
2160                             buf
, ac
->attr
.string(), ac
->value
.string()); 
2164 bool ResourceTable::getAttributeKeys( 
2165     uint32_t attrID
, Vector
<String16
>* outKeys
) 
2167     sp
<const Entry
> e 
= getEntry(attrID
); 
2169         const size_t N 
= e
->getBag().size(); 
2170         for (size_t i
=0; i
<N
; i
++) { 
2171             const String16
& key 
= e
->getBag().keyAt(i
); 
2172             if (key
.size() > 0 && key
.string()[0] != '^') { 
2181 bool ResourceTable::getAttributeEnum( 
2182     uint32_t attrID
, const char16_t* name
, size_t nameLen
, 
2183     Res_value
* outValue
) 
2185     //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string()); 
2186     String16 
nameStr(name
, nameLen
); 
2187     sp
<const Entry
> e 
= getEntry(attrID
); 
2189         const size_t N 
= e
->getBag().size(); 
2190         for (size_t i
=0; i
<N
; i
++) { 
2191             //printf("Comparing %s to %s\n", String8(name, nameLen).string(), 
2192             //       String8(e->getBag().keyAt(i)).string()); 
2193             if (e
->getBag().keyAt(i
) == nameStr
) { 
2194                 return getItemValue(attrID
, e
->getBag().valueAt(i
).bagKeyId
, outValue
); 
2201 bool ResourceTable::getAttributeFlags( 
2202     uint32_t attrID
, const char16_t* name
, size_t nameLen
, 
2203     Res_value
* outValue
) 
2205     outValue
->dataType 
= Res_value::TYPE_INT_HEX
; 
2208     //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string()); 
2209     String16 
nameStr(name
, nameLen
); 
2210     sp
<const Entry
> e 
= getEntry(attrID
); 
2212         const size_t N 
= e
->getBag().size(); 
2214         const char16_t* end 
= name 
+ nameLen
; 
2215         const char16_t* pos 
= name
; 
2216         bool failed 
= false; 
2217         while (pos 
< end 
&& !failed
) { 
2218             const char16_t* start 
= pos
; 
2220             while (pos 
< end 
&& *pos 
!= '|') { 
2224             String16 
nameStr(start
, pos
-start
); 
2226             for (i
=0; i
<N
; i
++) { 
2227                 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(), 
2228                 //       String8(e->getBag().keyAt(i)).string()); 
2229                 if (e
->getBag().keyAt(i
) == nameStr
) { 
2231                     bool got 
= getItemValue(attrID
, e
->getBag().valueAt(i
).bagKeyId
, &val
); 
2235                     //printf("Got value: 0x%08x\n", val.data); 
2236                     outValue
->data 
|= val
.data
; 
2242                 // Didn't find this flag identifier. 
2255 status_t 
ResourceTable::assignResourceIds() 
2257     const size_t N 
= mOrderedPackages
.size(); 
2259     status_t firstError 
= NO_ERROR
; 
2261     // First generate all bag attributes and assign indices. 
2262     for (pi
=0; pi
<N
; pi
++) { 
2263         sp
<Package
> p 
= mOrderedPackages
.itemAt(pi
); 
2264         if (p 
== NULL 
|| p
->getTypes().size() == 0) { 
2269         status_t err 
= p
->applyPublicTypeOrder(); 
2270         if (err 
!= NO_ERROR 
&& firstError 
== NO_ERROR
) { 
2274         // Generate attributes... 
2275         const size_t N 
= p
->getOrderedTypes().size(); 
2277         for (ti
=0; ti
<N
; ti
++) { 
2278             sp
<Type
> t 
= p
->getOrderedTypes().itemAt(ti
); 
2282             const size_t N 
= t
->getOrderedConfigs().size(); 
2283             for (size_t ci
=0; ci
<N
; ci
++) { 
2284                 sp
<ConfigList
> c 
= t
->getOrderedConfigs().itemAt(ci
); 
2288                 const size_t N 
= c
->getEntries().size(); 
2289                 for (size_t ei
=0; ei
<N
; ei
++) { 
2290                     sp
<Entry
> e 
= c
->getEntries().valueAt(ei
); 
2294                     status_t err 
= e
->generateAttributes(this, p
->getName()); 
2295                     if (err 
!= NO_ERROR 
&& firstError 
== NO_ERROR
) { 
2302         const SourcePos 
unknown(String8("????"), 0); 
2303         sp
<Type
> attr 
= p
->getType(String16("attr"), unknown
); 
2305         // Assign indices... 
2306         for (ti
=0; ti
<N
; ti
++) { 
2307             sp
<Type
> t 
= p
->getOrderedTypes().itemAt(ti
); 
2311             err 
= t
->applyPublicEntryOrder(); 
2312             if (err 
!= NO_ERROR 
&& firstError 
== NO_ERROR
) { 
2316             const size_t N 
= t
->getOrderedConfigs().size(); 
2319             LOG_ALWAYS_FATAL_IF(ti 
== 0 && attr 
!= t
, 
2320                                 "First type is not attr!"); 
2322             for (size_t ei
=0; ei
<N
; ei
++) { 
2323                 sp
<ConfigList
> c 
= t
->getOrderedConfigs().itemAt(ei
); 
2327                 c
->setEntryIndex(ei
); 
2331         // Assign resource IDs to keys in bags... 
2332         for (ti
=0; ti
<N
; ti
++) { 
2333             sp
<Type
> t 
= p
->getOrderedTypes().itemAt(ti
); 
2337             const size_t N 
= t
->getOrderedConfigs().size(); 
2338             for (size_t ci
=0; ci
<N
; ci
++) { 
2339                 sp
<ConfigList
> c 
= t
->getOrderedConfigs().itemAt(ci
); 
2340                 //printf("Ordered config #%d: %p\n", ci, c.get()); 
2341                 const size_t N 
= c
->getEntries().size(); 
2342                 for (size_t ei
=0; ei
<N
; ei
++) { 
2343                     sp
<Entry
> e 
= c
->getEntries().valueAt(ei
); 
2347                     status_t err 
= e
->assignResourceIds(this, p
->getName()); 
2348                     if (err 
!= NO_ERROR 
&& firstError 
== NO_ERROR
) { 
2358 status_t 
ResourceTable::addSymbols(const sp
<AaptSymbols
>& outSymbols
) { 
2359     const size_t N 
= mOrderedPackages
.size(); 
2362     for (pi
=0; pi
<N
; pi
++) { 
2363         sp
<Package
> p 
= mOrderedPackages
.itemAt(pi
); 
2364         if (p
->getTypes().size() == 0) { 
2369         const size_t N 
= p
->getOrderedTypes().size(); 
2372         for (ti
=0; ti
<N
; ti
++) { 
2373             sp
<Type
> t 
= p
->getOrderedTypes().itemAt(ti
); 
2377             const size_t N 
= t
->getOrderedConfigs().size(); 
2378             sp
<AaptSymbols
> typeSymbols
; 
2379             typeSymbols 
= outSymbols
->addNestedSymbol(String8(t
->getName()), t
->getPos()); 
2380             for (size_t ci
=0; ci
<N
; ci
++) { 
2381                 sp
<ConfigList
> c 
= t
->getOrderedConfigs().itemAt(ci
); 
2385                 uint32_t rid 
= getResId(p
, t
, ci
); 
2387                     return UNKNOWN_ERROR
; 
2389                 if (Res_GETPACKAGE(rid
) == (size_t)(p
->getAssignedId()-1)) { 
2390                     typeSymbols
->addSymbol(String8(c
->getName()), rid
, c
->getPos()); 
2392                     String16 
comment(c
->getComment()); 
2393                     typeSymbols
->appendComment(String8(c
->getName()), comment
, c
->getPos()); 
2394                     //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(), 
2395                     //     String8(comment).string()); 
2396                     comment 
= c
->getTypeComment(); 
2397                     typeSymbols
->appendTypeComment(String8(c
->getName()), comment
); 
2400                     printf("**** NO MATCH: 0x%08x vs 0x%08x\n", 
2401                            Res_GETPACKAGE(rid
), p
->getAssignedId()); 
2412 ResourceTable::addLocalization(const String16
& name
, const String8
& locale
) 
2414     mLocalizations
[name
].insert(locale
); 
2419  * Flag various sorts of localization problems.  '+' indicates checks already implemented; 
2420  * '-' indicates checks that will be implemented in the future. 
2422  * + A localized string for which no default-locale version exists => warning 
2423  * + A string for which no version in an explicitly-requested locale exists => warning 
2424  * + A localized translation of an translateable="false" string => warning 
2425  * - A localized string not provided in every locale used by the table 
2428 ResourceTable::validateLocalizations(void) 
2430     status_t err 
= NO_ERROR
; 
2431     const String8 defaultLocale
; 
2433     // For all strings... 
2434     for (map
<String16
, set
<String8
> >::iterator nameIter 
= mLocalizations
.begin(); 
2435          nameIter 
!= mLocalizations
.end(); 
2437         const set
<String8
>& configSet 
= nameIter
->second
;   // naming convenience 
2439         // Look for strings with no default localization 
2440         if (configSet
.count(defaultLocale
) == 0) { 
2441             fprintf(stdout
, "aapt: warning: string '%s' has no default translation in %s; found:", 
2442                     String8(nameIter
->first
).string(), mBundle
->getResourceSourceDirs()[0]); 
2443             for (set
<String8
>::iterator locales 
= configSet
.begin(); 
2444                  locales 
!= configSet
.end(); 
2446                 fprintf(stdout
, " %s", (*locales
).string()); 
2448             fprintf(stdout
, "\n"); 
2449             // !!! TODO: throw an error here in some circumstances 
2452         // Check that all requested localizations are present for this string 
2453         if (mBundle
->getConfigurations() != NULL 
&& mBundle
->getRequireLocalization()) { 
2454             const char* allConfigs 
= mBundle
->getConfigurations(); 
2455             const char* start 
= allConfigs
; 
2460                 comma 
= strchr(start
, ','); 
2461                 if (comma 
!= NULL
) { 
2462                     config
.setTo(start
, comma 
- start
); 
2465                     config
.setTo(start
); 
2468                 // don't bother with the pseudolocale "zz_ZZ" 
2469                 if (config 
!= "zz_ZZ") { 
2470                     if (configSet
.find(config
) == configSet
.end()) { 
2471                         // okay, no specific localization found.  it's possible that we are 
2472                         // requiring a specific regional localization [e.g. de_DE] but there is an 
2473                         // available string in the generic language localization [e.g. de]; 
2474                         // consider that string to have fulfilled the localization requirement. 
2475                         String8 
region(config
.string(), 2); 
2476                         if (configSet
.find(region
) == configSet
.end()) { 
2477                             if (configSet
.count(defaultLocale
) == 0) { 
2478                                 fprintf(stdout
, "aapt: warning: " 
2479                                         "**** string '%s' has no default or required localization " 
2481                                         String8(nameIter
->first
).string(), 
2483                                         mBundle
->getResourceSourceDirs()[0]); 
2488            } while (comma 
!= NULL
); 
2497 ResourceFilter::parse(const char* arg
) 
2503     const char* p 
= arg
; 
2512         String8 
part(p
, q
-p
); 
2514         if (part 
== "zz_ZZ") { 
2515             mContainsPseudo 
= true; 
2519         if (AaptGroupEntry::parseNamePart(part
, &axis
, &value
)) { 
2520             fprintf(stderr
, "Invalid configuration: %s\n", arg
); 
2521             fprintf(stderr
, "                       "); 
2522             for (int i
=0; i
<p
-arg
; i
++) { 
2523                 fprintf(stderr
, " "); 
2525             for (int i
=0; i
<q
-p
; i
++) { 
2526                 fprintf(stderr
, "^"); 
2528             fprintf(stderr
, "\n"); 
2532         ssize_t index 
= mData
.indexOfKey(axis
); 
2534             mData
.add(axis
, SortedVector
<uint32_t>()); 
2536         SortedVector
<uint32_t>& sv 
= mData
.editValueFor(axis
); 
2538         // if it's a locale with a region, also match an unmodified locale of the 
2540         if (axis 
== AXIS_LANGUAGE
) { 
2541             if (value 
& 0xffff0000) { 
2542                 sv
.add(value 
& 0x0000ffff); 
2554 ResourceFilter::match(int axis
, uint32_t value
) 
2557         // they didn't specify anything so take everything 
2560     ssize_t index 
= mData
.indexOfKey(axis
); 
2562         // we didn't request anything on this axis so take everything 
2565     const SortedVector
<uint32_t>& sv 
= mData
.valueAt(index
); 
2566     return sv
.indexOf(value
) >= 0; 
2570 ResourceFilter::match(const ResTable_config
& config
) 
2572     if (config
.locale
) { 
2573         uint32_t locale 
= (config
.country
[1] << 24) | (config
.country
[0] << 16) 
2574                 | (config
.language
[1] << 8) | (config
.language
[0]); 
2575         if (!match(AXIS_LANGUAGE
, locale
)) { 
2579     if (!match(AXIS_ORIENTATION
, config
.orientation
)) { 
2582     if (!match(AXIS_UIMODETYPE
, (config
.uiMode
&ResTable_config::MASK_UI_MODE_TYPE
))) { 
2585     if (!match(AXIS_UIMODENIGHT
, (config
.uiMode
&ResTable_config::MASK_UI_MODE_NIGHT
))) { 
2588     if (!match(AXIS_DENSITY
, config
.density
)) { 
2591     if (!match(AXIS_TOUCHSCREEN
, config
.touchscreen
)) { 
2594     if (!match(AXIS_KEYSHIDDEN
, config
.inputFlags
)) { 
2597     if (!match(AXIS_KEYBOARD
, config
.keyboard
)) { 
2600     if (!match(AXIS_NAVIGATION
, config
.navigation
)) { 
2603     if (!match(AXIS_SCREENSIZE
, config
.screenSize
)) { 
2606     if (!match(AXIS_VERSION
, config
.version
)) { 
2612 status_t 
ResourceTable::flatten(Bundle
* bundle
, const sp
<AaptFile
>& dest
) 
2614     ResourceFilter filter
; 
2615     status_t err 
= filter
.parse(bundle
->getConfigurations()); 
2616     if (err 
!= NO_ERROR
) { 
2620     const size_t N 
= mOrderedPackages
.size(); 
2623     bool useUTF8 
= !bundle
->getWantUTF16() && bundle
->isMinSdkAtLeast(SDK_FROYO
); 
2625     // Iterate through all data, collecting all values (strings, 
2626     // references, etc). 
2627     StringPool valueStrings 
= StringPool(false, useUTF8
); 
2628     for (pi
=0; pi
<N
; pi
++) { 
2629         sp
<Package
> p 
= mOrderedPackages
.itemAt(pi
); 
2630         if (p
->getTypes().size() == 0) { 
2635         StringPool typeStrings 
= StringPool(false, useUTF8
); 
2636         StringPool keyStrings 
= StringPool(false, useUTF8
); 
2638         const size_t N 
= p
->getOrderedTypes().size(); 
2639         for (size_t ti
=0; ti
<N
; ti
++) { 
2640             sp
<Type
> t 
= p
->getOrderedTypes().itemAt(ti
); 
2642                 typeStrings
.add(String16("<empty>"), false); 
2645             typeStrings
.add(t
->getName(), false); 
2647             const size_t N 
= t
->getOrderedConfigs().size(); 
2648             for (size_t ci
=0; ci
<N
; ci
++) { 
2649                 sp
<ConfigList
> c 
= t
->getOrderedConfigs().itemAt(ci
); 
2653                 const size_t N 
= c
->getEntries().size(); 
2654                 for (size_t ei
=0; ei
<N
; ei
++) { 
2655                     ConfigDescription config 
= c
->getEntries().keyAt(ei
); 
2656                     if (!filter
.match(config
)) { 
2659                     sp
<Entry
> e 
= c
->getEntries().valueAt(ei
); 
2663                     e
->setNameIndex(keyStrings
.add(e
->getName(), true)); 
2664                     status_t err 
= e
->prepareFlatten(&valueStrings
, this); 
2665                     if (err 
!= NO_ERROR
) { 
2672         p
->setTypeStrings(typeStrings
.createStringBlock()); 
2673         p
->setKeyStrings(keyStrings
.createStringBlock()); 
2678     // Now build the array of package chunks. 
2679     Vector
<sp
<AaptFile
> > flatPackages
; 
2680     for (pi
=0; pi
<N
; pi
++) { 
2681         sp
<Package
> p 
= mOrderedPackages
.itemAt(pi
); 
2682         if (p
->getTypes().size() == 0) { 
2687         const size_t N 
= p
->getTypeStrings().size(); 
2689         const size_t baseSize 
= sizeof(ResTable_package
); 
2691         // Start the package data. 
2692         sp
<AaptFile
> data 
= new AaptFile(String8(), AaptGroupEntry(), String8()); 
2693         ResTable_package
* header 
= (ResTable_package
*)data
->editData(baseSize
); 
2694         if (header 
== NULL
) { 
2695             fprintf(stderr
, "ERROR: out of memory creating ResTable_package\n"); 
2698         memset(header
, 0, sizeof(*header
)); 
2699         header
->header
.type 
= htods(RES_TABLE_PACKAGE_TYPE
); 
2700         header
->header
.headerSize 
= htods(sizeof(*header
)); 
2701         header
->id 
= htodl(p
->getAssignedId()); 
2702         strcpy16_htod(header
->name
, p
->getName().string()); 
2704         // Write the string blocks. 
2705         const size_t typeStringsStart 
= data
->getSize(); 
2706         sp
<AaptFile
> strFile 
= p
->getTypeStringsData(); 
2707         ssize_t amt 
= data
->writeData(strFile
->getData(), strFile
->getSize()); 
2708         #if PRINT_STRING_METRICS 
2709         fprintf(stderr
, "**** type strings: %d\n", amt
); 
2715         const size_t keyStringsStart 
= data
->getSize(); 
2716         strFile 
= p
->getKeyStringsData(); 
2717         amt 
= data
->writeData(strFile
->getData(), strFile
->getSize()); 
2718         #if PRINT_STRING_METRICS 
2719         fprintf(stderr
, "**** key strings: %d\n", amt
); 
2726         // Build the type chunks inside of this package. 
2727         for (size_t ti
=0; ti
<N
; ti
++) { 
2728             // Retrieve them in the same order as the type string block. 
2730             String16 
typeName(p
->getTypeStrings().stringAt(ti
, &len
)); 
2731             sp
<Type
> t 
= p
->getTypes().valueFor(typeName
); 
2732             LOG_ALWAYS_FATAL_IF(t 
== NULL 
&& typeName 
!= String16("<empty>"), 
2733                                 "Type name %s not found", 
2734                                 String8(typeName
).string()); 
2736             const size_t N 
= t 
!= NULL 
? t
->getOrderedConfigs().size() : 0; 
2738             // First write the typeSpec chunk, containing information about 
2739             // each resource entry in this type. 
2741                 const size_t typeSpecSize 
= sizeof(ResTable_typeSpec
) + sizeof(uint32_t)*N
; 
2742                 const size_t typeSpecStart 
= data
->getSize(); 
2743                 ResTable_typeSpec
* tsHeader 
= (ResTable_typeSpec
*) 
2744                     (((uint8_t*)data
->editData(typeSpecStart
+typeSpecSize
)) + typeSpecStart
); 
2745                 if (tsHeader 
== NULL
) { 
2746                     fprintf(stderr
, "ERROR: out of memory creating ResTable_typeSpec\n"); 
2749                 memset(tsHeader
, 0, sizeof(*tsHeader
)); 
2750                 tsHeader
->header
.type 
= htods(RES_TABLE_TYPE_SPEC_TYPE
); 
2751                 tsHeader
->header
.headerSize 
= htods(sizeof(*tsHeader
)); 
2752                 tsHeader
->header
.size 
= htodl(typeSpecSize
); 
2753                 tsHeader
->id 
= ti
+1; 
2754                 tsHeader
->entryCount 
= htodl(N
); 
2756                 uint32_t* typeSpecFlags 
= (uint32_t*) 
2757                     (((uint8_t*)data
->editData()) 
2758                         + typeSpecStart 
+ sizeof(ResTable_typeSpec
)); 
2759                 memset(typeSpecFlags
, 0, sizeof(uint32_t)*N
); 
2761                 for (size_t ei
=0; ei
<N
; ei
++) { 
2762                     sp
<ConfigList
> cl 
= t
->getOrderedConfigs().itemAt(ei
); 
2763                     if (cl
->getPublic()) { 
2764                         typeSpecFlags
[ei
] |= htodl(ResTable_typeSpec::SPEC_PUBLIC
); 
2766                     const size_t CN 
= cl
->getEntries().size(); 
2767                     for (size_t ci
=0; ci
<CN
; ci
++) { 
2768                         if (!filter
.match(cl
->getEntries().keyAt(ci
))) { 
2771                         for (size_t cj
=ci
+1; cj
<CN
; cj
++) { 
2772                             if (!filter
.match(cl
->getEntries().keyAt(cj
))) { 
2775                             typeSpecFlags
[ei
] |= htodl( 
2776                                 cl
->getEntries().keyAt(ci
).diff(cl
->getEntries().keyAt(cj
))); 
2782             // We need to write one type chunk for each configuration for 
2783             // which we have entries in this type. 
2784             const size_t NC 
= t
->getUniqueConfigs().size(); 
2786             const size_t typeSize 
= sizeof(ResTable_type
) + sizeof(uint32_t)*N
; 
2788             for (size_t ci
=0; ci
<NC
; ci
++) { 
2789                 ConfigDescription config 
= t
->getUniqueConfigs().itemAt(ci
); 
2791                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c " 
2792                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", 
2794                       config
.mcc
, config
.mnc
, 
2795                       config
.language
[0] ? config
.language
[0] : '-', 
2796                       config
.language
[1] ? config
.language
[1] : '-', 
2797                       config
.country
[0] ? config
.country
[0] : '-', 
2798                       config
.country
[1] ? config
.country
[1] : '-', 
2807                       config
.screenHeight
)); 
2809                 if (!filter
.match(config
)) { 
2813                 const size_t typeStart 
= data
->getSize(); 
2815                 ResTable_type
* tHeader 
= (ResTable_type
*) 
2816                     (((uint8_t*)data
->editData(typeStart
+typeSize
)) + typeStart
); 
2817                 if (tHeader 
== NULL
) { 
2818                     fprintf(stderr
, "ERROR: out of memory creating ResTable_type\n"); 
2822                 memset(tHeader
, 0, sizeof(*tHeader
)); 
2823                 tHeader
->header
.type 
= htods(RES_TABLE_TYPE_TYPE
); 
2824                 tHeader
->header
.headerSize 
= htods(sizeof(*tHeader
)); 
2826                 tHeader
->entryCount 
= htodl(N
); 
2827                 tHeader
->entriesStart 
= htodl(typeSize
); 
2828                 tHeader
->config 
= config
; 
2829                 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c " 
2830                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", 
2832                       tHeader
->config
.mcc
, tHeader
->config
.mnc
, 
2833                       tHeader
->config
.language
[0] ? tHeader
->config
.language
[0] : '-', 
2834                       tHeader
->config
.language
[1] ? tHeader
->config
.language
[1] : '-', 
2835                       tHeader
->config
.country
[0] ? tHeader
->config
.country
[0] : '-', 
2836                       tHeader
->config
.country
[1] ? tHeader
->config
.country
[1] : '-', 
2837                       tHeader
->config
.orientation
, 
2838                       tHeader
->config
.uiMode
, 
2839                       tHeader
->config
.touchscreen
, 
2840                       tHeader
->config
.density
, 
2841                       tHeader
->config
.keyboard
, 
2842                       tHeader
->config
.inputFlags
, 
2843                       tHeader
->config
.navigation
, 
2844                       tHeader
->config
.screenWidth
, 
2845                       tHeader
->config
.screenHeight
)); 
2846                 tHeader
->config
.swapHtoD(); 
2848                 // Build the entries inside of this type. 
2849                 for (size_t ei
=0; ei
<N
; ei
++) { 
2850                     sp
<ConfigList
> cl 
= t
->getOrderedConfigs().itemAt(ei
); 
2851                     sp
<Entry
> e 
= cl
->getEntries().valueFor(config
); 
2853                     // Set the offset for this entry in its type. 
2854                     uint32_t* index 
= (uint32_t*) 
2855                         (((uint8_t*)data
->editData()) 
2856                             + typeStart 
+ sizeof(ResTable_type
)); 
2858                         index
[ei
] = htodl(data
->getSize()-typeStart
-typeSize
); 
2860                         // Create the entry. 
2861                         ssize_t amt 
= e
->flatten(bundle
, data
, cl
->getPublic()); 
2866                         index
[ei
] = htodl(ResTable_type::NO_ENTRY
); 
2870                 // Fill in the rest of the type information. 
2871                 tHeader 
= (ResTable_type
*) 
2872                     (((uint8_t*)data
->editData()) + typeStart
); 
2873                 tHeader
->header
.size 
= htodl(data
->getSize()-typeStart
); 
2877         // Fill in the rest of the package information. 
2878         header 
= (ResTable_package
*)data
->editData(); 
2879         header
->header
.size 
= htodl(data
->getSize()); 
2880         header
->typeStrings 
= htodl(typeStringsStart
); 
2881         header
->lastPublicType 
= htodl(p
->getTypeStrings().size()); 
2882         header
->keyStrings 
= htodl(keyStringsStart
); 
2883         header
->lastPublicKey 
= htodl(p
->getKeyStrings().size()); 
2885         flatPackages
.add(data
); 
2888     // And now write out the final chunks. 
2889     const size_t dataStart 
= dest
->getSize(); 
2893         ResTable_header header
; 
2894         memset(&header
, 0, sizeof(header
)); 
2895         header
.header
.type 
= htods(RES_TABLE_TYPE
); 
2896         header
.header
.headerSize 
= htods(sizeof(header
)); 
2897         header
.packageCount 
= htodl(flatPackages
.size()); 
2898         status_t err 
= dest
->writeData(&header
, sizeof(header
)); 
2899         if (err 
!= NO_ERROR
) { 
2900             fprintf(stderr
, "ERROR: out of memory creating ResTable_header\n"); 
2905     ssize_t strStart 
= dest
->getSize(); 
2906     err 
= valueStrings
.writeStringBlock(dest
); 
2907     if (err 
!= NO_ERROR
) { 
2911     ssize_t amt 
= (dest
->getSize()-strStart
); 
2913     #if PRINT_STRING_METRICS 
2914     fprintf(stderr
, "**** value strings: %d\n", amt
); 
2915     fprintf(stderr
, "**** total strings: %d\n", strAmt
); 
2918     for (pi
=0; pi
<flatPackages
.size(); pi
++) { 
2919         err 
= dest
->writeData(flatPackages
[pi
]->getData(), 
2920                               flatPackages
[pi
]->getSize()); 
2921         if (err 
!= NO_ERROR
) { 
2922             fprintf(stderr
, "ERROR: out of memory creating package chunk for ResTable_header\n"); 
2927     ResTable_header
* header 
= (ResTable_header
*) 
2928         (((uint8_t*)dest
->getData()) + dataStart
); 
2929     header
->header
.size 
= htodl(dest
->getSize() - dataStart
); 
2931     NOISY(aout 
<< "Resource table:" 
2932           << HexDump(dest
->getData(), dest
->getSize()) << endl
); 
2934     #if PRINT_STRING_METRICS 
2935     fprintf(stderr
, "**** total resource table size: %d / %d%% strings\n", 
2936         dest
->getSize(), (strAmt
*100)/dest
->getSize()); 
2942 void ResourceTable::writePublicDefinitions(const String16
& package
, FILE* fp
) 
2945     "<!-- This file contains <public> resource definitions for all\n" 
2946     "     resources that were generated from the source data. -->\n" 
2950     writePublicDefinitions(package
, fp
, true); 
2951     writePublicDefinitions(package
, fp
, false); 
2958 void ResourceTable::writePublicDefinitions(const String16
& package
, FILE* fp
, bool pub
) 
2960     bool didHeader 
= false; 
2962     sp
<Package
> pkg 
= mPackages
.valueFor(package
); 
2964         const size_t NT 
= pkg
->getOrderedTypes().size(); 
2965         for (size_t i
=0; i
<NT
; i
++) { 
2966             sp
<Type
> t 
= pkg
->getOrderedTypes().itemAt(i
); 
2971             bool didType 
= false; 
2973             const size_t NC 
= t
->getOrderedConfigs().size(); 
2974             for (size_t j
=0; j
<NC
; j
++) { 
2975                 sp
<ConfigList
> c 
= t
->getOrderedConfigs().itemAt(j
); 
2980                 if (c
->getPublic() != pub
) { 
2990                         fprintf(fp
,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n"); 
2991                         fprintf(fp
,"       Changes to these definitions will break binary compatibility. -->\n\n"); 
2993                         fprintf(fp
,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n"); 
2994                         fprintf(fp
,"       You can make them public my moving these lines into a file in res/values. -->\n\n"); 
2999                     const size_t NE 
= c
->getEntries().size(); 
3000                     for (size_t k
=0; k
<NE
; k
++) { 
3001                         const SourcePos
& pos 
= c
->getEntries().valueAt(k
)->getPos(); 
3002                         if (pos
.file 
!= "") { 
3003                             fprintf(fp
,"  <!-- Declared at %s:%d -->\n", 
3004                                     pos
.file
.string(), pos
.line
); 
3008                 fprintf(fp
, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n", 
3009                         String8(t
->getName()).string(), 
3010                         String8(c
->getName()).string(), 
3011                         getResId(pkg
, t
, c
->getEntryIndex())); 
3017 ResourceTable::Item::Item(const SourcePos
& _sourcePos
, 
3019                           const String16
& _value
, 
3020                           const Vector
<StringPool::entry_style_span
>* _style
, 
3022     : sourcePos(_sourcePos
) 
3034 status_t 
ResourceTable::Entry::makeItABag(const SourcePos
& sourcePos
) 
3036     if (mType 
== TYPE_BAG
) { 
3039     if (mType 
== TYPE_UNKNOWN
) { 
3043     sourcePos
.error("Resource entry %s is already defined as a single item.\n" 
3044                     "%s:%d: Originally defined here.\n", 
3045                     String8(mName
).string(), 
3046                     mItem
.sourcePos
.file
.string(), mItem
.sourcePos
.line
); 
3047     return UNKNOWN_ERROR
; 
3050 status_t 
ResourceTable::Entry::setItem(const SourcePos
& sourcePos
, 
3051                                        const String16
& value
, 
3052                                        const Vector
<StringPool::entry_style_span
>* style
, 
3054                                        const bool overwrite
) 
3056     Item 
item(sourcePos
, false, value
, style
); 
3058     if (mType 
== TYPE_BAG
) { 
3059         const Item
& item(mBag
.valueAt(0)); 
3060         sourcePos
.error("Resource entry %s is already defined as a bag.\n" 
3061                         "%s:%d: Originally defined here.\n", 
3062                         String8(mName
).string(), 
3063                         item
.sourcePos
.file
.string(), item
.sourcePos
.line
); 
3064         return UNKNOWN_ERROR
; 
3066     if ( (mType 
!= TYPE_UNKNOWN
) && (overwrite 
== false) ) { 
3067         sourcePos
.error("Resource entry %s is already defined.\n" 
3068                         "%s:%d: Originally defined here.\n", 
3069                         String8(mName
).string(), 
3070                         mItem
.sourcePos
.file
.string(), mItem
.sourcePos
.line
); 
3071         return UNKNOWN_ERROR
; 
3076     mItemFormat 
= format
; 
3080 status_t 
ResourceTable::Entry::addToBag(const SourcePos
& sourcePos
, 
3081                                         const String16
& key
, const String16
& value
, 
3082                                         const Vector
<StringPool::entry_style_span
>* style
, 
3083                                         bool replace
, bool isId
, int32_t format
) 
3085     status_t err 
= makeItABag(sourcePos
); 
3086     if (err 
!= NO_ERROR
) { 
3090     Item 
item(sourcePos
, isId
, value
, style
, format
); 
3092     // XXX NOTE: there is an error if you try to have a bag with two keys, 
3093     // one an attr and one an id, with the same name.  Not something we 
3094     // currently ever have to worry about. 
3095     ssize_t origKey 
= mBag
.indexOfKey(key
); 
3098             const Item
& item(mBag
.valueAt(origKey
)); 
3099             sourcePos
.error("Resource entry %s already has bag item %s.\n" 
3100                     "%s:%d: Originally defined here.\n", 
3101                     String8(mName
).string(), String8(key
).string(), 
3102                     item
.sourcePos
.file
.string(), item
.sourcePos
.line
); 
3103             return UNKNOWN_ERROR
; 
3105         //printf("Replacing %s with %s\n", 
3106         //       String8(mBag.valueFor(key).value).string(), String8(value).string()); 
3107         mBag
.replaceValueFor(key
, item
); 
3110     mBag
.add(key
, item
); 
3114 status_t 
ResourceTable::Entry::emptyBag(const SourcePos
& sourcePos
) 
3116     status_t err 
= makeItABag(sourcePos
); 
3117     if (err 
!= NO_ERROR
) { 
3125 status_t 
ResourceTable::Entry::generateAttributes(ResourceTable
* table
, 
3126                                                   const String16
& package
) 
3128     const String16 
attr16("attr"); 
3129     const String16 
id16("id"); 
3130     const size_t N 
= mBag
.size(); 
3131     for (size_t i
=0; i
<N
; i
++) { 
3132         const String16
& key 
= mBag
.keyAt(i
); 
3133         const Item
& it 
= mBag
.valueAt(i
); 
3135             if (!table
->hasBagOrEntry(key
, &id16
, &package
)) { 
3136                 String16 
value("false"); 
3137                 status_t err 
= table
->addEntry(SourcePos(String8("<generated>"), 0), package
, 
3139                 if (err 
!= NO_ERROR
) { 
3143         } else if (!table
->hasBagOrEntry(key
, &attr16
, &package
)) { 
3146 //             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n", 
3147 //                     String8(key).string()); 
3148 //             const Item& item(mBag.valueAt(i)); 
3149 //             fprintf(stderr, "Referenced from file %s line %d\n", 
3150 //                     item.sourcePos.file.string(), item.sourcePos.line); 
3151 //             return UNKNOWN_ERROR; 
3154             sprintf(numberStr
, "%d", ResTable_map::TYPE_ANY
); 
3155             status_t err 
= table
->addBag(SourcePos("<generated>", 0), package
, 
3156                                          attr16
, key
, String16(""), 
3158                                          String16(numberStr
), NULL
, NULL
); 
3159             if (err 
!= NO_ERROR
) { 
3168 status_t 
ResourceTable::Entry::assignResourceIds(ResourceTable
* table
, 
3169                                                  const String16
& package
) 
3171     bool hasErrors 
= false; 
3173     if (mType 
== TYPE_BAG
) { 
3174         const char* errorMsg
; 
3175         const String16 
style16("style"); 
3176         const String16 
attr16("attr"); 
3177         const String16 
id16("id"); 
3179         if (mParent
.size() > 0) { 
3180             mParentId 
= table
->getResId(mParent
, &style16
, NULL
, &errorMsg
); 
3181             if (mParentId 
== 0) { 
3182                 mPos
.error("Error retrieving parent for item: %s '%s'.\n", 
3183                         errorMsg
, String8(mParent
).string()); 
3187         const size_t N 
= mBag
.size(); 
3188         for (size_t i
=0; i
<N
; i
++) { 
3189             const String16
& key 
= mBag
.keyAt(i
); 
3190             Item
& it 
= mBag
.editValueAt(i
); 
3191             it
.bagKeyId 
= table
->getResId(key
, 
3192                     it
.isId 
? &id16 
: &attr16
, NULL
, &errorMsg
); 
3193             //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId); 
3194             if (it
.bagKeyId 
== 0) { 
3195                 it
.sourcePos
.error("Error: %s: %s '%s'.\n", errorMsg
, 
3196                         String8(it
.isId 
? id16 
: attr16
).string(), 
3197                         String8(key
).string()); 
3202     return hasErrors 
? UNKNOWN_ERROR 
: NO_ERROR
; 
3205 status_t 
ResourceTable::Entry::prepareFlatten(StringPool
* strings
, ResourceTable
* table
) 
3207     if (mType 
== TYPE_ITEM
) { 
3209         AccessorCookie 
ac(it
.sourcePos
, String8(mName
), String8(it
.value
)); 
3210         if (!table
->stringToValue(&it
.parsedValue
, strings
, 
3211                                   it
.value
, false, true, 0, 
3212                                   &it
.style
, NULL
, &ac
, mItemFormat
)) { 
3213             return UNKNOWN_ERROR
; 
3215     } else if (mType 
== TYPE_BAG
) { 
3216         const size_t N 
= mBag
.size(); 
3217         for (size_t i
=0; i
<N
; i
++) { 
3218             const String16
& key 
= mBag
.keyAt(i
); 
3219             Item
& it 
= mBag
.editValueAt(i
); 
3220             AccessorCookie 
ac(it
.sourcePos
, String8(key
), String8(it
.value
)); 
3221             if (!table
->stringToValue(&it
.parsedValue
, strings
, 
3222                                       it
.value
, false, true, it
.bagKeyId
, 
3223                                       &it
.style
, NULL
, &ac
, it
.format
)) { 
3224                 return UNKNOWN_ERROR
; 
3228         mPos
.error("Error: entry %s is not a single item or a bag.\n", 
3229                    String8(mName
).string()); 
3230         return UNKNOWN_ERROR
; 
3235 ssize_t 
ResourceTable::Entry::flatten(Bundle
* bundle
, const sp
<AaptFile
>& data
, bool isPublic
) 
3238     ResTable_entry header
; 
3239     memset(&header
, 0, sizeof(header
)); 
3240     header
.size 
= htods(sizeof(header
)); 
3241     const type ty 
= this != NULL 
? mType 
: TYPE_ITEM
; 
3243         if (ty 
== TYPE_BAG
) { 
3244             header
.flags 
|= htods(header
.FLAG_COMPLEX
); 
3247             header
.flags 
|= htods(header
.FLAG_PUBLIC
); 
3249         header
.key
.index 
= htodl(mNameIndex
); 
3251     if (ty 
!= TYPE_BAG
) { 
3252         status_t err 
= data
->writeData(&header
, sizeof(header
)); 
3253         if (err 
!= NO_ERROR
) { 
3254             fprintf(stderr
, "ERROR: out of memory creating ResTable_entry\n"); 
3258         const Item
& it 
= mItem
; 
3260         memset(&par
, 0, sizeof(par
)); 
3261         par
.size 
= htods(it
.parsedValue
.size
); 
3262         par
.dataType 
= it
.parsedValue
.dataType
; 
3263         par
.res0 
= it
.parsedValue
.res0
; 
3264         par
.data 
= htodl(it
.parsedValue
.data
); 
3266         printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n", 
3267                String8(mName
).string(), it
.parsedValue
.dataType
, 
3268                it
.parsedValue
.data
, par
.res0
); 
3270         err 
= data
->writeData(&par
, it
.parsedValue
.size
); 
3271         if (err 
!= NO_ERROR
) { 
3272             fprintf(stderr
, "ERROR: out of memory creating Res_value\n"); 
3275         amt 
+= it
.parsedValue
.size
; 
3277         size_t N 
= mBag
.size(); 
3279         // Create correct ordering of items. 
3280         KeyedVector
<uint32_t, const Item
*> items
; 
3281         for (i
=0; i
<N
; i
++) { 
3282             const Item
& it 
= mBag
.valueAt(i
); 
3283             items
.add(it
.bagKeyId
, &it
); 
3287         ResTable_map_entry mapHeader
; 
3288         memcpy(&mapHeader
, &header
, sizeof(header
)); 
3289         mapHeader
.size 
= htods(sizeof(mapHeader
)); 
3290         mapHeader
.parent
.ident 
= htodl(mParentId
); 
3291         mapHeader
.count 
= htodl(N
); 
3292         status_t err 
= data
->writeData(&mapHeader
, sizeof(mapHeader
)); 
3293         if (err 
!= NO_ERROR
) { 
3294             fprintf(stderr
, "ERROR: out of memory creating ResTable_entry\n"); 
3298         for (i
=0; i
<N
; i
++) { 
3299             const Item
& it 
= *items
.valueAt(i
); 
3301             map
.name
.ident 
= htodl(it
.bagKeyId
); 
3302             map
.value
.size 
= htods(it
.parsedValue
.size
); 
3303             map
.value
.dataType 
= it
.parsedValue
.dataType
; 
3304             map
.value
.res0 
= it
.parsedValue
.res0
; 
3305             map
.value
.data 
= htodl(it
.parsedValue
.data
); 
3306             err 
= data
->writeData(&map
, sizeof(map
)); 
3307             if (err 
!= NO_ERROR
) { 
3308                 fprintf(stderr
, "ERROR: out of memory creating Res_value\n"); 
3317 void ResourceTable::ConfigList::appendComment(const String16
& comment
, 
3320     if (comment
.size() <= 0) { 
3323     if (onlyIfEmpty 
&& mComment
.size() > 0) { 
3326     if (mComment
.size() > 0) { 
3327         mComment
.append(String16("\n")); 
3329     mComment
.append(comment
); 
3332 void ResourceTable::ConfigList::appendTypeComment(const String16
& comment
) 
3334     if (comment
.size() <= 0) { 
3337     if (mTypeComment
.size() > 0) { 
3338         mTypeComment
.append(String16("\n")); 
3340     mTypeComment
.append(comment
); 
3343 status_t 
ResourceTable::Type::addPublic(const SourcePos
& sourcePos
, 
3344                                         const String16
& name
, 
3345                                         const uint32_t ident
) 
3348     int32_t entryIdx 
= Res_GETENTRY(ident
); 
3350         sourcePos
.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n", 
3351                 String8(mName
).string(), String8(name
).string(), ident
); 
3352         return UNKNOWN_ERROR
; 
3356     int32_t typeIdx 
= Res_GETTYPE(ident
); 
3359         if (mPublicIndex 
> 0 && mPublicIndex 
!= typeIdx
) { 
3360             sourcePos
.error("Public resource %s/%s has conflicting type codes for its" 
3361                     " public identifiers (0x%x vs 0x%x).\n", 
3362                     String8(mName
).string(), String8(name
).string(), 
3363                     mPublicIndex
, typeIdx
); 
3364             return UNKNOWN_ERROR
; 
3366         mPublicIndex 
= typeIdx
; 
3369     if (mFirstPublicSourcePos 
== NULL
) { 
3370         mFirstPublicSourcePos 
= new SourcePos(sourcePos
); 
3373     if (mPublic
.indexOfKey(name
) < 0) { 
3374         mPublic
.add(name
, Public(sourcePos
, String16(), ident
)); 
3376         Public
& p 
= mPublic
.editValueFor(name
); 
3377         if (p
.ident 
!= ident
) { 
3378             sourcePos
.error("Public resource %s/%s has conflicting public identifiers" 
3379                     " (0x%08x vs 0x%08x).\n" 
3380                     "%s:%d: Originally defined here.\n", 
3381                     String8(mName
).string(), String8(name
).string(), p
.ident
, ident
, 
3382                     p
.sourcePos
.file
.string(), p
.sourcePos
.line
); 
3383             return UNKNOWN_ERROR
; 
3390 void ResourceTable::Type::canAddEntry(const String16
& name
) 
3392     mCanAddEntries
.add(name
); 
3395 sp
<ResourceTable::Entry
> ResourceTable::Type::getEntry(const String16
& entry
, 
3396                                                        const SourcePos
& sourcePos
, 
3397                                                        const ResTable_config
* config
, 
3400                                                        bool autoAddOverlay
) 
3403     sp
<ConfigList
> c 
= mConfigs
.valueFor(entry
); 
3405         if (overlay 
&& !autoAddOverlay 
&& mCanAddEntries
.indexOf(entry
) < 0) { 
3406             sourcePos
.error("Resource at %s appears in overlay but not" 
3407                             " in the base package; use <add-resource> to add.\n", 
3408                             String8(entry
).string()); 
3411         c 
= new ConfigList(entry
, sourcePos
); 
3412         mConfigs
.add(entry
, c
); 
3413         pos 
= (int)mOrderedConfigs
.size(); 
3414         mOrderedConfigs
.add(c
); 
3416             c
->setEntryIndex(pos
); 
3420     ConfigDescription cdesc
; 
3421     if (config
) cdesc 
= *config
; 
3423     sp
<Entry
> e 
= c
->getEntries().valueFor(cdesc
); 
3425         if (config 
!= NULL
) { 
3426             NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c " 
3427                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", 
3428                       sourcePos
.file
.string(), sourcePos
.line
, 
3429                       config
->mcc
, config
->mnc
, 
3430                       config
->language
[0] ? config
->language
[0] : '-', 
3431                       config
->language
[1] ? config
->language
[1] : '-', 
3432                       config
->country
[0] ? config
->country
[0] : '-', 
3433                       config
->country
[1] ? config
->country
[1] : '-', 
3434                       config
->orientation
, 
3435                       config
->touchscreen
, 
3440                       config
->screenWidth
, 
3441                       config
->screenHeight
)); 
3443             NOISY(printf("New entry at %s:%d: NULL config\n", 
3444                       sourcePos
.file
.string(), sourcePos
.line
)); 
3446         e 
= new Entry(entry
, sourcePos
); 
3447         c
->addEntry(cdesc
, e
); 
3451                 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) { 
3452                     if (mOrderedConfigs[pos] == c) { 
3456                 if (pos >= (int)mOrderedConfigs.size()) { 
3457                     sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry"); 
3461             e->setEntryIndex(pos); 
3466     mUniqueConfigs
.add(cdesc
); 
3471 status_t 
ResourceTable::Type::applyPublicEntryOrder() 
3473     size_t N 
= mOrderedConfigs
.size(); 
3474     Vector
<sp
<ConfigList
> > origOrder(mOrderedConfigs
); 
3475     bool hasError 
= false; 
3478     for (i
=0; i
<N
; i
++) { 
3479         mOrderedConfigs
.replaceAt(NULL
, i
); 
3482     const size_t NP 
= mPublic
.size(); 
3483     //printf("Ordering %d configs from %d public defs\n", N, NP); 
3485     for (j
=0; j
<NP
; j
++) { 
3486         const String16
& name 
= mPublic
.keyAt(j
); 
3487         const Public
& p 
= mPublic
.valueAt(j
); 
3488         int32_t idx 
= Res_GETENTRY(p
.ident
); 
3489         //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n", 
3490         //       String8(mName).string(), String8(name).string(), p.ident, N); 
3492         for (i
=0; i
<N
; i
++) { 
3493             sp
<ConfigList
> e 
= origOrder
.itemAt(i
); 
3494             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string()); 
3495             if (e
->getName() == name
) { 
3496                 if (idx 
>= (int32_t)mOrderedConfigs
.size()) { 
3497                     p
.sourcePos
.error("Public entry identifier 0x%x entry index " 
3498                             "is larger than available symbols (index %d, total symbols %d).\n", 
3499                             p
.ident
, idx
, mOrderedConfigs
.size()); 
3501                 } else if (mOrderedConfigs
.itemAt(idx
) == NULL
) { 
3503                     e
->setPublicSourcePos(p
.sourcePos
); 
3504                     mOrderedConfigs
.replaceAt(e
, idx
); 
3505                     origOrder
.removeAt(i
); 
3510                     sp
<ConfigList
> oe 
= mOrderedConfigs
.itemAt(idx
); 
3512                     p
.sourcePos
.error("Multiple entry names declared for public entry" 
3513                             " identifier 0x%x in type %s (%s vs %s).\n" 
3514                             "%s:%d: Originally defined here.", 
3515                             idx
+1, String8(mName
).string(), 
3516                             String8(oe
->getName()).string(), 
3517                             String8(name
).string(), 
3518                             oe
->getPublicSourcePos().file
.string(), 
3519                             oe
->getPublicSourcePos().line
); 
3526             p
.sourcePos
.error("Public symbol %s/%s declared here is not defined.", 
3527                     String8(mName
).string(), String8(name
).string()); 
3532     //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size()); 
3534     if (N 
!= origOrder
.size()) { 
3535         printf("Internal error: remaining private symbol count mismatch\n"); 
3536         N 
= origOrder
.size(); 
3540     for (i
=0; i
<N
; i
++) { 
3541         sp
<ConfigList
> e 
= origOrder
.itemAt(i
); 
3542         // There will always be enough room for the remaining entries. 
3543         while (mOrderedConfigs
.itemAt(j
) != NULL
) { 
3546         mOrderedConfigs
.replaceAt(e
, j
); 
3550     return hasError 
? UNKNOWN_ERROR 
: NO_ERROR
; 
3553 ResourceTable::Package::Package(const String16
& name
, ssize_t includedId
) 
3554     : mName(name
), mIncludedId(includedId
), 
3555       mTypeStringsMapping(0xffffffff), 
3556       mKeyStringsMapping(0xffffffff) 
3560 sp
<ResourceTable::Type
> ResourceTable::Package::getType(const String16
& type
, 
3561                                                         const SourcePos
& sourcePos
, 
3564     sp
<Type
> t 
= mTypes
.valueFor(type
); 
3566         t 
= new Type(type
, sourcePos
); 
3567         mTypes
.add(type
, t
); 
3568         mOrderedTypes
.add(t
); 
3570             // For some reason the type's index is set to one plus the index 
3571             // in the mOrderedTypes list, rather than just the index. 
3572             t
->setIndex(mOrderedTypes
.size()); 
3578 status_t 
ResourceTable::Package::setTypeStrings(const sp
<AaptFile
>& data
) 
3580     mTypeStringsData 
= data
; 
3581     status_t err 
= setStrings(data
, &mTypeStrings
, &mTypeStringsMapping
); 
3582     if (err 
!= NO_ERROR
) { 
3583         fprintf(stderr
, "ERROR: Type string data is corrupt!\n"); 
3588 status_t 
ResourceTable::Package::setKeyStrings(const sp
<AaptFile
>& data
) 
3590     mKeyStringsData 
= data
; 
3591     status_t err 
= setStrings(data
, &mKeyStrings
, &mKeyStringsMapping
); 
3592     if (err 
!= NO_ERROR
) { 
3593         fprintf(stderr
, "ERROR: Key string data is corrupt!\n"); 
3598 status_t 
ResourceTable::Package::setStrings(const sp
<AaptFile
>& data
, 
3599                                             ResStringPool
* strings
, 
3600                                             DefaultKeyedVector
<String16
, uint32_t>* mappings
) 
3602     if (data
->getData() == NULL
) { 
3603         return UNKNOWN_ERROR
; 
3606     NOISY(aout 
<< "Setting restable string pool: " 
3607           << HexDump(data
->getData(), data
->getSize()) << endl
); 
3609     status_t err 
= strings
->setTo(data
->getData(), data
->getSize()); 
3610     if (err 
== NO_ERROR
) { 
3611         const size_t N 
= strings
->size(); 
3612         for (size_t i
=0; i
<N
; i
++) { 
3614             mappings
->add(String16(strings
->stringAt(i
, &len
)), i
); 
3620 status_t 
ResourceTable::Package::applyPublicTypeOrder() 
3622     size_t N 
= mOrderedTypes
.size(); 
3623     Vector
<sp
<Type
> > origOrder(mOrderedTypes
); 
3626     for (i
=0; i
<N
; i
++) { 
3627         mOrderedTypes
.replaceAt(NULL
, i
); 
3630     for (i
=0; i
<N
; i
++) { 
3631         sp
<Type
> t 
= origOrder
.itemAt(i
); 
3632         int32_t idx 
= t
->getPublicIndex(); 
3635             while (idx 
>= (int32_t)mOrderedTypes
.size()) { 
3636                 mOrderedTypes
.add(); 
3638             if (mOrderedTypes
.itemAt(idx
) != NULL
) { 
3639                 sp
<Type
> ot 
= mOrderedTypes
.itemAt(idx
); 
3640                 t
->getFirstPublicSourcePos().error("Multiple type names declared for public type" 
3641                         " identifier 0x%x (%s vs %s).\n" 
3642                         "%s:%d: Originally defined here.", 
3643                         idx
, String8(ot
->getName()).string(), 
3644                         String8(t
->getName()).string(), 
3645                         ot
->getFirstPublicSourcePos().file
.string(), 
3646                         ot
->getFirstPublicSourcePos().line
); 
3647                 return UNKNOWN_ERROR
; 
3649             mOrderedTypes
.replaceAt(t
, idx
); 
3650             origOrder
.removeAt(i
); 
3657     for (i
=0; i
<N
; i
++) { 
3658         sp
<Type
> t 
= origOrder
.itemAt(i
); 
3659         // There will always be enough room for the remaining types. 
3660         while (mOrderedTypes
.itemAt(j
) != NULL
) { 
3663         mOrderedTypes
.replaceAt(t
, j
); 
3669 sp
<ResourceTable::Package
> ResourceTable::getPackage(const String16
& package
) 
3671     sp
<Package
> p 
= mPackages
.valueFor(package
); 
3673         if (mIsAppPackage
) { 
3674             if (mHaveAppPackage
) { 
3675                 fprintf(stderr
, "Adding multiple application package resources; only one is allowed.\n" 
3676                                 "Use -x to create extended resources.\n"); 
3679             mHaveAppPackage 
= true; 
3680             p 
= new Package(package
, 127); 
3682             p 
= new Package(package
, mNextPackageId
); 
3684         //printf("*** NEW PACKAGE: \"%s\" id=%d\n", 
3685         //       String8(package).string(), p->getAssignedId()); 
3686         mPackages
.add(package
, p
); 
3687         mOrderedPackages
.add(p
); 
3693 sp
<ResourceTable::Type
> ResourceTable::getType(const String16
& package
, 
3694                                                const String16
& type
, 
3695                                                const SourcePos
& sourcePos
, 
3698     sp
<Package
> p 
= getPackage(package
); 
3702     return p
->getType(type
, sourcePos
, doSetIndex
); 
3705 sp
<ResourceTable::Entry
> ResourceTable::getEntry(const String16
& package
, 
3706                                                  const String16
& type
, 
3707                                                  const String16
& name
, 
3708                                                  const SourcePos
& sourcePos
, 
3710                                                  const ResTable_config
* config
, 
3713     sp
<Type
> t 
= getType(package
, type
, sourcePos
, doSetIndex
); 
3717     return t
->getEntry(name
, sourcePos
, config
, doSetIndex
, overlay
, mBundle
->getAutoAddOverlay()); 
3720 sp
<const ResourceTable::Entry
> ResourceTable::getEntry(uint32_t resID
, 
3721                                                        const ResTable_config
* config
) const 
3723     int pid 
= Res_GETPACKAGE(resID
)+1; 
3724     const size_t N 
= mOrderedPackages
.size(); 
3727     for (i
=0; i
<N
; i
++) { 
3728         sp
<Package
> check 
= mOrderedPackages
[i
]; 
3729         if (check
->getAssignedId() == pid
) { 
3736         fprintf(stderr
, "warning: Package not found for resource #%08x\n", resID
); 
3740     int tid 
= Res_GETTYPE(resID
); 
3741     if (tid 
< 0 || tid 
>= (int)p
->getOrderedTypes().size()) { 
3742         fprintf(stderr
, "warning: Type not found for resource #%08x\n", resID
); 
3745     sp
<Type
> t 
= p
->getOrderedTypes()[tid
]; 
3747     int eid 
= Res_GETENTRY(resID
); 
3748     if (eid 
< 0 || eid 
>= (int)t
->getOrderedConfigs().size()) { 
3749         fprintf(stderr
, "warning: Entry not found for resource #%08x\n", resID
); 
3753     sp
<ConfigList
> c 
= t
->getOrderedConfigs()[eid
]; 
3755         fprintf(stderr
, "warning: Entry not found for resource #%08x\n", resID
); 
3759     ConfigDescription cdesc
; 
3760     if (config
) cdesc 
= *config
; 
3761     sp
<Entry
> e 
= c
->getEntries().valueFor(cdesc
); 
3763         fprintf(stderr
, "warning: Entry configuration not found for resource #%08x\n", resID
); 
3770 const ResourceTable::Item
* ResourceTable::getItem(uint32_t resID
, uint32_t attrID
) const 
3772     sp
<const Entry
> e 
= getEntry(resID
); 
3777     const size_t N 
= e
->getBag().size(); 
3778     for (size_t i
=0; i
<N
; i
++) { 
3779         const Item
& it 
= e
->getBag().valueAt(i
); 
3780         if (it
.bagKeyId 
== 0) { 
3781             fprintf(stderr
, "warning: ID not yet assigned to '%s' in bag '%s'\n", 
3782                     String8(e
->getName()).string(), 
3783                     String8(e
->getBag().keyAt(i
)).string()); 
3785         if (it
.bagKeyId 
== attrID
) { 
3793 bool ResourceTable::getItemValue( 
3794     uint32_t resID
, uint32_t attrID
, Res_value
* outValue
) 
3796     const Item
* item 
= getItem(resID
, attrID
); 
3800         if (item
->evaluating
) { 
3801             sp
<const Entry
> e 
= getEntry(resID
); 
3802             const size_t N 
= e
->getBag().size(); 
3804             for (i
=0; i
<N
; i
++) { 
3805                 if (&e
->getBag().valueAt(i
) == item
) { 
3809             fprintf(stderr
, "warning: Circular reference detected in key '%s' of bag '%s'\n", 
3810                     String8(e
->getName()).string(), 
3811                     String8(e
->getBag().keyAt(i
)).string()); 
3814         item
->evaluating 
= true; 
3815         res 
= stringToValue(outValue
, NULL
, item
->value
, false, false, item
->bagKeyId
); 
3818                 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n", 
3819                        resID
, attrID
, String8(getEntry(resID
)->getName()).string(), 
3820                        outValue
->dataType
, outValue
->data
); 
3822                 printf("getItemValue of #%08x[#%08x]: failed\n", 
3826         item
->evaluating 
= false;