Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 // Build resource files from raw assets.
      5 //
      6 
      7 #include "ResourceTable.h"
      8 
      9 #include "XMLNode.h"
     10 
     11 #include <utils/ByteOrder.h>
     12 #include <utils/ResourceTypes.h>
     13 #include <stdarg.h>
     14 
     15 #define NOISY(x) //x
     16 
     17 status_t compileXmlFile(const sp<AaptAssets>& assets,
     18                         const sp<AaptFile>& target,
     19                         ResourceTable* table,
     20                         int options)
     21 {
     22     sp<XMLNode> root = XMLNode::parse(target);
     23     if (root == NULL) {
     24         return UNKNOWN_ERROR;
     25     }
     26 
     27     return compileXmlFile(assets, root, target, table, options);
     28 }
     29 
     30 status_t compileXmlFile(const sp<AaptAssets>& assets,
     31                         const sp<AaptFile>& target,
     32                         const sp<AaptFile>& outTarget,
     33                         ResourceTable* table,
     34                         int options)
     35 {
     36     sp<XMLNode> root = XMLNode::parse(target);
     37     if (root == NULL) {
     38         return UNKNOWN_ERROR;
     39     }
     40 
     41     return compileXmlFile(assets, root, outTarget, table, options);
     42 }
     43 
     44 status_t compileXmlFile(const sp<AaptAssets>& assets,
     45                         const sp<XMLNode>& root,
     46                         const sp<AaptFile>& target,
     47                         ResourceTable* table,
     48                         int options)
     49 {
     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);
     54     }
     55 
     56     if ((options&XML_COMPILE_UTF8) != 0) {
     57         root->setUTF8(true);
     58     }
     59 
     60     bool hasErrors = false;
     61 
     62     if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
     63         status_t err = root->assignResourceIds(assets, table);
     64         if (err != NO_ERROR) {
     65             hasErrors = true;
     66         }
     67     }
     68 
     69     status_t err = root->parseValues(assets, table);
     70     if (err != NO_ERROR) {
     71         hasErrors = true;
     72     }
     73 
     74     if (hasErrors) {
     75         return UNKNOWN_ERROR;
     76     }
     77 
     78     NOISY(printf("Input XML Resource:\n"));
     79     NOISY(root->print());
     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) {
     84         return err;
     85     }
     86 
     87     NOISY(printf("Output XML Resource:\n"));
     88     NOISY(ResXMLTree tree;
     89         tree.setTo(target->getData(), target->getSize());
     90         printXMLBlock(&tree));
     91 
     92     target->setCompressionMethod(ZipEntry::kCompressDeflated);
     93 
     94     return err;
     95 }
     96 
     97 #undef NOISY
     98 #define NOISY(x) //x
     99 
    100 struct flag_entry
    101 {
    102     const char16_t* name;
    103     size_t nameLen;
    104     uint32_t value;
    105     const char* description;
    106 };
    107 
    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' };
    128 
    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 },
    154     { NULL, 0, 0, NULL }
    155 };
    156 
    157 static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' };
    158 
    159 static const flag_entry l10nRequiredFlags[] = {
    160     { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL },
    161     { NULL, 0, 0, NULL }
    162 };
    163 
    164 static const char16_t nulStr[] = { 0 };
    165 
    166 static uint32_t parse_flags(const char16_t* str, size_t len,
    167                              const flag_entry* flags, bool* outError = NULL)
    168 {
    169     while (len > 0 && isspace(*str)) {
    170         str++;
    171         len--;
    172     }
    173     while (len > 0 && isspace(str[len-1])) {
    174         len--;
    175     }
    176 
    177     const char16_t* const end = str + len;
    178     uint32_t value = 0;
    179 
    180     while (str < end) {
    181         const char16_t* div = str;
    182         while (div < end && *div != '|') {
    183             div++;
    184         }
    185 
    186         const flag_entry* cur = flags;
    187         while (cur->name) {
    188             if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) {
    189                 value |= cur->value;
    190                 break;
    191             }
    192             cur++;
    193         }
    194 
    195         if (!cur->name) {
    196             if (outError) *outError = true;
    197             return 0;
    198         }
    199 
    200         str = div < end ? div+1 : div;
    201     }
    202 
    203     if (outError) *outError = false;
    204     return value;
    205 }
    206 
    207 static String16 mayOrMust(int type, int flags)
    208 {
    209     if ((type&(~flags)) == 0) {
    210         return String16("<p>Must");
    211     }
    212 
    213     return String16("<p>May");
    214 }
    215 
    216 static void appendTypeInfo(ResourceTable* outTable, const String16& pkg,
    217         const String16& typeName, const String16& ident, int type,
    218         const flag_entry* flags)
    219 {
    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);
    227             hadType = true;
    228         }
    229         flags++;
    230     }
    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."));
    238     }
    239 }
    240 
    241 struct PendingAttribute
    242 {
    243     const String16 myPackage;
    244     const SourcePos sourcePos;
    245     const bool appendComment;
    246     int32_t type;
    247     String16 ident;
    248     String16 comment;
    249     bool hasErrors;
    250     bool added;
    251 
    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)
    258         , hasErrors(false)
    259         , added(false)
    260     {
    261     }
    262 
    263     status_t createIfNeeded(ResourceTable* outTable)
    264     {
    265         if (added || hasErrors) {
    266             return NO_ERROR;
    267         }
    268         added = true;
    269 
    270         String16 attr16("attr");
    271 
    272         if (outTable->hasBagOrEntry(myPackage, attr16, ident)) {
    273             sourcePos.error("Attribute \"%s\" has already been defined\n",
    274                     String8(ident).string());
    275             hasErrors = true;
    276             return UNKNOWN_ERROR;
    277         }
    278 
    279         char numberStr[16];
    280         sprintf(numberStr, "%d", type);
    281         status_t err = outTable->addBag(sourcePos, myPackage,
    282                 attr16, ident, String16(""),
    283                 String16("^type"),
    284                 String16(numberStr), NULL, NULL);
    285         if (err != NO_ERROR) {
    286             hasErrors = true;
    287             return err;
    288         }
    289         outTable->appendComment(myPackage, attr16, ident, comment, appendComment);
    290         //printf("Attribute %s comment: %s\n", String8(ident).string(),
    291         //     String8(comment).string());
    292         return err;
    293     }
    294 };
    295 
    296 static status_t compileAttribute(const sp<AaptFile>& in,
    297                                  ResXMLTree& block,
    298                                  const String16& myPackage,
    299                                  ResourceTable* outTable,
    300                                  String16* outIdent = NULL,
    301                                  bool inStyleable = false)
    302 {
    303     PendingAttribute attr(myPackage, in, block, inStyleable);
    304 
    305     const String16 attr16("attr");
    306     const String16 id16("id");
    307 
    308     // Attribute type constants.
    309     const String16 enum16("enum");
    310     const String16 flag16("flag");
    311 
    312     ResXMLTree::event_code_t code;
    313     size_t len;
    314     status_t err;
    315 
    316     ssize_t identIdx = block.indexOfAttribute(NULL, "name");
    317     if (identIdx >= 0) {
    318         attr.ident = String16(block.getAttributeStringValue(identIdx, &len));
    319         if (outIdent) {
    320             *outIdent = attr.ident;
    321         }
    322     } else {
    323         attr.sourcePos.error("A 'name' attribute is required for <attr>\n");
    324         attr.hasErrors = true;
    325     }
    326 
    327     attr.comment = String16(
    328             block.getComment(&len) ? block.getComment(&len) : nulStr);
    329 
    330     ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
    331     if (typeIdx >= 0) {
    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;
    338         }
    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);
    344     }
    345 
    346     //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
    347 
    348     ssize_t minIdx = block.indexOfAttribute(NULL, "min");
    349     if (minIdx >= 0) {
    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;
    355         }
    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;
    362             }
    363         }
    364     }
    365 
    366     ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
    367     if (maxIdx >= 0) {
    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;
    373         }
    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;
    379         }
    380     }
    381 
    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;
    385     }
    386 
    387     ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization");
    388     if (l10nIdx >= 0) {
    389         const uint16_t* str = block.getAttributeStringValue(l10nIdx, &len);
    390         bool error;
    391         uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
    392         if (error) {
    393             attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
    394                     String8(str).string());
    395             attr.hasErrors = true;
    396         }
    397         attr.createIfNeeded(outTable);
    398         if (!attr.hasErrors) {
    399             char buf[11];
    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;
    405             }
    406         }
    407     }
    408 
    409     String16 enumOrFlagsComment;
    410 
    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;
    418             } else {
    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;
    423             }
    424 
    425             attr.createIfNeeded(outTable);
    426 
    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.
    430                 attr.type = 0;
    431             }
    432 
    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) {
    437                     char numberStr[16];
    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;
    444                     }
    445                 }
    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;
    451                 } else {
    452                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    453                             .error("<flag> attribute can not be used inside a enum format\n");
    454                     attr.hasErrors = true;
    455                 }
    456             }
    457 
    458             String16 itemIdent;
    459             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
    460             if (itemIdentIdx >= 0) {
    461                 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
    462             } else {
    463                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    464                         .error("A 'name' attribute is required for <enum> or <flag>\n");
    465                 attr.hasErrors = true;
    466             }
    467 
    468             String16 value;
    469             ssize_t valueIdx = block.indexOfAttribute(NULL, "value");
    470             if (valueIdx >= 0) {
    471                 value = String16(block.getAttributeStringValue(valueIdx, &len));
    472             } else {
    473                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    474                         .error("A 'value' attribute is required for <enum> or <flag>\n");
    475                 attr.hasErrors = true;
    476             }
    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,"
    480                         " not \"%s\"\n",
    481                         String8(value).string());
    482                 attr.hasErrors = true;
    483             }
    484 
    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;
    491                 }
    492             }
    493 
    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>"));
    506                 }
    507 
    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)));
    515                 }
    516                 enumOrFlagsComment.append(String16("</td></tr>"));
    517 
    518                 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
    519                                        myPackage,
    520                                        attr16, attr.ident, String16(""),
    521                                        itemIdent, value, NULL, NULL, false, true);
    522                 if (err != NO_ERROR) {
    523                     attr.hasErrors = true;
    524                 }
    525             }
    526         } else if (code == ResXMLTree::END_TAG) {
    527             if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
    528                 break;
    529             }
    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;
    536                 }
    537             } else {
    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;
    543                 }
    544             }
    545         }
    546     }
    547 
    548     if (!attr.hasErrors && attr.added) {
    549         appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags);
    550     }
    551 
    552     if (!attr.hasErrors && enumOrFlagsComment.size() > 0) {
    553         enumOrFlagsComment.append(String16("\n</table>"));
    554         outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment);
    555     }
    556 
    557 
    558     return NO_ERROR;
    559 }
    560 
    561 bool localeIsDefined(const ResTable_config& config)
    562 {
    563     return config.locale == 0;
    564 }
    565 
    566 status_t parseAndAddBag(Bundle* bundle,
    567                         const sp<AaptFile>& in,
    568                         ResXMLTree* block,
    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,
    575                         int32_t curFormat,
    576                         bool isFormatted,
    577                         const String16& product,
    578                         bool pseudolocalize,
    579                         const bool overwrite,
    580                         ResourceTable* outTable)
    581 {
    582     status_t err;
    583     const String16 item16("item");
    584 
    585     String16 str;
    586     Vector<StringPool::entry_style_span> spans;
    587     err = parseStyledString(bundle, in->getPrintableSource().string(),
    588                             block, item16, &str, &spans, isFormatted,
    589                             pseudolocalize);
    590     if (err != NO_ERROR) {
    591         return err;
    592     }
    593 
    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()));
    603 
    604     err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
    605                            myPackage, curType, ident, parentIdent, itemIdent, str,
    606                            &spans, &config, overwrite, false, curFormat);
    607     return err;
    608 }
    609 
    610 /*
    611  * Returns true if needle is one of the elements in the comma-separated list
    612  * haystack, false otherwise.
    613  */
    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();
    618 
    619     while (*haystack2 != '\0') {
    620         if (strncmp16(haystack2, needle2, needlesize) == 0) {
    621             if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
    622                 return true;
    623             }
    624         }
    625 
    626         while (*haystack2 != '\0' && *haystack2 != ',') {
    627             haystack2++;
    628         }
    629         if (*haystack2 == ',') {
    630             haystack2++;
    631         }
    632     }
    633 
    634     return false;
    635 }
    636 
    637 status_t parseAndAddEntry(Bundle* bundle,
    638                         const sp<AaptFile>& in,
    639                         ResXMLTree* block,
    640                         const ResTable_config& config,
    641                         const String16& myPackage,
    642                         const String16& curType,
    643                         const String16& ident,
    644                         const String16& curTag,
    645                         bool curIsStyled,
    646                         int32_t curFormat,
    647                         bool isFormatted,
    648                         const String16& product,
    649                         bool pseudolocalize,
    650                         const bool overwrite,
    651                         ResourceTable* outTable)
    652 {
    653     status_t err;
    654 
    655     String16 str;
    656     Vector<StringPool::entry_style_span> spans;
    657     err = parseStyledString(bundle, in->getPrintableSource().string(), block,
    658                             curTag, &str, curIsStyled ? &spans : NULL,
    659                             isFormatted, pseudolocalize);
    660 
    661     if (err < NO_ERROR) {
    662         return err;
    663     }
    664 
    665     /*
    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.
    669      */
    670 
    671     const char *bundleProduct = bundle->getProduct();
    672     if (bundleProduct == NULL) {
    673         bundleProduct = "";
    674     }
    675 
    676     if (product.size() != 0) {
    677         /*
    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.
    681          */
    682 
    683         if (bundleProduct[0] == '\0') {
    684             if (strcmp16(String16("default").string(), product.string()) != 0) {
    685                 return NO_ERROR;
    686             }
    687         } else {
    688             /*
    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.
    693              */
    694 
    695             if (isInProductList(product, String16(bundleProduct))) {
    696                 ;
    697             } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
    698                        !outTable->hasBagOrEntry(myPackage, curType, ident)) {
    699                 ;
    700             } else {
    701                 return NO_ERROR;
    702             }
    703         }
    704     }
    705 
    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()));
    711 
    712     err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
    713                              myPackage, curType, ident, str, &spans, &config,
    714                              false, curFormat, overwrite);
    715 
    716     return err;
    717 }
    718 
    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)
    725 {
    726     ResXMLTree block;
    727     status_t err = parseXMLResource(in, &block, false, true);
    728     if (err != NO_ERROR) {
    729         return err;
    730     }
    731 
    732     // Top-level tag.
    733     const String16 resources16("resources");
    734 
    735     // Identifier declaration tags.
    736     const String16 declare_styleable16("declare-styleable");
    737     const String16 attr16("attr");
    738 
    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");
    758 
    759     // Data creation tags.
    760     const String16 bag16("bag");
    761     const String16 item16("item");
    762 
    763     // Attribute type constants.
    764     const String16 enum16("enum");
    765 
    766     // plural values
    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");
    779 
    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 
    786     const String16 myPackage(assets->getPackage());
    787 
    788     bool hasErrors = false;
    789 
    790     bool fileIsTranslatable = true;
    791     if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
    792         fileIsTranslatable = false;
    793     }
    794 
    795     DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
    796 
    797     ResXMLTree::event_code_t code;
    798     do {
    799         code = block.next();
    800     } while (code == ResXMLTree::START_NAMESPACE);
    801 
    802     size_t len;
    803     if (code != ResXMLTree::START_TAG) {
    804         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
    805                 "No start tag found\n");
    806         return UNKNOWN_ERROR;
    807     }
    808     if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
    809         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
    810                 "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
    811         return UNKNOWN_ERROR;
    812     }
    813 
    814     ResTable_config curParams(defParams);
    815 
    816     ResTable_config pseudoParams(curParams);
    817         pseudoParams.language[0] = 'z';
    818         pseudoParams.language[1] = 'z';
    819         pseudoParams.country[0] = 'Z';
    820         pseudoParams.country[1] = 'Z';
    821 
    822     while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    823         if (code == ResXMLTree::START_TAG) {
    824             const String16* curTag = NULL;
    825             String16 curType;
    826             int32_t curFormat = ResTable_map::TYPE_ANY;
    827             bool curIsBag = false;
    828             bool curIsBagReplaceOnOverwrite = false;
    829             bool curIsStyled = false;
    830             bool curIsPseudolocalizable = false;
    831             bool curIsFormatted = fileIsTranslatable;
    832             bool localHasErrors = false;
    833 
    834             if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
    835                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    836                         && code != ResXMLTree::BAD_DOCUMENT) {
    837                     if (code == ResXMLTree::END_TAG) {
    838                         if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
    839                             break;
    840                         }
    841                     }
    842                 }
    843                 continue;
    844 
    845             } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
    846                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    847                         && code != ResXMLTree::BAD_DOCUMENT) {
    848                     if (code == ResXMLTree::END_TAG) {
    849                         if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
    850                             break;
    851                         }
    852                     }
    853                 }
    854                 continue;
    855 
    856             } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
    857                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
    858 
    859                 String16 type;
    860                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
    861                 if (typeIdx < 0) {
    862                     srcPos.error("A 'type' attribute is required for <public>\n");
    863                     hasErrors = localHasErrors = true;
    864                 }
    865                 type = String16(block.getAttributeStringValue(typeIdx, &len));
    866 
    867                 String16 name;
    868                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
    869                 if (nameIdx < 0) {
    870                     srcPos.error("A 'name' attribute is required for <public>\n");
    871                     hasErrors = localHasErrors = true;
    872                 }
    873                 name = String16(block.getAttributeStringValue(nameIdx, &len));
    874 
    875                 uint32_t ident = 0;
    876                 ssize_t identIdx = block.indexOfAttribute(NULL, "id");
    877                 if (identIdx >= 0) {
    878                     const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
    879                     Res_value identValue;
    880                     if (!ResTable::stringToInt(identStr, len, &identValue)) {
    881                         srcPos.error("Given 'id' attribute is not an integer: %s\n",
    882                                 String8(block.getAttributeStringValue(identIdx, &len)).string());
    883                         hasErrors = localHasErrors = true;
    884                     } else {
    885                         ident = identValue.data;
    886                         nextPublicId.replaceValueFor(type, ident+1);
    887                     }
    888                 } else if (nextPublicId.indexOfKey(type) < 0) {
    889                     srcPos.error("No 'id' attribute supplied <public>,"
    890                             " and no previous id defined in this file.\n");
    891                     hasErrors = localHasErrors = true;
    892                 } else if (!localHasErrors) {
    893                     ident = nextPublicId.valueFor(type);
    894                     nextPublicId.replaceValueFor(type, ident+1);
    895                 }
    896 
    897                 if (!localHasErrors) {
    898                     err = outTable->addPublic(srcPos, myPackage, type, name, ident);
    899                     if (err < NO_ERROR) {
    900                         hasErrors = localHasErrors = true;
    901                     }
    902                 }
    903                 if (!localHasErrors) {
    904                     sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
    905                     if (symbols != NULL) {
    906                         symbols = symbols->addNestedSymbol(String8(type), srcPos);
    907                     }
    908                     if (symbols != NULL) {
    909                         symbols->makeSymbolPublic(String8(name), srcPos);
    910                         String16 comment(
    911                             block.getComment(&len) ? block.getComment(&len) : nulStr);
    912                         symbols->appendComment(String8(name), comment, srcPos);
    913                     } else {
    914                         srcPos.error("Unable to create symbols!\n");
    915                         hasErrors = localHasErrors = true;
    916                     }
    917                 }
    918 
    919                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    920                     if (code == ResXMLTree::END_TAG) {
    921                         if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
    922                             break;
    923                         }
    924                     }
    925                 }
    926                 continue;
    927 
    928             } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
    929                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
    930 
    931                 String16 type;
    932                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
    933                 if (typeIdx < 0) {
    934                     srcPos.error("A 'type' attribute is required for <public-padding>\n");
    935                     hasErrors = localHasErrors = true;
    936                 }
    937                 type = String16(block.getAttributeStringValue(typeIdx, &len));
    938 
    939                 String16 name;
    940                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
    941                 if (nameIdx < 0) {
    942                     srcPos.error("A 'name' attribute is required for <public-padding>\n");
    943                     hasErrors = localHasErrors = true;
    944                 }
    945                 name = String16(block.getAttributeStringValue(nameIdx, &len));
    946 
    947                 uint32_t start = 0;
    948                 ssize_t startIdx = block.indexOfAttribute(NULL, "start");
    949                 if (startIdx >= 0) {
    950                     const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
    951                     Res_value startValue;
    952                     if (!ResTable::stringToInt(startStr, len, &startValue)) {
    953                         srcPos.error("Given 'start' attribute is not an integer: %s\n",
    954                                 String8(block.getAttributeStringValue(startIdx, &len)).string());
    955                         hasErrors = localHasErrors = true;
    956                     } else {
    957                         start = startValue.data;
    958                     }
    959                 } else if (nextPublicId.indexOfKey(type) < 0) {
    960                     srcPos.error("No 'start' attribute supplied <public-padding>,"
    961                             " and no previous id defined in this file.\n");
    962                     hasErrors = localHasErrors = true;
    963                 } else if (!localHasErrors) {
    964                     start = nextPublicId.valueFor(type);
    965                 }
    966 
    967                 uint32_t end = 0;
    968                 ssize_t endIdx = block.indexOfAttribute(NULL, "end");
    969                 if (endIdx >= 0) {
    970                     const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
    971                     Res_value endValue;
    972                     if (!ResTable::stringToInt(endStr, len, &endValue)) {
    973                         srcPos.error("Given 'end' attribute is not an integer: %s\n",
    974                                 String8(block.getAttributeStringValue(endIdx, &len)).string());
    975                         hasErrors = localHasErrors = true;
    976                     } else {
    977                         end = endValue.data;
    978                     }
    979                 } else {
    980                     srcPos.error("No 'end' attribute supplied <public-padding>\n");
    981                     hasErrors = localHasErrors = true;
    982                 }
    983 
    984                 if (end >= start) {
    985                     nextPublicId.replaceValueFor(type, end+1);
    986                 } else {
    987                     srcPos.error("Padding start '%ul' is after end '%ul'\n",
    988                             start, end);
    989                     hasErrors = localHasErrors = true;
    990                 }
    991 
    992                 String16 comment(
    993                     block.getComment(&len) ? block.getComment(&len) : nulStr);
    994                 for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
    995                     if (localHasErrors) {
    996                         break;
    997                     }
    998                     String16 curName(name);
    999                     char buf[64];
   1000                     sprintf(buf, "%d", (int)(end-curIdent+1));
   1001                     curName.append(String16(buf));
   1002 
   1003                     err = outTable->addEntry(srcPos, myPackage, type, curName,
   1004                                              String16("padding"), NULL, &curParams, false,
   1005                                              ResTable_map::TYPE_STRING, overwrite);
   1006                     if (err < NO_ERROR) {
   1007                         hasErrors = localHasErrors = true;
   1008                         break;
   1009                     }
   1010                     err = outTable->addPublic(srcPos, myPackage, type,
   1011                             curName, curIdent);
   1012                     if (err < NO_ERROR) {
   1013                         hasErrors = localHasErrors = true;
   1014                         break;
   1015                     }
   1016                     sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1017                     if (symbols != NULL) {
   1018                         symbols = symbols->addNestedSymbol(String8(type), srcPos);
   1019                     }
   1020                     if (symbols != NULL) {
   1021                         symbols->makeSymbolPublic(String8(curName), srcPos);
   1022                         symbols->appendComment(String8(curName), comment, srcPos);
   1023                     } else {
   1024                         srcPos.error("Unable to create symbols!\n");
   1025                         hasErrors = localHasErrors = true;
   1026                     }
   1027                 }
   1028 
   1029                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1030                     if (code == ResXMLTree::END_TAG) {
   1031                         if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
   1032                             break;
   1033                         }
   1034                     }
   1035                 }
   1036                 continue;
   1037 
   1038             } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
   1039                 String16 pkg;
   1040                 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
   1041                 if (pkgIdx < 0) {
   1042                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1043                             "A 'package' attribute is required for <private-symbols>\n");
   1044                     hasErrors = localHasErrors = true;
   1045                 }
   1046                 pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
   1047                 if (!localHasErrors) {
   1048                     assets->setSymbolsPrivatePackage(String8(pkg));
   1049                 }
   1050 
   1051                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1052                     if (code == ResXMLTree::END_TAG) {
   1053                         if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
   1054                             break;
   1055                         }
   1056                     }
   1057                 }
   1058                 continue;
   1059 
   1060             } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
   1061                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1062 
   1063                 String16 typeName;
   1064                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
   1065                 if (typeIdx < 0) {
   1066                     srcPos.error("A 'type' attribute is required for <add-resource>\n");
   1067                     hasErrors = localHasErrors = true;
   1068                 }
   1069                 typeName = String16(block.getAttributeStringValue(typeIdx, &len));
   1070 
   1071                 String16 name;
   1072                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1073                 if (nameIdx < 0) {
   1074                     srcPos.error("A 'name' attribute is required for <add-resource>\n");
   1075                     hasErrors = localHasErrors = true;
   1076                 }
   1077                 name = String16(block.getAttributeStringValue(nameIdx, &len));
   1078 
   1079                 outTable->canAddEntry(srcPos, myPackage, typeName, name);
   1080 
   1081                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1082                     if (code == ResXMLTree::END_TAG) {
   1083                         if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
   1084                             break;
   1085                         }
   1086                     }
   1087                 }
   1088                 continue;
   1089 
   1090             } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
   1091                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1092 
   1093                 String16 ident;
   1094                 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
   1095                 if (identIdx < 0) {
   1096                     srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
   1097                     hasErrors = localHasErrors = true;
   1098                 }
   1099                 ident = String16(block.getAttributeStringValue(identIdx, &len));
   1100 
   1101                 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1102                 if (!localHasErrors) {
   1103                     if (symbols != NULL) {
   1104                         symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
   1105                     }
   1106                     sp<AaptSymbols> styleSymbols = symbols;
   1107                     if (symbols != NULL) {
   1108                         symbols = symbols->addNestedSymbol(String8(ident), srcPos);
   1109                     }
   1110                     if (symbols == NULL) {
   1111                         srcPos.error("Unable to create symbols!\n");
   1112                         return UNKNOWN_ERROR;
   1113                     }
   1114 
   1115                     String16 comment(
   1116                         block.getComment(&len) ? block.getComment(&len) : nulStr);
   1117                     styleSymbols->appendComment(String8(ident), comment, srcPos);
   1118                 } else {
   1119                     symbols = NULL;
   1120                 }
   1121 
   1122                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1123                     if (code == ResXMLTree::START_TAG) {
   1124                         if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
   1125                             while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1126                                    && code != ResXMLTree::BAD_DOCUMENT) {
   1127                                 if (code == ResXMLTree::END_TAG) {
   1128                                     if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
   1129                                         break;
   1130                                     }
   1131                                 }
   1132                             }
   1133                             continue;
   1134                         } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
   1135                             while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1136                                    && code != ResXMLTree::BAD_DOCUMENT) {
   1137                                 if (code == ResXMLTree::END_TAG) {
   1138                                     if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
   1139                                         break;
   1140                                     }
   1141                                 }
   1142                             }
   1143                             continue;
   1144                         } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
   1145                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1146                                     "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
   1147                                     String8(block.getElementName(&len)).string());
   1148                             return UNKNOWN_ERROR;
   1149                         }
   1150 
   1151                         String16 comment(
   1152                             block.getComment(&len) ? block.getComment(&len) : nulStr);
   1153                         String16 itemIdent;
   1154                         err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
   1155                         if (err != NO_ERROR) {
   1156                             hasErrors = localHasErrors = true;
   1157                         }
   1158 
   1159                         if (symbols != NULL) {
   1160                             SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
   1161                             symbols->addSymbol(String8(itemIdent), 0, srcPos);
   1162                             symbols->appendComment(String8(itemIdent), comment, srcPos);
   1163                             //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
   1164                             //     String8(comment).string());
   1165                         }
   1166                     } else if (code == ResXMLTree::END_TAG) {
   1167                         if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
   1168                             break;
   1169                         }
   1170 
   1171                         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1172                                 "Found tag </%s> where </attr> is expected\n",
   1173                                 String8(block.getElementName(&len)).string());
   1174                         return UNKNOWN_ERROR;
   1175                     }
   1176                 }
   1177                 continue;
   1178 
   1179             } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
   1180                 err = compileAttribute(in, block, myPackage, outTable, NULL);
   1181                 if (err != NO_ERROR) {
   1182                     hasErrors = true;
   1183                 }
   1184                 continue;
   1185 
   1186             } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
   1187                 curTag = &item16;
   1188                 ssize_t attri = block.indexOfAttribute(NULL, "type");
   1189                 if (attri >= 0) {
   1190                     curType = String16(block.getAttributeStringValue(attri, &len));
   1191                     ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
   1192                     if (formatIdx >= 0) {
   1193                         String16 formatStr = String16(block.getAttributeStringValue(
   1194                                 formatIdx, &len));
   1195                         curFormat = parse_flags(formatStr.string(), formatStr.size(),
   1196                                                 gFormatFlags);
   1197                         if (curFormat == 0) {
   1198                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1199                                     "Tag <item> 'format' attribute value \"%s\" not valid\n",
   1200                                     String8(formatStr).string());
   1201                             hasErrors = localHasErrors = true;
   1202                         }
   1203                     }
   1204                 } else {
   1205                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1206                             "A 'type' attribute is required for <item>\n");
   1207                     hasErrors = localHasErrors = true;
   1208                 }
   1209                 curIsStyled = true;
   1210             } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
   1211                 // Note the existence and locale of every string we process
   1212                 char rawLocale[16];
   1213                 curParams.getLocale(rawLocale);
   1214                 String8 locale(rawLocale);
   1215                 String16 name;
   1216                 String16 translatable;
   1217                 String16 formatted;
   1218 
   1219                 size_t n = block.getAttributeCount();
   1220                 for (size_t i = 0; i < n; i++) {
   1221                     size_t length;
   1222                     const uint16_t* attr = block.getAttributeName(i, &length);
   1223                     if (strcmp16(attr, name16.string()) == 0) {
   1224                         name.setTo(block.getAttributeStringValue(i, &length));
   1225                     } else if (strcmp16(attr, translatable16.string()) == 0) {
   1226                         translatable.setTo(block.getAttributeStringValue(i, &length));
   1227                     } else if (strcmp16(attr, formatted16.string()) == 0) {
   1228                         formatted.setTo(block.getAttributeStringValue(i, &length));
   1229                     }
   1230                 }
   1231 
   1232                 if (name.size() > 0) {
   1233                     if (translatable == false16) {
   1234                         curIsFormatted = false;
   1235                         // Untranslatable strings must only exist in the default [empty] locale
   1236                         if (locale.size() > 0) {
   1237                             fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
   1238                                     " in locale '%s'\n", String8(name).string(),
   1239                                     bundle->getResourceSourceDirs()[0],
   1240                                     locale.string());
   1241                             // hasErrors = localHasErrors = true;
   1242                         } else {
   1243                             // Intentionally empty block:
   1244                             //
   1245                             // Don't add untranslatable strings to the localization table; that
   1246                             // way if we later see localizations of them, they'll be flagged as
   1247                             // having no default translation.
   1248                         }
   1249                     } else {
   1250                         outTable->addLocalization(name, locale);
   1251                     }
   1252 
   1253                     if (formatted == false16) {
   1254                         curIsFormatted = false;
   1255                     }
   1256                 }
   1257 
   1258                 curTag = &string16;
   1259                 curType = string16;
   1260                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
   1261                 curIsStyled = true;
   1262                 curIsPseudolocalizable = true;
   1263             } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
   1264                 curTag = &drawable16;
   1265                 curType = drawable16;
   1266                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
   1267             } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
   1268                 curTag = &color16;
   1269                 curType = color16;
   1270                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
   1271             } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
   1272                 curTag = &bool16;
   1273                 curType = bool16;
   1274                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
   1275             } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
   1276                 curTag = &integer16;
   1277                 curType = integer16;
   1278                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
   1279             } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
   1280                 curTag = &dimen16;
   1281                 curType = dimen16;
   1282                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
   1283             } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
   1284                 curTag = &fraction16;
   1285                 curType = fraction16;
   1286                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
   1287             } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
   1288                 curTag = &bag16;
   1289                 curIsBag = true;
   1290                 ssize_t attri = block.indexOfAttribute(NULL, "type");
   1291                 if (attri >= 0) {
   1292                     curType = String16(block.getAttributeStringValue(attri, &len));
   1293                 } else {
   1294                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1295                             "A 'type' attribute is required for <bag>\n");
   1296                     hasErrors = localHasErrors = true;
   1297                 }
   1298             } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
   1299                 curTag = &style16;
   1300                 curType = style16;
   1301                 curIsBag = true;
   1302             } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
   1303                 curTag = &plurals16;
   1304                 curType = plurals16;
   1305                 curIsBag = true;
   1306             } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
   1307                 curTag = &array16;
   1308                 curType = array16;
   1309                 curIsBag = true;
   1310                 curIsBagReplaceOnOverwrite = true;
   1311                 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
   1312                 if (formatIdx >= 0) {
   1313                     String16 formatStr = String16(block.getAttributeStringValue(
   1314                             formatIdx, &len));
   1315                     curFormat = parse_flags(formatStr.string(), formatStr.size(),
   1316                                             gFormatFlags);
   1317                     if (curFormat == 0) {
   1318                         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1319                                 "Tag <array> 'format' attribute value \"%s\" not valid\n",
   1320                                 String8(formatStr).string());
   1321                         hasErrors = localHasErrors = true;
   1322                     }
   1323                 }
   1324             } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
   1325                 curTag = &string_array16;
   1326                 curType = array16;
   1327                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
   1328                 curIsBag = true;
   1329                 curIsBagReplaceOnOverwrite = true;
   1330                 curIsPseudolocalizable = true;
   1331             } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
   1332                 curTag = &integer_array16;
   1333                 curType = array16;
   1334                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
   1335                 curIsBag = true;
   1336                 curIsBagReplaceOnOverwrite = true;
   1337             } else {
   1338                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1339                         "Found tag %s where item is expected\n",
   1340                         String8(block.getElementName(&len)).string());
   1341                 return UNKNOWN_ERROR;
   1342             }
   1343 
   1344             String16 ident;
   1345             ssize_t identIdx = block.indexOfAttribute(NULL, "name");
   1346             if (identIdx >= 0) {
   1347                 ident = String16(block.getAttributeStringValue(identIdx, &len));
   1348             } else {
   1349                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1350                         "A 'name' attribute is required for <%s>\n",
   1351                         String8(*curTag).string());
   1352                 hasErrors = localHasErrors = true;
   1353             }
   1354 
   1355             String16 product;
   1356             identIdx = block.indexOfAttribute(NULL, "product");
   1357             if (identIdx >= 0) {
   1358                 product = String16(block.getAttributeStringValue(identIdx, &len));
   1359             }
   1360 
   1361             String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
   1362 
   1363             if (curIsBag) {
   1364                 // Figure out the parent of this bag...
   1365                 String16 parentIdent;
   1366                 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
   1367                 if (parentIdentIdx >= 0) {
   1368                     parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
   1369                 } else {
   1370                     ssize_t sep = ident.findLast('.');
   1371                     if (sep >= 0) {
   1372                         parentIdent.setTo(ident, sep);
   1373                     }
   1374                 }
   1375 
   1376                 if (!localHasErrors) {
   1377                     err = outTable->startBag(SourcePos(in->getPrintableSource(),
   1378                             block.getLineNumber()), myPackage, curType, ident,
   1379                             parentIdent, &curParams,
   1380                             overwrite, curIsBagReplaceOnOverwrite);
   1381                     if (err != NO_ERROR) {
   1382                         hasErrors = localHasErrors = true;
   1383                     }
   1384                 }
   1385 
   1386                 ssize_t elmIndex = 0;
   1387                 char elmIndexStr[14];
   1388                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1389                         && code != ResXMLTree::BAD_DOCUMENT) {
   1390 
   1391                     if (code == ResXMLTree::START_TAG) {
   1392                         if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
   1393                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1394                                     "Tag <%s> can not appear inside <%s>, only <item>\n",
   1395                                     String8(block.getElementName(&len)).string(),
   1396                                     String8(*curTag).string());
   1397                             return UNKNOWN_ERROR;
   1398                         }
   1399 
   1400                         String16 itemIdent;
   1401                         if (curType == array16) {
   1402                             sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
   1403                             itemIdent = String16(elmIndexStr);
   1404                         } else if (curType == plurals16) {
   1405                             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
   1406                             if (itemIdentIdx >= 0) {
   1407                                 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
   1408                                 if (quantity16 == other16) {
   1409                                     itemIdent = quantityOther16;
   1410                                 }
   1411                                 else if (quantity16 == zero16) {
   1412                                     itemIdent = quantityZero16;
   1413                                 }
   1414                                 else if (quantity16 == one16) {
   1415                                     itemIdent = quantityOne16;
   1416                                 }
   1417                                 else if (quantity16 == two16) {
   1418                                     itemIdent = quantityTwo16;
   1419                                 }
   1420                                 else if (quantity16 == few16) {
   1421                                     itemIdent = quantityFew16;
   1422                                 }
   1423                                 else if (quantity16 == many16) {
   1424                                     itemIdent = quantityMany16;
   1425                                 }
   1426                                 else {
   1427                                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1428                                             "Illegal 'quantity' attribute is <item> inside <plurals>\n");
   1429                                     hasErrors = localHasErrors = true;
   1430                                 }
   1431                             } else {
   1432                                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1433                                         "A 'quantity' attribute is required for <item> inside <plurals>\n");
   1434                                 hasErrors = localHasErrors = true;
   1435                             }
   1436                         } else {
   1437                             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
   1438                             if (itemIdentIdx >= 0) {
   1439                                 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
   1440                             } else {
   1441                                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1442                                         "A 'name' attribute is required for <item>\n");
   1443                                 hasErrors = localHasErrors = true;
   1444                             }
   1445                         }
   1446 
   1447                         ResXMLParser::ResXMLPosition parserPosition;
   1448                         block.getPosition(&parserPosition);
   1449 
   1450                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
   1451                                 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
   1452                                 product, false, overwrite, outTable);
   1453                         if (err == NO_ERROR) {
   1454                             if (curIsPseudolocalizable && localeIsDefined(curParams)
   1455                                     && bundle->getPseudolocalize()) {
   1456                                 // pseudolocalize here
   1457 #if 1
   1458                                 block.setPosition(parserPosition);
   1459                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
   1460                                         curType, ident, parentIdent, itemIdent, curFormat,
   1461                                         curIsFormatted, product, true, overwrite, outTable);
   1462 #endif
   1463                             }
   1464                         }
   1465                         if (err != NO_ERROR) {
   1466                             hasErrors = localHasErrors = true;
   1467                         }
   1468                     } else if (code == ResXMLTree::END_TAG) {
   1469                         if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
   1470                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1471                                     "Found tag </%s> where </%s> is expected\n",
   1472                                     String8(block.getElementName(&len)).string(),
   1473                                     String8(*curTag).string());
   1474                             return UNKNOWN_ERROR;
   1475                         }
   1476                         break;
   1477                     }
   1478                 }
   1479             } else {
   1480                 ResXMLParser::ResXMLPosition parserPosition;
   1481                 block.getPosition(&parserPosition);
   1482 
   1483                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
   1484                         *curTag, curIsStyled, curFormat, curIsFormatted,
   1485                         product, false, overwrite, outTable);
   1486 
   1487                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
   1488                     hasErrors = localHasErrors = true;
   1489                 }
   1490                 else if (err == NO_ERROR) {
   1491                     if (curIsPseudolocalizable && localeIsDefined(curParams)
   1492                             && bundle->getPseudolocalize()) {
   1493                         // pseudolocalize here
   1494                         block.setPosition(parserPosition);
   1495                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
   1496                                 ident, *curTag, curIsStyled, curFormat,
   1497                                 curIsFormatted, product,
   1498                                 true, overwrite, outTable);
   1499                         if (err != NO_ERROR) {
   1500                             hasErrors = localHasErrors = true;
   1501                         }
   1502                     }
   1503                 }
   1504             }
   1505 
   1506 #if 0
   1507             if (comment.size() > 0) {
   1508                 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
   1509                        String8(curType).string(), String8(ident).string(),
   1510                        String8(comment).string());
   1511             }
   1512 #endif
   1513             if (!localHasErrors) {
   1514                 outTable->appendComment(myPackage, curType, ident, comment, false);
   1515             }
   1516         }
   1517         else if (code == ResXMLTree::END_TAG) {
   1518             if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
   1519                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1520                         "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
   1521                 return UNKNOWN_ERROR;
   1522             }
   1523         }
   1524         else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
   1525         }
   1526         else if (code == ResXMLTree::TEXT) {
   1527             if (isWhitespace(block.getText(&len))) {
   1528                 continue;
   1529             }
   1530             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1531                     "Found text \"%s\" where item tag is expected\n",
   1532                     String8(block.getText(&len)).string());
   1533             return UNKNOWN_ERROR;
   1534         }
   1535     }
   1536 
   1537     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   1538 }
   1539 
   1540 ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
   1541     : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
   1542       mIsAppPackage(!bundle->getExtending()),
   1543       mNumLocal(0),
   1544       mBundle(bundle)
   1545 {
   1546 }
   1547 
   1548 status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
   1549 {
   1550     status_t err = assets->buildIncludedResources(bundle);
   1551     if (err != NO_ERROR) {
   1552         return err;
   1553     }
   1554 
   1555     // For future reference to included resources.
   1556     mAssets = assets;
   1557 
   1558     const ResTable& incl = assets->getIncludedResources();
   1559 
   1560     // Retrieve all the packages.
   1561     const size_t N = incl.getBasePackageCount();
   1562     for (size_t phase=0; phase<2; phase++) {
   1563         for (size_t i=0; i<N; i++) {
   1564             String16 name(incl.getBasePackageName(i));
   1565             uint32_t id = incl.getBasePackageId(i);
   1566             // First time through: only add base packages (id
   1567             // is not 0); second time through add the other
   1568             // packages.
   1569             if (phase != 0) {
   1570                 if (id != 0) {
   1571                     // Skip base packages -- already one.
   1572                     id = 0;
   1573                 } else {
   1574                     // Assign a dynamic id.
   1575                     id = mNextPackageId;
   1576                 }
   1577             } else if (id != 0) {
   1578                 if (id == 127) {
   1579                     if (mHaveAppPackage) {
   1580                         fprintf(stderr, "Included resources have two application packages!\n");
   1581                         return UNKNOWN_ERROR;
   1582                     }
   1583                     mHaveAppPackage = true;
   1584                 }
   1585                 if (mNextPackageId > id) {
   1586                     fprintf(stderr, "Included base package ID %d already in use!\n", id);
   1587                     return UNKNOWN_ERROR;
   1588                 }
   1589             }
   1590             if (id != 0) {
   1591                 NOISY(printf("Including package %s with ID=%d\n",
   1592                              String8(name).string(), id));
   1593                 sp<Package> p = new Package(name, id);
   1594                 mPackages.add(name, p);
   1595                 mOrderedPackages.add(p);
   1596 
   1597                 if (id >= mNextPackageId) {
   1598                     mNextPackageId = id+1;
   1599                 }
   1600             }
   1601         }
   1602     }
   1603 
   1604     // Every resource table always has one first entry, the bag attributes.
   1605     const SourcePos unknown(String8("????"), 0);
   1606     sp<Type> attr = getType(mAssetsPackage, String16("attr"), unknown);
   1607 
   1608     return NO_ERROR;
   1609 }
   1610 
   1611 status_t ResourceTable::addPublic(const SourcePos& sourcePos,
   1612                                   const String16& package,
   1613                                   const String16& type,
   1614                                   const String16& name,
   1615                                   const uint32_t ident)
   1616 {
   1617     uint32_t rid = mAssets->getIncludedResources()
   1618         .identifierForName(name.string(), name.size(),
   1619                            type.string(), type.size(),
   1620                            package.string(), package.size());
   1621     if (rid != 0) {
   1622         sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
   1623                 String8(type).string(), String8(name).string(),
   1624                 String8(package).string());
   1625         return UNKNOWN_ERROR;
   1626     }
   1627 
   1628     sp<Type> t = getType(package, type, sourcePos);
   1629     if (t == NULL) {
   1630         return UNKNOWN_ERROR;
   1631     }
   1632     return t->addPublic(sourcePos, name, ident);
   1633 }
   1634 
   1635 status_t ResourceTable::addEntry(const SourcePos& sourcePos,
   1636                                  const String16& package,
   1637                                  const String16& type,
   1638                                  const String16& name,
   1639                                  const String16& value,
   1640                                  const Vector<StringPool::entry_style_span>* style,
   1641                                  const ResTable_config* params,
   1642                                  const bool doSetIndex,
   1643                                  const int32_t format,
   1644                                  const bool overwrite)
   1645 {
   1646     // Check for adding entries in other packages...  for now we do
   1647     // nothing.  We need to do the right thing here to support skinning.
   1648     uint32_t rid = mAssets->getIncludedResources()
   1649         .identifierForName(name.string(), name.size(),
   1650                            type.string(), type.size(),
   1651                            package.string(), package.size());
   1652     if (rid != 0) {
   1653         return NO_ERROR;
   1654     }
   1655 
   1656 #if 0
   1657     if (name == String16("left")) {
   1658         printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n",
   1659                sourcePos.file.string(), sourcePos.line, String8(type).string(),
   1660                String8(value).string());
   1661     }
   1662 #endif
   1663 
   1664     sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
   1665                            params, doSetIndex);
   1666     if (e == NULL) {
   1667         return UNKNOWN_ERROR;
   1668     }
   1669     status_t err = e->setItem(sourcePos, value, style, format, overwrite);
   1670     if (err == NO_ERROR) {
   1671         mNumLocal++;
   1672     }
   1673     return err;
   1674 }
   1675 
   1676 status_t ResourceTable::startBag(const SourcePos& sourcePos,
   1677                                  const String16& package,
   1678                                  const String16& type,
   1679                                  const String16& name,
   1680                                  const String16& bagParent,
   1681                                  const ResTable_config* params,
   1682                                  bool overlay,
   1683                                  bool replace, bool isId)
   1684 {
   1685     status_t result = NO_ERROR;
   1686 
   1687     // Check for adding entries in other packages...  for now we do
   1688     // nothing.  We need to do the right thing here to support skinning.
   1689     uint32_t rid = mAssets->getIncludedResources()
   1690     .identifierForName(name.string(), name.size(),
   1691                        type.string(), type.size(),
   1692                        package.string(), package.size());
   1693     if (rid != 0) {
   1694         return NO_ERROR;
   1695     }
   1696 
   1697 #if 0
   1698     if (name == String16("left")) {
   1699         printf("Adding bag left: file=%s, line=%d, type=%s\n",
   1700                sourcePos.file.striing(), sourcePos.line, String8(type).string());
   1701     }
   1702 #endif
   1703     if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
   1704         bool canAdd = false;
   1705         sp<Package> p = mPackages.valueFor(package);
   1706         if (p != NULL) {
   1707             sp<Type> t = p->getTypes().valueFor(type);
   1708             if (t != NULL) {
   1709                 if (t->getCanAddEntries().indexOf(name) >= 0) {
   1710                     canAdd = true;
   1711                 }
   1712             }
   1713         }
   1714         if (!canAdd) {
   1715             sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
   1716                             String8(name).string());
   1717             return UNKNOWN_ERROR;
   1718         }
   1719     }
   1720     sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
   1721     if (e == NULL) {
   1722         return UNKNOWN_ERROR;
   1723     }
   1724 
   1725     // If a parent is explicitly specified, set it.
   1726     if (bagParent.size() > 0) {
   1727         String16 curPar = e->getParent();
   1728         if (curPar.size() > 0 && curPar != bagParent) {
   1729             sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
   1730                             String8(e->getParent()).string(),
   1731                             String8(bagParent).string());
   1732             return UNKNOWN_ERROR;
   1733         }
   1734         e->setParent(bagParent);
   1735     }
   1736 
   1737     if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
   1738         return result;
   1739     }
   1740 
   1741     if (overlay && replace) {
   1742         return e->emptyBag(sourcePos);
   1743     }
   1744     return result;
   1745 }
   1746 
   1747 status_t ResourceTable::addBag(const SourcePos& sourcePos,
   1748                                const String16& package,
   1749                                const String16& type,
   1750                                const String16& name,
   1751                                const String16& bagParent,
   1752                                const String16& bagKey,
   1753                                const String16& value,
   1754                                const Vector<StringPool::entry_style_span>* style,
   1755                                const ResTable_config* params,
   1756                                bool replace, bool isId, const int32_t format)
   1757 {
   1758     // Check for adding entries in other packages...  for now we do
   1759     // nothing.  We need to do the right thing here to support skinning.
   1760     uint32_t rid = mAssets->getIncludedResources()
   1761         .identifierForName(name.string(), name.size(),
   1762                            type.string(), type.size(),
   1763                            package.string(), package.size());
   1764     if (rid != 0) {
   1765         return NO_ERROR;
   1766     }
   1767 
   1768 #if 0
   1769     if (name == String16("left")) {
   1770         printf("Adding bag left: file=%s, line=%d, type=%s\n",
   1771                sourcePos.file.striing(), sourcePos.line, String8(type).string());
   1772     }
   1773 #endif
   1774     sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
   1775     if (e == NULL) {
   1776         return UNKNOWN_ERROR;
   1777     }
   1778 
   1779     // If a parent is explicitly specified, set it.
   1780     if (bagParent.size() > 0) {
   1781         String16 curPar = e->getParent();
   1782         if (curPar.size() > 0 && curPar != bagParent) {
   1783             sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
   1784                     String8(e->getParent()).string(),
   1785                     String8(bagParent).string());
   1786             return UNKNOWN_ERROR;
   1787         }
   1788         e->setParent(bagParent);
   1789     }
   1790 
   1791     const bool first = e->getBag().indexOfKey(bagKey) < 0;
   1792     status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
   1793     if (err == NO_ERROR && first) {
   1794         mNumLocal++;
   1795     }
   1796     return err;
   1797 }
   1798 
   1799 bool ResourceTable::hasBagOrEntry(const String16& package,
   1800                                   const String16& type,
   1801                                   const String16& name) const
   1802 {
   1803     // First look for this in the included resources...
   1804     uint32_t rid = mAssets->getIncludedResources()
   1805         .identifierForName(name.string(), name.size(),
   1806                            type.string(), type.size(),
   1807                            package.string(), package.size());
   1808     if (rid != 0) {
   1809         return true;
   1810     }
   1811 
   1812     sp<Package> p = mPackages.valueFor(package);
   1813     if (p != NULL) {
   1814         sp<Type> t = p->getTypes().valueFor(type);
   1815         if (t != NULL) {
   1816             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1817             if (c != NULL) return true;
   1818         }
   1819     }
   1820 
   1821     return false;
   1822 }
   1823 
   1824 bool ResourceTable::hasBagOrEntry(const String16& ref,
   1825                                   const String16* defType,
   1826                                   const String16* defPackage)
   1827 {
   1828     String16 package, type, name;
   1829     if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
   1830                 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
   1831         return false;
   1832     }
   1833     return hasBagOrEntry(package, type, name);
   1834 }
   1835 
   1836 bool ResourceTable::appendComment(const String16& package,
   1837                                   const String16& type,
   1838                                   const String16& name,
   1839                                   const String16& comment,
   1840                                   bool onlyIfEmpty)
   1841 {
   1842     if (comment.size() <= 0) {
   1843         return true;
   1844     }
   1845 
   1846     sp<Package> p = mPackages.valueFor(package);
   1847     if (p != NULL) {
   1848         sp<Type> t = p->getTypes().valueFor(type);
   1849         if (t != NULL) {
   1850             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1851             if (c != NULL) {
   1852                 c->appendComment(comment, onlyIfEmpty);
   1853                 return true;
   1854             }
   1855         }
   1856     }
   1857     return false;
   1858 }
   1859 
   1860 bool ResourceTable::appendTypeComment(const String16& package,
   1861                                       const String16& type,
   1862                                       const String16& name,
   1863                                       const String16& comment)
   1864 {
   1865     if (comment.size() <= 0) {
   1866         return true;
   1867     }
   1868 
   1869     sp<Package> p = mPackages.valueFor(package);
   1870     if (p != NULL) {
   1871         sp<Type> t = p->getTypes().valueFor(type);
   1872         if (t != NULL) {
   1873             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1874             if (c != NULL) {
   1875                 c->appendTypeComment(comment);
   1876                 return true;
   1877             }
   1878         }
   1879     }
   1880     return false;
   1881 }
   1882 
   1883 void ResourceTable::canAddEntry(const SourcePos& pos,
   1884         const String16& package, const String16& type, const String16& name)
   1885 {
   1886     sp<Type> t = getType(package, type, pos);
   1887     if (t != NULL) {
   1888         t->canAddEntry(name);
   1889     }
   1890 }
   1891 
   1892 size_t ResourceTable::size() const {
   1893     return mPackages.size();
   1894 }
   1895 
   1896 size_t ResourceTable::numLocalResources() const {
   1897     return mNumLocal;
   1898 }
   1899 
   1900 bool ResourceTable::hasResources() const {
   1901     return mNumLocal > 0;
   1902 }
   1903 
   1904 sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
   1905 {
   1906     sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   1907     status_t err = flatten(bundle, data);
   1908     return err == NO_ERROR ? data : NULL;
   1909 }
   1910 
   1911 inline uint32_t ResourceTable::getResId(const sp<Package>& p,
   1912                                         const sp<Type>& t,
   1913                                         uint32_t nameId)
   1914 {
   1915     return makeResId(p->getAssignedId(), t->getIndex(), nameId);
   1916 }
   1917 
   1918 uint32_t ResourceTable::getResId(const String16& package,
   1919                                  const String16& type,
   1920                                  const String16& name,
   1921                                  bool onlyPublic) const
   1922 {
   1923     sp<Package> p = mPackages.valueFor(package);
   1924     if (p == NULL) return 0;
   1925 
   1926     // First look for this in the included resources...
   1927     uint32_t specFlags = 0;
   1928     uint32_t rid = mAssets->getIncludedResources()
   1929         .identifierForName(name.string(), name.size(),
   1930                            type.string(), type.size(),
   1931                            package.string(), package.size(),
   1932                            &specFlags);
   1933     if (rid != 0) {
   1934         if (onlyPublic) {
   1935             if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   1936                 return 0;
   1937             }
   1938         }
   1939 
   1940         if (Res_INTERNALID(rid)) {
   1941             return rid;
   1942         }
   1943         return Res_MAKEID(p->getAssignedId()-1,
   1944                           Res_GETTYPE(rid),
   1945                           Res_GETENTRY(rid));
   1946     }
   1947 
   1948     sp<Type> t = p->getTypes().valueFor(type);
   1949     if (t == NULL) return 0;
   1950     sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1951     if (c == NULL) return 0;
   1952     int32_t ei = c->getEntryIndex();
   1953     if (ei < 0) return 0;
   1954     return getResId(p, t, ei);
   1955 }
   1956 
   1957 uint32_t ResourceTable::getResId(const String16& ref,
   1958                                  const String16* defType,
   1959                                  const String16* defPackage,
   1960                                  const char** outErrorMsg,
   1961                                  bool onlyPublic) const
   1962 {
   1963     String16 package, type, name;
   1964     if (!ResTable::expandResourceRef(
   1965         ref.string(), ref.size(), &package, &type, &name,
   1966         defType, defPackage ? defPackage:&mAssetsPackage,
   1967         outErrorMsg)) {
   1968         NOISY(printf("Expanding resource: ref=%s\n",
   1969                      String8(ref).string()));
   1970         NOISY(printf("Expanding resource: defType=%s\n",
   1971                      defType ? String8(*defType).string() : "NULL"));
   1972         NOISY(printf("Expanding resource: defPackage=%s\n",
   1973                      defPackage ? String8(*defPackage).string() : "NULL"));
   1974         NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
   1975         NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
   1976                      String8(package).string(), String8(type).string(),
   1977                      String8(name).string()));
   1978         return 0;
   1979     }
   1980     uint32_t res = getResId(package, type, name, onlyPublic);
   1981     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
   1982                  String8(package).string(), String8(type).string(),
   1983                  String8(name).string(), res));
   1984     if (res == 0) {
   1985         if (outErrorMsg)
   1986             *outErrorMsg = "No resource found that matches the given name";
   1987     }
   1988     return res;
   1989 }
   1990 
   1991 bool ResourceTable::isValidResourceName(const String16& s)
   1992 {
   1993     const char16_t* p = s.string();
   1994     bool first = true;
   1995     while (*p) {
   1996         if ((*p >= 'a' && *p <= 'z')
   1997             || (*p >= 'A' && *p <= 'Z')
   1998             || *p == '_'
   1999             || (!first && *p >= '0' && *p <= '9')) {
   2000             first = false;
   2001             p++;
   2002             continue;
   2003         }
   2004         return false;
   2005     }
   2006     return true;
   2007 }
   2008 
   2009 bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
   2010                                   const String16& str,
   2011                                   bool preserveSpaces, bool coerceType,
   2012                                   uint32_t attrID,
   2013                                   const Vector<StringPool::entry_style_span>* style,
   2014                                   String16* outStr, void* accessorCookie,
   2015                                   uint32_t attrType)
   2016 {
   2017     String16 finalStr;
   2018 
   2019     bool res = true;
   2020     if (style == NULL || style->size() == 0) {
   2021         // Text is not styled so it can be any type...  let's figure it out.
   2022         res = mAssets->getIncludedResources()
   2023             .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
   2024                             coerceType, attrID, NULL, &mAssetsPackage, this,
   2025                            accessorCookie, attrType);
   2026     } else {
   2027         // Styled text can only be a string, and while collecting the style
   2028         // information we have already processed that string!
   2029         outValue->size = sizeof(Res_value);
   2030         outValue->res0 = 0;
   2031         outValue->dataType = outValue->TYPE_STRING;
   2032         outValue->data = 0;
   2033         finalStr = str;
   2034     }
   2035 
   2036     if (!res) {
   2037         return false;
   2038     }
   2039 
   2040     if (outValue->dataType == outValue->TYPE_STRING) {
   2041         // Should do better merging styles.
   2042         if (pool) {
   2043             if (style != NULL && style->size() > 0) {
   2044                 outValue->data = pool->add(finalStr, *style);
   2045             } else {
   2046                 outValue->data = pool->add(finalStr, true);
   2047             }
   2048         } else {
   2049             // Caller will fill this in later.
   2050             outValue->data = 0;
   2051         }
   2052 
   2053         if (outStr) {
   2054             *outStr = finalStr;
   2055         }
   2056 
   2057     }
   2058 
   2059     return true;
   2060 }
   2061 
   2062 uint32_t ResourceTable::getCustomResource(
   2063     const String16& package, const String16& type, const String16& name) const
   2064 {
   2065     //printf("getCustomResource: %s %s %s\n", String8(package).string(),
   2066     //       String8(type).string(), String8(name).string());
   2067     sp<Package> p = mPackages.valueFor(package);
   2068     if (p == NULL) return 0;
   2069     sp<Type> t = p->getTypes().valueFor(type);
   2070     if (t == NULL) return 0;
   2071     sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2072     if (c == NULL) return 0;
   2073     int32_t ei = c->getEntryIndex();
   2074     if (ei < 0) return 0;
   2075     return getResId(p, t, ei);
   2076 }
   2077 
   2078 uint32_t ResourceTable::getCustomResourceWithCreation(
   2079         const String16& package, const String16& type, const String16& name,
   2080         const bool createIfNotFound)
   2081 {
   2082     uint32_t resId = getCustomResource(package, type, name);
   2083     if (resId != 0 || !createIfNotFound) {
   2084         return resId;
   2085     }
   2086     String16 value("false");
   2087 
   2088     status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
   2089     if (status == NO_ERROR) {
   2090         resId = getResId(package, type, name);
   2091         return resId;
   2092     }
   2093     return 0;
   2094 }
   2095 
   2096 uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
   2097 {
   2098     return origPackage;
   2099 }
   2100 
   2101 bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
   2102 {
   2103     //printf("getAttributeType #%08x\n", attrID);
   2104     Res_value value;
   2105     if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
   2106         //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
   2107         //       String8(getEntry(attrID)->getName()).string(), value.data);
   2108         *outType = value.data;
   2109         return true;
   2110     }
   2111     return false;
   2112 }
   2113 
   2114 bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
   2115 {
   2116     //printf("getAttributeMin #%08x\n", attrID);
   2117     Res_value value;
   2118     if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
   2119         *outMin = value.data;
   2120         return true;
   2121     }
   2122     return false;
   2123 }
   2124 
   2125 bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
   2126 {
   2127     //printf("getAttributeMax #%08x\n", attrID);
   2128     Res_value value;
   2129     if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
   2130         *outMax = value.data;
   2131         return true;
   2132     }
   2133     return false;
   2134 }
   2135 
   2136 uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
   2137 {
   2138     //printf("getAttributeL10N #%08x\n", attrID);
   2139     Res_value value;
   2140     if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
   2141         return value.data;
   2142     }
   2143     return ResTable_map::L10N_NOT_REQUIRED;
   2144 }
   2145 
   2146 bool ResourceTable::getLocalizationSetting()
   2147 {
   2148     return mBundle->getRequireLocalization();
   2149 }
   2150 
   2151 void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
   2152 {
   2153     if (accessorCookie != NULL && fmt != NULL) {
   2154         AccessorCookie* ac = (AccessorCookie*)accessorCookie;
   2155         int retval=0;
   2156         char buf[1024];
   2157         va_list ap;
   2158         va_start(ap, fmt);
   2159         retval = vsnprintf(buf, sizeof(buf), fmt, ap);
   2160         va_end(ap);
   2161         ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
   2162                             buf, ac->attr.string(), ac->value.string());
   2163     }
   2164 }
   2165 
   2166 bool ResourceTable::getAttributeKeys(
   2167     uint32_t attrID, Vector<String16>* outKeys)
   2168 {
   2169     sp<const Entry> e = getEntry(attrID);
   2170     if (e != NULL) {
   2171         const size_t N = e->getBag().size();
   2172         for (size_t i=0; i<N; i++) {
   2173             const String16& key = e->getBag().keyAt(i);
   2174             if (key.size() > 0 && key.string()[0] != '^') {
   2175                 outKeys->add(key);
   2176             }
   2177         }
   2178         return true;
   2179     }
   2180     return false;
   2181 }
   2182 
   2183 bool ResourceTable::getAttributeEnum(
   2184     uint32_t attrID, const char16_t* name, size_t nameLen,
   2185     Res_value* outValue)
   2186 {
   2187     //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
   2188     String16 nameStr(name, nameLen);
   2189     sp<const Entry> e = getEntry(attrID);
   2190     if (e != NULL) {
   2191         const size_t N = e->getBag().size();
   2192         for (size_t i=0; i<N; i++) {
   2193             //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
   2194             //       String8(e->getBag().keyAt(i)).string());
   2195             if (e->getBag().keyAt(i) == nameStr) {
   2196                 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
   2197             }
   2198         }
   2199     }
   2200     return false;
   2201 }
   2202 
   2203 bool ResourceTable::getAttributeFlags(
   2204     uint32_t attrID, const char16_t* name, size_t nameLen,
   2205     Res_value* outValue)
   2206 {
   2207     outValue->dataType = Res_value::TYPE_INT_HEX;
   2208     outValue->data = 0;
   2209 
   2210     //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
   2211     String16 nameStr(name, nameLen);
   2212     sp<const Entry> e = getEntry(attrID);
   2213     if (e != NULL) {
   2214         const size_t N = e->getBag().size();
   2215 
   2216         const char16_t* end = name + nameLen;
   2217         const char16_t* pos = name;
   2218         bool failed = false;
   2219         while (pos < end && !failed) {
   2220             const char16_t* start = pos;
   2221             end++;
   2222             while (pos < end && *pos != '|') {
   2223                 pos++;
   2224             }
   2225 
   2226             String16 nameStr(start, pos-start);
   2227             size_t i;
   2228             for (i=0; i<N; i++) {
   2229                 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
   2230                 //       String8(e->getBag().keyAt(i)).string());
   2231                 if (e->getBag().keyAt(i) == nameStr) {
   2232                     Res_value val;
   2233                     bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
   2234                     if (!got) {
   2235                         return false;
   2236                     }
   2237                     //printf("Got value: 0x%08x\n", val.data);
   2238                     outValue->data |= val.data;
   2239                     break;
   2240                 }
   2241             }
   2242 
   2243             if (i >= N) {
   2244                 // Didn't find this flag identifier.
   2245                 return false;
   2246             }
   2247             if (pos < end) {
   2248                 pos++;
   2249             }
   2250         }
   2251 
   2252         return true;
   2253     }
   2254     return false;
   2255 }
   2256 
   2257 status_t ResourceTable::assignResourceIds()
   2258 {
   2259     const size_t N = mOrderedPackages.size();
   2260     size_t pi;
   2261     status_t firstError = NO_ERROR;
   2262 
   2263     // First generate all bag attributes and assign indices.
   2264     for (pi=0; pi<N; pi++) {
   2265         sp<Package> p = mOrderedPackages.itemAt(pi);
   2266         if (p == NULL || p->getTypes().size() == 0) {
   2267             // Empty, skip!
   2268             continue;
   2269         }
   2270 
   2271         status_t err = p->applyPublicTypeOrder();
   2272         if (err != NO_ERROR && firstError == NO_ERROR) {
   2273             firstError = err;
   2274         }
   2275 
   2276         // Generate attributes...
   2277         const size_t N = p->getOrderedTypes().size();
   2278         size_t ti;
   2279         for (ti=0; ti<N; ti++) {
   2280             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2281             if (t == NULL) {
   2282                 continue;
   2283             }
   2284             const size_t N = t->getOrderedConfigs().size();
   2285             for (size_t ci=0; ci<N; ci++) {
   2286                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2287                 if (c == NULL) {
   2288                     continue;
   2289                 }
   2290                 const size_t N = c->getEntries().size();
   2291                 for (size_t ei=0; ei<N; ei++) {
   2292                     sp<Entry> e = c->getEntries().valueAt(ei);
   2293                     if (e == NULL) {
   2294                         continue;
   2295                     }
   2296                     status_t err = e->generateAttributes(this, p->getName());
   2297                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2298                         firstError = err;
   2299                     }
   2300                 }
   2301             }
   2302         }
   2303 
   2304         const SourcePos unknown(String8("????"), 0);
   2305         sp<Type> attr = p->getType(String16("attr"), unknown);
   2306 
   2307         // Assign indices...
   2308         for (ti=0; ti<N; ti++) {
   2309             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2310             if (t == NULL) {
   2311                 continue;
   2312             }
   2313             err = t->applyPublicEntryOrder();
   2314             if (err != NO_ERROR && firstError == NO_ERROR) {
   2315                 firstError = err;
   2316             }
   2317 
   2318             const size_t N = t->getOrderedConfigs().size();
   2319             t->setIndex(ti+1);
   2320 
   2321             LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
   2322                                 "First type is not attr!");
   2323 
   2324             for (size_t ei=0; ei<N; ei++) {
   2325                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
   2326                 if (c == NULL) {
   2327                     continue;
   2328                 }
   2329                 c->setEntryIndex(ei);
   2330             }
   2331         }
   2332 
   2333         // Assign resource IDs to keys in bags...
   2334         for (ti=0; ti<N; ti++) {
   2335             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2336             if (t == NULL) {
   2337                 continue;
   2338             }
   2339             const size_t N = t->getOrderedConfigs().size();
   2340             for (size_t ci=0; ci<N; ci++) {
   2341                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2342                 //printf("Ordered config #%d: %p\n", ci, c.get());
   2343                 const size_t N = c->getEntries().size();
   2344                 for (size_t ei=0; ei<N; ei++) {
   2345                     sp<Entry> e = c->getEntries().valueAt(ei);
   2346                     if (e == NULL) {
   2347                         continue;
   2348                     }
   2349                     status_t err = e->assignResourceIds(this, p->getName());
   2350                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2351                         firstError = err;
   2352                     }
   2353                 }
   2354             }
   2355         }
   2356     }
   2357     return firstError;
   2358 }
   2359 
   2360 status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
   2361     const size_t N = mOrderedPackages.size();
   2362     size_t pi;
   2363 
   2364     for (pi=0; pi<N; pi++) {
   2365         sp<Package> p = mOrderedPackages.itemAt(pi);
   2366         if (p->getTypes().size() == 0) {
   2367             // Empty, skip!
   2368             continue;
   2369         }
   2370 
   2371         const size_t N = p->getOrderedTypes().size();
   2372         size_t ti;
   2373 
   2374         for (ti=0; ti<N; ti++) {
   2375             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2376             if (t == NULL) {
   2377                 continue;
   2378             }
   2379             const size_t N = t->getOrderedConfigs().size();
   2380             sp<AaptSymbols> typeSymbols;
   2381             typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
   2382             for (size_t ci=0; ci<N; ci++) {
   2383                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2384                 if (c == NULL) {
   2385                     continue;
   2386                 }
   2387                 uint32_t rid = getResId(p, t, ci);
   2388                 if (rid == 0) {
   2389                     return UNKNOWN_ERROR;
   2390                 }
   2391                 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
   2392                     typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
   2393 
   2394                     String16 comment(c->getComment());
   2395                     typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
   2396                     //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
   2397                     //     String8(comment).string());
   2398                     comment = c->getTypeComment();
   2399                     typeSymbols->appendTypeComment(String8(c->getName()), comment);
   2400                 } else {
   2401 #if 0
   2402                     printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
   2403                            Res_GETPACKAGE(rid), p->getAssignedId());
   2404 #endif
   2405                 }
   2406             }
   2407         }
   2408     }
   2409     return NO_ERROR;
   2410 }
   2411 
   2412 
   2413 void
   2414 ResourceTable::addLocalization(const String16& name, const String8& locale)
   2415 {
   2416     mLocalizations[name].insert(locale);
   2417 }
   2418 
   2419 
   2420 /*!
   2421  * Flag various sorts of localization problems.  '+' indicates checks already implemented;
   2422  * '-' indicates checks that will be implemented in the future.
   2423  *
   2424  * + A localized string for which no default-locale version exists => warning
   2425  * + A string for which no version in an explicitly-requested locale exists => warning
   2426  * + A localized translation of an translateable="false" string => warning
   2427  * - A localized string not provided in every locale used by the table
   2428  */
   2429 status_t
   2430 ResourceTable::validateLocalizations(void)
   2431 {
   2432     status_t err = NO_ERROR;
   2433     const String8 defaultLocale;
   2434 
   2435     // For all strings...
   2436     for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
   2437          nameIter != mLocalizations.end();
   2438          nameIter++) {
   2439         const set<String8>& configSet = nameIter->second;   // naming convenience
   2440 
   2441         // Look for strings with no default localization
   2442         if (configSet.count(defaultLocale) == 0) {
   2443             fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
   2444                     String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
   2445             for (set<String8>::iterator locales = configSet.begin();
   2446                  locales != configSet.end();
   2447                  locales++) {
   2448                 fprintf(stdout, " %s", (*locales).string());
   2449             }
   2450             fprintf(stdout, "\n");
   2451             // !!! TODO: throw an error here in some circumstances
   2452         }
   2453 
   2454         // Check that all requested localizations are present for this string
   2455         if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
   2456             const char* allConfigs = mBundle->getConfigurations();
   2457             const char* start = allConfigs;
   2458             const char* comma;
   2459 
   2460             do {
   2461                 String8 config;
   2462                 comma = strchr(start, ',');
   2463                 if (comma != NULL) {
   2464                     config.setTo(start, comma - start);
   2465                     start = comma + 1;
   2466                 } else {
   2467                     config.setTo(start);
   2468                 }
   2469 
   2470                 // don't bother with the pseudolocale "zz_ZZ"
   2471                 if (config != "zz_ZZ") {
   2472                     if (configSet.find(config) == configSet.end()) {
   2473                         // okay, no specific localization found.  it's possible that we are
   2474                         // requiring a specific regional localization [e.g. de_DE] but there is an
   2475                         // available string in the generic language localization [e.g. de];
   2476                         // consider that string to have fulfilled the localization requirement.
   2477                         String8 region(config.string(), 2);
   2478                         if (configSet.find(region) == configSet.end()) {
   2479                             if (configSet.count(defaultLocale) == 0) {
   2480                                 fprintf(stdout, "aapt: warning: "
   2481                                         "**** string '%s' has no default or required localization "
   2482                                         "for '%s' in %s\n",
   2483                                         String8(nameIter->first).string(),
   2484                                         config.string(),
   2485                                         mBundle->getResourceSourceDirs()[0]);
   2486                             }
   2487                         }
   2488                     }
   2489                 }
   2490            } while (comma != NULL);
   2491         }
   2492     }
   2493 
   2494     return err;
   2495 }
   2496 
   2497 
   2498 status_t
   2499 ResourceFilter::parse(const char* arg)
   2500 {
   2501     if (arg == NULL) {
   2502         return 0;
   2503     }
   2504 
   2505     const char* p = arg;
   2506     const char* q;
   2507 
   2508     while (true) {
   2509         q = strchr(p, ',');
   2510         if (q == NULL) {
   2511             q = p + strlen(p);
   2512         }
   2513 
   2514         String8 part(p, q-p);
   2515 
   2516         if (part == "zz_ZZ") {
   2517             mContainsPseudo = true;
   2518         }
   2519         int axis;
   2520         uint32_t value;
   2521         if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
   2522             fprintf(stderr, "Invalid configuration: %s\n", arg);
   2523             fprintf(stderr, "                       ");
   2524             for (int i=0; i<p-arg; i++) {
   2525                 fprintf(stderr, " ");
   2526             }
   2527             for (int i=0; i<q-p; i++) {
   2528                 fprintf(stderr, "^");
   2529             }
   2530             fprintf(stderr, "\n");
   2531             return 1;
   2532         }
   2533 
   2534         ssize_t index = mData.indexOfKey(axis);
   2535         if (index < 0) {
   2536             mData.add(axis, SortedVector<uint32_t>());
   2537         }
   2538         SortedVector<uint32_t>& sv = mData.editValueFor(axis);
   2539         sv.add(value);
   2540         // if it's a locale with a region, also match an unmodified locale of the
   2541         // same language
   2542         if (axis == AXIS_LANGUAGE) {
   2543             if (value & 0xffff0000) {
   2544                 sv.add(value & 0x0000ffff);
   2545             }
   2546         }
   2547         p = q;
   2548         if (!*p) break;
   2549         p++;
   2550     }
   2551 
   2552     return NO_ERROR;
   2553 }
   2554 
   2555 bool
   2556 ResourceFilter::match(int axis, uint32_t value)
   2557 {
   2558     if (value == 0) {
   2559         // they didn't specify anything so take everything
   2560         return true;
   2561     }
   2562     ssize_t index = mData.indexOfKey(axis);
   2563     if (index < 0) {
   2564         // we didn't request anything on this axis so take everything
   2565         return true;
   2566     }
   2567     const SortedVector<uint32_t>& sv = mData.valueAt(index);
   2568     return sv.indexOf(value) >= 0;
   2569 }
   2570 
   2571 bool
   2572 ResourceFilter::match(const ResTable_config& config)
   2573 {
   2574     if (config.locale) {
   2575         uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
   2576                 | (config.language[1] << 8) | (config.language[0]);
   2577         if (!match(AXIS_LANGUAGE, locale)) {
   2578             return false;
   2579         }
   2580     }
   2581     if (!match(AXIS_ORIENTATION, config.orientation)) {
   2582         return false;
   2583     }
   2584     if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
   2585         return false;
   2586     }
   2587     if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
   2588         return false;
   2589     }
   2590     if (!match(AXIS_DENSITY, config.density)) {
   2591         return false;
   2592     }
   2593     if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
   2594         return false;
   2595     }
   2596     if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
   2597         return false;
   2598     }
   2599     if (!match(AXIS_KEYBOARD, config.keyboard)) {
   2600         return false;
   2601     }
   2602     if (!match(AXIS_NAVIGATION, config.navigation)) {
   2603         return false;
   2604     }
   2605     if (!match(AXIS_SCREENSIZE, config.screenSize)) {
   2606         return false;
   2607     }
   2608     if (!match(AXIS_VERSION, config.version)) {
   2609         return false;
   2610     }
   2611     return true;
   2612 }
   2613 
   2614 status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
   2615 {
   2616     ResourceFilter filter;
   2617     status_t err = filter.parse(bundle->getConfigurations());
   2618     if (err != NO_ERROR) {
   2619         return err;
   2620     }
   2621 
   2622     const size_t N = mOrderedPackages.size();
   2623     size_t pi;
   2624 
   2625     bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
   2626 
   2627     // Iterate through all data, collecting all values (strings,
   2628     // references, etc).
   2629     StringPool valueStrings = StringPool(false, useUTF8);
   2630     for (pi=0; pi<N; pi++) {
   2631         sp<Package> p = mOrderedPackages.itemAt(pi);
   2632         if (p->getTypes().size() == 0) {
   2633             // Empty, skip!
   2634             continue;
   2635         }
   2636 
   2637         StringPool typeStrings = StringPool(false, useUTF8);
   2638         StringPool keyStrings = StringPool(false, useUTF8);
   2639 
   2640         const size_t N = p->getOrderedTypes().size();
   2641         for (size_t ti=0; ti<N; ti++) {
   2642             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2643             if (t == NULL) {
   2644                 typeStrings.add(String16("<empty>"), false);
   2645                 continue;
   2646             }
   2647             typeStrings.add(t->getName(), false);
   2648 
   2649             const size_t N = t->getOrderedConfigs().size();
   2650             for (size_t ci=0; ci<N; ci++) {
   2651                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2652                 if (c == NULL) {
   2653                     continue;
   2654                 }
   2655                 const size_t N = c->getEntries().size();
   2656                 for (size_t ei=0; ei<N; ei++) {
   2657                     ConfigDescription config = c->getEntries().keyAt(ei);
   2658                     if (!filter.match(config)) {
   2659                         continue;
   2660                     }
   2661                     sp<Entry> e = c->getEntries().valueAt(ei);
   2662                     if (e == NULL) {
   2663                         continue;
   2664                     }
   2665                     e->setNameIndex(keyStrings.add(e->getName(), true));
   2666                     status_t err = e->prepareFlatten(&valueStrings, this);
   2667                     if (err != NO_ERROR) {
   2668                         return err;
   2669                     }
   2670                 }
   2671             }
   2672         }
   2673 
   2674         p->setTypeStrings(typeStrings.createStringBlock());
   2675         p->setKeyStrings(keyStrings.createStringBlock());
   2676     }
   2677 
   2678     ssize_t strAmt = 0;
   2679 
   2680     // Now build the array of package chunks.
   2681     Vector<sp<AaptFile> > flatPackages;
   2682     for (pi=0; pi<N; pi++) {
   2683         sp<Package> p = mOrderedPackages.itemAt(pi);
   2684         if (p->getTypes().size() == 0) {
   2685             // Empty, skip!
   2686             continue;
   2687         }
   2688 
   2689         const size_t N = p->getTypeStrings().size();
   2690 
   2691         const size_t baseSize = sizeof(ResTable_package);
   2692 
   2693         // Start the package data.
   2694         sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   2695         ResTable_package* header = (ResTable_package*)data->editData(baseSize);
   2696         if (header == NULL) {
   2697             fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
   2698             return NO_MEMORY;
   2699         }
   2700         memset(header, 0, sizeof(*header));
   2701         header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
   2702         header->header.headerSize = htods(sizeof(*header));
   2703         header->id = htodl(p->getAssignedId());
   2704         strcpy16_htod(header->name, p->getName().string());
   2705 
   2706         // Write the string blocks.
   2707         const size_t typeStringsStart = data->getSize();
   2708         sp<AaptFile> strFile = p->getTypeStringsData();
   2709         ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
   2710         #if PRINT_STRING_METRICS
   2711         fprintf(stderr, "**** type strings: %d\n", amt);
   2712         #endif
   2713         strAmt += amt;
   2714         if (amt < 0) {
   2715             return amt;
   2716         }
   2717         const size_t keyStringsStart = data->getSize();
   2718         strFile = p->getKeyStringsData();
   2719         amt = data->writeData(strFile->getData(), strFile->getSize());
   2720         #if PRINT_STRING_METRICS
   2721         fprintf(stderr, "**** key strings: %d\n", amt);
   2722         #endif
   2723         strAmt += amt;
   2724         if (amt < 0) {
   2725             return amt;
   2726         }
   2727 
   2728         // Build the type chunks inside of this package.
   2729         for (size_t ti=0; ti<N; ti++) {
   2730             // Retrieve them in the same order as the type string block.
   2731             size_t len;
   2732             String16 typeName(p->getTypeStrings().stringAt(ti, &len));
   2733             sp<Type> t = p->getTypes().valueFor(typeName);
   2734             LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
   2735                                 "Type name %s not found",
   2736                                 String8(typeName).string());
   2737 
   2738             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
   2739 
   2740             // First write the typeSpec chunk, containing information about
   2741             // each resource entry in this type.
   2742             {
   2743                 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
   2744                 const size_t typeSpecStart = data->getSize();
   2745                 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
   2746                     (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
   2747                 if (tsHeader == NULL) {
   2748                     fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
   2749                     return NO_MEMORY;
   2750                 }
   2751                 memset(tsHeader, 0, sizeof(*tsHeader));
   2752                 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
   2753                 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
   2754                 tsHeader->header.size = htodl(typeSpecSize);
   2755                 tsHeader->id = ti+1;
   2756                 tsHeader->entryCount = htodl(N);
   2757 
   2758                 uint32_t* typeSpecFlags = (uint32_t*)
   2759                     (((uint8_t*)data->editData())
   2760                         + typeSpecStart + sizeof(ResTable_typeSpec));
   2761                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
   2762 
   2763                 for (size_t ei=0; ei<N; ei++) {
   2764                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   2765                     if (cl->getPublic()) {
   2766                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
   2767                     }
   2768                     const size_t CN = cl->getEntries().size();
   2769                     for (size_t ci=0; ci<CN; ci++) {
   2770                         if (!filter.match(cl->getEntries().keyAt(ci))) {
   2771                             continue;
   2772                         }
   2773                         for (size_t cj=ci+1; cj<CN; cj++) {
   2774                             if (!filter.match(cl->getEntries().keyAt(cj))) {
   2775                                 continue;
   2776                             }
   2777                             typeSpecFlags[ei] |= htodl(
   2778                                 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
   2779                         }
   2780                     }
   2781                 }
   2782             }
   2783 
   2784             // We need to write one type chunk for each configuration for
   2785             // which we have entries in this type.
   2786             const size_t NC = t->getUniqueConfigs().size();
   2787 
   2788             const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
   2789 
   2790             for (size_t ci=0; ci<NC; ci++) {
   2791                 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
   2792 
   2793                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
   2794                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
   2795                       ti+1,
   2796                       config.mcc, config.mnc,
   2797                       config.language[0] ? config.language[0] : '-',
   2798                       config.language[1] ? config.language[1] : '-',
   2799                       config.country[0] ? config.country[0] : '-',
   2800                       config.country[1] ? config.country[1] : '-',
   2801                       config.orientation,
   2802                       config.uiMode,
   2803                       config.touchscreen,
   2804                       config.density,
   2805                       config.keyboard,
   2806                       config.inputFlags,
   2807                       config.navigation,
   2808                       config.screenWidth,
   2809                       config.screenHeight));
   2810 
   2811                 if (!filter.match(config)) {
   2812                     continue;
   2813                 }
   2814 
   2815                 const size_t typeStart = data->getSize();
   2816 
   2817                 ResTable_type* tHeader = (ResTable_type*)
   2818                     (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
   2819                 if (tHeader == NULL) {
   2820                     fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
   2821                     return NO_MEMORY;
   2822                 }
   2823 
   2824                 memset(tHeader, 0, sizeof(*tHeader));
   2825                 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
   2826                 tHeader->header.headerSize = htods(sizeof(*tHeader));
   2827                 tHeader->id = ti+1;
   2828                 tHeader->entryCount = htodl(N);
   2829                 tHeader->entriesStart = htodl(typeSize);
   2830                 tHeader->config = config;
   2831                 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
   2832                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
   2833                       ti+1,
   2834                       tHeader->config.mcc, tHeader->config.mnc,
   2835                       tHeader->config.language[0] ? tHeader->config.language[0] : '-',
   2836                       tHeader->config.language[1] ? tHeader->config.language[1] : '-',
   2837                       tHeader->config.country[0] ? tHeader->config.country[0] : '-',
   2838                       tHeader->config.country[1] ? tHeader->config.country[1] : '-',
   2839                       tHeader->config.orientation,
   2840                       tHeader->config.uiMode,
   2841                       tHeader->config.touchscreen,
   2842                       tHeader->config.density,
   2843                       tHeader->config.keyboard,
   2844                       tHeader->config.inputFlags,
   2845                       tHeader->config.navigation,
   2846                       tHeader->config.screenWidth,
   2847                       tHeader->config.screenHeight));
   2848                 tHeader->config.swapHtoD();
   2849 
   2850                 // Build the entries inside of this type.
   2851                 for (size_t ei=0; ei<N; ei++) {
   2852                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   2853                     sp<Entry> e = cl->getEntries().valueFor(config);
   2854 
   2855                     // Set the offset for this entry in its type.
   2856                     uint32_t* index = (uint32_t*)
   2857                         (((uint8_t*)data->editData())
   2858                             + typeStart + sizeof(ResTable_type));
   2859                     if (e != NULL) {
   2860                         index[ei] = htodl(data->getSize()-typeStart-typeSize);
   2861 
   2862                         // Create the entry.
   2863                         ssize_t amt = e->flatten(bundle, data, cl->getPublic());
   2864                         if (amt < 0) {
   2865                             return amt;
   2866                         }
   2867                     } else {
   2868                         index[ei] = htodl(ResTable_type::NO_ENTRY);
   2869                     }
   2870                 }
   2871 
   2872                 // Fill in the rest of the type information.
   2873                 tHeader = (ResTable_type*)
   2874                     (((uint8_t*)data->editData()) + typeStart);
   2875                 tHeader->header.size = htodl(data->getSize()-typeStart);
   2876             }
   2877         }
   2878 
   2879         // Fill in the rest of the package information.
   2880         header = (ResTable_package*)data->editData();
   2881         header->header.size = htodl(data->getSize());
   2882         header->typeStrings = htodl(typeStringsStart);
   2883         header->lastPublicType = htodl(p->getTypeStrings().size());
   2884         header->keyStrings = htodl(keyStringsStart);
   2885         header->lastPublicKey = htodl(p->getKeyStrings().size());
   2886 
   2887         flatPackages.add(data);
   2888     }
   2889 
   2890     // And now write out the final chunks.
   2891     const size_t dataStart = dest->getSize();
   2892 
   2893     {
   2894         // blah
   2895         ResTable_header header;
   2896         memset(&header, 0, sizeof(header));
   2897         header.header.type = htods(RES_TABLE_TYPE);
   2898         header.header.headerSize = htods(sizeof(header));
   2899         header.packageCount = htodl(flatPackages.size());
   2900         status_t err = dest->writeData(&header, sizeof(header));
   2901         if (err != NO_ERROR) {
   2902             fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
   2903             return err;
   2904         }
   2905     }
   2906 
   2907     ssize_t strStart = dest->getSize();
   2908     err = valueStrings.writeStringBlock(dest);
   2909     if (err != NO_ERROR) {
   2910         return err;
   2911     }
   2912 
   2913     ssize_t amt = (dest->getSize()-strStart);
   2914     strAmt += amt;
   2915     #if PRINT_STRING_METRICS
   2916     fprintf(stderr, "**** value strings: %d\n", amt);
   2917     fprintf(stderr, "**** total strings: %d\n", strAmt);
   2918     #endif
   2919 
   2920     for (pi=0; pi<flatPackages.size(); pi++) {
   2921         err = dest->writeData(flatPackages[pi]->getData(),
   2922                               flatPackages[pi]->getSize());
   2923         if (err != NO_ERROR) {
   2924             fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
   2925             return err;
   2926         }
   2927     }
   2928 
   2929     ResTable_header* header = (ResTable_header*)
   2930         (((uint8_t*)dest->getData()) + dataStart);
   2931     header->header.size = htodl(dest->getSize() - dataStart);
   2932 
   2933     NOISY(aout << "Resource table:"
   2934           << HexDump(dest->getData(), dest->getSize()) << endl);
   2935 
   2936     #if PRINT_STRING_METRICS
   2937     fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
   2938         dest->getSize(), (strAmt*100)/dest->getSize());
   2939     #endif
   2940 
   2941     return NO_ERROR;
   2942 }
   2943 
   2944 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
   2945 {
   2946     fprintf(fp,
   2947     "<!-- This file contains <public> resource definitions for all\n"
   2948     "     resources that were generated from the source data. -->\n"
   2949     "\n"
   2950     "<resources>\n");
   2951 
   2952     writePublicDefinitions(package, fp, true);
   2953     writePublicDefinitions(package, fp, false);
   2954 
   2955     fprintf(fp,
   2956     "\n"
   2957     "</resources>\n");
   2958 }
   2959 
   2960 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
   2961 {
   2962     bool didHeader = false;
   2963 
   2964     sp<Package> pkg = mPackages.valueFor(package);
   2965     if (pkg != NULL) {
   2966         const size_t NT = pkg->getOrderedTypes().size();
   2967         for (size_t i=0; i<NT; i++) {
   2968             sp<Type> t = pkg->getOrderedTypes().itemAt(i);
   2969             if (t == NULL) {
   2970                 continue;
   2971             }
   2972 
   2973             bool didType = false;
   2974 
   2975             const size_t NC = t->getOrderedConfigs().size();
   2976             for (size_t j=0; j<NC; j++) {
   2977                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
   2978                 if (c == NULL) {
   2979                     continue;
   2980                 }
   2981 
   2982                 if (c->getPublic() != pub) {
   2983                     continue;
   2984                 }
   2985 
   2986                 if (!didType) {
   2987                     fprintf(fp, "\n");
   2988                     didType = true;
   2989                 }
   2990                 if (!didHeader) {
   2991                     if (pub) {
   2992                         fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
   2993                         fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
   2994                     } else {
   2995                         fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
   2996                         fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
   2997                     }
   2998                     didHeader = true;
   2999                 }
   3000                 if (!pub) {
   3001                     const size_t NE = c->getEntries().size();
   3002                     for (size_t k=0; k<NE; k++) {
   3003                         const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
   3004                         if (pos.file != "") {
   3005                             fprintf(fp,"  <!-- Declared at %s:%d -->\n",
   3006                                     pos.file.string(), pos.line);
   3007                         }
   3008                     }
   3009                 }
   3010                 fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
   3011                         String8(t->getName()).string(),
   3012                         String8(c->getName()).string(),
   3013                         getResId(pkg, t, c->getEntryIndex()));
   3014             }
   3015         }
   3016     }
   3017 }
   3018 
   3019 ResourceTable::Item::Item(const SourcePos& _sourcePos,
   3020                           bool _isId,
   3021                           const String16& _value,
   3022                           const Vector<StringPool::entry_style_span>* _style,
   3023                           int32_t _format)
   3024     : sourcePos(_sourcePos)
   3025     , isId(_isId)
   3026     , value(_value)
   3027     , format(_format)
   3028     , bagKeyId(0)
   3029     , evaluating(false)
   3030 {
   3031     if (_style) {
   3032         style = *_style;
   3033     }
   3034 }
   3035 
   3036 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
   3037 {
   3038     if (mType == TYPE_BAG) {
   3039         return NO_ERROR;
   3040     }
   3041     if (mType == TYPE_UNKNOWN) {
   3042         mType = TYPE_BAG;
   3043         return NO_ERROR;
   3044     }
   3045     sourcePos.error("Resource entry %s is already defined as a single item.\n"
   3046                     "%s:%d: Originally defined here.\n",
   3047                     String8(mName).string(),
   3048                     mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3049     return UNKNOWN_ERROR;
   3050 }
   3051 
   3052 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
   3053                                        const String16& value,
   3054                                        const Vector<StringPool::entry_style_span>* style,
   3055                                        int32_t format,
   3056                                        const bool overwrite)
   3057 {
   3058     Item item(sourcePos, false, value, style);
   3059 
   3060     if (mType == TYPE_BAG) {
   3061         const Item& item(mBag.valueAt(0));
   3062         sourcePos.error("Resource entry %s is already defined as a bag.\n"
   3063                         "%s:%d: Originally defined here.\n",
   3064                         String8(mName).string(),
   3065                         item.sourcePos.file.string(), item.sourcePos.line);
   3066         return UNKNOWN_ERROR;
   3067     }
   3068     if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
   3069         sourcePos.error("Resource entry %s is already defined.\n"
   3070                         "%s:%d: Originally defined here.\n",
   3071                         String8(mName).string(),
   3072                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3073         return UNKNOWN_ERROR;
   3074     }
   3075 
   3076     mType = TYPE_ITEM;
   3077     mItem = item;
   3078     mItemFormat = format;
   3079     return NO_ERROR;
   3080 }
   3081 
   3082 status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
   3083                                         const String16& key, const String16& value,
   3084                                         const Vector<StringPool::entry_style_span>* style,
   3085                                         bool replace, bool isId, int32_t format)
   3086 {
   3087     status_t err = makeItABag(sourcePos);
   3088     if (err != NO_ERROR) {
   3089         return err;
   3090     }
   3091 
   3092     Item item(sourcePos, isId, value, style, format);
   3093 
   3094     // XXX NOTE: there is an error if you try to have a bag with two keys,
   3095     // one an attr and one an id, with the same name.  Not something we
   3096     // currently ever have to worry about.
   3097     ssize_t origKey = mBag.indexOfKey(key);
   3098     if (origKey >= 0) {
   3099         if (!replace) {
   3100             const Item& item(mBag.valueAt(origKey));
   3101             sourcePos.error("Resource entry %s already has bag item %s.\n"
   3102                     "%s:%d: Originally defined here.\n",
   3103                     String8(mName).string(), String8(key).string(),
   3104                     item.sourcePos.file.string(), item.sourcePos.line);
   3105             return UNKNOWN_ERROR;
   3106         }
   3107         //printf("Replacing %s with %s\n",
   3108         //       String8(mBag.valueFor(key).value).string(), String8(value).string());
   3109         mBag.replaceValueFor(key, item);
   3110     }
   3111 
   3112     mBag.add(key, item);
   3113     return NO_ERROR;
   3114 }
   3115 
   3116 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
   3117 {
   3118     status_t err = makeItABag(sourcePos);
   3119     if (err != NO_ERROR) {
   3120         return err;
   3121     }
   3122 
   3123     mBag.clear();
   3124     return NO_ERROR;
   3125 }
   3126 
   3127 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
   3128                                                   const String16& package)
   3129 {
   3130     const String16 attr16("attr");
   3131     const String16 id16("id");
   3132     const size_t N = mBag.size();
   3133     for (size_t i=0; i<N; i++) {
   3134         const String16& key = mBag.keyAt(i);
   3135         const Item& it = mBag.valueAt(i);
   3136         if (it.isId) {
   3137             if (!table->hasBagOrEntry(key, &id16, &package)) {
   3138                 String16 value("false");
   3139                 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
   3140                                                id16, key, value);
   3141                 if (err != NO_ERROR) {
   3142                     return err;
   3143                 }
   3144             }
   3145         } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
   3146 
   3147 #if 1
   3148 //             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
   3149 //                     String8(key).string());
   3150 //             const Item& item(mBag.valueAt(i));
   3151 //             fprintf(stderr, "Referenced from file %s line %d\n",
   3152 //                     item.sourcePos.file.string(), item.sourcePos.line);
   3153 //             return UNKNOWN_ERROR;
   3154 #else
   3155             char numberStr[16];
   3156             sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
   3157             status_t err = table->addBag(SourcePos("<generated>", 0), package,
   3158                                          attr16, key, String16(""),
   3159                                          String16("^type"),
   3160                                          String16(numberStr), NULL, NULL);
   3161             if (err != NO_ERROR) {
   3162                 return err;
   3163             }
   3164 #endif
   3165         }
   3166     }
   3167     return NO_ERROR;
   3168 }
   3169 
   3170 status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
   3171                                                  const String16& package)
   3172 {
   3173     bool hasErrors = false;
   3174 
   3175     if (mType == TYPE_BAG) {
   3176         const char* errorMsg;
   3177         const String16 style16("style");
   3178         const String16 attr16("attr");
   3179         const String16 id16("id");
   3180         mParentId = 0;
   3181         if (mParent.size() > 0) {
   3182             mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
   3183             if (mParentId == 0) {
   3184                 mPos.error("Error retrieving parent for item: %s '%s'.\n",
   3185                         errorMsg, String8(mParent).string());
   3186                 hasErrors = true;
   3187             }
   3188         }
   3189         const size_t N = mBag.size();
   3190         for (size_t i=0; i<N; i++) {
   3191             const String16& key = mBag.keyAt(i);
   3192             Item& it = mBag.editValueAt(i);
   3193             it.bagKeyId = table->getResId(key,
   3194                     it.isId ? &id16 : &attr16, NULL, &errorMsg);
   3195             //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
   3196             if (it.bagKeyId == 0) {
   3197                 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
   3198                         String8(it.isId ? id16 : attr16).string(),
   3199                         String8(key).string());
   3200                 hasErrors = true;
   3201             }
   3202         }
   3203     }
   3204     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   3205 }
   3206 
   3207 status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
   3208 {
   3209     if (mType == TYPE_ITEM) {
   3210         Item& it = mItem;
   3211         AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
   3212         if (!table->stringToValue(&it.parsedValue, strings,
   3213                                   it.value, false, true, 0,
   3214                                   &it.style, NULL, &ac, mItemFormat)) {
   3215             return UNKNOWN_ERROR;
   3216         }
   3217     } else if (mType == TYPE_BAG) {
   3218         const size_t N = mBag.size();
   3219         for (size_t i=0; i<N; i++) {
   3220             const String16& key = mBag.keyAt(i);
   3221             Item& it = mBag.editValueAt(i);
   3222             AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
   3223             if (!table->stringToValue(&it.parsedValue, strings,
   3224                                       it.value, false, true, it.bagKeyId,
   3225                                       &it.style, NULL, &ac, it.format)) {
   3226                 return UNKNOWN_ERROR;
   3227             }
   3228         }
   3229     } else {
   3230         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3231                    String8(mName).string());
   3232         return UNKNOWN_ERROR;
   3233     }
   3234     return NO_ERROR;
   3235 }
   3236 
   3237 ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
   3238 {
   3239     size_t amt = 0;
   3240     ResTable_entry header;
   3241     memset(&header, 0, sizeof(header));
   3242     header.size = htods(sizeof(header));
   3243     const type ty = this != NULL ? mType : TYPE_ITEM;
   3244     if (this != NULL) {
   3245         if (ty == TYPE_BAG) {
   3246             header.flags |= htods(header.FLAG_COMPLEX);
   3247         }
   3248         if (isPublic) {
   3249             header.flags |= htods(header.FLAG_PUBLIC);
   3250         }
   3251         header.key.index = htodl(mNameIndex);
   3252     }
   3253     if (ty != TYPE_BAG) {
   3254         status_t err = data->writeData(&header, sizeof(header));
   3255         if (err != NO_ERROR) {
   3256             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3257             return err;
   3258         }
   3259 
   3260         const Item& it = mItem;
   3261         Res_value par;
   3262         memset(&par, 0, sizeof(par));
   3263         par.size = htods(it.parsedValue.size);
   3264         par.dataType = it.parsedValue.dataType;
   3265         par.res0 = it.parsedValue.res0;
   3266         par.data = htodl(it.parsedValue.data);
   3267         #if 0
   3268         printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
   3269                String8(mName).string(), it.parsedValue.dataType,
   3270                it.parsedValue.data, par.res0);
   3271         #endif
   3272         err = data->writeData(&par, it.parsedValue.size);
   3273         if (err != NO_ERROR) {
   3274             fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3275             return err;
   3276         }
   3277         amt += it.parsedValue.size;
   3278     } else {
   3279         size_t N = mBag.size();
   3280         size_t i;
   3281         // Create correct ordering of items.
   3282         KeyedVector<uint32_t, const Item*> items;
   3283         for (i=0; i<N; i++) {
   3284             const Item& it = mBag.valueAt(i);
   3285             items.add(it.bagKeyId, &it);
   3286         }
   3287         N = items.size();
   3288 
   3289         ResTable_map_entry mapHeader;
   3290         memcpy(&mapHeader, &header, sizeof(header));
   3291         mapHeader.size = htods(sizeof(mapHeader));
   3292         mapHeader.parent.ident = htodl(mParentId);
   3293         mapHeader.count = htodl(N);
   3294         status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
   3295         if (err != NO_ERROR) {
   3296             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3297             return err;
   3298         }
   3299 
   3300         for (i=0; i<N; i++) {
   3301             const Item& it = *items.valueAt(i);
   3302             ResTable_map map;
   3303             map.name.ident = htodl(it.bagKeyId);
   3304             map.value.size = htods(it.parsedValue.size);
   3305             map.value.dataType = it.parsedValue.dataType;
   3306             map.value.res0 = it.parsedValue.res0;
   3307             map.value.data = htodl(it.parsedValue.data);
   3308             err = data->writeData(&map, sizeof(map));
   3309             if (err != NO_ERROR) {
   3310                 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3311                 return err;
   3312             }
   3313             amt += sizeof(map);
   3314         }
   3315     }
   3316     return amt;
   3317 }
   3318 
   3319 void ResourceTable::ConfigList::appendComment(const String16& comment,
   3320                                               bool onlyIfEmpty)
   3321 {
   3322     if (comment.size() <= 0) {
   3323         return;
   3324     }
   3325     if (onlyIfEmpty && mComment.size() > 0) {
   3326         return;
   3327     }
   3328     if (mComment.size() > 0) {
   3329         mComment.append(String16("\n"));
   3330     }
   3331     mComment.append(comment);
   3332 }
   3333 
   3334 void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
   3335 {
   3336     if (comment.size() <= 0) {
   3337         return;
   3338     }
   3339     if (mTypeComment.size() > 0) {
   3340         mTypeComment.append(String16("\n"));
   3341     }
   3342     mTypeComment.append(comment);
   3343 }
   3344 
   3345 status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
   3346                                         const String16& name,
   3347                                         const uint32_t ident)
   3348 {
   3349     #if 0
   3350     int32_t entryIdx = Res_GETENTRY(ident);
   3351     if (entryIdx < 0) {
   3352         sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
   3353                 String8(mName).string(), String8(name).string(), ident);
   3354         return UNKNOWN_ERROR;
   3355     }
   3356     #endif
   3357 
   3358     int32_t typeIdx = Res_GETTYPE(ident);
   3359     if (typeIdx >= 0) {
   3360         typeIdx++;
   3361         if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
   3362             sourcePos.error("Public resource %s/%s has conflicting type codes for its"
   3363                     " public identifiers (0x%x vs 0x%x).\n",
   3364                     String8(mName).string(), String8(name).string(),
   3365                     mPublicIndex, typeIdx);
   3366             return UNKNOWN_ERROR;
   3367         }
   3368         mPublicIndex = typeIdx;
   3369     }
   3370 
   3371     if (mFirstPublicSourcePos == NULL) {
   3372         mFirstPublicSourcePos = new SourcePos(sourcePos);
   3373     }
   3374 
   3375     if (mPublic.indexOfKey(name) < 0) {
   3376         mPublic.add(name, Public(sourcePos, String16(), ident));
   3377     } else {
   3378         Public& p = mPublic.editValueFor(name);
   3379         if (p.ident != ident) {
   3380             sourcePos.error("Public resource %s/%s has conflicting public identifiers"
   3381                     " (0x%08x vs 0x%08x).\n"
   3382                     "%s:%d: Originally defined here.\n",
   3383                     String8(mName).string(), String8(name).string(), p.ident, ident,
   3384                     p.sourcePos.file.string(), p.sourcePos.line);
   3385             return UNKNOWN_ERROR;
   3386         }
   3387     }
   3388 
   3389     return NO_ERROR;
   3390 }
   3391 
   3392 void ResourceTable::Type::canAddEntry(const String16& name)
   3393 {
   3394     mCanAddEntries.add(name);
   3395 }
   3396 
   3397 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
   3398                                                        const SourcePos& sourcePos,
   3399                                                        const ResTable_config* config,
   3400                                                        bool doSetIndex,
   3401                                                        bool overlay,
   3402                                                        bool autoAddOverlay)
   3403 {
   3404     int pos = -1;
   3405     sp<ConfigList> c = mConfigs.valueFor(entry);
   3406     if (c == NULL) {
   3407         if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
   3408             sourcePos.error("Resource at %s appears in overlay but not"
   3409                             " in the base package; use <add-resource> to add.\n",
   3410                             String8(entry).string());
   3411             return NULL;
   3412         }
   3413         c = new ConfigList(entry, sourcePos);
   3414         mConfigs.add(entry, c);
   3415         pos = (int)mOrderedConfigs.size();
   3416         mOrderedConfigs.add(c);
   3417         if (doSetIndex) {
   3418             c->setEntryIndex(pos);
   3419         }
   3420     }
   3421 
   3422     ConfigDescription cdesc;
   3423     if (config) cdesc = *config;
   3424 
   3425     sp<Entry> e = c->getEntries().valueFor(cdesc);
   3426     if (e == NULL) {
   3427         if (config != NULL) {
   3428             NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
   3429                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
   3430                       sourcePos.file.string(), sourcePos.line,
   3431                       config->mcc, config->mnc,
   3432                       config->language[0] ? config->language[0] : '-',
   3433                       config->language[1] ? config->language[1] : '-',
   3434                       config->country[0] ? config->country[0] : '-',
   3435                       config->country[1] ? config->country[1] : '-',
   3436                       config->orientation,
   3437                       config->touchscreen,
   3438                       config->density,
   3439                       config->keyboard,
   3440                       config->inputFlags,
   3441                       config->navigation,
   3442                       config->screenWidth,
   3443                       config->screenHeight));
   3444         } else {
   3445             NOISY(printf("New entry at %s:%d: NULL config\n",
   3446                       sourcePos.file.string(), sourcePos.line));
   3447         }
   3448         e = new Entry(entry, sourcePos);
   3449         c->addEntry(cdesc, e);
   3450         /*
   3451         if (doSetIndex) {
   3452             if (pos < 0) {
   3453                 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
   3454                     if (mOrderedConfigs[pos] == c) {
   3455                         break;
   3456                     }
   3457                 }
   3458                 if (pos >= (int)mOrderedConfigs.size()) {
   3459                     sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
   3460                     return NULL;
   3461                 }
   3462             }
   3463             e->setEntryIndex(pos);
   3464         }
   3465         */
   3466     }
   3467 
   3468     mUniqueConfigs.add(cdesc);
   3469 
   3470     return e;
   3471 }
   3472 
   3473 status_t ResourceTable::Type::applyPublicEntryOrder()
   3474 {
   3475     size_t N = mOrderedConfigs.size();
   3476     Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
   3477     bool hasError = false;
   3478 
   3479     size_t i;
   3480     for (i=0; i<N; i++) {
   3481         mOrderedConfigs.replaceAt(NULL, i);
   3482     }
   3483 
   3484     const size_t NP = mPublic.size();
   3485     //printf("Ordering %d configs from %d public defs\n", N, NP);
   3486     size_t j;
   3487     for (j=0; j<NP; j++) {
   3488         const String16& name = mPublic.keyAt(j);
   3489         const Public& p = mPublic.valueAt(j);
   3490         int32_t idx = Res_GETENTRY(p.ident);
   3491         //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
   3492         //       String8(mName).string(), String8(name).string(), p.ident, N);
   3493         bool found = false;
   3494         for (i=0; i<N; i++) {
   3495             sp<ConfigList> e = origOrder.itemAt(i);
   3496             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
   3497             if (e->getName() == name) {
   3498                 if (idx >= (int32_t)mOrderedConfigs.size()) {
   3499                     p.sourcePos.error("Public entry identifier 0x%x entry index "
   3500                             "is larger than available symbols (index %d, total symbols %d).\n",
   3501                             p.ident, idx, mOrderedConfigs.size());
   3502                     hasError = true;
   3503                 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
   3504                     e->setPublic(true);
   3505                     e->setPublicSourcePos(p.sourcePos);
   3506                     mOrderedConfigs.replaceAt(e, idx);
   3507                     origOrder.removeAt(i);
   3508                     N--;
   3509                     found = true;
   3510                     break;
   3511                 } else {
   3512                     sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
   3513 
   3514                     p.sourcePos.error("Multiple entry names declared for public entry"
   3515                             " identifier 0x%x in type %s (%s vs %s).\n"
   3516                             "%s:%d: Originally defined here.",
   3517                             idx+1, String8(mName).string(),
   3518                             String8(oe->getName()).string(),
   3519                             String8(name).string(),
   3520                             oe->getPublicSourcePos().file.string(),
   3521                             oe->getPublicSourcePos().line);
   3522                     hasError = true;
   3523                 }
   3524             }
   3525         }
   3526 
   3527         if (!found) {
   3528             p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
   3529                     String8(mName).string(), String8(name).string());
   3530             hasError = true;
   3531         }
   3532     }
   3533 
   3534     //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
   3535 
   3536     if (N != origOrder.size()) {
   3537         printf("Internal error: remaining private symbol count mismatch\n");
   3538         N = origOrder.size();
   3539     }
   3540 
   3541     j = 0;
   3542     for (i=0; i<N; i++) {
   3543         sp<ConfigList> e = origOrder.itemAt(i);
   3544         // There will always be enough room for the remaining entries.
   3545         while (mOrderedConfigs.itemAt(j) != NULL) {
   3546             j++;
   3547         }
   3548         mOrderedConfigs.replaceAt(e, j);
   3549         j++;
   3550     }
   3551 
   3552     return hasError ? UNKNOWN_ERROR : NO_ERROR;
   3553 }
   3554 
   3555 ResourceTable::Package::Package(const String16& name, ssize_t includedId)
   3556     : mName(name), mIncludedId(includedId),
   3557       mTypeStringsMapping(0xffffffff),
   3558       mKeyStringsMapping(0xffffffff)
   3559 {
   3560 }
   3561 
   3562 sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
   3563                                                         const SourcePos& sourcePos,
   3564                                                         bool doSetIndex)
   3565 {
   3566     sp<Type> t = mTypes.valueFor(type);
   3567     if (t == NULL) {
   3568         t = new Type(type, sourcePos);
   3569         mTypes.add(type, t);
   3570         mOrderedTypes.add(t);
   3571         if (doSetIndex) {
   3572             // For some reason the type's index is set to one plus the index
   3573             // in the mOrderedTypes list, rather than just the index.
   3574             t->setIndex(mOrderedTypes.size());
   3575         }
   3576     }
   3577     return t;
   3578 }
   3579 
   3580 status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
   3581 {
   3582     mTypeStringsData = data;
   3583     status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
   3584     if (err != NO_ERROR) {
   3585         fprintf(stderr, "ERROR: Type string data is corrupt!\n");
   3586     }
   3587     return err;
   3588 }
   3589 
   3590 status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
   3591 {
   3592     mKeyStringsData = data;
   3593     status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
   3594     if (err != NO_ERROR) {
   3595         fprintf(stderr, "ERROR: Key string data is corrupt!\n");
   3596     }
   3597     return err;
   3598 }
   3599 
   3600 status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
   3601                                             ResStringPool* strings,
   3602                                             DefaultKeyedVector<String16, uint32_t>* mappings)
   3603 {
   3604     if (data->getData() == NULL) {
   3605         return UNKNOWN_ERROR;
   3606     }
   3607 
   3608     NOISY(aout << "Setting restable string pool: "
   3609           << HexDump(data->getData(), data->getSize()) << endl);
   3610 
   3611     status_t err = strings->setTo(data->getData(), data->getSize());
   3612     if (err == NO_ERROR) {
   3613         const size_t N = strings->size();
   3614         for (size_t i=0; i<N; i++) {
   3615             size_t len;
   3616             mappings->add(String16(strings->stringAt(i, &len)), i);
   3617         }
   3618     }
   3619     return err;
   3620 }
   3621 
   3622 status_t ResourceTable::Package::applyPublicTypeOrder()
   3623 {
   3624     size_t N = mOrderedTypes.size();
   3625     Vector<sp<Type> > origOrder(mOrderedTypes);
   3626 
   3627     size_t i;
   3628     for (i=0; i<N; i++) {
   3629         mOrderedTypes.replaceAt(NULL, i);
   3630     }
   3631 
   3632     for (i=0; i<N; i++) {
   3633         sp<Type> t = origOrder.itemAt(i);
   3634         int32_t idx = t->getPublicIndex();
   3635         if (idx > 0) {
   3636             idx--;
   3637             while (idx >= (int32_t)mOrderedTypes.size()) {
   3638                 mOrderedTypes.add();
   3639             }
   3640             if (mOrderedTypes.itemAt(idx) != NULL) {
   3641                 sp<Type> ot = mOrderedTypes.itemAt(idx);
   3642                 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
   3643                         " identifier 0x%x (%s vs %s).\n"
   3644                         "%s:%d: Originally defined here.",
   3645                         idx, String8(ot->getName()).string(),
   3646                         String8(t->getName()).string(),
   3647                         ot->getFirstPublicSourcePos().file.string(),
   3648                         ot->getFirstPublicSourcePos().line);
   3649                 return UNKNOWN_ERROR;
   3650             }
   3651             mOrderedTypes.replaceAt(t, idx);
   3652             origOrder.removeAt(i);
   3653             i--;
   3654             N--;
   3655         }
   3656     }
   3657 
   3658     size_t j=0;
   3659     for (i=0; i<N; i++) {
   3660         sp<Type> t = origOrder.itemAt(i);
   3661         // There will always be enough room for the remaining types.
   3662         while (mOrderedTypes.itemAt(j) != NULL) {
   3663             j++;
   3664         }
   3665         mOrderedTypes.replaceAt(t, j);
   3666     }
   3667 
   3668     return NO_ERROR;
   3669 }
   3670 
   3671 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
   3672 {
   3673     sp<Package> p = mPackages.valueFor(package);
   3674     if (p == NULL) {
   3675         if (mIsAppPackage) {
   3676             if (mHaveAppPackage) {
   3677                 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
   3678                                 "Use -x to create extended resources.\n");
   3679                 return NULL;
   3680             }
   3681             mHaveAppPackage = true;
   3682             p = new Package(package, 127);
   3683         } else {
   3684             p = new Package(package, mNextPackageId);
   3685         }
   3686         //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
   3687         //       String8(package).string(), p->getAssignedId());
   3688         mPackages.add(package, p);
   3689         mOrderedPackages.add(p);
   3690         mNextPackageId++;
   3691     }
   3692     return p;
   3693 }
   3694 
   3695 sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
   3696                                                const String16& type,
   3697                                                const SourcePos& sourcePos,
   3698                                                bool doSetIndex)
   3699 {
   3700     sp<Package> p = getPackage(package);
   3701     if (p == NULL) {
   3702         return NULL;
   3703     }
   3704     return p->getType(type, sourcePos, doSetIndex);
   3705 }
   3706 
   3707 sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
   3708                                                  const String16& type,
   3709                                                  const String16& name,
   3710                                                  const SourcePos& sourcePos,
   3711                                                  bool overlay,
   3712                                                  const ResTable_config* config,
   3713                                                  bool doSetIndex)
   3714 {
   3715     sp<Type> t = getType(package, type, sourcePos, doSetIndex);
   3716     if (t == NULL) {
   3717         return NULL;
   3718     }
   3719     return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
   3720 }
   3721 
   3722 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
   3723                                                        const ResTable_config* config) const
   3724 {
   3725     int pid = Res_GETPACKAGE(resID)+1;
   3726     const size_t N = mOrderedPackages.size();
   3727     size_t i;
   3728     sp<Package> p;
   3729     for (i=0; i<N; i++) {
   3730         sp<Package> check = mOrderedPackages[i];
   3731         if (check->getAssignedId() == pid) {
   3732             p = check;
   3733             break;
   3734         }
   3735 
   3736     }
   3737     if (p == NULL) {
   3738         fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
   3739         return NULL;
   3740     }
   3741 
   3742     int tid = Res_GETTYPE(resID);
   3743     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
   3744         fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
   3745         return NULL;
   3746     }
   3747     sp<Type> t = p->getOrderedTypes()[tid];
   3748 
   3749     int eid = Res_GETENTRY(resID);
   3750     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
   3751         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   3752         return NULL;
   3753     }
   3754 
   3755     sp<ConfigList> c = t->getOrderedConfigs()[eid];
   3756     if (c == NULL) {
   3757         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   3758         return NULL;
   3759     }
   3760 
   3761     ConfigDescription cdesc;
   3762     if (config) cdesc = *config;
   3763     sp<Entry> e = c->getEntries().valueFor(cdesc);
   3764     if (c == NULL) {
   3765         fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
   3766         return NULL;
   3767     }
   3768 
   3769     return e;
   3770 }
   3771 
   3772 const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
   3773 {
   3774     sp<const Entry> e = getEntry(resID);
   3775     if (e == NULL) {
   3776         return NULL;
   3777     }
   3778 
   3779     const size_t N = e->getBag().size();
   3780     for (size_t i=0; i<N; i++) {
   3781         const Item& it = e->getBag().valueAt(i);
   3782         if (it.bagKeyId == 0) {
   3783             fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
   3784                     String8(e->getName()).string(),
   3785                     String8(e->getBag().keyAt(i)).string());
   3786         }
   3787         if (it.bagKeyId == attrID) {
   3788             return &it;
   3789         }
   3790     }
   3791 
   3792     return NULL;
   3793 }
   3794 
   3795 bool ResourceTable::getItemValue(
   3796     uint32_t resID, uint32_t attrID, Res_value* outValue)
   3797 {
   3798     const Item* item = getItem(resID, attrID);
   3799 
   3800     bool res = false;
   3801     if (item != NULL) {
   3802         if (item->evaluating) {
   3803             sp<const Entry> e = getEntry(resID);
   3804             const size_t N = e->getBag().size();
   3805             size_t i;
   3806             for (i=0; i<N; i++) {
   3807                 if (&e->getBag().valueAt(i) == item) {
   3808                     break;
   3809                 }
   3810             }
   3811             fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
   3812                     String8(e->getName()).string(),
   3813                     String8(e->getBag().keyAt(i)).string());
   3814             return false;
   3815         }
   3816         item->evaluating = true;
   3817         res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
   3818         NOISY(
   3819             if (res) {
   3820                 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
   3821                        resID, attrID, String8(getEntry(resID)->getName()).string(),
   3822                        outValue->dataType, outValue->data);
   3823             } else {
   3824                 printf("getItemValue of #%08x[#%08x]: failed\n",
   3825                        resID, attrID);
   3826             }
   3827         );
   3828         item->evaluating = false;
   3829     }
   3830     return res;
   3831 }
   3832