X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/2728889bb51286e4003d3375302b20a402903d72..54f200b09c1a7a5e0ea5fe5d8f0601002b6040c9:/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"
- "Constant | Value | Description |
"));
+ "Constant | Value | Description |
"));
}
- enumOrFlagsComment.append(String16("\n"));
+ 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(" | "));
+ 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,