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