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