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