X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/cae1f49dce76fa9e98d85af80e24f252ccc6d273..d21107ab35ee636ae9baaf34b5b7d6e86a90ac24:/ResourceTable.cpp diff --git a/ResourceTable.cpp b/ResourceTable.cpp index 8dbc12e..818c3c6 100644 --- a/ResourceTable.cpp +++ b/ResourceTable.cpp @@ -27,6 +27,20 @@ status_t compileXmlFile(const sp& assets, 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, @@ -39,6 +53,10 @@ status_t compileXmlFile(const sp& assets, root->removeWhitespace(false, NULL); } + if ((options&XML_COMPILE_UTF8) != 0) { + root->setUTF8(true); + } + bool hasErrors = false; if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) { @@ -378,7 +396,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); @@ -480,22 +498,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, @@ -555,6 +573,8 @@ 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) @@ -565,7 +585,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; @@ -587,6 +607,32 @@ status_t parseAndAddBag(Bundle* bundle, 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, @@ -598,6 +644,8 @@ 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) @@ -608,12 +656,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)) { + ; + } 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], @@ -663,6 +752,7 @@ status_t compileResourceFile(Bundle* bundle, const String16 public16("public"); const String16 public_padding16("public-padding"); const String16 private_symbols16("private-symbols"); + const String16 add_resource16("add-resource"); const String16 skip16("skip"); const String16 eat_comment16("eat-comment"); @@ -690,12 +780,18 @@ status_t compileResourceFile(Bundle* bundle, // 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; - + + bool fileIsTranslatable = true; + if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) { + fileIsTranslatable = false; + } + DefaultKeyedVector nextPublicId(0); ResXMLTree::event_code_t code; @@ -732,6 +828,7 @@ status_t compileResourceFile(Bundle* bundle, bool curIsBagReplaceOnOverwrite = false; bool curIsStyled = false; bool curIsPseudolocalizable = false; + bool curIsFormatted = fileIsTranslatable; bool localHasErrors = false; if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { @@ -960,6 +1057,36 @@ status_t compileResourceFile(Bundle* bundle, } 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()); @@ -1087,6 +1214,7 @@ status_t compileResourceFile(Bundle* bundle, String8 locale(rawLocale); String16 name; String16 translatable; + String16 formatted; size_t n = block.getAttributeCount(); for (size_t i = 0; i < n; i++) { @@ -1096,11 +1224,14 @@ status_t compileResourceFile(Bundle* bundle, 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" @@ -1118,6 +1249,10 @@ status_t compileResourceFile(Bundle* bundle, } else { outTable->addLocalization(name, locale); } + + if (formatted == false16) { + curIsFormatted = false; + } } curTag = &string16; @@ -1187,6 +1322,22 @@ 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; @@ -1217,6 +1368,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) { @@ -1307,8 +1464,8 @@ status_t compileResourceFile(Bundle* bundle, block.getPosition(&parserPosition); err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, - ident, parentIdent, itemIdent, curFormat, - false, overwrite, outTable); + ident, parentIdent, itemIdent, curFormat, curIsFormatted, + product, false, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) && bundle->getPseudolocalize()) { @@ -1316,8 +1473,8 @@ status_t compileResourceFile(Bundle* bundle, #if 1 block.setPosition(parserPosition); err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, - curType, ident, parentIdent, itemIdent, curFormat, true, - overwrite, outTable); + curType, ident, parentIdent, itemIdent, curFormat, + curIsFormatted, product, true, overwrite, outTable); #endif } } @@ -1340,7 +1497,8 @@ status_t compileResourceFile(Bundle* bundle, block.getPosition(&parserPosition); err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, - *curTag, curIsStyled, curFormat, false, overwrite, 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; @@ -1351,7 +1509,9 @@ status_t compileResourceFile(Bundle* bundle, // pseudolocalize here block.setPosition(parserPosition); err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, - ident, *curTag, curIsStyled, curFormat, true, overwrite, outTable); + ident, *curTag, curIsStyled, curFormat, + curIsFormatted, product, + true, overwrite, outTable); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1556,10 +1716,22 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - if (overlay && !hasBagOrEntry(package, type, name)) { - sourcePos.error("Can't add new bags in an overlay. See '%s'\n", - String8(name).string()); - return UNKNOWN_ERROR; + 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) { @@ -1568,13 +1740,6 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, // 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); } @@ -1622,13 +1787,6 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos, // 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); } @@ -1724,6 +1882,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(); } @@ -2312,13 +2479,12 @@ ResourceTable::validateLocalizations(void) String8 region(config.string(), 2); if (configSet.find(region) == configSet.end()) { if (configSet.count(defaultLocale) == 0) { - fprintf(stdout, "aapt: error: " - "*** string '%s' has no default or required localization " + 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]); - err = UNKNOWN_ERROR; } } } @@ -2417,6 +2583,12 @@ ResourceFilter::match(const ResTable_config& config) if (!match(AXIS_ORIENTATION, config.orientation)) { return false; } + if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) { + return false; + } + if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) { + return false; + } if (!match(AXIS_DENSITY, config.density)) { return false; } @@ -2452,9 +2624,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) const size_t N = mOrderedPackages.size(); size_t pi; + bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO); + // Iterate through all data, collecting all values (strings, // references, etc). - StringPool valueStrings; + StringPool valueStrings = StringPool(false, useUTF8); for (pi=0; pi p = mOrderedPackages.itemAt(pi); if (p->getTypes().size() == 0) { @@ -2462,8 +2636,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) continue; } - StringPool typeStrings; - StringPool keyStrings; + StringPool typeStrings = StringPool(false, useUTF8); + StringPool keyStrings = StringPool(false, useUTF8); const size_t N = p->getOrderedTypes().size(); for (size_t ti=0; ti& 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 w:%d h:%d\n", ti+1, config.mcc, config.mnc, config.language[0] ? config.language[0] : '-', @@ -2627,6 +2801,7 @@ 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, @@ -2656,7 +2831,7 @@ 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 w:%d h:%d\n", ti+1, tHeader->config.mcc, tHeader->config.mnc, tHeader->config.language[0] ? tHeader->config.language[0] : '-', @@ -2664,6 +2839,7 @@ 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, @@ -3215,18 +3391,25 @@ 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 overlay) + bool overlay, + bool autoAddOverlay) { int pos = -1; sp c = mConfigs.valueFor(entry); if (c == NULL) { - if (overlay == true) { - sourcePos.error("Resource %s appears in overlay but not" - " in the base package.\n", String8(entry).string()); + 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); @@ -3535,7 +3718,7 @@ sp ResourceTable::getEntry(const String16& package, if (t == NULL) { return NULL; } - return t->getEntry(name, sourcePos, config, doSetIndex, overlay); + return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay()); } sp ResourceTable::getEntry(uint32_t resID,
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("