#include <stdarg.h>
#define NOISY(x) //x
-#define NOISY_REF(x) //x
status_t compileXmlFile(const sp<AaptAssets>& assets,
const sp<AaptFile>& target,
const String16& parentIdent,
const String16& itemIdent,
int32_t curFormat,
+ bool isFormatted,
+ const String16& product,
bool pseudolocalize,
const bool overwrite,
ResourceTable* outTable)
String16 str;
Vector<StringPool::entry_style_span> spans;
err = parseStyledString(bundle, in->getPrintableSource().string(),
- block, item16, &str, &spans,
+ block, item16, &str, &spans, isFormatted,
pseudolocalize);
if (err != NO_ERROR) {
return err;
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<AaptFile>& in,
const String16& curTag,
bool curIsStyled,
int32_t curFormat,
+ bool isFormatted,
+ const String16& product,
bool pseudolocalize,
const bool overwrite,
ResourceTable* outTable)
Vector<StringPool::entry_style_span> 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],
// 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<String16, uint32_t> nextPublicId(0);
ResXMLTree::event_code_t code;
bool curIsBagReplaceOnOverwrite = false;
bool curIsStyled = false;
bool curIsPseudolocalizable = false;
+ bool curIsFormatted = fileIsTranslatable;
bool localHasErrors = false;
if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
String8 locale(rawLocale);
String16 name;
String16 translatable;
+ String16 formatted;
size_t n = block.getAttributeCount();
for (size_t i = 0; i < n; i++) {
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"
} else {
outTable->addLocalization(name, locale);
}
+
+ if (formatted == false16) {
+ curIsFormatted = false;
+ }
}
curTag = &string16;
}
}
} 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;
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) {
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()) {
#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
}
}
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;
// 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;
}
// 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);
}
// 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);
}
// information we have already processed that string!
outValue->size = sizeof(Res_value);
outValue->res0 = 0;
- outValue->dataType = Res_value::TYPE_STRING;
+ outValue->dataType = outValue->TYPE_STRING;
outValue->data = 0;
finalStr = str;
}
return false;
}
- if (outValue->dataType == Res_value::TYPE_STRING) {
+ if (outValue->dataType == outValue->TYPE_STRING) {
// Should do better merging styles.
if (pool) {
if (style != NULL && style->size() > 0) {
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<String8>::iterator locales = configSet.begin();
+ for (set<String8>::const_iterator locales = configSet.begin();
locales != configSet.end();
locales++) {
fprintf(stdout, " %s", (*locales).string());
}
bool
-ResourceFilter::match(int axis, uint32_t value)
+ResourceFilter::match(int axis, uint32_t value) const
{
if (value == 0) {
// they didn't specify anything so take everything
}
bool
-ResourceFilter::match(const ResTable_config& config)
+ResourceFilter::match(const ResTable_config& config) const
{
if (config.locale) {
uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
const size_t N = mOrderedPackages.size();
size_t pi;
+ const static String16 mipmap16("mipmap");
+
+ bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
+
// Iterate through all data, collecting all values (strings,
// references, etc).
- StringPool valueStrings = StringPool(false, bundle->getUTF8());
- ResourceConfigReferences configRefs;
+ StringPool valueStrings = StringPool(false, useUTF8);
for (pi=0; pi<N; pi++) {
sp<Package> p = mOrderedPackages.itemAt(pi);
if (p->getTypes().size() == 0) {
continue;
}
- StringPool typeStrings = StringPool(false, bundle->getUTF8());
- StringPool keyStrings = StringPool(false, bundle->getUTF8());
+ StringPool typeStrings = StringPool(false, useUTF8);
+ StringPool keyStrings = StringPool(false, useUTF8);
const size_t N = p->getOrderedTypes().size();
for (size_t ti=0; ti<N; ti++) {
typeStrings.add(String16("<empty>"), false);
continue;
}
- typeStrings.add(t->getName(), false);
+ const String16 typeName(t->getName());
+ typeStrings.add(typeName, false);
+
+ const bool filterable = (typeName != mipmap16);
const size_t N = t->getOrderedConfigs().size();
for (size_t ci=0; ci<N; ci++) {
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
ConfigDescription config = c->getEntries().keyAt(ei);
- if (!filter.match(config)) {
+ if (filterable && !filter.match(config)) {
continue;
}
sp<Entry> e = c->getEntries().valueAt(ei);
if (err != NO_ERROR) {
return err;
}
- if (e->getType() == Entry::TYPE_ITEM) {
- const Item* item = e->getItem();
- if (item != NULL) {
- uint32_t poolIndex = item->parsedValue.data;
- configRefs.add(poolIndex, config);
- }
- }
}
}
}
p->setKeyStrings(keyStrings.createStringBlock());
}
- NOISY_REF(configRefs.dump();)
-
- // Trim all entries in config tables that are not roots for that string.
- // i.e., rely on the resource system to grab the string from the more
- // generic pool during runtime to save space.
- for (pi=0; pi<N; pi++) {
- sp<Package> p = mOrderedPackages.itemAt(pi);
- if (p->getTypes().size() == 0) {
- // Empty, skip!
- continue;
- }
- const size_t TN = p->getOrderedTypes().size();
- for (size_t ti=0; ti<TN; ti++) {
- sp<Type> t = p->getOrderedTypes().itemAt(ti);
- if (t == NULL) {
- continue;
- }
- size_t CN = t->getOrderedConfigs().size();
- for (size_t ci=0; ci<CN; ci++) {
- sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
- if (c == NULL) {
- continue;
- }
- DefaultKeyedVector<ConfigDescription, sp<Entry> > newEntries;
- size_t EN = c->getEntries().size();
- for (size_t ei=0; ei<EN; ++ei) {
- ConfigDescription config = c->getEntries().keyAt(ei);
- if (!filter.match(config)) {
- continue;
- }
- sp<Entry> e = c->getEntries().valueAt(ei);
- if (e == NULL) {
- continue;
- }
- if (e->getType() == Entry::TYPE_ITEM) {
- const Item* item = e->getItem();
- if (item != NULL) {
- uint32_t poolIndex = item->parsedValue.data;
- if (!configRefs.isRoot(poolIndex, config)) {
- NOISY_REF(
- printf(" ConfigRef %d: removing ", poolIndex);
- DEBUG_LANG(config)
- printf("\n");
- )
- c->removeEntryAt(ei);
- t->removeUniqueConfig(config);
- --ei; --EN;
- }
- }
- }
- }
- if (EN == 0) {
- // We removed all the entries from a config, so remove the
- // config itself.
- NOISY_REF(
- printf(" ConfigRef REMOVED ENTIRE CONFIG\n");
- )
- t->removeOrderedConfigAt(ci);
- --ci; --CN;
- }
- }
- }
- }
-
ssize_t strAmt = 0;
// Now build the array of package chunks.
"Type name %s not found",
String8(typeName).string());
+ const bool filterable = (typeName != mipmap16);
+
const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
// First write the typeSpec chunk, containing information about
(((uint8_t*)data->editData())
+ typeSpecStart + sizeof(ResTable_typeSpec));
memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
-
+
for (size_t ei=0; ei<N; ei++) {
sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
if (cl->getPublic()) {
}
const size_t CN = cl->getEntries().size();
for (size_t ci=0; ci<CN; ci++) {
- if (!filter.match(cl->getEntries().keyAt(ci))) {
+ if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
continue;
}
for (size_t cj=ci+1; cj<CN; cj++) {
- if (!filter.match(cl->getEntries().keyAt(cj))) {
+ if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
continue;
}
typeSpecFlags[ei] |= htodl(
config.screenWidth,
config.screenHeight));
- if (!filter.match(config)) {
+ if (filterable && !filter.match(config)) {
continue;
}
}
return res;
}
-
-#define DEBUG_LANG(x) printf("lang=%c%c cnt=%c%c ", \
- (x).language[0] ? (x).language[0] : '-', \
- (x).language[1] ? (x).language[1] : '-', \
- (x).country[0] ? (x).country[0] : '-', \
- (x).country[1] ? (x).country[1] : '-');
-
-status_t ResourceConfigReferences::add(uint32_t id, const ResTable_config& config)
-{
- ssize_t index = mRoots.indexOfKey(id);
- if (index < 0) {
- index = mRoots.add(id, Vector<const ResTable_config*>());
- }
- Vector<const ResTable_config*>& configRoots = mRoots.editValueFor(id);
-
- if (!configRoots.isEmpty()) {
- ssize_t NR = configRoots.size();
- for (int ri=0; ri<NR; ++ri) {
- const ResTable_config* current = configRoots[ri];
-
- if (config.match(*current)) {
- // We already have something more generic than our incoming string.
- NOISY_REF(
- printf(" ConfigRef %d: ignoring ", id);
- DEBUG_LANG(config)
- printf("\n");
- )
- return NO_ERROR;
- } else if (current->match(config)) {
- // more generic
- NOISY_REF(
- printf(" ConfigRef %d: remove ", id);
- DEBUG_LANG(current)
- printf("\n");
- )
- configRoots.removeItemsAt(ri);
- --ri; --NR;
- }
- }
- }
- NOISY_REF(
- printf(" ConfigRef %d: add ", id);
- DEBUG_LANG(config)
- printf("\n");
- )
- ResTable_config *configCopy = (ResTable_config*)malloc(sizeof(ResTable_config));
- memcpy(configCopy, &config, sizeof(ResTable_config));
- configRoots.add(configCopy);
-
- return NO_ERROR;
-}
-
-void ResourceConfigReferences::dump()
-{
- printf("ResourceConfigReferences\n");
- const ssize_t NR = mRoots.size();
- for (int ri=0; ri<NR; ++ri) {
- const Vector<const ResTable_config*>& configRoots = mRoots.valueAt(ri);
- printf(" String %d\n", mRoots.keyAt(ri));
- const ssize_t NC = configRoots.size();
- for (int ci=0; ci<NC; ++ci) {
- printf(" ");
- DEBUG_LANG(*configRoots[ci])
- printf("\n");
- }
- }
-}
-
-bool ResourceConfigReferences::isRoot(uint32_t id, const ResTable_config& config)
-{
- const Vector<const ResTable_config*>& configRoots = mRoots.editValueFor(id);
- const ssize_t NR = configRoots.size();
- for (int ri = 0; ri<NR; ++ri) {
- if (configRoots[ri]->match(config)) {
- return true;
- }
- }
- return false;
-}
-
-ResourceConfigReferences::~ResourceConfigReferences()
-{
- const ssize_t NR = mRoots.size();
- for (int ri=0; ri<NR; ++ri) {
- Vector<const ResTable_config*> configRoots = mRoots.editValueAt(ri);
- const ssize_t NC = configRoots.size();
- for (int ci=0; ci<NC; ++ci) {
- ResTable_config* config = const_cast<ResTable_config*>(configRoots[ci]);
- free(config);
- }
- }
-}