Merge "AAPT2: Introduce notion of 'product' to ResourceTable" into nyc-dev
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 88b6270..cb82ac3 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -38,6 +38,7 @@
io/ZipArchive.cpp \
link/AutoVersioner.cpp \
link/ManifestFixer.cpp \
+ link/ProductFilter.cpp \
link/PrivateAttributeMover.cpp \
link/ReferenceLinker.cpp \
link/TableMerger.cpp \
@@ -83,6 +84,7 @@
link/AutoVersioner_test.cpp \
link/ManifestFixer_test.cpp \
link/PrivateAttributeMover_test.cpp \
+ link/ProductFilter_test.cpp \
link/ReferenceLinker_test.cpp \
link/TableMerger_test.cpp \
link/XmlReferenceLinker_test.cpp \
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index b4e75f9..4bea129 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -144,8 +144,8 @@
PrintVisitor visitor;
for (const auto& value : entry->values) {
- std::cout << " (" << value.config << ") ";
- value.value->accept(&visitor);
+ std::cout << " (" << value->config << ") ";
+ value->value->accept(&visitor);
std::cout << std::endl;
}
}
@@ -176,7 +176,7 @@
if (result) {
ResourceEntry* entry = result.value().entry;
for (const auto& value : entry->values) {
- if (Style* style = valueCast<Style>(value.value.get())) {
+ if (Style* style = valueCast<Style>(value->value.get())) {
if (style->parent && style->parent.value().name) {
parents.insert(style->parent.value().name.value());
stylesToVisit.push(style->parent.value().name.value());
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index b37d366..b100e84 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -19,7 +19,6 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "util/Comparators.h"
#include "util/ImmutableMap.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
@@ -71,6 +70,7 @@
struct ParsedResource {
ResourceName name;
ConfigDescription config;
+ std::string product;
Source source;
ResourceId id;
Maybe<SymbolState> symbolState;
@@ -79,35 +79,6 @@
std::list<ParsedResource> childResources;
};
-bool ResourceParser::shouldStripResource(const ResourceNameRef& name,
- const Maybe<std::u16string>& product) const {
- if (product) {
- for (const std::u16string& productToMatch : mOptions.products) {
- if (product.value() == productToMatch) {
- // We specified a product, and it is in the list, so don't strip.
- return false;
- }
- }
- }
-
- // Nothing matched, try 'default'. Default only matches if we didn't already use another
- // product variant.
- if (!product || product.value() == u"default") {
- if (Maybe<ResourceTable::SearchResult> result = mTable->findResource(name)) {
- const ResourceEntry* entry = result.value().entry;
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), mConfig,
- cmp::lessThanConfig);
- if (iter != entry->values.end() && iter->config == mConfig && !iter->value->isWeak()) {
- // We have a value for this config already, and it is not weak,
- // so filter out this default.
- return true;
- }
- }
- return false;
- }
- return true;
-}
-
// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
if (res->symbolState) {
@@ -125,7 +96,8 @@
res->value->setComment(std::move(res->comment));
res->value->setSource(std::move(res->source));
- if (!table->addResource(res->name, res->id, res->config, std::move(res->value), diag)) {
+ if (!table->addResource(res->name, res->id, res->config, res->product,
+ std::move(res->value), diag)) {
return false;
}
}
@@ -295,9 +267,8 @@
parsedResource.comment = std::move(comment);
// Extract the product name if it exists.
- Maybe<std::u16string> product;
if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
- product = maybeProduct.value().toString();
+ parsedResource.product = util::utf16ToUtf8(maybeProduct.value());
}
// Parse the resource regardless of product.
@@ -306,12 +277,7 @@
continue;
}
- // We successfully parsed the resource. Check if we should include it or strip it.
- if (shouldStripResource(parsedResource.name, product)) {
- // Record that we stripped out this resource name.
- // We will check that at least one variant of this resource was included.
- strippedResources.insert(parsedResource.name);
- } else if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
+ if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
error = true;
}
}
@@ -524,7 +490,7 @@
// name.package can be empty here, as it will assume the package name of the table.
std::unique_ptr<Id> id = util::make_unique<Id>();
id->setSource(mSource.withLine(beginXmlLine));
- mTable->addResource(name, {}, std::move(id), mDiag);
+ mTable->addResource(name, {}, {}, std::move(id), mDiag);
};
// Process the raw value.
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 51cbbe1..ee5b337 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -34,13 +34,6 @@
struct ResourceParserOptions {
/**
- * Optional product names by which to filter resources.
- * This is like a preprocessor definition in that we strip out resources
- * that don't match before we compile them.
- */
- std::vector<std::u16string> products;
-
- /**
* Whether the default setting for this parser is to allow translation.
*/
bool translatable = true;
@@ -106,9 +99,6 @@
bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool shouldStripResource(const ResourceNameRef& name,
- const Maybe<std::u16string>& product) const;
-
IDiagnostics* mDiag;
ResourceTable* mTable;
Source mSource;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index cf0fcd1..3450de9 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -48,24 +48,13 @@
}
::testing::AssertionResult testParse(const StringPiece& str) {
- return testParse(str, ConfigDescription{}, {});
+ return testParse(str, ConfigDescription{});
}
::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
- return testParse(str, config, {});
- }
-
- ::testing::AssertionResult testParse(const StringPiece& str,
- std::initializer_list<std::u16string> products) {
- return testParse(str, {}, std::move(products));
- }
-
- ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
- std::initializer_list<std::u16string> products) {
std::stringstream input(kXmlPreamble);
input << "<resources>\n" << str << "\n</resources>" << std::endl;
ResourceParserOptions parserOptions;
- parserOptions.products = products;
ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
parserOptions);
xml::XmlPullParser xmlParser(input);
@@ -546,7 +535,7 @@
ASSERT_NE(nullptr, id);
}
-TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
+TEST_F(ResourceParserTest, KeepAllProducts) {
std::string input = R"EOF(
<string name="foo" product="phone">hi</string>
<string name="foo" product="no-sdcard">ho</string>
@@ -555,33 +544,26 @@
<string name="bit" product="phablet">hoot</string>
<string name="bot" product="default">yes</string>
)EOF";
- ASSERT_TRUE(testParse(input, { std::u16string(u"no-sdcard"), std::u16string(u"phablet") }));
+ ASSERT_TRUE(testParse(input));
- String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
- ASSERT_NE(nullptr, fooStr);
- EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value);
-
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bit"));
- EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bot"));
-}
-
-TEST_F(ResourceParserTest, FilterProductsThatBothMatchInOrder) {
- std::string input = R"EOF(
- <string name="foo" product="phone">phone</string>
- <string name="foo" product="default">default</string>
- )EOF";
- ASSERT_TRUE(testParse(input, { std::u16string(u"phone") }));
-
- String* foo = test::getValue<String>(&mTable, u"@string/foo");
- ASSERT_NE(nullptr, foo);
- EXPECT_EQ(std::u16string(u"phone"), *foo->value);
-}
-
-TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
- std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
- ASSERT_FALSE(testParse(input, { std::u16string(u"phone") }));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo",
+ ConfigDescription::defaultConfig(),
+ "phone"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo",
+ ConfigDescription::defaultConfig(),
+ "no-sdcard"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bar",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/baz",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bit",
+ ConfigDescription::defaultConfig(),
+ "phablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bot",
+ ConfigDescription::defaultConfig(),
+ "default"));
}
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 8a3d047..3e73be4 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -19,8 +19,6 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
-#include "util/Comparators.h"
#include "util/Util.h"
#include <algorithm>
@@ -124,6 +122,73 @@
return entries.emplace(iter, new ResourceEntry(name))->get();
}
+ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
+ return findValue(config, StringPiece());
+}
+
+struct ConfigKey {
+ const ConfigDescription* config;
+ const StringPiece& product;
+};
+
+bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
+ int cmp = lhs->config.compare(*rhs.config);
+ if (cmp == 0) {
+ cmp = StringPiece(lhs->product).compare(rhs.product);
+ }
+ return cmp < 0;
+}
+
+ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
+ const StringPiece& product) {
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{ &config, product }, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ return nullptr;
+}
+
+ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product) {
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{ &config, product }, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ ResourceConfigValue* newValue = values.insert(
+ iter, util::make_unique<ResourceConfigValue>(config, product))->get();
+ return newValue;
+}
+
+std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) {
+ std::vector<ResourceConfigValue*> results;
+
+ auto iter = values.begin();
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
+ ++iter;
+ break;
+ }
+ }
+
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
+ }
+ }
+ return results;
+}
+
/**
* The default handler for collisions. A return value of -1 means keep the
* existing value, 0 means fail, and +1 means take the incoming value.
@@ -188,56 +253,69 @@
static constexpr const char16_t* kValidNameChars = u"._-";
static constexpr const char16_t* kValidNameMangledChars = u"._-$";
-bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
- std::unique_ptr<Value> value, IDiagnostics* diag) {
- return addResourceImpl(name, {}, config, std::move(value), kValidNameChars,
- resolveValueCollision, diag);
-}
-
-bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, std::unique_ptr<Value> value,
+bool ResourceTable::addResource(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, resId, config, std::move(value), kValidNameChars,
+ return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
resolveValueCollision, diag);
}
-bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
+bool ResourceTable::addResource(const ResourceNameRef& name,
+ const ResourceId resId,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag) {
+ return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
+ resolveValueCollision, diag);
+}
+
+bool ResourceTable::addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
IDiagnostics* diag) {
return addFileReference(name, config, source, path, resolveValueCollision, diag);
}
-bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
+bool ResourceTable::addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
std::function<int(Value*,Value*)> conflictResolver,
IDiagnostics* diag) {
std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
stringPool.makeRef(path));
fileRef->setSource(source);
- return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars,
- conflictResolver, diag);
+ return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
+ kValidNameChars, conflictResolver, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, ResourceId{}, config, std::move(value), kValidNameMangledChars,
- resolveValueCollision, diag);
+ return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
+ kValidNameMangledChars, resolveValueCollision, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
const ResourceId id,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars,
+ return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
resolveValueCollision, diag);
}
bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
const ResourceId resId,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
const char16_t* validChars,
std::function<int(Value*,Value*)> conflictResolver,
@@ -298,21 +376,21 @@
return false;
}
- const auto endIter = entry->values.end();
- auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
- if (iter == endIter || iter->config != config) {
- // This resource did not exist before, add it.
- entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
+ ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
+ if (!configValue->value) {
+ // Resource does not exist, add it now.
+ configValue->value = std::move(value);
+
} else {
- int collisionResult = conflictResolver(iter->value.get(), value.get());
+ int collisionResult = conflictResolver(configValue->value.get(), value.get());
if (collisionResult > 0) {
// Take the incoming value.
- iter->value = std::move(value);
+ configValue->value = std::move(value);
} else if (collisionResult == 0) {
diag->error(DiagMessage(value->getSource())
<< "duplicate value for resource '" << name << "' "
<< "with config '" << config << "'");
- diag->error(DiagMessage(iter->value->getSource())
+ diag->error(DiagMessage(configValue->value->getSource())
<< "resource previously defined here");
return false;
}
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 6b7b07e..8ffff1f 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -24,9 +24,12 @@
#include "Source.h"
#include "StringPool.h"
+#include <android-base/macros.h>
+#include <map>
#include <memory>
#include <string>
#include <tuple>
+#include <unordered_map>
#include <vector>
namespace aapt {
@@ -46,19 +49,36 @@
std::u16string comment;
};
-/**
- * Represents a value defined for a given configuration.
- */
-struct ResourceConfigValue {
- ConfigDescription config;
+class ResourceConfigValue {
+public:
+ /**
+ * The configuration for which this value is defined.
+ */
+ const ConfigDescription config;
+
+ /**
+ * The product for which this value is defined.
+ */
+ const std::string product;
+
+ /**
+ * The actual Value.
+ */
std::unique_ptr<Value> value;
+
+ ResourceConfigValue(const ConfigDescription& config, const StringPiece& product) :
+ config(config), product(product.toString()) { }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
};
/**
* Represents a resource entry, which may have
* varying values for each defined configuration.
*/
-struct ResourceEntry {
+class ResourceEntry {
+public:
/**
* The name of the resource. Immutable, as
* this determines the order of this resource
@@ -72,24 +92,33 @@
Maybe<uint16_t> id;
/**
- * Whether this resource is public (and must maintain the same
- * entry ID across builds).
+ * Whether this resource is public (and must maintain the same entry ID across builds).
*/
Symbol symbolStatus;
/**
* The resource's values for each configuration.
*/
- std::vector<ResourceConfigValue> values;
+ std::vector<std::unique_ptr<ResourceConfigValue>> values;
ResourceEntry(const StringPiece16& name) : name(name.toString()) { }
+
+ ResourceConfigValue* findValue(const ConfigDescription& config);
+ ResourceConfigValue* findValue(const ConfigDescription& config, const StringPiece& product);
+ ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product);
+ std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
};
/**
* Represents a resource type, which holds entries defined
* for this type.
*/
-struct ResourceTableType {
+class ResourceTableType {
+public:
/**
* The logical type of resource (string, drawable, layout, etc.).
*/
@@ -114,8 +143,10 @@
explicit ResourceTableType(const ResourceType type) : type(type) { }
ResourceEntry* findEntry(const StringPiece16& name);
-
ResourceEntry* findOrCreateEntry(const StringPiece16& name);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
};
enum class PackageType {
@@ -125,16 +156,20 @@
Dynamic
};
-struct ResourceTablePackage {
+class ResourceTablePackage {
+public:
PackageType type = PackageType::App;
Maybe<uint8_t> id;
std::u16string name;
std::vector<std::unique_ptr<ResourceTableType>> types;
+ ResourceTablePackage() = default;
ResourceTableType* findType(ResourceType type);
-
ResourceTableType* findOrCreateType(const ResourceType type);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
/**
@@ -144,8 +179,6 @@
class ResourceTable {
public:
ResourceTable() = default;
- ResourceTable(const ResourceTable&) = delete;
- ResourceTable& operator=(const ResourceTable&) = delete;
/**
* When a collision of resources occurs, this method decides which value to keep.
@@ -155,38 +188,59 @@
*/
static int resolveValueCollision(Value* existing, Value* incoming);
- bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
- std::unique_ptr<Value> value, IDiagnostics* diag);
-
- bool addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, std::unique_ptr<Value> value,
+ bool addResource(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
IDiagnostics* diag);
- bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
+ bool addResource(const ResourceNameRef& name,
+ const ResourceId resId,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
+
+ bool addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
IDiagnostics* diag);
- bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path,
- std::function<int(Value*,Value*)> conflictResolver, IDiagnostics* diag);
+ bool addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece16& path,
+ std::function<int(Value*,Value*)> conflictResolver,
+ IDiagnostics* diag);
/**
* Same as addResource, but doesn't verify the validity of the name. This is used
* when loading resources from an existing binary resource table that may have mangled
* names.
*/
- bool addResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
- std::unique_ptr<Value> value, IDiagnostics* diag);
-
- bool addResourceAllowMangled(const ResourceNameRef& name, const ResourceId id,
- const ConfigDescription& config, std::unique_ptr<Value> value,
+ bool addResourceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
IDiagnostics* diag);
- bool setSymbolState(const ResourceNameRef& name, const ResourceId resId,
- const Symbol& symbol, IDiagnostics* diag);
+ bool addResourceAllowMangled(const ResourceNameRef& name,
+ const ResourceId id,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool setSymbolStateAllowMangled(const ResourceNameRef& name, const ResourceId resId,
- const Symbol& symbol, IDiagnostics* diag);
+ bool setSymbolState(const ResourceNameRef& name,
+ const ResourceId resId,
+ const Symbol& symbol,
+ IDiagnostics* diag);
+
+ bool setSymbolStateAllowMangled(const ResourceNameRef& name,
+ const ResourceId resId,
+ const Symbol& symbol,
+ IDiagnostics* diag);
struct SearchResult {
ResourceTablePackage* package;
@@ -229,13 +283,19 @@
bool addResourceImpl(const ResourceNameRef& name,
ResourceId resId,
const ConfigDescription& config,
+ const StringPiece& product,
std::unique_ptr<Value> value,
const char16_t* validChars,
std::function<int(Value*,Value*)> conflictResolver,
IDiagnostics* diag);
- bool setSymbolStateImpl(const ResourceNameRef& name, ResourceId resId,
- const Symbol& symbol, const char16_t* validChars, IDiagnostics* diag);
+ bool setSymbolStateImpl(const ResourceNameRef& name,
+ ResourceId resId,
+ const Symbol& symbol,
+ const char16_t* validChars,
+ IDiagnostics* diag);
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceTable);
};
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 42508fe..180bd11 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -43,13 +43,13 @@
EXPECT_FALSE(table.addResource(
ResourceNameRef(u"android", ResourceType::kId, u"hey,there"),
- ConfigDescription{},
+ ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
&mDiagnostics));
EXPECT_FALSE(table.addResource(
ResourceNameRef(u"android", ResourceType::kId, u"hey:there"),
- ConfigDescription{},
+ ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
&mDiagnostics));
}
@@ -59,6 +59,7 @@
EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"),
ConfigDescription{},
+ "",
test::ValueBuilder<Id>()
.setSource("test/path/file.xml", 23u).build(),
&mDiagnostics));
@@ -76,24 +77,28 @@
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:attr/layout_width"),
config,
+ "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
&mDiagnostics));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:attr/id"),
config,
+ "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
&mDiagnostics));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:string/ok"),
config,
+ "",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
&mDiagnostics));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:string/ok"),
languageConfig,
+ "",
test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
.setSource("test/path/file.xml", 20u)
.build(),
@@ -110,18 +115,49 @@
ResourceTable table;
ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
- util::make_unique<Attribute>(true), &mDiagnostics));
+ "", util::make_unique<Attribute>(true), &mDiagnostics));
Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_TRUE(attr->isWeak());
ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
- util::make_unique<Attribute>(false), &mDiagnostics));
+ "", util::make_unique<Attribute>(false), &mDiagnostics));
attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_FALSE(attr->isWeak());
}
+TEST_F(ResourceTableTest, ProductVaryingValues) {
+ ResourceTable table;
+
+ EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
+ test::parseConfigOrDie("land"),
+ "tablet",
+ util::make_unique<Id>(),
+ &mDiagnostics));
+ EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
+ test::parseConfigOrDie("land"),
+ "phone",
+ util::make_unique<Id>(),
+ &mDiagnostics));
+
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
+ test::parseConfigOrDie("land"),
+ "tablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
+ test::parseConfigOrDie("land"),
+ "phone"));
+
+ Maybe<ResourceTable::SearchResult> sr = table.findResource(
+ test::parseNameOrDie(u"@android:string/foo"));
+ AAPT_ASSERT_TRUE(sr);
+ std::vector<ResourceConfigValue*> values = sr.value().entry->findAllValues(
+ test::parseConfigOrDie("land"));
+ ASSERT_EQ(2u, values.size());
+ EXPECT_EQ(std::string("phone"), values[0]->product);
+ EXPECT_EQ(std::string("tablet"), values[1]->product);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 5493039..ea2aa55 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -146,7 +146,7 @@
for (auto& type : pkg->types) {
for (auto& entry : type->entries) {
for (auto& configValue : entry->values) {
- configValue.value->accept(visitor);
+ configValue->value->accept(visitor);
}
}
}
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 1eefb82..0dd8e18 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -107,7 +107,6 @@
struct CompileOptions {
std::string outputPath;
Maybe<std::string> resDir;
- std::vector<std::u16string> products;
bool pseudolocalize = false;
bool legacyMode = false;
bool verbose = false;
@@ -198,7 +197,6 @@
xml::XmlPullParser xmlParser(fin);
ResourceParserOptions parserOptions;
- parserOptions.products = options.products;
parserOptions.errorOnPositionalArguments = !options.legacyMode;
// If the filename includes donottranslate, then the default translatable is false.
@@ -457,11 +455,8 @@
int compile(const std::vector<StringPiece>& args) {
CompileOptions options;
- Maybe<std::string> productList;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
- .optionalFlag("--product", "Comma separated list of product types to compile",
- &productList)
.optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
.optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
"(en-XA and ar-XB)", &options.pseudolocalize)
@@ -472,12 +467,6 @@
return 1;
}
- if (productList) {
- for (StringPiece part : util::tokenize<char>(productList.value(), ',')) {
- options.products.push_back(util::utf8ToUtf16(part));
- }
- }
-
CompileContext context;
std::unique_ptr<IArchiveWriter> archiveWriter;
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 2963d13..be26b52 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -19,7 +19,6 @@
#include "ValueVisitor.h"
#include "compile/PseudolocaleGenerator.h"
#include "compile/Pseudolocalizer.h"
-#include "util/Comparators.h"
namespace aapt {
@@ -208,10 +207,12 @@
return modified;
}
-void pseudolocalizeIfNeeded(std::vector<ResourceConfigValue>* configValues,
- Pseudolocalizer::Method method, StringPool* pool, Value* value) {
+void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
+ ResourceConfigValue* originalValue,
+ StringPool* pool,
+ ResourceEntry* entry) {
Visitor visitor(pool, method);
- value->accept(&visitor);
+ originalValue->value->accept(&visitor);
std::unique_ptr<Value> localizedValue;
if (visitor.mValue) {
@@ -220,16 +221,18 @@
localizedValue = std::move(visitor.mItem);
}
- if (localizedValue) {
- ConfigDescription pseudolocalizedConfig = modifyConfigForPseudoLocale(ConfigDescription{},
- method);
- auto iter = std::lower_bound(configValues->begin(), configValues->end(),
- pseudolocalizedConfig, cmp::lessThanConfig);
- if (iter == configValues->end() || iter->config != pseudolocalizedConfig) {
- // The pseudolocalized config doesn't exist, add it.
- configValues->insert(iter, ResourceConfigValue{ pseudolocalizedConfig,
- std::move(localizedValue) });
- }
+ if (!localizedValue) {
+ return;
+ }
+
+ ConfigDescription configWithAccent = modifyConfigForPseudoLocale(
+ originalValue->config, method);
+
+ ResourceConfigValue* newConfigValue = entry->findOrCreateValue(
+ configWithAccent, originalValue->product);
+ if (!newConfigValue->value) {
+ // Only use auto-generated pseudo-localization if none is defined.
+ newConfigValue->value = std::move(localizedValue);
}
}
@@ -239,18 +242,13 @@
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
- ConfigDescription{}, cmp::lessThanConfig);
- if (iter != entry->values.end() && iter->config == ConfigDescription{}) {
- // Only pseudolocalize the default configuration.
-
- // The iterator will be invalidated, so grab a pointer to the value.
- Value* originalValue = iter->value.get();
-
- pseudolocalizeIfNeeded(&entry->values, Pseudolocalizer::Method::kAccent,
- &table->stringPool, originalValue);
- pseudolocalizeIfNeeded(&entry->values, Pseudolocalizer::Method::kBidi,
- &table->stringPool, originalValue);
+ std::vector<ResourceConfigValue*> values = entry->findAllValues(
+ ConfigDescription::defaultConfig());
+ for (ResourceConfigValue* value : values) {
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
+ &table->stringPool, entry.get());
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
+ &table->stringPool, entry.get());
}
}
}
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 71ab3db..da81046 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -402,10 +402,10 @@
const size_t configCount = entry->values.size();
for (size_t i = 0; i < configCount; i++) {
- const ConfigDescription& config = entry->values[i].config;
+ const ConfigDescription& config = entry->values[i]->config;
for (size_t j = i + 1; j < configCount; j++) {
configMasks[entry->id.value()] |= util::hostToDevice32(
- config.diff(entry->values[j].config));
+ config.diff(entry->values[j]->config));
}
}
}
@@ -445,8 +445,8 @@
// Group values by configuration.
for (auto& configValue : entry->values) {
- configToEntryListMap[configValue.config].push_back(FlatEntry{
- entry, configValue.value.get(), keyIndex });
+ configToEntryListMap[configValue->config].push_back(FlatEntry{
+ entry, configValue->value.get(), keyIndex });
}
}
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 7280f3a..6e340a2 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -23,7 +23,6 @@
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinitionWriter.h"
#include "java/JavaClassGenerator.h"
-#include "util/Comparators.h"
#include "util/StringPiece.h"
#include <algorithm>
@@ -258,12 +257,12 @@
}
for (const auto& configValue : entry->values) {
- processor.appendComment(configValue.value->getComment());
+ processor.appendComment(configValue->value->getComment());
}
// If this is an Attribute, append the format Javadoc.
if (!entry->values.empty()) {
- if (Attribute* attr = valueCast<Attribute>(entry->values.front().value.get())) {
+ if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
// We list out the available values for the given attribute.
addAttributeFormatDoc(&processor, attr);
}
@@ -272,7 +271,7 @@
if (type->type == ResourceType::kStyleable) {
assert(!entry->values.empty());
const Styleable* styleable = static_cast<const Styleable*>(
- entry->values.front().value.get());
+ entry->values.front()->value.get());
writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate,
unmangledName, styleable);
} else {
@@ -311,11 +310,10 @@
if (type->type == ResourceType::kAttr) {
// Also include private attributes in this same class.
- auto iter = std::lower_bound(package->types.begin(), package->types.end(),
- ResourceType::kAttrPrivate, cmp::lessThanType);
- if (iter != package->types.end() && (*iter)->type == ResourceType::kAttrPrivate) {
+ ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
+ if (privType) {
result = writeEntriesForClass(&classDef, packageNameToGenerate,
- package.get(), iter->get());
+ package.get(), privType);
if (!result) {
return false;
}
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index c7e603e..459c330 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -18,9 +18,7 @@
#include "ResourceTable.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
-
#include "link/Linkers.h"
-#include "util/Comparators.h"
#include <algorithm>
#include <cassert>
@@ -31,7 +29,12 @@
const int sdkVersionToGenerate) {
assert(sdkVersionToGenerate > config.sdkVersion);
const auto endIter = entry->values.end();
- auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
+ auto iter = entry->values.begin();
+ for (; iter != endIter; ++iter) {
+ if ((*iter)->config == config) {
+ break;
+ }
+ }
// The source config came from this list, so it should be here.
assert(iter != entry->values.end());
@@ -45,10 +48,10 @@
// are no higher sdk level versions of this resource.
ConfigDescription tempConfig(config);
for (; iter != endIter; ++iter) {
- tempConfig.sdkVersion = iter->config.sdkVersion;
- if (tempConfig == iter->config) {
+ tempConfig.sdkVersion = (*iter)->config.sdkVersion;
+ if (tempConfig == (*iter)->config) {
// The two configs are the same, check the sdk version.
- return sdkVersionToGenerate < iter->config.sdkVersion;
+ return sdkVersionToGenerate < (*iter)->config.sdkVersion;
}
}
@@ -65,14 +68,14 @@
for (auto& entry : type->entries) {
for (size_t i = 0; i < entry->values.size(); i++) {
- ResourceConfigValue& configValue = entry->values[i];
- if (configValue.config.sdkVersion >= SDK_LOLLIPOP_MR1) {
+ ResourceConfigValue* configValue = entry->values[i].get();
+ if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
// If this configuration is only used on L-MR1 then we don't need
// to do anything since we use private attributes since that version.
continue;
}
- if (Style* style = valueCast<Style>(configValue.value.get())) {
+ if (Style* style = valueCast<Style>(configValue->value.get())) {
Maybe<size_t> minSdkStripped;
std::vector<Style::Entry> stripped;
@@ -82,7 +85,7 @@
// Find the SDK level that is higher than the configuration allows.
const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
- if (sdkLevel > std::max<size_t>(configValue.config.sdkVersion, 1)) {
+ if (sdkLevel > std::max<size_t>(configValue->config.sdkVersion, 1)) {
// Record that we are about to strip this.
stripped.emplace_back(std::move(*iter));
@@ -105,10 +108,10 @@
// there is no other defined resource for the version we want to
// generate.
if (shouldGenerateVersionedResource(entry.get(),
- configValue.config,
+ configValue->config,
minSdkStripped.value())) {
// Let's create a new Style for this versioned resource.
- ConfigDescription newConfig(configValue.config);
+ ConfigDescription newConfig(configValue->config);
newConfig.sdkVersion = minSdkStripped.value();
std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
@@ -121,14 +124,8 @@
std::make_move_iterator(stripped.end()));
// Insert the new Resource into the correct place.
- auto iter = std::lower_bound(entry->values.begin(),
- entry->values.end(),
- newConfig,
- cmp::lessThanConfig);
-
- entry->values.insert(
- iter,
- ResourceConfigValue{ newConfig, std::move(newStyle) });
+ entry->findOrCreateValue(newConfig, {})->value =
+ std::move(newStyle);
}
}
}
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 29bcc93..9b3a87c 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -15,9 +15,7 @@
*/
#include "ConfigDescription.h"
-
#include "link/Linkers.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -31,9 +29,9 @@
const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
ResourceEntry entry(u"foo");
- entry.values.push_back(ResourceConfigValue{ defaultConfig });
- entry.values.push_back(ResourceConfigValue{ landConfig });
- entry.values.push_back(ResourceConfigValue{ sw600dpLandConfig });
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
@@ -45,9 +43,9 @@
const ConfigDescription v21Config = test::parseConfigOrDie("v21");
ResourceEntry entry(u"foo");
- entry.values.push_back(ResourceConfigValue{ defaultConfig });
- entry.values.push_back(ResourceConfigValue{ sw600dpV13Config });
- entry.values.push_back(ResourceConfigValue{ v21Config });
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpV13Config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(v21Config, ""));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8e32179..3437ac0 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -31,6 +31,7 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "link/ProductFilter.h"
#include "link/ReferenceLinker.h"
#include "link/ManifestFixer.h"
#include "link/TableMerger.h"
@@ -70,6 +71,7 @@
Maybe<std::u16string> privateSymbols;
ManifestFixerOptions manifestFixerOptions;
IConfigFilter* configFilter = nullptr;
+ std::unordered_set<std::string> products;
};
struct LinkContext : public IAaptContext {
@@ -292,16 +294,16 @@
for (const auto& configValue : entry->values) {
// Special case the occurrence of an ID that is being generated for the
// 'android' package. This is due to legacy reasons.
- if (valueCast<Id>(configValue.value.get()) &&
+ if (valueCast<Id>(configValue->value.get()) &&
package->name == u"android") {
mContext->getDiagnostics()->warn(
- DiagMessage(configValue.value->getSource())
+ DiagMessage(configValue->value->getSource())
<< "generated id '" << resName
<< "' for external package '" << package->name
<< "'");
} else {
mContext->getDiagnostics()->error(
- DiagMessage(configValue.value->getSource())
+ DiagMessage(configValue->value->getSource())
<< "defined resource '" << resName
<< "' for external package '" << package->name
<< "'");
@@ -512,7 +514,10 @@
std::unique_ptr<Id> id = util::make_unique<Id>();
id->setSource(fileDesc->source.withLine(exportedSymbol.line));
- bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
+ bool result = mFinalTable.addResourceAllowMangled(resName,
+ ConfigDescription::defaultConfig(),
+ std::string(),
+ std::move(id),
mContext->getDiagnostics());
if (!result) {
return false;
@@ -681,6 +686,12 @@
mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
return 1;
}
+
+ ProductFilter productFilter(mOptions.products);
+ if (!productFilter.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
+ return 1;
+ }
}
proguard::KeepSet proguardKeepSet;
@@ -931,6 +942,7 @@
Maybe<std::string> customJavaPackage;
std::vector<std::string> extraJavaPackages;
Maybe<std::string> configs;
+ Maybe<std::string> productList;
bool legacyXFlag = false;
bool requireLocalization = false;
Flags flags = Flags()
@@ -954,6 +966,8 @@
&requireLocalization)
.optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
"is all configurations", &configs)
+ .optionalFlag("--product", "Comma separated list of product names to keep",
+ &productList)
.optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
"by -o",
&options.outputToDirectory)
@@ -1039,6 +1053,14 @@
}
}
+ if (productList) {
+ for (StringPiece product : util::tokenize<char>(productList.value(), ',')) {
+ if (product != "" && product != "default") {
+ options.products.insert(product.toString());
+ }
+ }
+ }
+
AxisConfigFilter filter;
if (configs) {
for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 4d3a483..ec532ab 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -26,7 +26,7 @@
namespace aapt {
class ResourceTable;
-struct ResourceEntry;
+class ResourceEntry;
struct ConfigDescription;
/**
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
new file mode 100644
index 0000000..8784e89
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "link/ProductFilter.h"
+
+namespace aapt {
+
+ProductFilter::ResourceConfigValueIter
+ProductFilter::selectProductToKeep(const ResourceNameRef& name,
+ const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end,
+ IDiagnostics* diag) {
+ ResourceConfigValueIter defaultProductIter = end;
+ ResourceConfigValueIter selectedProductIter = end;
+
+ for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
+ ResourceConfigValue* configValue = iter->get();
+ if (mProducts.find(configValue->product) != mProducts.end()) {
+ if (selectedProductIter != end) {
+ // We have two possible values for this product!
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "selection of product '" << configValue->product
+ << "' for resource " << name << " is ambiguous");
+
+ ResourceConfigValue* previouslySelectedConfigValue = selectedProductIter->get();
+ diag->note(DiagMessage(previouslySelectedConfigValue->value->getSource())
+ << "product '" << previouslySelectedConfigValue->product
+ << "' is also a candidate");
+ return end;
+ }
+
+ // Select this product.
+ selectedProductIter = iter;
+ }
+
+ if (configValue->product.empty() || configValue->product == "default") {
+ if (defaultProductIter != end) {
+ // We have two possible default values.
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "multiple default products defined for resource " << name);
+
+ ResourceConfigValue* previouslyDefaultConfigValue = defaultProductIter->get();
+ diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
+ << "default product also defined here");
+ return end;
+ }
+
+ // Mark the default.
+ defaultProductIter = iter;
+ }
+ }
+
+ if (defaultProductIter == end) {
+ diag->error(DiagMessage() << "no default product defined for resource " << name);
+ return end;
+ }
+
+ if (selectedProductIter == end) {
+ selectedProductIter = defaultProductIter;
+ }
+ return selectedProductIter;
+}
+
+bool ProductFilter::consume(IAaptContext* context, ResourceTable* table) {
+ bool error = false;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
+
+ ResourceConfigValueIter iter = entry->values.begin();
+ ResourceConfigValueIter startRangeIter = iter;
+ while (iter != entry->values.end()) {
+ ++iter;
+ if (iter == entry->values.end() ||
+ (*iter)->config != (*startRangeIter)->config) {
+
+ // End of the array, or we saw a different config,
+ // so this must be the end of a range of products.
+ // Select the product to keep from the set of products defined.
+ ResourceNameRef name(pkg->name, type->type, entry->name);
+ auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
+ context->getDiagnostics());
+ if (valueToKeep == iter) {
+ // An error occurred, we could not pick a product.
+ error = true;
+ } else {
+ // We selected a product to keep. Move it to the new array.
+ newValues.push_back(std::move(*valueToKeep));
+ }
+
+ // Start the next range of products.
+ startRangeIter = iter;
+ }
+ }
+
+ // Now move the new values in to place.
+ entry->values = std::move(newValues);
+ }
+ }
+ }
+ return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h
new file mode 100644
index 0000000..d2edd38
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_LINK_PRODUCTFILTER_H
+#define AAPT_LINK_PRODUCTFILTER_H
+
+#include "ResourceTable.h"
+#include "process/IResourceTableConsumer.h"
+
+#include <android-base/macros.h>
+#include <unordered_set>
+
+namespace aapt {
+
+class ProductFilter {
+public:
+ using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+
+ ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { }
+
+ ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name,
+ const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end,
+ IDiagnostics* diag);
+
+ bool consume(IAaptContext* context, ResourceTable* table);
+
+private:
+ std::unordered_set<std::string> mProducts;
+
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINK_PRODUCTFILTER_H */
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
new file mode 100644
index 0000000..f4f756a
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "link/ProductFilter.h"
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ProductFilterTest, SelectTwoProducts) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ const ConfigDescription land = test::parseConfigOrDie("land");
+ const ConfigDescription port = test::parseConfigOrDie("port");
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ land, "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("land/default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ land, "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("land/tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ port, "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("port/default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ port, "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("port/tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({ "tablet" });
+ ASSERT_TRUE(filter.consume(context.get(), &table));
+
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ land, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ land, "tablet"));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ port, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ port, "tablet"));
+}
+
+TEST(ProductFilterTest, SelectDefaultProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({});
+ ASSERT_TRUE(filter.consume(context.get(), &table));
+
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ ConfigDescription::defaultConfig(),
+ "tablet"));
+}
+
+TEST(ProductFilterTest, FailOnAmbiguousProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "no-sdcard",
+ test::ValueBuilder<Id>()
+ .setSource(Source("no-sdcard.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({ "tablet", "no-sdcard" });
+ ASSERT_FALSE(filter.consume(context.get(), &table));
+}
+
+TEST(ProductFilterTest, FailOnMultipleDefaults) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source(".xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "default",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({});
+ ASSERT_FALSE(filter.consume(context.get(), &table));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 2743539..ef3fe4f 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -316,7 +316,7 @@
&table->stringPool, &declStack, &callSite);
for (auto& configValue : entry->values) {
- configValue.value->accept(&visitor);
+ configValue->value->accept(&visitor);
}
if (visitor.hasError()) {
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index e01a004..2ecd5b0 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -18,9 +18,7 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
#include "link/TableMerger.h"
-#include "util/Comparators.h"
#include "util/Util.h"
#include <cassert>
@@ -197,28 +195,28 @@
ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
- for (ResourceConfigValue& srcValue : srcEntry->values) {
- auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
- srcValue.config, cmp::lessThanConfig);
+ for (auto& srcValue : srcEntry->values) {
+ ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
+ srcValue->product);
const bool stripConfig = mOptions.filter ?
- !mOptions.filter->match(srcValue.config) : false;
+ !mOptions.filter->match(srcValue->config) : false;
- if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
+ if (dstValue) {
const int collisionResult = ResourceTable::resolveValueCollision(
- iter->value.get(), srcValue.value.get());
+ dstValue->value.get(), srcValue->value.get());
if (collisionResult == 0 && !overlay) {
// Error!
ResourceNameRef resourceName(srcPackage->name,
srcType->type,
srcEntry->name);
- mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
+ mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
<< "resource '" << resourceName
<< "' has a conflicting value for "
<< "configuration ("
- << srcValue.config << ")");
- mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
+ << srcValue->config << ")");
+ mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
<< "originally defined here");
error = true;
continue;
@@ -227,16 +225,18 @@
continue;
}
- } else if (!stripConfig){
- // Insert a place holder value. We will fill it in below.
- iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
}
if (stripConfig) {
continue;
}
- if (FileReference* f = valueCast<FileReference>(srcValue.value.get())) {
+ if (!dstValue) {
+ // Force create the entry if we didn't have it.
+ dstValue = dstEntry->findOrCreateValue(srcValue->config, srcValue->product);
+ }
+
+ if (FileReference* f = valueCast<FileReference>(srcValue->value.get())) {
std::unique_ptr<FileReference> newFileRef;
if (manglePackage) {
newFileRef = cloneAndMangleFile(srcPackage->name, *f);
@@ -246,15 +246,15 @@
}
if (callback) {
- if (!callback(resName, iter->config, newFileRef.get(), f)) {
+ if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
error = true;
continue;
}
}
- iter->value = std::move(newFileRef);
+ dstValue->value = std::move(newFileRef);
} else {
- iter->value = std::unique_ptr<Value>(srcValue.value->clone(
+ dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
&mMasterTable->stringPool));
}
}
@@ -290,7 +290,8 @@
ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
pkg->findOrCreateType(fileDesc.name.type)
->findOrCreateEntry(fileDesc.name.entry)
- ->values.push_back(ResourceConfigValue{ fileDesc.config, std::move(fileRef) });
+ ->findOrCreateValue(fileDesc.config, {})
+ ->value = std::move(fileRef);
auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
FileReference* newFile, FileReference* oldFile) -> bool {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 6ad2f9c..b6030a2 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -17,9 +17,7 @@
#include "ConfigDescription.h"
#include "Resource.h"
#include "ValueVisitor.h"
-
#include "process/SymbolTable.h"
-#include "util/Comparators.h"
#include "util/Util.h"
#include <androidfw/AssetManager.h>
@@ -55,12 +53,10 @@
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
- auto iter = std::lower_bound(sr.entry->values.begin(), sr.entry->values.end(),
- kDefaultConfig, cmp::lessThanConfig);
-
- if (iter != sr.entry->values.end() && iter->config == kDefaultConfig) {
+ ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
+ if (configValue) {
// This resource has an Attribute.
- if (Attribute* attr = valueCast<Attribute>(iter->value.get())) {
+ if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
symbol->attribute = util::make_unique<Attribute>(*attr);
} else {
return {};
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 1310aa6..9856a00 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -19,7 +19,6 @@
#include "ValueVisitor.h"
#include "proto/ProtoHelpers.h"
#include "proto/ProtoSerialize.h"
-#include "util/Comparators.h"
#include <androidfw/ResourceTypes.h>
@@ -134,21 +133,19 @@
return {};
}
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
- config, cmp::lessThanConfig);
- if (iter != entry->values.end() && iter->config == config) {
+ ResourceConfigValue* configValue = entry->findOrCreateValue(config,
+ pbConfig.product());
+ if (configValue->value) {
// Duplicate config.
mDiag->error(DiagMessage(mSource) << "duplicate configuration");
return {};
}
- std::unique_ptr<Value> value = deserializeValueFromPb(pbConfigValue.value(),
- config,
- &table->stringPool);
- if (!value) {
+ configValue->value = deserializeValueFromPb(pbConfigValue.value(),
+ config, &table->stringPool);
+ if (!configValue->value) {
return {};
}
- entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
}
}
}
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 4a2176d..bba2da4 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -242,21 +242,24 @@
for (auto& configValue : entry->values) {
pb::ConfigValue* pbConfigValue = pbEntry->add_config_values();
- serializeConfig(configValue.config, pbConfigValue->mutable_config());
-
- pb::Value* pbValue = pbConfigValue->mutable_value();
- serializeSourceToPb(configValue.value->getSource(), &sourcePool,
- pbValue->mutable_source());
- if (!configValue.value->getComment().empty()) {
- pbValue->set_comment(util::utf16ToUtf8(configValue.value->getComment()));
+ serializeConfig(configValue->config, pbConfigValue->mutable_config());
+ if (!configValue->product.empty()) {
+ pbConfigValue->mutable_config()->set_product(configValue->product);
}
- if (configValue.value->isWeak()) {
+ pb::Value* pbValue = pbConfigValue->mutable_value();
+ serializeSourceToPb(configValue->value->getSource(), &sourcePool,
+ pbValue->mutable_source());
+ if (!configValue->value->getComment().empty()) {
+ pbValue->set_comment(util::utf16ToUtf8(configValue->value->getComment()));
+ }
+
+ if (configValue->value->isWeak()) {
pbValue->set_weak(true);
}
PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue);
- configValue.value->accept(&visitor);
+ configValue->value->accept(&visitor);
}
}
}
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 1061b8f..70a33f7 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -49,9 +49,19 @@
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one"));
ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"),
- ConfigDescription{}, std::move(plural),
+ ConfigDescription{}, std::string(), std::move(plural),
context->getDiagnostics()));
+ // Make a resource with different products.
+ ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"),
+ test::parseConfigOrDie("land"), std::string(),
+ test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"),
+ test::parseConfigOrDie("land"), std::string("tablet"),
+ test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
+ context->getDiagnostics()));
+
std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
ASSERT_NE(nullptr, pbTable);
@@ -69,6 +79,17 @@
AAPT_ASSERT_TRUE(result);
EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state);
EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+
+ // Find the product-dependent values
+ BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>(
+ newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "");
+ ASSERT_NE(nullptr, prim);
+ EXPECT_EQ(123u, prim->value.data);
+
+ prim = test::getValueForConfigAndProduct<BinaryPrimitive>(
+ newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet");
+ ASSERT_NE(nullptr, prim);
+ EXPECT_EQ(321u, prim->value.data);
}
TEST(TableProtoSerializer, SerializeFileHeader) {
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 579a46e..834caf8 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -104,8 +104,8 @@
const ConfigDescription& config,
std::unique_ptr<Value> value) {
ResourceName resName = parseNameOrDie(name);
- bool result = mTable->addResourceAllowMangled(resName, id, config, std::move(value),
- &mDiagnostics);
+ bool result = mTable->addResourceAllowMangled(resName, id, config, std::string(),
+ std::move(value), &mDiagnostics);
assert(result);
return *this;
}
@@ -132,6 +132,14 @@
return reference;
}
+inline std::unique_ptr<BinaryPrimitive> buildPrimitive(uint8_t type, uint32_t data) {
+ android::Res_value value = {};
+ value.size = sizeof(value);
+ value.dataType = type;
+ value.data = data;
+ return util::make_unique<BinaryPrimitive>(value);
+}
+
template <typename T>
class ValueBuilder {
private:
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 51e2dd4..348c32a 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -52,6 +52,11 @@
void note(const DiagMessage& message) override {}
};
+inline IDiagnostics* getDiagnostics() {
+ static DummyDiagnosticsImpl diag;
+ return &diag;
+}
+
inline ResourceName parseNameOrDie(const StringPiece16& str) {
ResourceNameRef ref;
bool result = ResourceUtils::tryParseReference(str, &ref);
@@ -66,23 +71,25 @@
return config;
}
-template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece16& resName,
- const ConfigDescription& config) {
+template <typename T> T* getValueForConfigAndProduct(ResourceTable* table,
+ const StringPiece16& resName,
+ const ConfigDescription& config,
+ const StringPiece& product) {
Maybe<ResourceTable::SearchResult> result = table->findResource(parseNameOrDie(resName));
if (result) {
- ResourceEntry* entry = result.value().entry;
- auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), config,
- [](const ResourceConfigValue& a, const ConfigDescription& b)
- -> bool {
- return a.config < b;
- });
- if (iter != entry->values.end() && iter->config == config) {
- return valueCast<T>(iter->value.get());
+ ResourceConfigValue* configValue = result.value().entry->findValue(config, product);
+ if (configValue) {
+ return valueCast<T>(configValue->value.get());
}
}
return nullptr;
}
+template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece16& resName,
+ const ConfigDescription& config) {
+ return getValueForConfigAndProduct<T>(table, resName, config, {});
+}
+
template <typename T> T* getValue(ResourceTable* table, const StringPiece16& resName) {
return getValueForConfig<T>(table, resName, {});
}
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 3417703..33b505e 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -352,7 +352,7 @@
return false;
}
- if (!mTable->addResourceAllowMangled(name, config, std::move(resourceValue),
+ if (!mTable->addResourceAllowMangled(name, config, {}, std::move(resourceValue),
mContext->getDiagnostics())) {
return false;
}
diff --git a/tools/aapt2/util/Comparators.h b/tools/aapt2/util/Comparators.h
deleted file mode 100644
index 0ee0bf3..0000000
--- a/tools/aapt2/util/Comparators.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_UTIL_COMPARATORS_H
-#define AAPT_UTIL_COMPARATORS_H
-
-#include "ConfigDescription.h"
-#include "ResourceTable.h"
-
-namespace aapt {
-namespace cmp {
-
-inline bool lessThanConfig(const ResourceConfigValue& a, const ConfigDescription& b) {
- return a.config < b;
-}
-
-inline bool lessThanType(const std::unique_ptr<ResourceTableType>& a, ResourceType b) {
- return a->type < b;
-}
-
-} // namespace cmp
-} // namespace aapt
-
-#endif /* AAPT_UTIL_COMPARATORS_H */