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