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