X-Git-Url: https://git.saurik.com/android/aapt.git/blobdiff_plain/98749835c8a75f60e3ff6e35c8ae62d11924effc..340d52d89027de6cab28e981402de4ea925d7936:/ResourceTable.cpp diff --git a/ResourceTable.cpp b/ResourceTable.cpp index f3ca23d..d98fe65 100644 --- a/ResourceTable.cpp +++ b/ResourceTable.cpp @@ -7,9 +7,10 @@ #include "ResourceTable.h" #include "XMLNode.h" +#include "ResourceFilter.h" +#include #include -#include #include #define NOISY(x) //x @@ -695,7 +696,7 @@ status_t parseAndAddEntry(Bundle* bundle, if (isInProductList(product, String16(bundleProduct))) { ; } else if (strcmp16(String16("default").string(), product.string()) == 0 && - !outTable->hasBagOrEntry(myPackage, curType, ident)) { + !outTable->hasBagOrEntry(myPackage, curType, ident, config)) { ; } else { return NO_ERROR; @@ -752,6 +753,7 @@ status_t compileResourceFile(Bundle* bundle, 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"); @@ -1057,6 +1059,49 @@ 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()); @@ -1823,6 +1868,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) @@ -2015,7 +2091,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; @@ -2043,10 +2120,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. @@ -2218,10 +2304,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++; } @@ -2247,9 +2331,7 @@ bool ResourceTable::getAttributeFlags( // Didn't find this flag identifier. return false; } - if (pos < end) { - pos++; - } + pos++; } return true; @@ -2497,135 +2579,6 @@ ResourceTable::validateLocalizations(void) return err; } - -status_t -ResourceFilter::parse(const char* arg) -{ - if (arg == NULL) { - return 0; - } - - const char* p = arg; - const char* q; - - while (true) { - q = strchr(p, ','); - if (q == NULL) { - q = p + strlen(p); - } - - 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); - } - } - p = q; - if (!*p) break; - p++; - } - - return NO_ERROR; -} - -bool -ResourceFilter::match(int axis, uint32_t value) const -{ - 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; -} - -bool -ResourceFilter::match(const ResTable_config& config) const -{ - 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; - } - } - 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; - } - 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_SMALLESTSCREENWIDTHDP, config.smallestScreenWidthDp)) { - return false; - } - if (!match(AXIS_SCREENWIDTHDP, config.screenWidthDp)) { - return false; - } - if (!match(AXIS_SCREENHEIGHTDP, config.screenHeightDp)) { - return false; - } - if (!match(AXIS_SCREENLAYOUTSIZE, config.screenLayout&ResTable_config::MASK_SCREENSIZE)) { - return false; - } - if (!match(AXIS_VERSION, config.version)) { - return false; - } - return true; -} - status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) { ResourceFilter filter; @@ -2634,16 +2587,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->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO); + bool useUTF8 = !bundle->getUTF16StringsOption(); // Iterate through all data, collecting all values (strings, // references, etc). - StringPool valueStrings = StringPool(false, useUTF8); + StringPool valueStrings(useUTF8); + Vector > allEntries; for (pi=0; pi p = mOrderedPackages.itemAt(pi); if (p->getTypes().size() == 0) { @@ -2651,8 +2607,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) continue; } - StringPool typeStrings = StringPool(false, useUTF8); - StringPool keyStrings = StringPool(false, useUTF8); + StringPool typeStrings(useUTF8); + StringPool keyStrings(useUTF8); const size_t N = p->getOrderedTypes().size(); for (size_t ti=0; ti& dest) 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(); @@ -2683,10 +2652,21 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) 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); } } } @@ -2695,6 +2675,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. @@ -2758,6 +2749,12 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& dest) 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. @@ -2894,6 +2891,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); } @@ -2904,6 +2902,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. @@ -3209,7 +3215,7 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table, const String16 id16("id"); mParentId = 0; if (mParent.size() > 0) { - mParentId = table->getResId(mParent, &style16, NULL, &errorMsg, false); + mParentId = table->getResId(mParent, &style16, NULL, &errorMsg); if (mParentId == 0) { mPos.error("Error retrieving parent for item: %s '%s'.\n", errorMsg, String8(mParent).string()); @@ -3221,7 +3227,7 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table, const String16& key = mBag.keyAt(i); Item& it = mBag.editValueAt(i); it.bagKeyId = table->getResId(key, - it.isId ? &id16 : &attr16, NULL, &errorMsg, false); + it.isId ? &id16 : &attr16, NULL, &errorMsg); //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId); if (it.bagKeyId == 0) { it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg, @@ -3234,14 +3240,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) { @@ -3252,7 +3260,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; } } @@ -3264,6 +3273,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; @@ -3706,9 +3738,7 @@ sp ResourceTable::getPackage(const String16& package) { sp p = mPackages.valueFor(package); if (p == NULL) { - if (mBundle->getIsOverlayPackage()) { - p = new Package(package, 0x00); - } else if (mIsAppPackage) { + if (mIsAppPackage) { if (mHaveAppPackage) { fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n" "Use -x to create extended resources.\n");