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)
2051{
2052    String16 finalStr;
2053
2054    bool res = true;
2055    if (style == NULL || style->size() == 0) {
2056        // Text is not styled so it can be any type...  let's figure it out.
2057        res = mAssets->getIncludedResources()
2058            .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
2059                            coerceType, attrID, NULL, &mAssetsPackage, this,
2060                           accessorCookie, attrType);
2061    } else {
2062        // Styled text can only be a string, and while collecting the style
2063        // information we have already processed that string!
2064        outValue->size = sizeof(Res_value);
2065        outValue->res0 = 0;
2066        outValue->dataType = outValue->TYPE_STRING;
2067        outValue->data = 0;
2068        finalStr = str;
2069    }
2070
2071    if (!res) {
2072        return false;
2073    }
2074
2075    if (outValue->dataType == outValue->TYPE_STRING) {
2076        // Should do better merging styles.
2077        if (pool) {
2078            if (style != NULL && style->size() > 0) {
2079                outValue->data = pool->add(finalStr, *style);
2080            } else {
2081                outValue->data = pool->add(finalStr, true);
2082            }
2083        } else {
2084            // Caller will fill this in later.
2085            outValue->data = 0;
2086        }
2087
2088        if (outStr) {
2089            *outStr = finalStr;
2090        }
2091
2092    }
2093
2094    return true;
2095}
2096
2097uint32_t ResourceTable::getCustomResource(
2098    const String16& package, const String16& type, const String16& name) const
2099{
2100    //printf("getCustomResource: %s %s %s\n", String8(package).string(),
2101    //       String8(type).string(), String8(name).string());
2102    sp<Package> p = mPackages.valueFor(package);
2103    if (p == NULL) return 0;
2104    sp<Type> t = p->getTypes().valueFor(type);
2105    if (t == NULL) return 0;
2106    sp<ConfigList> c =  t->getConfigs().valueFor(name);
2107    if (c == NULL) return 0;
2108    int32_t ei = c->getEntryIndex();
2109    if (ei < 0) return 0;
2110    return getResId(p, t, ei);
2111}
2112
2113uint32_t ResourceTable::getCustomResourceWithCreation(
2114        const String16& package, const String16& type, const String16& name,
2115        const bool createIfNotFound)
2116{
2117    uint32_t resId = getCustomResource(package, type, name);
2118    if (resId != 0 || !createIfNotFound) {
2119        return resId;
2120    }
2121    String16 value("false");
2122
2123    status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
2124    if (status == NO_ERROR) {
2125        resId = getResId(package, type, name);
2126        return resId;
2127    }
2128    return 0;
2129}
2130
2131uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
2132{
2133    return origPackage;
2134}
2135
2136bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
2137{
2138    //printf("getAttributeType #%08x\n", attrID);
2139    Res_value value;
2140    if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
2141        //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
2142        //       String8(getEntry(attrID)->getName()).string(), value.data);
2143        *outType = value.data;
2144        return true;
2145    }
2146    return false;
2147}
2148
2149bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
2150{
2151    //printf("getAttributeMin #%08x\n", attrID);
2152    Res_value value;
2153    if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
2154        *outMin = value.data;
2155        return true;
2156    }
2157    return false;
2158}
2159
2160bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
2161{
2162    //printf("getAttributeMax #%08x\n", attrID);
2163    Res_value value;
2164    if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
2165        *outMax = value.data;
2166        return true;
2167    }
2168    return false;
2169}
2170
2171uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
2172{
2173    //printf("getAttributeL10N #%08x\n", attrID);
2174    Res_value value;
2175    if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
2176        return value.data;
2177    }
2178    return ResTable_map::L10N_NOT_REQUIRED;
2179}
2180
2181bool ResourceTable::getLocalizationSetting()
2182{
2183    return mBundle->getRequireLocalization();
2184}
2185
2186void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
2187{
2188    if (accessorCookie != NULL && fmt != NULL) {
2189        AccessorCookie* ac = (AccessorCookie*)accessorCookie;
2190        int retval=0;
2191        char buf[1024];
2192        va_list ap;
2193        va_start(ap, fmt);
2194        retval = vsnprintf(buf, sizeof(buf), fmt, ap);
2195        va_end(ap);
2196        ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
2197                            buf, ac->attr.string(), ac->value.string());
2198    }
2199}
2200
2201bool ResourceTable::getAttributeKeys(
2202    uint32_t attrID, Vector<String16>* outKeys)
2203{
2204    sp<const Entry> e = getEntry(attrID);
2205    if (e != NULL) {
2206        const size_t N = e->getBag().size();
2207        for (size_t i=0; i<N; i++) {
2208            const String16& key = e->getBag().keyAt(i);
2209            if (key.size() > 0 && key.string()[0] != '^') {
2210                outKeys->add(key);
2211            }
2212        }
2213        return true;
2214    }
2215    return false;
2216}
2217
2218bool ResourceTable::getAttributeEnum(
2219    uint32_t attrID, const char16_t* name, size_t nameLen,
2220    Res_value* outValue)
2221{
2222    //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2223    String16 nameStr(name, nameLen);
2224    sp<const Entry> e = getEntry(attrID);
2225    if (e != NULL) {
2226        const size_t N = e->getBag().size();
2227        for (size_t i=0; i<N; i++) {
2228            //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2229            //       String8(e->getBag().keyAt(i)).string());
2230            if (e->getBag().keyAt(i) == nameStr) {
2231                return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2232            }
2233        }
2234    }
2235    return false;
2236}
2237
2238bool ResourceTable::getAttributeFlags(
2239    uint32_t attrID, const char16_t* name, size_t nameLen,
2240    Res_value* outValue)
2241{
2242    outValue->dataType = Res_value::TYPE_INT_HEX;
2243    outValue->data = 0;
2244
2245    //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2246    String16 nameStr(name, nameLen);
2247    sp<const Entry> e = getEntry(attrID);
2248    if (e != NULL) {
2249        const size_t N = e->getBag().size();
2250
2251        const char16_t* end = name + nameLen;
2252        const char16_t* pos = name;
2253        bool failed = false;
2254        while (pos < end && !failed) {
2255            const char16_t* start = pos;
2256            end++;
2257            while (pos < end && *pos != '|') {
2258                pos++;
2259            }
2260
2261            String16 nameStr(start, pos-start);
2262            size_t i;
2263            for (i=0; i<N; i++) {
2264                //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2265                //       String8(e->getBag().keyAt(i)).string());
2266                if (e->getBag().keyAt(i) == nameStr) {
2267                    Res_value val;
2268                    bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2269                    if (!got) {
2270                        return false;
2271                    }
2272                    //printf("Got value: 0x%08x\n", val.data);
2273                    outValue->data |= val.data;
2274                    break;
2275                }
2276            }
2277
2278            if (i >= N) {
2279                // Didn't find this flag identifier.
2280                return false;
2281            }
2282            if (pos < end) {
2283                pos++;
2284            }
2285        }
2286
2287        return true;
2288    }
2289    return false;
2290}
2291
2292status_t ResourceTable::assignResourceIds()
2293{
2294    const size_t N = mOrderedPackages.size();
2295    size_t pi;
2296    status_t firstError = NO_ERROR;
2297
2298    // First generate all bag attributes and assign indices.
2299    for (pi=0; pi<N; pi++) {
2300        sp<Package> p = mOrderedPackages.itemAt(pi);
2301        if (p == NULL || p->getTypes().size() == 0) {
2302            // Empty, skip!
2303            continue;
2304        }
2305
2306        status_t err = p->applyPublicTypeOrder();
2307        if (err != NO_ERROR && firstError == NO_ERROR) {
2308            firstError = err;
2309        }
2310
2311        // Generate attributes...
2312        const size_t N = p->getOrderedTypes().size();
2313        size_t ti;
2314        for (ti=0; ti<N; ti++) {
2315            sp<Type> t = p->getOrderedTypes().itemAt(ti);
2316            if (t == NULL) {
2317                continue;
2318            }
2319            const size_t N = t->getOrderedConfigs().size();
2320            for (size_t ci=0; ci<N; ci++) {
2321                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2322                if (c == NULL) {
2323                    continue;
2324                }
2325                const size_t N = c->getEntries().size();
2326                for (size_t ei=0; ei<N; ei++) {
2327                    sp<Entry> e = c->getEntries().valueAt(ei);
2328                    if (e == NULL) {
2329                        continue;
2330                    }
2331                    status_t err = e->generateAttributes(this, p->getName());
2332                    if (err != NO_ERROR && firstError == NO_ERROR) {
2333                        firstError = err;
2334                    }
2335                }
2336            }
2337        }
2338
2339        const SourcePos unknown(String8("????"), 0);
2340        sp<Type> attr = p->getType(String16("attr"), unknown);
2341
2342        // Assign indices...
2343        for (ti=0; ti<N; ti++) {
2344            sp<Type> t = p->getOrderedTypes().itemAt(ti);
2345            if (t == NULL) {
2346                continue;
2347            }
2348            err = t->applyPublicEntryOrder();
2349            if (err != NO_ERROR && firstError == NO_ERROR) {
2350                firstError = err;
2351            }
2352
2353            const size_t N = t->getOrderedConfigs().size();
2354            t->setIndex(ti+1);
2355
2356            LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2357                                "First type is not attr!");
2358
2359            for (size_t ei=0; ei<N; ei++) {
2360                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2361                if (c == NULL) {
2362                    continue;
2363                }
2364                c->setEntryIndex(ei);
2365            }
2366        }
2367
2368        // Assign resource IDs to keys in bags...
2369        for (ti=0; ti<N; ti++) {
2370            sp<Type> t = p->getOrderedTypes().itemAt(ti);
2371            if (t == NULL) {
2372                continue;
2373            }
2374            const size_t N = t->getOrderedConfigs().size();
2375            for (size_t ci=0; ci<N; ci++) {
2376                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2377                //printf("Ordered config #%d: %p\n", ci, c.get());
2378                const size_t N = c->getEntries().size();
2379                for (size_t ei=0; ei<N; ei++) {
2380                    sp<Entry> e = c->getEntries().valueAt(ei);
2381                    if (e == NULL) {
2382                        continue;
2383                    }
2384                    status_t err = e->assignResourceIds(this, p->getName());
2385                    if (err != NO_ERROR && firstError == NO_ERROR) {
2386                        firstError = err;
2387                    }
2388                }
2389            }
2390        }
2391    }
2392    return firstError;
2393}
2394
2395status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
2396    const size_t N = mOrderedPackages.size();
2397    size_t pi;
2398
2399    for (pi=0; pi<N; pi++) {
2400        sp<Package> p = mOrderedPackages.itemAt(pi);
2401        if (p->getTypes().size() == 0) {
2402            // Empty, skip!
2403            continue;
2404        }
2405
2406        const size_t N = p->getOrderedTypes().size();
2407        size_t ti;
2408
2409        for (ti=0; ti<N; ti++) {
2410            sp<Type> t = p->getOrderedTypes().itemAt(ti);
2411            if (t == NULL) {
2412                continue;
2413            }
2414            const size_t N = t->getOrderedConfigs().size();
2415            sp<AaptSymbols> typeSymbols;
2416            typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2417            for (size_t ci=0; ci<N; ci++) {
2418                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2419                if (c == NULL) {
2420                    continue;
2421                }
2422                uint32_t rid = getResId(p, t, ci);
2423                if (rid == 0) {
2424                    return UNKNOWN_ERROR;
2425                }
2426                if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
2427                    typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2428
2429                    String16 comment(c->getComment());
2430                    typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
2431                    //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
2432                    //     String8(comment).string());
2433                    comment = c->getTypeComment();
2434                    typeSymbols->appendTypeComment(String8(c->getName()), comment);
2435                } else {
2436#if 0
2437                    printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
2438                           Res_GETPACKAGE(rid), p->getAssignedId());
2439#endif
2440                }
2441            }
2442        }
2443    }
2444    return NO_ERROR;
2445}
2446
2447
2448void
2449ResourceTable::addLocalization(const String16& name, const String8& locale)
2450{
2451    mLocalizations[name].insert(locale);
2452}
2453
2454
2455/*!
2456 * Flag various sorts of localization problems.  '+' indicates checks already implemented;
2457 * '-' indicates checks that will be implemented in the future.
2458 *
2459 * + A localized string for which no default-locale version exists => warning
2460 * + A string for which no version in an explicitly-requested locale exists => warning
2461 * + A localized translation of an translateable="false" string => warning
2462 * - A localized string not provided in every locale used by the table
2463 */
2464status_t
2465ResourceTable::validateLocalizations(void)
2466{
2467    status_t err = NO_ERROR;
2468    const String8 defaultLocale;
2469
2470    // For all strings...
2471    for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
2472         nameIter != mLocalizations.end();
2473         nameIter++) {
2474        const set<String8>& configSet = nameIter->second;   // naming convenience
2475
2476        // Look for strings with no default localization
2477        if (configSet.count(defaultLocale) == 0) {
2478            fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
2479                    String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
2480            for (set<String8>::const_iterator locales = configSet.begin();
2481                 locales != configSet.end();
2482                 locales++) {
2483                fprintf(stdout, " %s", (*locales).string());
2484            }
2485            fprintf(stdout, "\n");
2486            // !!! TODO: throw an error here in some circumstances
2487        }
2488
2489        // Check that all requested localizations are present for this string
2490        if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
2491            const char* allConfigs = mBundle->getConfigurations();
2492            const char* start = allConfigs;
2493            const char* comma;
2494
2495            do {
2496                String8 config;
2497                comma = strchr(start, ',');
2498                if (comma != NULL) {
2499                    config.setTo(start, comma - start);
2500                    start = comma + 1;
2501                } else {
2502                    config.setTo(start);
2503                }
2504
2505                // don't bother with the pseudolocale "zz_ZZ"
2506                if (config != "zz_ZZ") {
2507                    if (configSet.find(config) == configSet.end()) {
2508                        // okay, no specific localization found.  it's possible that we are
2509                        // requiring a specific regional localization [e.g. de_DE] but there is an
2510                        // available string in the generic language localization [e.g. de];
2511                        // consider that string to have fulfilled the localization requirement.
2512                        String8 region(config.string(), 2);
2513                        if (configSet.find(region) == configSet.end()) {
2514                            if (configSet.count(defaultLocale) == 0) {
2515                                fprintf(stdout, "aapt: warning: "
2516                                        "**** string '%s' has no default or required localization "
2517                                        "for '%s' in %s\n",
2518                                        String8(nameIter->first).string(),
2519                                        config.string(),
2520                                        mBundle->getResourceSourceDirs()[0]);
2521                            }
2522                        }
2523                    }
2524                }
2525           } while (comma != NULL);
2526        }
2527    }
2528
2529    return err;
2530}
2531
2532status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
2533{
2534    ResourceFilter filter;
2535    status_t err = filter.parse(bundle->getConfigurations());
2536    if (err != NO_ERROR) {
2537        return err;
2538    }
2539
2540    const size_t N = mOrderedPackages.size();
2541    size_t pi;
2542
2543    const static String16 mipmap16("mipmap");
2544
2545    bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
2546
2547    // Iterate through all data, collecting all values (strings,
2548    // references, etc).
2549    StringPool valueStrings = StringPool(false, useUTF8);
2550    for (pi=0; pi<N; pi++) {
2551        sp<Package> p = mOrderedPackages.itemAt(pi);
2552        if (p->getTypes().size() == 0) {
2553            // Empty, skip!
2554            continue;
2555        }
2556
2557        StringPool typeStrings = StringPool(false, useUTF8);
2558        StringPool keyStrings = StringPool(false, useUTF8);
2559
2560        const size_t N = p->getOrderedTypes().size();
2561        for (size_t ti=0; ti<N; ti++) {
2562            sp<Type> t = p->getOrderedTypes().itemAt(ti);
2563            if (t == NULL) {
2564                typeStrings.add(String16("<empty>"), false);
2565                continue;
2566            }
2567            const String16 typeName(t->getName());
2568            typeStrings.add(typeName, false);
2569
2570            const bool filterable = (typeName != mipmap16);
2571
2572            const size_t N = t->getOrderedConfigs().size();
2573            for (size_t ci=0; ci<N; ci++) {
2574                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2575                if (c == NULL) {
2576                    continue;
2577                }
2578                const size_t N = c->getEntries().size();
2579                for (size_t ei=0; ei<N; ei++) {
2580                    ConfigDescription config = c->getEntries().keyAt(ei);
2581                    if (filterable && !filter.match(config)) {
2582                        continue;
2583                    }
2584                    sp<Entry> e = c->getEntries().valueAt(ei);
2585                    if (e == NULL) {
2586                        continue;
2587                    }
2588                    e->setNameIndex(keyStrings.add(e->getName(), true));
2589                    status_t err = e->prepareFlatten(&valueStrings, this);
2590                    if (err != NO_ERROR) {
2591                        return err;
2592                    }
2593                }
2594            }
2595        }
2596
2597        p->setTypeStrings(typeStrings.createStringBlock());
2598        p->setKeyStrings(keyStrings.createStringBlock());
2599    }
2600
2601    ssize_t strAmt = 0;
2602
2603    // Now build the array of package chunks.
2604    Vector<sp<AaptFile> > flatPackages;
2605    for (pi=0; pi<N; pi++) {
2606        sp<Package> p = mOrderedPackages.itemAt(pi);
2607        if (p->getTypes().size() == 0) {
2608            // Empty, skip!
2609            continue;
2610        }
2611
2612        const size_t N = p->getTypeStrings().size();
2613
2614        const size_t baseSize = sizeof(ResTable_package);
2615
2616        // Start the package data.
2617        sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
2618        ResTable_package* header = (ResTable_package*)data->editData(baseSize);
2619        if (header == NULL) {
2620            fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
2621            return NO_MEMORY;
2622        }
2623        memset(header, 0, sizeof(*header));
2624        header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
2625        header->header.headerSize = htods(sizeof(*header));
2626        header->id = htodl(p->getAssignedId());
2627        strcpy16_htod(header->name, p->getName().string());
2628
2629        // Write the string blocks.
2630        const size_t typeStringsStart = data->getSize();
2631        sp<AaptFile> strFile = p->getTypeStringsData();
2632        ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
2633        #if PRINT_STRING_METRICS
2634        fprintf(stderr, "**** type strings: %d\n", amt);
2635        #endif
2636        strAmt += amt;
2637        if (amt < 0) {
2638            return amt;
2639        }
2640        const size_t keyStringsStart = data->getSize();
2641        strFile = p->getKeyStringsData();
2642        amt = data->writeData(strFile->getData(), strFile->getSize());
2643        #if PRINT_STRING_METRICS
2644        fprintf(stderr, "**** key strings: %d\n", amt);
2645        #endif
2646        strAmt += amt;
2647        if (amt < 0) {
2648            return amt;
2649        }
2650
2651        // Build the type chunks inside of this package.
2652        for (size_t ti=0; ti<N; ti++) {
2653            // Retrieve them in the same order as the type string block.
2654            size_t len;
2655            String16 typeName(p->getTypeStrings().stringAt(ti, &len));
2656            sp<Type> t = p->getTypes().valueFor(typeName);
2657            LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
2658                                "Type name %s not found",
2659                                String8(typeName).string());
2660
2661            const bool filterable = (typeName != mipmap16);
2662
2663            const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
2664
2665            // First write the typeSpec chunk, containing information about
2666            // each resource entry in this type.
2667            {
2668                const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
2669                const size_t typeSpecStart = data->getSize();
2670                ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
2671                    (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
2672                if (tsHeader == NULL) {
2673                    fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
2674                    return NO_MEMORY;
2675                }
2676                memset(tsHeader, 0, sizeof(*tsHeader));
2677                tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
2678                tsHeader->header.headerSize = htods(sizeof(*tsHeader));
2679                tsHeader->header.size = htodl(typeSpecSize);
2680                tsHeader->id = ti+1;
2681                tsHeader->entryCount = htodl(N);
2682
2683                uint32_t* typeSpecFlags = (uint32_t*)
2684                    (((uint8_t*)data->editData())
2685                        + typeSpecStart + sizeof(ResTable_typeSpec));
2686                memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
2687
2688                for (size_t ei=0; ei<N; ei++) {
2689                    sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2690                    if (cl->getPublic()) {
2691                        typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
2692                    }
2693                    const size_t CN = cl->getEntries().size();
2694                    for (size_t ci=0; ci<CN; ci++) {
2695                        if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
2696                            continue;
2697                        }
2698                        for (size_t cj=ci+1; cj<CN; cj++) {
2699                            if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
2700                                continue;
2701                            }
2702                            typeSpecFlags[ei] |= htodl(
2703                                cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
2704                        }
2705                    }
2706                }
2707            }
2708
2709            // We need to write one type chunk for each configuration for
2710            // which we have entries in this type.
2711            const size_t NC = t->getUniqueConfigs().size();
2712
2713            const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
2714
2715            for (size_t ci=0; ci<NC; ci++) {
2716                ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
2717
2718                NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
2719                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2720                     "sw%ddp w%ddp h%ddp\n",
2721                      ti+1,
2722                      config.mcc, config.mnc,
2723                      config.language[0] ? config.language[0] : '-',
2724                      config.language[1] ? config.language[1] : '-',
2725                      config.country[0] ? config.country[0] : '-',
2726                      config.country[1] ? config.country[1] : '-',
2727                      config.orientation,
2728                      config.uiMode,
2729                      config.touchscreen,
2730                      config.density,
2731                      config.keyboard,
2732                      config.inputFlags,
2733                      config.navigation,
2734                      config.screenWidth,
2735                      config.screenHeight,
2736                      config.smallestScreenWidthDp,
2737                      config.screenWidthDp,
2738                      config.screenHeightDp));
2739
2740                if (filterable && !filter.match(config)) {
2741                    continue;
2742                }
2743
2744                const size_t typeStart = data->getSize();
2745
2746                ResTable_type* tHeader = (ResTable_type*)
2747                    (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
2748                if (tHeader == NULL) {
2749                    fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
2750                    return NO_MEMORY;
2751                }
2752
2753                memset(tHeader, 0, sizeof(*tHeader));
2754                tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
2755                tHeader->header.headerSize = htods(sizeof(*tHeader));
2756                tHeader->id = ti+1;
2757                tHeader->entryCount = htodl(N);
2758                tHeader->entriesStart = htodl(typeSize);
2759                tHeader->config = config;
2760                NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
2761                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
2762                     "sw%ddp w%ddp h%ddp\n",
2763                      ti+1,
2764                      tHeader->config.mcc, tHeader->config.mnc,
2765                      tHeader->config.language[0] ? tHeader->config.language[0] : '-',
2766                      tHeader->config.language[1] ? tHeader->config.language[1] : '-',
2767                      tHeader->config.country[0] ? tHeader->config.country[0] : '-',
2768                      tHeader->config.country[1] ? tHeader->config.country[1] : '-',
2769                      tHeader->config.orientation,
2770                      tHeader->config.uiMode,
2771                      tHeader->config.touchscreen,
2772                      tHeader->config.density,
2773                      tHeader->config.keyboard,
2774                      tHeader->config.inputFlags,
2775                      tHeader->config.navigation,
2776                      tHeader->config.screenWidth,
2777                      tHeader->config.screenHeight,
2778                      tHeader->config.smallestScreenWidthDp,
2779                      tHeader->config.screenWidthDp,
2780                      tHeader->config.screenHeightDp));
2781                tHeader->config.swapHtoD();
2782
2783                // Build the entries inside of this type.
2784                for (size_t ei=0; ei<N; ei++) {
2785                    sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
2786                    sp<Entry> e = cl->getEntries().valueFor(config);
2787
2788                    // Set the offset for this entry in its type.
2789                    uint32_t* index = (uint32_t*)
2790                        (((uint8_t*)data->editData())
2791                            + typeStart + sizeof(ResTable_type));
2792                    if (e != NULL) {
2793                        index[ei] = htodl(data->getSize()-typeStart-typeSize);
2794
2795                        // Create the entry.
2796                        ssize_t amt = e->flatten(bundle, data, cl->getPublic());
2797                        if (amt < 0) {
2798                            return amt;
2799                        }
2800                    } else {
2801                        index[ei] = htodl(ResTable_type::NO_ENTRY);
2802                    }
2803                }
2804
2805                // Fill in the rest of the type information.
2806                tHeader = (ResTable_type*)
2807                    (((uint8_t*)data->editData()) + typeStart);
2808                tHeader->header.size = htodl(data->getSize()-typeStart);
2809            }
2810        }
2811
2812        // Fill in the rest of the package information.
2813        header = (ResTable_package*)data->editData();
2814        header->header.size = htodl(data->getSize());
2815        header->typeStrings = htodl(typeStringsStart);
2816        header->lastPublicType = htodl(p->getTypeStrings().size());
2817        header->keyStrings = htodl(keyStringsStart);
2818        header->lastPublicKey = htodl(p->getKeyStrings().size());
2819
2820        flatPackages.add(data);
2821    }
2822
2823    // And now write out the final chunks.
2824    const size_t dataStart = dest->getSize();
2825
2826    {
2827        // blah
2828        ResTable_header header;
2829        memset(&header, 0, sizeof(header));
2830        header.header.type = htods(RES_TABLE_TYPE);
2831        header.header.headerSize = htods(sizeof(header));
2832        header.packageCount = htodl(flatPackages.size());
2833        status_t err = dest->writeData(&header, sizeof(header));
2834        if (err != NO_ERROR) {
2835            fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
2836            return err;
2837        }
2838    }
2839
2840    ssize_t strStart = dest->getSize();
2841    err = valueStrings.writeStringBlock(dest);
2842    if (err != NO_ERROR) {
2843        return err;
2844    }
2845
2846    ssize_t amt = (dest->getSize()-strStart);
2847    strAmt += amt;
2848    #if PRINT_STRING_METRICS
2849    fprintf(stderr, "**** value strings: %d\n", amt);
2850    fprintf(stderr, "**** total strings: %d\n", strAmt);
2851    #endif
2852
2853    for (pi=0; pi<flatPackages.size(); pi++) {
2854        err = dest->writeData(flatPackages[pi]->getData(),
2855                              flatPackages[pi]->getSize());
2856        if (err != NO_ERROR) {
2857            fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
2858            return err;
2859        }
2860    }
2861
2862    ResTable_header* header = (ResTable_header*)
2863        (((uint8_t*)dest->getData()) + dataStart);
2864    header->header.size = htodl(dest->getSize() - dataStart);
2865
2866    NOISY(aout << "Resource table:"
2867          << HexDump(dest->getData(), dest->getSize()) << endl);
2868
2869    #if PRINT_STRING_METRICS
2870    fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
2871        dest->getSize(), (strAmt*100)/dest->getSize());
2872    #endif
2873
2874    return NO_ERROR;
2875}
2876
2877void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
2878{
2879    fprintf(fp,
2880    "<!-- This file contains <public> resource definitions for all\n"
2881    "     resources that were generated from the source data. -->\n"
2882    "\n"
2883    "<resources>\n");
2884
2885    writePublicDefinitions(package, fp, true);
2886    writePublicDefinitions(package, fp, false);
2887
2888    fprintf(fp,
2889    "\n"
2890    "</resources>\n");
2891}
2892
2893void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
2894{
2895    bool didHeader = false;
2896
2897    sp<Package> pkg = mPackages.valueFor(package);
2898    if (pkg != NULL) {
2899        const size_t NT = pkg->getOrderedTypes().size();
2900        for (size_t i=0; i<NT; i++) {
2901            sp<Type> t = pkg->getOrderedTypes().itemAt(i);
2902            if (t == NULL) {
2903                continue;
2904            }
2905
2906            bool didType = false;
2907
2908            const size_t NC = t->getOrderedConfigs().size();
2909            for (size_t j=0; j<NC; j++) {
2910                sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
2911                if (c == NULL) {
2912                    continue;
2913                }
2914
2915                if (c->getPublic() != pub) {
2916                    continue;
2917                }
2918
2919                if (!didType) {
2920                    fprintf(fp, "\n");
2921                    didType = true;
2922                }
2923                if (!didHeader) {
2924                    if (pub) {
2925                        fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
2926                        fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
2927                    } else {
2928                        fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
2929                        fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
2930                    }
2931                    didHeader = true;
2932                }
2933                if (!pub) {
2934                    const size_t NE = c->getEntries().size();
2935                    for (size_t k=0; k<NE; k++) {
2936                        const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
2937                        if (pos.file != "") {
2938                            fprintf(fp,"  <!-- Declared at %s:%d -->\n",
2939                                    pos.file.string(), pos.line);
2940                        }
2941                    }
2942                }
2943                fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
2944                        String8(t->getName()).string(),
2945                        String8(c->getName()).string(),
2946                        getResId(pkg, t, c->getEntryIndex()));
2947            }
2948        }
2949    }
2950}
2951
2952ResourceTable::Item::Item(const SourcePos& _sourcePos,
2953                          bool _isId,
2954                          const String16& _value,
2955                          const Vector<StringPool::entry_style_span>* _style,
2956                          int32_t _format)
2957    : sourcePos(_sourcePos)
2958    , isId(_isId)
2959    , value(_value)
2960    , format(_format)
2961    , bagKeyId(0)
2962    , evaluating(false)
2963{
2964    if (_style) {
2965        style = *_style;
2966    }
2967}
2968
2969status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
2970{
2971    if (mType == TYPE_BAG) {
2972        return NO_ERROR;
2973    }
2974    if (mType == TYPE_UNKNOWN) {
2975        mType = TYPE_BAG;
2976        return NO_ERROR;
2977    }
2978    sourcePos.error("Resource entry %s is already defined as a single item.\n"
2979                    "%s:%d: Originally defined here.\n",
2980                    String8(mName).string(),
2981                    mItem.sourcePos.file.string(), mItem.sourcePos.line);
2982    return UNKNOWN_ERROR;
2983}
2984
2985status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
2986                                       const String16& value,
2987                                       const Vector<StringPool::entry_style_span>* style,
2988                                       int32_t format,
2989                                       const bool overwrite)
2990{
2991    Item item(sourcePos, false, value, style);
2992
2993    if (mType == TYPE_BAG) {
2994        const Item& item(mBag.valueAt(0));
2995        sourcePos.error("Resource entry %s is already defined as a bag.\n"
2996                        "%s:%d: Originally defined here.\n",
2997                        String8(mName).string(),
2998                        item.sourcePos.file.string(), item.sourcePos.line);
2999        return UNKNOWN_ERROR;
3000    }
3001    if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
3002        sourcePos.error("Resource entry %s is already defined.\n"
3003                        "%s:%d: Originally defined here.\n",
3004                        String8(mName).string(),
3005                        mItem.sourcePos.file.string(), mItem.sourcePos.line);
3006        return UNKNOWN_ERROR;
3007    }
3008
3009    mType = TYPE_ITEM;
3010    mItem = item;
3011    mItemFormat = format;
3012    return NO_ERROR;
3013}
3014
3015status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
3016                                        const String16& key, const String16& value,
3017                                        const Vector<StringPool::entry_style_span>* style,
3018                                        bool replace, bool isId, int32_t format)
3019{
3020    status_t err = makeItABag(sourcePos);
3021    if (err != NO_ERROR) {
3022        return err;
3023    }
3024
3025    Item item(sourcePos, isId, value, style, format);
3026
3027    // XXX NOTE: there is an error if you try to have a bag with two keys,
3028    // one an attr and one an id, with the same name.  Not something we
3029    // currently ever have to worry about.
3030    ssize_t origKey = mBag.indexOfKey(key);
3031    if (origKey >= 0) {
3032        if (!replace) {
3033            const Item& item(mBag.valueAt(origKey));
3034            sourcePos.error("Resource entry %s already has bag item %s.\n"
3035                    "%s:%d: Originally defined here.\n",
3036                    String8(mName).string(), String8(key).string(),
3037                    item.sourcePos.file.string(), item.sourcePos.line);
3038            return UNKNOWN_ERROR;
3039        }
3040        //printf("Replacing %s with %s\n",
3041        //       String8(mBag.valueFor(key).value).string(), String8(value).string());
3042        mBag.replaceValueFor(key, item);
3043    }
3044
3045    mBag.add(key, item);
3046    return NO_ERROR;
3047}
3048
3049status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
3050{
3051    status_t err = makeItABag(sourcePos);
3052    if (err != NO_ERROR) {
3053        return err;
3054    }
3055
3056    mBag.clear();
3057    return NO_ERROR;
3058}
3059
3060status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
3061                                                  const String16& package)
3062{
3063    const String16 attr16("attr");
3064    const String16 id16("id");
3065    const size_t N = mBag.size();
3066    for (size_t i=0; i<N; i++) {
3067        const String16& key = mBag.keyAt(i);
3068        const Item& it = mBag.valueAt(i);
3069        if (it.isId) {
3070            if (!table->hasBagOrEntry(key, &id16, &package)) {
3071                String16 value("false");
3072                status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
3073                                               id16, key, value);
3074                if (err != NO_ERROR) {
3075                    return err;
3076                }
3077            }
3078        } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
3079
3080#if 1
3081//             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
3082//                     String8(key).string());
3083//             const Item& item(mBag.valueAt(i));
3084//             fprintf(stderr, "Referenced from file %s line %d\n",
3085//                     item.sourcePos.file.string(), item.sourcePos.line);
3086//             return UNKNOWN_ERROR;
3087#else
3088            char numberStr[16];
3089            sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
3090            status_t err = table->addBag(SourcePos("<generated>", 0), package,
3091                                         attr16, key, String16(""),
3092                                         String16("^type"),
3093                                         String16(numberStr), NULL, NULL);
3094            if (err != NO_ERROR) {
3095                return err;
3096            }
3097#endif
3098        }
3099    }
3100    return NO_ERROR;
3101}
3102
3103status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
3104                                                 const String16& package)
3105{
3106    bool hasErrors = false;
3107
3108    if (mType == TYPE_BAG) {
3109        const char* errorMsg;
3110        const String16 style16("style");
3111        const String16 attr16("attr");
3112        const String16 id16("id");
3113        mParentId = 0;
3114        if (mParent.size() > 0) {
3115            mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
3116            if (mParentId == 0) {
3117                mPos.error("Error retrieving parent for item: %s '%s'.\n",
3118                        errorMsg, String8(mParent).string());
3119                hasErrors = true;
3120            }
3121        }
3122        const size_t N = mBag.size();
3123        for (size_t i=0; i<N; i++) {
3124            const String16& key = mBag.keyAt(i);
3125            Item& it = mBag.editValueAt(i);
3126            it.bagKeyId = table->getResId(key,
3127                    it.isId ? &id16 : &attr16, NULL, &errorMsg);
3128            //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3129            if (it.bagKeyId == 0) {
3130                it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3131                        String8(it.isId ? id16 : attr16).string(),
3132                        String8(key).string());
3133                hasErrors = true;
3134            }
3135        }
3136    }
3137    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
3138}
3139
3140status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
3141{
3142    if (mType == TYPE_ITEM) {
3143        Item& it = mItem;
3144        AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3145        if (!table->stringToValue(&it.parsedValue, strings,
3146                                  it.value, false, true, 0,
3147                                  &it.style, NULL, &ac, mItemFormat)) {
3148            return UNKNOWN_ERROR;
3149        }
3150    } else if (mType == TYPE_BAG) {
3151        const size_t N = mBag.size();
3152        for (size_t i=0; i<N; i++) {
3153            const String16& key = mBag.keyAt(i);
3154            Item& it = mBag.editValueAt(i);
3155            AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3156            if (!table->stringToValue(&it.parsedValue, strings,
3157                                      it.value, false, true, it.bagKeyId,
3158                                      &it.style, NULL, &ac, it.format)) {
3159                return UNKNOWN_ERROR;
3160            }
3161        }
3162    } else {
3163        mPos.error("Error: entry %s is not a single item or a bag.\n",
3164                   String8(mName).string());
3165        return UNKNOWN_ERROR;
3166    }
3167    return NO_ERROR;
3168}
3169
3170ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
3171{
3172    size_t amt = 0;
3173    ResTable_entry header;
3174    memset(&header, 0, sizeof(header));
3175    header.size = htods(sizeof(header));
3176    const type ty = this != NULL ? mType : TYPE_ITEM;
3177    if (this != NULL) {
3178        if (ty == TYPE_BAG) {
3179            header.flags |= htods(header.FLAG_COMPLEX);
3180        }
3181        if (isPublic) {
3182            header.flags |= htods(header.FLAG_PUBLIC);
3183        }
3184        header.key.index = htodl(mNameIndex);
3185    }
3186    if (ty != TYPE_BAG) {
3187        status_t err = data->writeData(&header, sizeof(header));
3188        if (err != NO_ERROR) {
3189            fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3190            return err;
3191        }
3192
3193        const Item& it = mItem;
3194        Res_value par;
3195        memset(&par, 0, sizeof(par));
3196        par.size = htods(it.parsedValue.size);
3197        par.dataType = it.parsedValue.dataType;
3198        par.res0 = it.parsedValue.res0;
3199        par.data = htodl(it.parsedValue.data);
3200        #if 0
3201        printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3202               String8(mName).string(), it.parsedValue.dataType,
3203               it.parsedValue.data, par.res0);
3204        #endif
3205        err = data->writeData(&par, it.parsedValue.size);
3206        if (err != NO_ERROR) {
3207            fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3208            return err;
3209        }
3210        amt += it.parsedValue.size;
3211    } else {
3212        size_t N = mBag.size();
3213        size_t i;
3214        // Create correct ordering of items.
3215        KeyedVector<uint32_t, const Item*> items;
3216        for (i=0; i<N; i++) {
3217            const Item& it = mBag.valueAt(i);
3218            items.add(it.bagKeyId, &it);
3219        }
3220        N = items.size();
3221
3222        ResTable_map_entry mapHeader;
3223        memcpy(&mapHeader, &header, sizeof(header));
3224        mapHeader.size = htods(sizeof(mapHeader));
3225        mapHeader.parent.ident = htodl(mParentId);
3226        mapHeader.count = htodl(N);
3227        status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3228        if (err != NO_ERROR) {
3229            fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3230            return err;
3231        }
3232
3233        for (i=0; i<N; i++) {
3234            const Item& it = *items.valueAt(i);
3235            ResTable_map map;
3236            map.name.ident = htodl(it.bagKeyId);
3237            map.value.size = htods(it.parsedValue.size);
3238            map.value.dataType = it.parsedValue.dataType;
3239            map.value.res0 = it.parsedValue.res0;
3240            map.value.data = htodl(it.parsedValue.data);
3241            err = data->writeData(&map, sizeof(map));
3242            if (err != NO_ERROR) {
3243                fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3244                return err;
3245            }
3246            amt += sizeof(map);
3247        }
3248    }
3249    return amt;
3250}
3251
3252void ResourceTable::ConfigList::appendComment(const String16& comment,
3253                                              bool onlyIfEmpty)
3254{
3255    if (comment.size() <= 0) {
3256        return;
3257    }
3258    if (onlyIfEmpty && mComment.size() > 0) {
3259        return;
3260    }
3261    if (mComment.size() > 0) {
3262        mComment.append(String16("\n"));
3263    }
3264    mComment.append(comment);
3265}
3266
3267void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3268{
3269    if (comment.size() <= 0) {
3270        return;
3271    }
3272    if (mTypeComment.size() > 0) {
3273        mTypeComment.append(String16("\n"));
3274    }
3275    mTypeComment.append(comment);
3276}
3277
3278status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3279                                        const String16& name,
3280                                        const uint32_t ident)
3281{
3282    #if 0
3283    int32_t entryIdx = Res_GETENTRY(ident);
3284    if (entryIdx < 0) {
3285        sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3286                String8(mName).string(), String8(name).string(), ident);
3287        return UNKNOWN_ERROR;
3288    }
3289    #endif
3290
3291    int32_t typeIdx = Res_GETTYPE(ident);
3292    if (typeIdx >= 0) {
3293        typeIdx++;
3294        if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3295            sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3296                    " public identifiers (0x%x vs 0x%x).\n",
3297                    String8(mName).string(), String8(name).string(),
3298                    mPublicIndex, typeIdx);
3299            return UNKNOWN_ERROR;
3300        }
3301        mPublicIndex = typeIdx;
3302    }
3303
3304    if (mFirstPublicSourcePos == NULL) {
3305        mFirstPublicSourcePos = new SourcePos(sourcePos);
3306    }
3307
3308    if (mPublic.indexOfKey(name) < 0) {
3309        mPublic.add(name, Public(sourcePos, String16(), ident));
3310    } else {
3311        Public& p = mPublic.editValueFor(name);
3312        if (p.ident != ident) {
3313            sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3314                    " (0x%08x vs 0x%08x).\n"
3315                    "%s:%d: Originally defined here.\n",
3316                    String8(mName).string(), String8(name).string(), p.ident, ident,
3317                    p.sourcePos.file.string(), p.sourcePos.line);
3318            return UNKNOWN_ERROR;
3319        }
3320    }
3321
3322    return NO_ERROR;
3323}
3324
3325void ResourceTable::Type::canAddEntry(const String16& name)
3326{
3327    mCanAddEntries.add(name);
3328}
3329
3330sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3331                                                       const SourcePos& sourcePos,
3332                                                       const ResTable_config* config,
3333                                                       bool doSetIndex,
3334                                                       bool overlay,
3335                                                       bool autoAddOverlay)
3336{
3337    int pos = -1;
3338    sp<ConfigList> c = mConfigs.valueFor(entry);
3339    if (c == NULL) {
3340        if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
3341            sourcePos.error("Resource at %s appears in overlay but not"
3342                            " in the base package; use <add-resource> to add.\n",
3343                            String8(entry).string());
3344            return NULL;
3345        }
3346        c = new ConfigList(entry, sourcePos);
3347        mConfigs.add(entry, c);
3348        pos = (int)mOrderedConfigs.size();
3349        mOrderedConfigs.add(c);
3350        if (doSetIndex) {
3351            c->setEntryIndex(pos);
3352        }
3353    }
3354
3355    ConfigDescription cdesc;
3356    if (config) cdesc = *config;
3357
3358    sp<Entry> e = c->getEntries().valueFor(cdesc);
3359    if (e == NULL) {
3360        if (config != NULL) {
3361            NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
3362                    "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3363                    "sw%ddp w%ddp h%ddp\n",
3364                      sourcePos.file.string(), sourcePos.line,
3365                      config->mcc, config->mnc,
3366                      config->language[0] ? config->language[0] : '-',
3367                      config->language[1] ? config->language[1] : '-',
3368                      config->country[0] ? config->country[0] : '-',
3369                      config->country[1] ? config->country[1] : '-',
3370                      config->orientation,
3371                      config->touchscreen,
3372                      config->density,
3373                      config->keyboard,
3374                      config->inputFlags,
3375                      config->navigation,
3376                      config->screenWidth,
3377                      config->screenHeight,
3378                      config->smallestScreenWidthDp,
3379                      config->screenWidthDp,
3380                      config->screenHeightDp));
3381        } else {
3382            NOISY(printf("New entry at %s:%d: NULL config\n",
3383                      sourcePos.file.string(), sourcePos.line));
3384        }
3385        e = new Entry(entry, sourcePos);
3386        c->addEntry(cdesc, e);
3387        /*
3388        if (doSetIndex) {
3389            if (pos < 0) {
3390                for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3391                    if (mOrderedConfigs[pos] == c) {
3392                        break;
3393                    }
3394                }
3395                if (pos >= (int)mOrderedConfigs.size()) {
3396                    sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3397                    return NULL;
3398                }
3399            }
3400            e->setEntryIndex(pos);
3401        }
3402        */
3403    }
3404
3405    mUniqueConfigs.add(cdesc);
3406
3407    return e;
3408}
3409
3410status_t ResourceTable::Type::applyPublicEntryOrder()
3411{
3412    size_t N = mOrderedConfigs.size();
3413    Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
3414    bool hasError = false;
3415
3416    size_t i;
3417    for (i=0; i<N; i++) {
3418        mOrderedConfigs.replaceAt(NULL, i);
3419    }
3420
3421    const size_t NP = mPublic.size();
3422    //printf("Ordering %d configs from %d public defs\n", N, NP);
3423    size_t j;
3424    for (j=0; j<NP; j++) {
3425        const String16& name = mPublic.keyAt(j);
3426        const Public& p = mPublic.valueAt(j);
3427        int32_t idx = Res_GETENTRY(p.ident);
3428        //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
3429        //       String8(mName).string(), String8(name).string(), p.ident, N);
3430        bool found = false;
3431        for (i=0; i<N; i++) {
3432            sp<ConfigList> e = origOrder.itemAt(i);
3433            //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
3434            if (e->getName() == name) {
3435                if (idx >= (int32_t)mOrderedConfigs.size()) {
3436                    p.sourcePos.error("Public entry identifier 0x%x entry index "
3437                            "is larger than available symbols (index %d, total symbols %d).\n",
3438                            p.ident, idx, mOrderedConfigs.size());
3439                    hasError = true;
3440                } else if (mOrderedConfigs.itemAt(idx) == NULL) {
3441                    e->setPublic(true);
3442                    e->setPublicSourcePos(p.sourcePos);
3443                    mOrderedConfigs.replaceAt(e, idx);
3444                    origOrder.removeAt(i);
3445                    N--;
3446                    found = true;
3447                    break;
3448                } else {
3449                    sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
3450
3451                    p.sourcePos.error("Multiple entry names declared for public entry"
3452                            " identifier 0x%x in type %s (%s vs %s).\n"
3453                            "%s:%d: Originally defined here.",
3454                            idx+1, String8(mName).string(),
3455                            String8(oe->getName()).string(),
3456                            String8(name).string(),
3457                            oe->getPublicSourcePos().file.string(),
3458                            oe->getPublicSourcePos().line);
3459                    hasError = true;
3460                }
3461            }
3462        }
3463
3464        if (!found) {
3465            p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
3466                    String8(mName).string(), String8(name).string());
3467            hasError = true;
3468        }
3469    }
3470
3471    //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
3472
3473    if (N != origOrder.size()) {
3474        printf("Internal error: remaining private symbol count mismatch\n");
3475        N = origOrder.size();
3476    }
3477
3478    j = 0;
3479    for (i=0; i<N; i++) {
3480        sp<ConfigList> e = origOrder.itemAt(i);
3481        // There will always be enough room for the remaining entries.
3482        while (mOrderedConfigs.itemAt(j) != NULL) {
3483            j++;
3484        }
3485        mOrderedConfigs.replaceAt(e, j);
3486        j++;
3487    }
3488
3489    return hasError ? UNKNOWN_ERROR : NO_ERROR;
3490}
3491
3492ResourceTable::Package::Package(const String16& name, ssize_t includedId)
3493    : mName(name), mIncludedId(includedId),
3494      mTypeStringsMapping(0xffffffff),
3495      mKeyStringsMapping(0xffffffff)
3496{
3497}
3498
3499sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
3500                                                        const SourcePos& sourcePos,
3501                                                        bool doSetIndex)
3502{
3503    sp<Type> t = mTypes.valueFor(type);
3504    if (t == NULL) {
3505        t = new Type(type, sourcePos);
3506        mTypes.add(type, t);
3507        mOrderedTypes.add(t);
3508        if (doSetIndex) {
3509            // For some reason the type's index is set to one plus the index
3510            // in the mOrderedTypes list, rather than just the index.
3511            t->setIndex(mOrderedTypes.size());
3512        }
3513    }
3514    return t;
3515}
3516
3517status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
3518{
3519    mTypeStringsData = data;
3520    status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
3521    if (err != NO_ERROR) {
3522        fprintf(stderr, "ERROR: Type string data is corrupt!\n");
3523    }
3524    return err;
3525}
3526
3527status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
3528{
3529    mKeyStringsData = data;
3530    status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
3531    if (err != NO_ERROR) {
3532        fprintf(stderr, "ERROR: Key string data is corrupt!\n");
3533    }
3534    return err;
3535}
3536
3537status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
3538                                            ResStringPool* strings,
3539                                            DefaultKeyedVector<String16, uint32_t>* mappings)
3540{
3541    if (data->getData() == NULL) {
3542        return UNKNOWN_ERROR;
3543    }
3544
3545    NOISY(aout << "Setting restable string pool: "
3546          << HexDump(data->getData(), data->getSize()) << endl);
3547
3548    status_t err = strings->setTo(data->getData(), data->getSize());
3549    if (err == NO_ERROR) {
3550        const size_t N = strings->size();
3551        for (size_t i=0; i<N; i++) {
3552            size_t len;
3553            mappings->add(String16(strings->stringAt(i, &len)), i);
3554        }
3555    }
3556    return err;
3557}
3558
3559status_t ResourceTable::Package::applyPublicTypeOrder()
3560{
3561    size_t N = mOrderedTypes.size();
3562    Vector<sp<Type> > origOrder(mOrderedTypes);
3563
3564    size_t i;
3565    for (i=0; i<N; i++) {
3566        mOrderedTypes.replaceAt(NULL, i);
3567    }
3568
3569    for (i=0; i<N; i++) {
3570        sp<Type> t = origOrder.itemAt(i);
3571        int32_t idx = t->getPublicIndex();
3572        if (idx > 0) {
3573            idx--;
3574            while (idx >= (int32_t)mOrderedTypes.size()) {
3575                mOrderedTypes.add();
3576            }
3577            if (mOrderedTypes.itemAt(idx) != NULL) {
3578                sp<Type> ot = mOrderedTypes.itemAt(idx);
3579                t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
3580                        " identifier 0x%x (%s vs %s).\n"
3581                        "%s:%d: Originally defined here.",
3582                        idx, String8(ot->getName()).string(),
3583                        String8(t->getName()).string(),
3584                        ot->getFirstPublicSourcePos().file.string(),
3585                        ot->getFirstPublicSourcePos().line);
3586                return UNKNOWN_ERROR;
3587            }
3588            mOrderedTypes.replaceAt(t, idx);
3589            origOrder.removeAt(i);
3590            i--;
3591            N--;
3592        }
3593    }
3594
3595    size_t j=0;
3596    for (i=0; i<N; i++) {
3597        sp<Type> t = origOrder.itemAt(i);
3598        // There will always be enough room for the remaining types.
3599        while (mOrderedTypes.itemAt(j) != NULL) {
3600            j++;
3601        }
3602        mOrderedTypes.replaceAt(t, j);
3603    }
3604
3605    return NO_ERROR;
3606}
3607
3608sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
3609{
3610    sp<Package> p = mPackages.valueFor(package);
3611    if (p == NULL) {
3612        if (mBundle->getIsOverlayPackage()) {
3613            p = new Package(package, 0x00);
3614        } else if (mIsAppPackage) {
3615            if (mHaveAppPackage) {
3616                fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
3617                                "Use -x to create extended resources.\n");
3618                return NULL;
3619            }
3620            mHaveAppPackage = true;
3621            p = new Package(package, 127);
3622        } else {
3623            p = new Package(package, mNextPackageId);
3624        }
3625        //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
3626        //       String8(package).string(), p->getAssignedId());
3627        mPackages.add(package, p);
3628        mOrderedPackages.add(p);
3629        mNextPackageId++;
3630    }
3631    return p;
3632}
3633
3634sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
3635                                               const String16& type,
3636                                               const SourcePos& sourcePos,
3637                                               bool doSetIndex)
3638{
3639    sp<Package> p = getPackage(package);
3640    if (p == NULL) {
3641        return NULL;
3642    }
3643    return p->getType(type, sourcePos, doSetIndex);
3644}
3645
3646sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
3647                                                 const String16& type,
3648                                                 const String16& name,
3649                                                 const SourcePos& sourcePos,
3650                                                 bool overlay,
3651                                                 const ResTable_config* config,
3652                                                 bool doSetIndex)
3653{
3654    sp<Type> t = getType(package, type, sourcePos, doSetIndex);
3655    if (t == NULL) {
3656        return NULL;
3657    }
3658    return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
3659}
3660
3661sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
3662                                                       const ResTable_config* config) const
3663{
3664    int pid = Res_GETPACKAGE(resID)+1;
3665    const size_t N = mOrderedPackages.size();
3666    size_t i;
3667    sp<Package> p;
3668    for (i=0; i<N; i++) {
3669        sp<Package> check = mOrderedPackages[i];
3670        if (check->getAssignedId() == pid) {
3671            p = check;
3672            break;
3673        }
3674
3675    }
3676    if (p == NULL) {
3677        fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
3678        return NULL;
3679    }
3680
3681    int tid = Res_GETTYPE(resID);
3682    if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
3683        fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
3684        return NULL;
3685    }
3686    sp<Type> t = p->getOrderedTypes()[tid];
3687
3688    int eid = Res_GETENTRY(resID);
3689    if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
3690        fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
3691        return NULL;
3692    }
3693
3694    sp<ConfigList> c = t->getOrderedConfigs()[eid];
3695    if (c == NULL) {
3696        fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
3697        return NULL;
3698    }
3699
3700    ConfigDescription cdesc;
3701    if (config) cdesc = *config;
3702    sp<Entry> e = c->getEntries().valueFor(cdesc);
3703    if (c == NULL) {
3704        fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
3705        return NULL;
3706    }
3707
3708    return e;
3709}
3710
3711const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
3712{
3713    sp<const Entry> e = getEntry(resID);
3714    if (e == NULL) {
3715        return NULL;
3716    }
3717
3718    const size_t N = e->getBag().size();
3719    for (size_t i=0; i<N; i++) {
3720        const Item& it = e->getBag().valueAt(i);
3721        if (it.bagKeyId == 0) {
3722            fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
3723                    String8(e->getName()).string(),
3724                    String8(e->getBag().keyAt(i)).string());
3725        }
3726        if (it.bagKeyId == attrID) {
3727            return &it;
3728        }
3729    }
3730
3731    return NULL;
3732}
3733
3734bool ResourceTable::getItemValue(
3735    uint32_t resID, uint32_t attrID, Res_value* outValue)
3736{
3737    const Item* item = getItem(resID, attrID);
3738
3739    bool res = false;
3740    if (item != NULL) {
3741        if (item->evaluating) {
3742            sp<const Entry> e = getEntry(resID);
3743            const size_t N = e->getBag().size();
3744            size_t i;
3745            for (i=0; i<N; i++) {
3746                if (&e->getBag().valueAt(i) == item) {
3747                    break;
3748                }
3749            }
3750            fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
3751                    String8(e->getName()).string(),
3752                    String8(e->getBag().keyAt(i)).string());
3753            return false;
3754        }
3755        item->evaluating = true;
3756        res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
3757        NOISY(
3758            if (res) {
3759                printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
3760                       resID, attrID, String8(getEntry(resID)->getName()).string(),
3761                       outValue->dataType, outValue->data);
3762            } else {
3763                printf("getItemValue of #%08x[#%08x]: failed\n",
3764                       resID, attrID);
3765            }
3766        );
3767        item->evaluating = false;
3768    }
3769    return res;
3770}
3771