Add namespace handling in attribute values
Previously, you could only reference namespace prefixes in attribute names:
<View xmlns:appcompat="http://schemas.android.com/apk/res/android.support.v7.appcompat"
appcompat:name="hey"
...
Now you can also reference them in resource names within an attribute value:
...
android:text="@appcompat:string/confirm"
...
Which will be treated as "@android.support.v7.appcompat:string/confirm".
Change-Id: Ib076e867a990c80cf877a704eb77cd1ef0b23b52
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 943892d..e7e824c 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -175,23 +175,16 @@
}
std::unique_ptr<Reference> ResourceParser::tryParseReference(const StringPiece16& str,
- const StringPiece16& defaultPackage,
bool* outCreate) {
ResourceNameRef ref;
bool privateRef = false;
if (tryParseReference(str, &ref, outCreate, &privateRef)) {
- if (ref.package.empty()) {
- ref.package = defaultPackage;
- }
std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
value->privateReference = privateRef;
return value;
}
if (tryParseAttributeReference(str, &ref)) {
- if (ref.package.empty()) {
- ref.package = defaultPackage;
- }
*outCreate = false;
return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
}
@@ -330,7 +323,7 @@
StringPiece16 trimmedStr(util::trimWhitespace(str));
uint32_t data = 0;
if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
- data = 1;
+ data = 0xffffffffu;
} else if (trimmedStr != u"false" && trimmedStr != u"FALSE") {
return {};
}
@@ -397,7 +390,7 @@
}
std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
- const StringPiece16& value, uint32_t typeMask, const StringPiece16& defaultPackage,
+ const StringPiece16& value, uint32_t typeMask,
std::function<void(const ResourceName&)> onCreateReference) {
std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
if (nullOrEmpty) {
@@ -405,7 +398,7 @@
}
bool create = false;
- std::unique_ptr<Reference> reference = tryParseReference(value, defaultPackage, &create);
+ std::unique_ptr<Reference> reference = tryParseReference(value, &create);
if (reference) {
if (create && onCreateReference) {
onCreateReference(reference->name);
@@ -457,11 +450,10 @@
* allows.
*/
std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
- const StringPiece16& str, const Attribute& attr, const StringPiece16& defaultPackage,
+ const StringPiece16& str, const Attribute& attr,
std::function<void(const ResourceName&)> onCreateReference) {
const uint32_t typeMask = attr.typeMask;
- std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, defaultPackage,
- onCreateReference);
+ std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
if (value) {
return value;
}
@@ -770,14 +762,25 @@
}
auto onCreateReference = [&](const ResourceName& name) {
+ // name.package can be empty here, as it will assume the package name of the table.
mTable->addResource(name, {}, mSource.line(beginXmlLine), util::make_unique<Id>());
};
// Process the raw value.
std::unique_ptr<Item> processedItem = parseItemForAttribute(rawValue, typeMask,
- mTable->getPackage(),
onCreateReference);
if (processedItem) {
+ // Fix up the reference.
+ visitFunc<Reference>(*processedItem, [&](Reference& ref) {
+ if (!ref.name.package.empty()) {
+ // The package name was set, so lookup its alias.
+ parser->applyPackageAlias(&ref.name.package, mTable->getPackage());
+ } else {
+ // The package name was left empty, so it assumes the default package
+ // without alias lookup.
+ ref.name.package = mTable->getPackage();
+ }
+ });
return processedItem;
}
@@ -1093,7 +1096,7 @@
return true;
}
-static bool parseXmlAttributeName(StringPiece16 str, ResourceNameRef* outRef) {
+static bool parseXmlAttributeName(StringPiece16 str, ResourceName* outName) {
str = util::trimWhitespace(str);
const char16_t* const start = str.data();
const char16_t* const end = start + str.size();
@@ -1110,12 +1113,12 @@
p++;
}
- outRef->package = package;
- outRef->type = ResourceType::kAttr;
+ outName->package = package.toString();
+ outName->type = ResourceType::kAttr;
if (name.size() == 0) {
- outRef->entry = str;
+ outName->entry = str.toString();
} else {
- outRef->entry = name;
+ outName->entry = name.toString();
}
return true;
}
@@ -1130,8 +1133,8 @@
return false;
}
- ResourceNameRef keyRef;
- if (!parseXmlAttributeName(nameAttrIter->value, &keyRef)) {
+ ResourceName key;
+ if (!parseXmlAttributeName(nameAttrIter->value, &key)) {
mLogger.error(parser->getLineNumber())
<< "invalid attribute name '"
<< nameAttrIter->value
@@ -1140,14 +1143,15 @@
return false;
}
- if (keyRef.package.empty()) {
- keyRef.package = mTable->getPackage();
+ if (!key.package.empty()) {
+ // We have a package name set, so lookup its alias.
+ parser->applyPackageAlias(&key.package, mTable->getPackage());
+ } else {
+ // The package name was omitted, so use the default package name with
+ // no alias lookup.
+ key.package = mTable->getPackage();
}
- // Create a copy instead of a reference because we
- // are about to invalidate keyRef when advancing the parser.
- ResourceName key = keyRef.toResourceName();
-
std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
if (!value) {
return false;
@@ -1170,7 +1174,11 @@
return false;
}
- if (style->parent.name.package.empty()) {
+ if (!style->parent.name.package.empty()) {
+ // Try to interpret the package name as an alias. These take precedence.
+ parser->applyPackageAlias(&style->parent.name.package, mTable->getPackage());
+ } else {
+ // If no package is specified, this can not be an alias and is the local package.
style->parent.name.package = mTable->getPackage();
}
}