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