X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/dadd9c1fc18bd05c84a357b56e945b5829b3bd95..f56456f04916d9c21f4876a58a2a2b75262a9b03:/ResourceTable.cpp diff --git a/ResourceTable.cpp b/ResourceTable.cpp index 6fe196a..6e522a2 100644 --- a/ResourceTable.cpp +++ b/ResourceTable.cpp @@ -115,7 +115,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" @@ -546,6 +546,7 @@ status_t parseAndAddBag(Bundle* bundle, const String16& itemIdent, int32_t curFormat, bool pseudolocalize, + const bool overwrite, ResourceTable* outTable) { status_t err; @@ -572,7 +573,7 @@ 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; } @@ -588,6 +589,7 @@ status_t parseAndAddEntry(Bundle* bundle, bool curIsStyled, int32_t curFormat, bool pseudolocalize, + const bool overwrite, ResourceTable* outTable) { status_t err; @@ -610,7 +612,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 +621,7 @@ status_t compileResourceFile(Bundle* bundle, const sp& assets, const sp& in, const ResTable_config& defParams, + const bool overwrite, ResourceTable* outTable) { ResXMLTree block; @@ -638,8 +641,10 @@ 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"); @@ -671,6 +676,10 @@ 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 false16("false"); const String16 myPackage(assets->getPackage()); @@ -950,6 +959,45 @@ 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; + + 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)); + } + } + + if (name.size() > 0) { + if (translatable == false16) { + // 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); + } + } + curTag = &string16; curType = string16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; @@ -963,6 +1011,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 +1023,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; @@ -1053,7 +1109,8 @@ status_t compileResourceFile(Bundle* bundle, if (!localHasErrors) { err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), - myPackage, curType, ident, parentIdent, &curParams); + myPackage, curType, ident, parentIdent, &curParams, + overwrite); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1124,7 +1181,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, + false, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) && bundle->getPseudolocalize()) { @@ -1133,7 +1191,7 @@ status_t compileResourceFile(Bundle* bundle, block.setPosition(parserPosition); err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, true, - outTable); + overwrite, outTable); #endif } } @@ -1156,7 +1214,7 @@ 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, false, overwrite, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; @@ -1167,7 +1225,7 @@ 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, true, false, outTable); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1312,7 +1370,8 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos, const Vector* 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. @@ -1336,7 +1395,7 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos, 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++; } @@ -1351,6 +1410,8 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, const ResTable_config* params, 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() @@ -1384,8 +1445,12 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, } e->setParent(bagParent); } - - return e->makeItABag(sourcePos); + + if ((result = e->makeItABag(sourcePos)) != NO_ERROR) { + return result; + } + + return e->emptyBag(sourcePos); } status_t ResourceTable::addBag(const SourcePos& sourcePos, @@ -1553,17 +1618,26 @@ 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 { 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; } @@ -1584,7 +1658,8 @@ uint32_t ResourceTable::getResId(const String16& package, 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; if (!ResTable::expandResourceRef( @@ -1603,7 +1678,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); NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n", String8(package).string(), String8(type).string(), String8(name).string(), res)); @@ -2036,6 +2111,92 @@ status_t ResourceTable::addSymbols(const sp& outSymbols) { } +void +ResourceTable::addLocalization(const String16& name, const String8& locale) +{ + mLocalizations[name].insert(locale); +} + + +/*! + * 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::iterator locales = configSet.begin(); + locales != configSet.end(); + locales++) { + fprintf(stdout, " %s", (*locales).string()); + } + fprintf(stdout, "\n"); + // !!! TODO: throw an error here in some circumstances + } + + // 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); + } + + // 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: error: " + "*** 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; + } + } + } + } + } while (comma != NULL); + } + } + + return err; +} + + status_t ResourceFilter::parse(const char* arg) { @@ -2187,6 +2348,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) } const size_t N = c->getEntries().size(); for (size_t ei=0; eigetEntries().keyAt(ei); + if (!filter.match(config)) { + continue; + } sp e = c->getEntries().valueAt(ei); if (e == NULL) { continue; @@ -2579,7 +2744,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,7 +2757,7 @@ 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(), @@ -2639,6 +2805,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) { @@ -3330,4 +3507,3 @@ bool ResourceTable::getItemValue( } return res; } -