X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/dadd9c1fc18bd05c84a357b56e945b5829b3bd95..17f321193829cfb0d99b4d839d32e8904e013cb1:/ResourceTable.cpp diff --git a/ResourceTable.cpp b/ResourceTable.cpp index 6fe196a..52ebaf0 100644 --- a/ResourceTable.cpp +++ b/ResourceTable.cpp @@ -7,9 +7,11 @@ #include "ResourceTable.h" #include "XMLNode.h" +#include "ResourceFilter.h" +#include "ResourceIdCache.h" +#include #include -#include #include #define NOISY(x) //x @@ -23,12 +25,40 @@ status_t compileXmlFile(const sp& assets, if (root == NULL) { return UNKNOWN_ERROR; } + + return compileXmlFile(assets, root, target, table, options); +} + +status_t compileXmlFile(const sp& assets, + const sp& target, + const sp& outTarget, + ResourceTable* table, + int options) +{ + sp root = XMLNode::parse(target); + if (root == NULL) { + return UNKNOWN_ERROR; + } + + return compileXmlFile(assets, root, outTarget, table, options); +} + +status_t compileXmlFile(const sp& assets, + const sp& root, + const sp& target, + ResourceTable* table, + int options) +{ if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) { root->removeWhitespace(true, NULL); } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) { root->removeWhitespace(false, NULL); } + if ((options&XML_COMPILE_UTF8) != 0) { + root->setUTF8(true); + } + bool hasErrors = false; if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) { @@ -115,7 +145,7 @@ static const flag_entry gFormatFlags[] = { "a floating point value, such as \"1.2\"."}, { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION, "a dimension value, which is a floating point number appended with a unit such as \"14.5sp\".\n" - "Available units are: px (pixels), db (density-independent pixels), sp (scaled pixels based on preferred font size),\n" + "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n" "in (inches), mm (millimeters)." }, { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION, "a fractional value, which is a floating point number appended with either % or %p, such as \"14.5%\".\n" @@ -368,7 +398,7 @@ static status_t compileAttribute(const sp& in, } attr.createIfNeeded(outTable); if (!attr.hasErrors) { - char buf[10]; + char buf[11]; sprintf(buf, "%d", l10n_required); err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident, String16(""), String16("^l10n"), String16(buf), NULL, NULL); @@ -470,22 +500,22 @@ static status_t compileAttribute(const sp& in, enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM) ? String16(" be one of the following constant values.") : String16(" be one or more (separated by '|') of the following constant values.")); - enumOrFlagsComment.append(String16("

\n\n" + enumOrFlagsComment.append(String16("

\n
\n" "\n" "\n" "\n" - "")); + "")); } - enumOrFlagsComment.append(String16("\n")); + enumOrFlagsComment.append(String16("")); err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), myPackage, @@ -545,7 +575,10 @@ status_t parseAndAddBag(Bundle* bundle, const String16& parentIdent, const String16& itemIdent, int32_t curFormat, + bool isFormatted, + const String16& product, bool pseudolocalize, + const bool overwrite, ResourceTable* outTable) { status_t err; @@ -554,7 +587,7 @@ status_t parseAndAddBag(Bundle* bundle, String16 str; Vector spans; err = parseStyledString(bundle, in->getPrintableSource().string(), - block, item16, &str, &spans, + block, item16, &str, &spans, isFormatted, pseudolocalize); if (err != NO_ERROR) { return err; @@ -572,10 +605,36 @@ status_t parseAndAddBag(Bundle* bundle, err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()), myPackage, curType, ident, parentIdent, itemIdent, str, - &spans, &config, false, false, curFormat); + &spans, &config, overwrite, false, curFormat); return err; } +/* + * Returns true if needle is one of the elements in the comma-separated list + * haystack, false otherwise. + */ +bool isInProductList(const String16& needle, const String16& haystack) { + const char16_t *needle2 = needle.string(); + const char16_t *haystack2 = haystack.string(); + size_t needlesize = needle.size(); + + while (*haystack2 != '\0') { + if (strncmp16(haystack2, needle2, needlesize) == 0) { + if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') { + return true; + } + } + + while (*haystack2 != '\0' && *haystack2 != ',') { + haystack2++; + } + if (*haystack2 == ',') { + haystack2++; + } + } + + return false; +} status_t parseAndAddEntry(Bundle* bundle, const sp& in, @@ -587,7 +646,10 @@ status_t parseAndAddEntry(Bundle* bundle, const String16& curTag, bool curIsStyled, int32_t curFormat, + bool isFormatted, + const String16& product, bool pseudolocalize, + const bool overwrite, ResourceTable* outTable) { status_t err; @@ -596,12 +658,53 @@ status_t parseAndAddEntry(Bundle* bundle, Vector spans; err = parseStyledString(bundle, in->getPrintableSource().string(), block, curTag, &str, curIsStyled ? &spans : NULL, - pseudolocalize); + isFormatted, pseudolocalize); if (err < NO_ERROR) { return err; } + /* + * If a product type was specified on the command line + * and also in the string, and the two are not the same, + * return without adding the string. + */ + + const char *bundleProduct = bundle->getProduct(); + if (bundleProduct == NULL) { + bundleProduct = ""; + } + + if (product.size() != 0) { + /* + * If the command-line-specified product is empty, only "default" + * matches. Other variants are skipped. This is so generation + * of the R.java file when the product is not known is predictable. + */ + + if (bundleProduct[0] == '\0') { + if (strcmp16(String16("default").string(), product.string()) != 0) { + return NO_ERROR; + } + } else { + /* + * The command-line product is not empty. + * If the product for this string is on the command-line list, + * it matches. "default" also matches, but only if nothing + * else has matched already. + */ + + if (isInProductList(product, String16(bundleProduct))) { + ; + } else if (strcmp16(String16("default").string(), product.string()) == 0 && + !outTable->hasBagOrEntry(myPackage, curType, ident, config)) { + ; + } else { + return NO_ERROR; + } + } + } + NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", config.language[0], config.language[1], config.country[0], config.country[1], @@ -610,7 +713,7 @@ status_t parseAndAddEntry(Bundle* bundle, err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()), myPackage, curType, ident, str, &spans, &config, - false, curFormat); + false, curFormat, overwrite); return err; } @@ -619,6 +722,7 @@ status_t compileResourceFile(Bundle* bundle, const sp& assets, const sp& in, const ResTable_config& defParams, + const bool overwrite, ResourceTable* outTable) { ResXMLTree block; @@ -638,15 +742,20 @@ status_t compileResourceFile(Bundle* bundle, const String16 string16("string"); const String16 drawable16("drawable"); const String16 color16("color"); + const String16 bool16("bool"); const String16 integer16("integer"); const String16 dimen16("dimen"); + const String16 fraction16("fraction"); const String16 style16("style"); const String16 plurals16("plurals"); const String16 array16("array"); const String16 string_array16("string-array"); const String16 integer_array16("integer-array"); const String16 public16("public"); + const String16 public_padding16("public-padding"); const String16 private_symbols16("private-symbols"); + const String16 java_symbol16("java-symbol"); + const String16 add_resource16("add-resource"); const String16 skip16("skip"); const String16 eat_comment16("eat-comment"); @@ -671,12 +780,22 @@ status_t compileResourceFile(Bundle* bundle, const String16 many16("many"); const String16 quantityMany16("^many"); + // useful attribute names and special values + const String16 name16("name"); + const String16 translatable16("translatable"); + const String16 formatted16("formatted"); + const String16 false16("false"); const String16 myPackage(assets->getPackage()); bool hasErrors = false; - - uint32_t nextPublicId = 0; + + bool fileIsTranslatable = true; + if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) { + fileIsTranslatable = false; + } + + DefaultKeyedVector nextPublicId(0); ResXMLTree::event_code_t code; do { @@ -709,8 +828,10 @@ status_t compileResourceFile(Bundle* bundle, String16 curType; int32_t curFormat = ResTable_map::TYPE_ANY; bool curIsBag = false; + bool curIsBagReplaceOnOverwrite = false; bool curIsStyled = false; bool curIsPseudolocalizable = false; + bool curIsFormatted = fileIsTranslatable; bool localHasErrors = false; if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { @@ -765,15 +886,15 @@ status_t compileResourceFile(Bundle* bundle, hasErrors = localHasErrors = true; } else { ident = identValue.data; - nextPublicId = ident+1; + nextPublicId.replaceValueFor(type, ident+1); } - } else if (nextPublicId == 0) { + } else if (nextPublicId.indexOfKey(type) < 0) { srcPos.error("No 'id' attribute supplied ," " and no previous id defined in this file.\n"); hasErrors = localHasErrors = true; } else if (!localHasErrors) { - ident = nextPublicId; - nextPublicId++; + ident = nextPublicId.valueFor(type); + nextPublicId.replaceValueFor(type, ident+1); } if (!localHasErrors) { @@ -807,6 +928,116 @@ status_t compileResourceFile(Bundle* bundle, } continue; + } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); + + String16 type; + ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); + if (typeIdx < 0) { + srcPos.error("A 'type' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + type = String16(block.getAttributeStringValue(typeIdx, &len)); + + String16 name; + ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); + if (nameIdx < 0) { + srcPos.error("A 'name' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + name = String16(block.getAttributeStringValue(nameIdx, &len)); + + uint32_t start = 0; + ssize_t startIdx = block.indexOfAttribute(NULL, "start"); + if (startIdx >= 0) { + const char16_t* startStr = block.getAttributeStringValue(startIdx, &len); + Res_value startValue; + if (!ResTable::stringToInt(startStr, len, &startValue)) { + srcPos.error("Given 'start' attribute is not an integer: %s\n", + String8(block.getAttributeStringValue(startIdx, &len)).string()); + hasErrors = localHasErrors = true; + } else { + start = startValue.data; + } + } else if (nextPublicId.indexOfKey(type) < 0) { + srcPos.error("No 'start' attribute supplied ," + " and no previous id defined in this file.\n"); + hasErrors = localHasErrors = true; + } else if (!localHasErrors) { + start = nextPublicId.valueFor(type); + } + + uint32_t end = 0; + ssize_t endIdx = block.indexOfAttribute(NULL, "end"); + if (endIdx >= 0) { + const char16_t* endStr = block.getAttributeStringValue(endIdx, &len); + Res_value endValue; + if (!ResTable::stringToInt(endStr, len, &endValue)) { + srcPos.error("Given 'end' attribute is not an integer: %s\n", + String8(block.getAttributeStringValue(endIdx, &len)).string()); + hasErrors = localHasErrors = true; + } else { + end = endValue.data; + } + } else { + srcPos.error("No 'end' attribute supplied \n"); + hasErrors = localHasErrors = true; + } + + if (end >= start) { + nextPublicId.replaceValueFor(type, end+1); + } else { + srcPos.error("Padding start '%ul' is after end '%ul'\n", + start, end); + hasErrors = localHasErrors = true; + } + + String16 comment( + block.getComment(&len) ? block.getComment(&len) : nulStr); + for (uint32_t curIdent=start; curIdent<=end; curIdent++) { + if (localHasErrors) { + break; + } + String16 curName(name); + char buf[64]; + sprintf(buf, "%d", (int)(end-curIdent+1)); + curName.append(String16(buf)); + + err = outTable->addEntry(srcPos, myPackage, type, curName, + String16("padding"), NULL, &curParams, false, + ResTable_map::TYPE_STRING, overwrite); + if (err < NO_ERROR) { + hasErrors = localHasErrors = true; + break; + } + err = outTable->addPublic(srcPos, myPackage, type, + curName, curIdent); + if (err < NO_ERROR) { + hasErrors = localHasErrors = true; + break; + } + sp symbols = assets->getSymbolsFor(String8("R")); + if (symbols != NULL) { + symbols = symbols->addNestedSymbol(String8(type), srcPos); + } + if (symbols != NULL) { + symbols->makeSymbolPublic(String8(curName), srcPos); + symbols->appendComment(String8(curName), comment, srcPos); + } else { + srcPos.error("Unable to create symbols!\n"); + hasErrors = localHasErrors = true; + } + } + + while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + break; + } + } + } + continue; + } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) { String16 pkg; ssize_t pkgIdx = block.indexOfAttribute(NULL, "package"); @@ -829,6 +1060,79 @@ status_t compileResourceFile(Bundle* bundle, } continue; + } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) { + SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); + + String16 type; + ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); + if (typeIdx < 0) { + srcPos.error("A 'type' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + type = String16(block.getAttributeStringValue(typeIdx, &len)); + + String16 name; + ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); + if (nameIdx < 0) { + srcPos.error("A 'name' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + name = String16(block.getAttributeStringValue(nameIdx, &len)); + + sp symbols = assets->getJavaSymbolsFor(String8("R")); + if (symbols != NULL) { + symbols = symbols->addNestedSymbol(String8(type), srcPos); + } + if (symbols != NULL) { + symbols->makeSymbolJavaSymbol(String8(name), srcPos); + String16 comment( + block.getComment(&len) ? block.getComment(&len) : nulStr); + symbols->appendComment(String8(name), comment, srcPos); + } else { + srcPos.error("Unable to create symbols!\n"); + hasErrors = localHasErrors = true; + } + + while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) { + break; + } + } + } + continue; + + + } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) { + SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); + + String16 typeName; + ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); + if (typeIdx < 0) { + srcPos.error("A 'type' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + typeName = String16(block.getAttributeStringValue(typeIdx, &len)); + + String16 name; + ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); + if (nameIdx < 0) { + srcPos.error("A 'name' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + name = String16(block.getAttributeStringValue(nameIdx, &len)); + + outTable->canAddEntry(srcPos, myPackage, typeName, name); + + while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) { + break; + } + } + } + continue; + } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) { SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); @@ -950,6 +1254,53 @@ status_t compileResourceFile(Bundle* bundle, } curIsStyled = true; } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) { + // Note the existence and locale of every string we process + char rawLocale[16]; + curParams.getLocale(rawLocale); + String8 locale(rawLocale); + String16 name; + String16 translatable; + String16 formatted; + + size_t n = block.getAttributeCount(); + for (size_t i = 0; i < n; i++) { + size_t length; + const uint16_t* attr = block.getAttributeName(i, &length); + if (strcmp16(attr, name16.string()) == 0) { + name.setTo(block.getAttributeStringValue(i, &length)); + } else if (strcmp16(attr, translatable16.string()) == 0) { + translatable.setTo(block.getAttributeStringValue(i, &length)); + } else if (strcmp16(attr, formatted16.string()) == 0) { + formatted.setTo(block.getAttributeStringValue(i, &length)); + } + } + + if (name.size() > 0) { + if (translatable == false16) { + curIsFormatted = false; + // Untranslatable strings must only exist in the default [empty] locale + if (locale.size() > 0) { + fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists" + " in locale '%s'\n", String8(name).string(), + bundle->getResourceSourceDirs()[0], + locale.string()); + // hasErrors = localHasErrors = true; + } else { + // Intentionally empty block: + // + // Don't add untranslatable strings to the localization table; that + // way if we later see localizations of them, they'll be flagged as + // having no default translation. + } + } else { + outTable->addLocalization(name, locale); + } + + if (formatted == false16) { + curIsFormatted = false; + } + } + curTag = &string16; curType = string16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; @@ -963,6 +1314,10 @@ status_t compileResourceFile(Bundle* bundle, curTag = &color16; curType = color16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR; + } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) { + curTag = &bool16; + curType = bool16; + curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN; } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) { curTag = &integer16; curType = integer16; @@ -971,6 +1326,10 @@ status_t compileResourceFile(Bundle* bundle, curTag = &dimen16; curType = dimen16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION; + } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) { + curTag = &fraction16; + curType = fraction16; + curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION; } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) { curTag = &bag16; curIsBag = true; @@ -994,6 +1353,7 @@ status_t compileResourceFile(Bundle* bundle, curTag = &array16; curType = array16; curIsBag = true; + curIsBagReplaceOnOverwrite = true; ssize_t formatIdx = block.indexOfAttribute(NULL, "format"); if (formatIdx >= 0) { String16 formatStr = String16(block.getAttributeStringValue( @@ -1008,16 +1368,34 @@ status_t compileResourceFile(Bundle* bundle, } } } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) { + // Check whether these strings need valid formats. + // (simplified form of what string16 does above) + size_t n = block.getAttributeCount(); + for (size_t i = 0; i < n; i++) { + size_t length; + const uint16_t* attr = block.getAttributeName(i, &length); + if (strcmp16(attr, translatable16.string()) == 0 + || strcmp16(attr, formatted16.string()) == 0) { + const uint16_t* value = block.getAttributeStringValue(i, &length); + if (strcmp16(value, false16.string()) == 0) { + curIsFormatted = false; + break; + } + } + } + curTag = &string_array16; curType = array16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsBag = true; + curIsBagReplaceOnOverwrite = true; curIsPseudolocalizable = true; } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) { curTag = &integer_array16; curType = array16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER; curIsBag = true; + curIsBagReplaceOnOverwrite = true; } else { SourcePos(in->getPrintableSource(), block.getLineNumber()).error( "Found tag %s where item is expected\n", @@ -1036,6 +1414,12 @@ status_t compileResourceFile(Bundle* bundle, hasErrors = localHasErrors = true; } + String16 product; + identIdx = block.indexOfAttribute(NULL, "product"); + if (identIdx >= 0) { + product = String16(block.getAttributeStringValue(identIdx, &len)); + } + String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr); if (curIsBag) { @@ -1052,8 +1436,10 @@ status_t compileResourceFile(Bundle* bundle, } if (!localHasErrors) { - err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), - myPackage, curType, ident, parentIdent, &curParams); + err = outTable->startBag(SourcePos(in->getPrintableSource(), + block.getLineNumber()), myPackage, curType, ident, + parentIdent, &curParams, + overwrite, curIsBagReplaceOnOverwrite); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1124,7 +1510,8 @@ status_t compileResourceFile(Bundle* bundle, block.getPosition(&parserPosition); err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, - ident, parentIdent, itemIdent, curFormat, false, outTable); + ident, parentIdent, itemIdent, curFormat, curIsFormatted, + product, false, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) && bundle->getPseudolocalize()) { @@ -1132,8 +1519,8 @@ status_t compileResourceFile(Bundle* bundle, #if 1 block.setPosition(parserPosition); err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, - curType, ident, parentIdent, itemIdent, curFormat, true, - outTable); + curType, ident, parentIdent, itemIdent, curFormat, + curIsFormatted, product, true, overwrite, outTable); #endif } } @@ -1156,7 +1543,8 @@ status_t compileResourceFile(Bundle* bundle, block.getPosition(&parserPosition); err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, - *curTag, curIsStyled, curFormat, false, outTable); + *curTag, curIsStyled, curFormat, curIsFormatted, + product, false, overwrite, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; @@ -1167,7 +1555,9 @@ status_t compileResourceFile(Bundle* bundle, // pseudolocalize here block.setPosition(parserPosition); err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, - ident, *curTag, curIsStyled, curFormat, true, outTable); + ident, *curTag, curIsStyled, curFormat, + curIsFormatted, product, + true, overwrite, outTable); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1249,7 +1639,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp* style, const ResTable_config* params, const bool doSetIndex, - const int32_t format) + const int32_t format, + const bool overwrite) { // Check for adding entries in other packages... for now we do // nothing. We need to do the right thing here to support skinning. @@ -1331,12 +1722,13 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos, String8(value).string()); } #endif - - sp e = getEntry(package, type, name, sourcePos, params, doSetIndex); + + sp e = getEntry(package, type, name, sourcePos, overwrite, + params, doSetIndex); if (e == NULL) { return UNKNOWN_ERROR; } - status_t err = e->setItem(sourcePos, value, style, format); + status_t err = e->setItem(sourcePos, value, style, format, overwrite); if (err == NO_ERROR) { mNumLocal++; } @@ -1349,8 +1741,11 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, const String16& name, const String16& bagParent, const ResTable_config* params, + bool overlay, bool replace, bool isId) { + status_t result = NO_ERROR; + // Check for adding entries in other packages... for now we do // nothing. We need to do the right thing here to support skinning. uint32_t rid = mAssets->getIncludedResources() @@ -1367,25 +1762,41 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - - sp e = getEntry(package, type, name, sourcePos, params); + if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) { + bool canAdd = false; + sp p = mPackages.valueFor(package); + if (p != NULL) { + sp t = p->getTypes().valueFor(type); + if (t != NULL) { + if (t->getCanAddEntries().indexOf(name) >= 0) { + canAdd = true; + } + } + } + if (!canAdd) { + sourcePos.error("Resource does not already exist in overlay at '%s'; use to add.\n", + String8(name).string()); + return UNKNOWN_ERROR; + } + } + sp e = getEntry(package, type, name, sourcePos, overlay, params); if (e == NULL) { return UNKNOWN_ERROR; } // If a parent is explicitly specified, set it. if (bagParent.size() > 0) { - String16 curPar = e->getParent(); - if (curPar.size() > 0 && curPar != bagParent) { - sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n", - String8(e->getParent()).string(), - String8(bagParent).string()); - return UNKNOWN_ERROR; - } e->setParent(bagParent); } - - return e->makeItABag(sourcePos); + + if ((result = e->makeItABag(sourcePos)) != NO_ERROR) { + return result; + } + + if (overlay && replace) { + return e->emptyBag(sourcePos); + } + return result; } status_t ResourceTable::addBag(const SourcePos& sourcePos, @@ -1415,21 +1826,13 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - - sp e = getEntry(package, type, name, sourcePos, params); + sp e = getEntry(package, type, name, sourcePos, replace, params); if (e == NULL) { return UNKNOWN_ERROR; } // If a parent is explicitly specified, set it. if (bagParent.size() > 0) { - String16 curPar = e->getParent(); - if (curPar.size() > 0 && curPar != bagParent) { - sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n", - String8(e->getParent()).string(), - String8(bagParent).string()); - return UNKNOWN_ERROR; - } e->setParent(bagParent); } @@ -1466,6 +1869,37 @@ bool ResourceTable::hasBagOrEntry(const String16& package, return false; } +bool ResourceTable::hasBagOrEntry(const String16& package, + const String16& type, + const String16& name, + const ResTable_config& config) const +{ + // First look for this in the included resources... + uint32_t rid = mAssets->getIncludedResources() + .identifierForName(name.string(), name.size(), + type.string(), type.size(), + package.string(), package.size()); + if (rid != 0) { + return true; + } + + sp p = mPackages.valueFor(package); + if (p != NULL) { + sp t = p->getTypes().valueFor(type); + if (t != NULL) { + sp c = t->getConfigs().valueFor(name); + if (c != NULL) { + sp e = c->getEntries().valueFor(config); + if (e != NULL) { + return true; + } + } + } + } + + return false; +} + bool ResourceTable::hasBagOrEntry(const String16& ref, const String16* defType, const String16* defPackage) @@ -1525,6 +1959,15 @@ bool ResourceTable::appendTypeComment(const String16& package, return false; } +void ResourceTable::canAddEntry(const SourcePos& pos, + const String16& package, const String16& type, const String16& name) +{ + sp t = getType(package, type, pos); + if (t != NULL) { + t->canAddEntry(name); + } +} + size_t ResourceTable::size() const { return mPackages.size(); } @@ -1553,23 +1996,34 @@ inline uint32_t ResourceTable::getResId(const sp& p, uint32_t ResourceTable::getResId(const String16& package, const String16& type, - const String16& name) const + const String16& name, + bool onlyPublic) const { + uint32_t id = ResourceIdCache::lookup(package, type, name, onlyPublic); + if (id != 0) return id; // cache hit + sp p = mPackages.valueFor(package); if (p == NULL) return 0; // First look for this in the included resources... + uint32_t specFlags = 0; uint32_t rid = mAssets->getIncludedResources() .identifierForName(name.string(), name.size(), type.string(), type.size(), - package.string(), package.size()); + package.string(), package.size(), + &specFlags); if (rid != 0) { + if (onlyPublic) { + if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) { + return 0; + } + } + if (Res_INTERNALID(rid)) { - return rid; + return ResourceIdCache::store(package, type, name, onlyPublic, rid); } - return Res_MAKEID(p->getAssignedId()-1, - Res_GETTYPE(rid), - Res_GETENTRY(rid)); + return ResourceIdCache::store(package, type, name, onlyPublic, + Res_MAKEID(p->getAssignedId()-1, Res_GETTYPE(rid), Res_GETENTRY(rid))); } sp t = p->getTypes().valueFor(type); @@ -1578,19 +2032,23 @@ uint32_t ResourceTable::getResId(const String16& package, if (c == NULL) return 0; int32_t ei = c->getEntryIndex(); if (ei < 0) return 0; - return getResId(p, t, ei); + + return ResourceIdCache::store(package, type, name, onlyPublic, + getResId(p, t, ei)); } uint32_t ResourceTable::getResId(const String16& ref, const String16* defType, const String16* defPackage, - const char** outErrorMsg) const + const char** outErrorMsg, + bool onlyPublic) const { String16 package, type, name; + bool refOnlyPublic = true; if (!ResTable::expandResourceRef( ref.string(), ref.size(), &package, &type, &name, defType, defPackage ? defPackage:&mAssetsPackage, - outErrorMsg)) { + outErrorMsg, &refOnlyPublic)) { NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string())); NOISY(printf("Expanding resource: defType=%s\n", @@ -1603,7 +2061,7 @@ uint32_t ResourceTable::getResId(const String16& ref, String8(name).string())); return 0; } - uint32_t res = getResId(package, type, name); + uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic); NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", String8(package).string(), String8(type).string(), String8(name).string(), res)); @@ -1638,7 +2096,8 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool, uint32_t attrID, const Vector* style, String16* outStr, void* accessorCookie, - uint32_t attrType) + uint32_t attrType, const String8* configTypeName, + const ConfigDescription* config) { String16 finalStr; @@ -1666,10 +2125,19 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool, if (outValue->dataType == outValue->TYPE_STRING) { // Should do better merging styles. if (pool) { + String8 configStr; + if (config != NULL) { + configStr = config->toString(); + } else { + configStr = "(null)"; + } + NOISY(printf("Adding to pool string style #%d config %s: %s\n", + style != NULL ? style->size() : 0, + configStr.string(), String8(finalStr).string())); if (style != NULL && style->size() > 0) { - outValue->data = pool->add(finalStr, *style); + outValue->data = pool->add(finalStr, *style, configTypeName, config); } else { - outValue->data = pool->add(finalStr, true); + outValue->data = pool->add(finalStr, true, configTypeName, config); } } else { // Caller will fill this in later. @@ -1841,10 +2309,8 @@ bool ResourceTable::getAttributeFlags( const char16_t* end = name + nameLen; const char16_t* pos = name; - bool failed = false; - while (pos < end && !failed) { + while (pos < end) { const char16_t* start = pos; - end++; while (pos < end && *pos != '|') { pos++; } @@ -1870,9 +2336,7 @@ bool ResourceTable::getAttributeFlags( // Didn't find this flag identifier. return false; } - if (pos < end) { - pos++; - } + pos++; } return true; @@ -2036,114 +2500,88 @@ status_t ResourceTable::addSymbols(const sp& outSymbols) { } -status_t -ResourceFilter::parse(const char* arg) +void +ResourceTable::addLocalization(const String16& name, const String8& locale) { - if (arg == NULL) { - return 0; - } - - const char* p = arg; - const char* q; - - while (true) { - q = strchr(p, ','); - if (q == NULL) { - q = p + strlen(p); - } + mLocalizations[name].insert(locale); +} - String8 part(p, q-p); - if (part == "zz_ZZ") { - mContainsPseudo = true; - } - int axis; - uint32_t value; - if (AaptGroupEntry::parseNamePart(part, &axis, &value)) { - fprintf(stderr, "Invalid configuration: %s\n", arg); - fprintf(stderr, " "); - for (int i=0; i()); - } - SortedVector& sv = mData.editValueFor(axis); - sv.add(value); - // if it's a locale with a region, also match an unmodified locale of the - // same language - if (axis == AXIS_LANGUAGE) { - if (value & 0xffff0000) { - sv.add(value & 0x0000ffff); +/*! + * Flag various sorts of localization problems. '+' indicates checks already implemented; + * '-' indicates checks that will be implemented in the future. + * + * + A localized string for which no default-locale version exists => warning + * + A string for which no version in an explicitly-requested locale exists => warning + * + A localized translation of an translateable="false" string => warning + * - A localized string not provided in every locale used by the table + */ +status_t +ResourceTable::validateLocalizations(void) +{ + status_t err = NO_ERROR; + const String8 defaultLocale; + + // For all strings... + for (map >::iterator nameIter = mLocalizations.begin(); + nameIter != mLocalizations.end(); + nameIter++) { + const set& configSet = nameIter->second; // naming convenience + + // Look for strings with no default localization + if (configSet.count(defaultLocale) == 0) { + fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:", + String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]); + for (set::const_iterator locales = configSet.begin(); + locales != configSet.end(); + locales++) { + fprintf(stdout, " %s", (*locales).string()); } + fprintf(stdout, "\n"); + // !!! TODO: throw an error here in some circumstances } - p = q; - if (!*p) break; - p++; - } - - return NO_ERROR; -} -bool -ResourceFilter::match(int axis, uint32_t value) -{ - if (value == 0) { - // they didn't specify anything so take everything - return true; - } - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - // we didn't request anything on this axis so take everything - return true; - } - const SortedVector& sv = mData.valueAt(index); - return sv.indexOf(value) >= 0; -} + // Check that all requested localizations are present for this string + if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) { + const char* allConfigs = mBundle->getConfigurations(); + const char* start = allConfigs; + const char* comma; + + do { + String8 config; + comma = strchr(start, ','); + if (comma != NULL) { + config.setTo(start, comma - start); + start = comma + 1; + } else { + config.setTo(start); + } -bool -ResourceFilter::match(const ResTable_config& config) -{ - if (config.locale) { - uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16) - | (config.language[1] << 8) | (config.language[0]); - if (!match(AXIS_LANGUAGE, locale)) { - return false; + // don't bother with the pseudolocale "zz_ZZ" + if (config != "zz_ZZ") { + if (configSet.find(config) == configSet.end()) { + // okay, no specific localization found. it's possible that we are + // requiring a specific regional localization [e.g. de_DE] but there is an + // available string in the generic language localization [e.g. de]; + // consider that string to have fulfilled the localization requirement. + String8 region(config.string(), 2); + if (configSet.find(region) == configSet.end()) { + if (configSet.count(defaultLocale) == 0) { + fprintf(stdout, "aapt: warning: " + "**** string '%s' has no default or required localization " + "for '%s' in %s\n", + String8(nameIter->first).string(), + config.string(), + mBundle->getResourceSourceDirs()[0]); + } + } + } + } + } while (comma != NULL); } } - if (!match(AXIS_ORIENTATION, config.orientation)) { - return false; - } - if (!match(AXIS_DENSITY, config.density)) { - return false; - } - if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) { - return false; - } - if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) { - return false; - } - if (!match(AXIS_KEYBOARD, config.keyboard)) { - return false; - } - if (!match(AXIS_NAVIGATION, config.navigation)) { - return false; - } - if (!match(AXIS_SCREENSIZE, config.screenSize)) { - return false; - } - if (!match(AXIS_VERSION, config.version)) { - return false; - } - return true; + + return err; } status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) @@ -2154,12 +2592,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) return err; } + const ConfigDescription nullConfig; + const size_t N = mOrderedPackages.size(); size_t pi; + const static String16 mipmap16("mipmap"); + + bool useUTF8 = !bundle->getUTF16StringsOption(); + // Iterate through all data, collecting all values (strings, // references, etc). - StringPool valueStrings; + StringPool valueStrings(useUTF8); + Vector > allEntries; for (pi=0; pi p = mOrderedPackages.itemAt(pi); if (p->getTypes().size() == 0) { @@ -2167,8 +2612,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) continue; } - StringPool typeStrings; - StringPool keyStrings; + StringPool typeStrings(useUTF8); + StringPool keyStrings(useUTF8); const size_t N = p->getOrderedTypes().size(); for (size_t ti=0; ti& dest) typeStrings.add(String16(""), false); continue; } - typeStrings.add(t->getName(), false); + const String16 typeName(t->getName()); + typeStrings.add(typeName, false); + + // This is a hack to tweak the sorting order of the final strings, + // to put stuff that is generally not language-specific first. + String8 configTypeName(typeName); + if (configTypeName == "drawable" || configTypeName == "layout" + || configTypeName == "color" || configTypeName == "anim" + || configTypeName == "interpolator" || configTypeName == "animator" + || configTypeName == "xml" || configTypeName == "menu" + || configTypeName == "mipmap" || configTypeName == "raw") { + configTypeName = "1complex"; + } else { + configTypeName = "2value"; + } + + const bool filterable = (typeName != mipmap16); const size_t N = t->getOrderedConfigs().size(); for (size_t ci=0; ci& dest) } const size_t N = c->getEntries().size(); for (size_t ei=0; eigetEntries().keyAt(ei); + if (filterable && !filter.match(config)) { + continue; + } sp e = c->getEntries().valueAt(ei); if (e == NULL) { continue; } e->setNameIndex(keyStrings.add(e->getName(), true)); - status_t err = e->prepareFlatten(&valueStrings, this); + + // If this entry has no values for other configs, + // and is the default config, then it is special. Otherwise + // we want to add it with the config info. + ConfigDescription* valueConfig = NULL; + if (N != 1 || config == nullConfig) { + valueConfig = &config; + } + + status_t err = e->prepareFlatten(&valueStrings, this, + &configTypeName, &config); if (err != NO_ERROR) { return err; } + allEntries.add(e); } } } @@ -2204,6 +2680,17 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) p->setKeyStrings(keyStrings.createStringBlock()); } + if (bundle->getOutputAPKFile() != NULL) { + // Now we want to sort the value strings for better locality. This will + // cause the positions of the strings to change, so we need to go back + // through out resource entries and update them accordingly. Only need + // to do this if actually writing the output file. + valueStrings.sortByConfig(); + for (pi=0; piremapStringValue(&valueStrings); + } + } + ssize_t strAmt = 0; // Now build the array of package chunks. @@ -2264,7 +2751,15 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) "Type name %s not found", String8(typeName).string()); + const bool filterable = (typeName != mipmap16); + const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0; + + // Until a non-NO_ENTRY value has been written for a resource, + // that resource is invalid; validResources[i] represents + // the item at t->getOrderedConfigs().itemAt(i). + Vector validResources; + validResources.insertAt(false, 0, N); // First write the typeSpec chunk, containing information about // each resource entry in this type. @@ -2288,7 +2783,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) (((uint8_t*)data->editData()) + typeSpecStart + sizeof(ResTable_typeSpec)); memset(typeSpecFlags, 0, sizeof(uint32_t)*N); - + for (size_t ei=0; ei cl = t->getOrderedConfigs().itemAt(ei); if (cl->getPublic()) { @@ -2296,11 +2791,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) } const size_t CN = cl->getEntries().size(); for (size_t ci=0; cigetEntries().keyAt(ci))) { + if (filterable && !filter.match(cl->getEntries().keyAt(ci))) { continue; } for (size_t cj=ci+1; cjgetEntries().keyAt(cj))) { + if (filterable && !filter.match(cl->getEntries().keyAt(cj))) { continue; } typeSpecFlags[ei] |= htodl( @@ -2320,7 +2815,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) ConfigDescription config = t->getUniqueConfigs().itemAt(ci); NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " + "sw%ddp w%ddp h%ddp dir:%d\n", ti+1, config.mcc, config.mnc, config.language[0] ? config.language[0] : '-', @@ -2328,15 +2824,20 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) config.country[0] ? config.country[0] : '-', config.country[1] ? config.country[1] : '-', config.orientation, + config.uiMode, config.touchscreen, config.density, config.keyboard, config.inputFlags, config.navigation, config.screenWidth, - config.screenHeight)); + config.screenHeight, + config.smallestScreenWidthDp, + config.screenWidthDp, + config.screenHeightDp, + config.layoutDirection)); - if (!filter.match(config)) { + if (filterable && !filter.match(config)) { continue; } @@ -2357,7 +2858,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) tHeader->entriesStart = htodl(typeSize); tHeader->config = config; NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " + "sw%ddp w%ddp h%ddp dir:%d\n", ti+1, tHeader->config.mcc, tHeader->config.mnc, tHeader->config.language[0] ? tHeader->config.language[0] : '-', @@ -2365,13 +2867,18 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) tHeader->config.country[0] ? tHeader->config.country[0] : '-', tHeader->config.country[1] ? tHeader->config.country[1] : '-', tHeader->config.orientation, + tHeader->config.uiMode, tHeader->config.touchscreen, tHeader->config.density, tHeader->config.keyboard, tHeader->config.inputFlags, tHeader->config.navigation, tHeader->config.screenWidth, - tHeader->config.screenHeight)); + tHeader->config.screenHeight, + tHeader->config.smallestScreenWidthDp, + tHeader->config.screenWidthDp, + tHeader->config.screenHeightDp, + tHeader->config.layoutDirection)); tHeader->config.swapHtoD(); // Build the entries inside of this type. @@ -2391,6 +2898,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) if (amt < 0) { return amt; } + validResources.editItemAt(ei) = true; } else { index[ei] = htodl(ResTable_type::NO_ENTRY); } @@ -2401,6 +2909,14 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) (((uint8_t*)data->editData()) + typeStart); tHeader->header.size = htodl(data->getSize()-typeStart); } + + for (size_t i = 0; i < N; ++i) { + if (!validResources[i]) { + sp c = t->getOrderedConfigs().itemAt(i); + fprintf(stderr, "warning: no entries written for %s/%s\n", + String8(typeName).string(), String8(c->getName()).string()); + } + } } // Fill in the rest of the package information. @@ -2579,7 +3095,8 @@ status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos) status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos, const String16& value, const Vector* style, - int32_t format) + int32_t format, + const bool overwrite) { Item item(sourcePos, false, value, style); @@ -2591,14 +3108,14 @@ status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos, item.sourcePos.file.string(), item.sourcePos.line); return UNKNOWN_ERROR; } - if (mType != TYPE_UNKNOWN) { + if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) { sourcePos.error("Resource entry %s is already defined.\n" "%s:%d: Originally defined here.\n", String8(mName).string(), mItem.sourcePos.file.string(), mItem.sourcePos.line); return UNKNOWN_ERROR; } - + mType = TYPE_ITEM; mItem = item; mItemFormat = format; @@ -2639,6 +3156,17 @@ status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos, return NO_ERROR; } +status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos) +{ + status_t err = makeItABag(sourcePos); + if (err != NO_ERROR) { + return err; + } + + mBag.clear(); + return NO_ERROR; +} + status_t ResourceTable::Entry::generateAttributes(ResourceTable* table, const String16& package) { @@ -2719,14 +3247,16 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table, return hasErrors ? UNKNOWN_ERROR : NO_ERROR; } -status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table) +status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table, + const String8* configTypeName, const ConfigDescription* config) { if (mType == TYPE_ITEM) { Item& it = mItem; AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value)); if (!table->stringToValue(&it.parsedValue, strings, it.value, false, true, 0, - &it.style, NULL, &ac, mItemFormat)) { + &it.style, NULL, &ac, mItemFormat, + configTypeName, config)) { return UNKNOWN_ERROR; } } else if (mType == TYPE_BAG) { @@ -2737,7 +3267,8 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable AccessorCookie ac(it.sourcePos, String8(key), String8(it.value)); if (!table->stringToValue(&it.parsedValue, strings, it.value, false, true, it.bagKeyId, - &it.style, NULL, &ac, it.format)) { + &it.style, NULL, &ac, it.format, + configTypeName, config)) { return UNKNOWN_ERROR; } } @@ -2749,6 +3280,29 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable return NO_ERROR; } +status_t ResourceTable::Entry::remapStringValue(StringPool* strings) +{ + if (mType == TYPE_ITEM) { + Item& it = mItem; + if (it.parsedValue.dataType == Res_value::TYPE_STRING) { + it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data); + } + } else if (mType == TYPE_BAG) { + const size_t N = mBag.size(); + for (size_t i=0; imapOriginalPosToNewPos(it.parsedValue.data); + } + } + } else { + mPos.error("Error: entry %s is not a single item or a bag.\n", + String8(mName).string()); + return UNKNOWN_ERROR; + } + return NO_ERROR; +} + ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp& data, bool isPublic) { size_t amt = 0; @@ -2904,14 +3458,27 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos, return NO_ERROR; } +void ResourceTable::Type::canAddEntry(const String16& name) +{ + mCanAddEntries.add(name); +} + sp ResourceTable::Type::getEntry(const String16& entry, const SourcePos& sourcePos, const ResTable_config* config, - bool doSetIndex) + bool doSetIndex, + bool overlay, + bool autoAddOverlay) { int pos = -1; sp c = mConfigs.valueFor(entry); if (c == NULL) { + if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) { + sourcePos.error("Resource at %s appears in overlay but not" + " in the base package; use to add.\n", + String8(entry).string()); + return NULL; + } c = new ConfigList(entry, sourcePos); mConfigs.add(entry, c); pos = (int)mOrderedConfigs.size(); @@ -2928,7 +3495,8 @@ sp ResourceTable::Type::getEntry(const String16& entry, if (e == NULL) { if (config != NULL) { NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d " + "sw%ddp w%ddp h%ddp dir:%d\n", sourcePos.file.string(), sourcePos.line, config->mcc, config->mnc, config->language[0] ? config->language[0] : '-', @@ -2942,7 +3510,11 @@ sp ResourceTable::Type::getEntry(const String16& entry, config->inputFlags, config->navigation, config->screenWidth, - config->screenHeight)); + config->screenHeight, + config->smallestScreenWidthDp, + config->screenWidthDp, + config->screenHeightDp, + config->layoutDirection)); } else { NOISY(printf("New entry at %s:%d: NULL config\n", sourcePos.file.string(), sourcePos.line)); @@ -3210,6 +3782,7 @@ sp ResourceTable::getEntry(const String16& package, const String16& type, const String16& name, const SourcePos& sourcePos, + bool overlay, const ResTable_config* config, bool doSetIndex) { @@ -3217,7 +3790,7 @@ sp ResourceTable::getEntry(const String16& package, if (t == NULL) { return NULL; } - return t->getEntry(name, sourcePos, config, doSetIndex); + return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay()); } sp ResourceTable::getEntry(uint32_t resID, @@ -3236,26 +3809,26 @@ sp ResourceTable::getEntry(uint32_t resID, } if (p == NULL) { - fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID); + fprintf(stderr, "warning: Package not found for resource #%08x\n", resID); return NULL; } int tid = Res_GETTYPE(resID); if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) { - fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID); + fprintf(stderr, "warning: Type not found for resource #%08x\n", resID); return NULL; } sp t = p->getOrderedTypes()[tid]; int eid = Res_GETENTRY(resID); if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) { - fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID); + fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID); return NULL; } sp c = t->getOrderedConfigs()[eid]; if (c == NULL) { - fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID); + fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID); return NULL; } @@ -3263,7 +3836,7 @@ sp ResourceTable::getEntry(uint32_t resID, if (config) cdesc = *config; sp e = c->getEntries().valueFor(cdesc); if (c == NULL) { - fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID); + fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID); return NULL; } @@ -3281,7 +3854,7 @@ const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrI for (size_t i=0; igetBag().valueAt(i); if (it.bagKeyId == 0) { - fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n", + fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n", String8(e->getName()).string(), String8(e->getBag().keyAt(i)).string()); } @@ -3309,7 +3882,7 @@ bool ResourceTable::getItemValue( break; } } - fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n", + fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n", String8(e->getName()).string(), String8(e->getBag().keyAt(i)).string()); return false; @@ -3330,4 +3903,3 @@ bool ResourceTable::getItemValue( } return res; } -
ConstantValueDescription
ConstantValueDescription
")); + enumOrFlagsComment.append(String16("\n
")); enumOrFlagsComment.append(itemIdent); - enumOrFlagsComment.append(String16("")); + enumOrFlagsComment.append(String16("")); enumOrFlagsComment.append(value); - enumOrFlagsComment.append(String16("")); + enumOrFlagsComment.append(String16("")); if (block.getComment(&len)) { enumOrFlagsComment.append(String16(block.getComment(&len))); } - enumOrFlagsComment.append(String16("