AAPT2: Fail compiling when private symbols are referenced
Also moved some XML specific stuff into its own directory, and refactored ReferenceLinker a bit. Change-Id: I912247a82023c1bbf72dc191fbdaf62858cbec0c
This commit is contained in:
@@ -58,8 +58,9 @@ sources := \
|
||||
ResourceValues.cpp \
|
||||
SdkConstants.cpp \
|
||||
StringPool.cpp \
|
||||
XmlDom.cpp \
|
||||
XmlPullParser.cpp
|
||||
xml/XmlDom.cpp \
|
||||
xml/XmlPullParser.cpp \
|
||||
xml/XmlUtil.cpp
|
||||
|
||||
testSources := \
|
||||
compile/IdAssigner_test.cpp \
|
||||
@@ -90,8 +91,9 @@ testSources := \
|
||||
ResourceUtils_test.cpp \
|
||||
StringPool_test.cpp \
|
||||
ValueVisitor_test.cpp \
|
||||
XmlDom_test.cpp \
|
||||
XmlPullParser_test.cpp
|
||||
xml/XmlDom_test.cpp \
|
||||
xml/XmlPullParser_test.cpp \
|
||||
xml/XmlUtil_test.cpp
|
||||
|
||||
toolSources := \
|
||||
compile/Compile.cpp \
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "ValueVisitor.h"
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -29,26 +28,6 @@ namespace aapt {
|
||||
|
||||
constexpr const char16_t* sXliffNamespaceUri = u"urn:oasis:names:tc:xliff:document:1.2";
|
||||
|
||||
static Maybe<StringPiece16> findAttribute(XmlPullParser* parser, const StringPiece16& name) {
|
||||
auto iter = parser->findAttribute(u"", name);
|
||||
if (iter != parser->endAttributes()) {
|
||||
return StringPiece16(util::trimWhitespace(iter->value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static Maybe<StringPiece16> findNonEmptyAttribute(XmlPullParser* parser,
|
||||
const StringPiece16& name) {
|
||||
auto iter = parser->findAttribute(u"", name);
|
||||
if (iter != parser->endAttributes()) {
|
||||
StringPiece16 trimmed = util::trimWhitespace(iter->value);
|
||||
if (!trimmed.empty()) {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
|
||||
*/
|
||||
@@ -65,7 +44,7 @@ ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const S
|
||||
/**
|
||||
* Build a string from XML that converts nested elements into Span objects.
|
||||
*/
|
||||
bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
|
||||
bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString,
|
||||
StyleString* outStyleString) {
|
||||
std::vector<Span> spanStack;
|
||||
|
||||
@@ -74,9 +53,9 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou
|
||||
outStyleString->spans.clear();
|
||||
util::StringBuilder builder;
|
||||
size_t depth = 1;
|
||||
while (XmlPullParser::isGoodEvent(parser->next())) {
|
||||
const XmlPullParser::Event event = parser->getEvent();
|
||||
if (event == XmlPullParser::Event::kEndElement) {
|
||||
while (xml::XmlPullParser::isGoodEvent(parser->next())) {
|
||||
const xml::XmlPullParser::Event event = parser->getEvent();
|
||||
if (event == xml::XmlPullParser::Event::kEndElement) {
|
||||
if (!parser->getElementNamespace().empty()) {
|
||||
// We already warned and skipped the start element, so just skip here too
|
||||
continue;
|
||||
@@ -91,11 +70,11 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou
|
||||
outStyleString->spans.push_back(spanStack.back());
|
||||
spanStack.pop_back();
|
||||
|
||||
} else if (event == XmlPullParser::Event::kText) {
|
||||
} else if (event == xml::XmlPullParser::Event::kText) {
|
||||
outRawString->append(parser->getText());
|
||||
builder.append(parser->getText());
|
||||
|
||||
} else if (event == XmlPullParser::Event::kStartElement) {
|
||||
} else if (event == xml::XmlPullParser::Event::kStartElement) {
|
||||
if (!parser->getElementNamespace().empty()) {
|
||||
if (parser->getElementNamespace() != sXliffNamespaceUri) {
|
||||
// Only warn if this isn't an xliff namespace.
|
||||
@@ -128,7 +107,7 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou
|
||||
spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
|
||||
}
|
||||
|
||||
} else if (event == XmlPullParser::Event::kComment) {
|
||||
} else if (event == xml::XmlPullParser::Event::kComment) {
|
||||
// Skip
|
||||
} else {
|
||||
assert(false);
|
||||
@@ -140,11 +119,11 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool ResourceParser::parse(XmlPullParser* parser) {
|
||||
bool ResourceParser::parse(xml::XmlPullParser* parser) {
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Skip comments and text.
|
||||
continue;
|
||||
}
|
||||
@@ -159,7 +138,7 @@ bool ResourceParser::parse(XmlPullParser* parser) {
|
||||
break;
|
||||
};
|
||||
|
||||
if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
|
||||
if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
|
||||
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
|
||||
<< "xml parser error: " << parser->getLastError());
|
||||
return false;
|
||||
@@ -167,10 +146,11 @@ bool ResourceParser::parse(XmlPullParser* parser) {
|
||||
return !error;
|
||||
}
|
||||
|
||||
static bool shouldStripResource(XmlPullParser* parser, const Maybe<std::u16string> productToMatch) {
|
||||
assert(parser->getEvent() == XmlPullParser::Event::kStartElement);
|
||||
static bool shouldStripResource(const xml::XmlPullParser* parser,
|
||||
const Maybe<std::u16string> productToMatch) {
|
||||
assert(parser->getEvent() == xml::XmlPullParser::Event::kStartElement);
|
||||
|
||||
if (Maybe<StringPiece16> maybeProduct = findNonEmptyAttribute(parser, u"product")) {
|
||||
if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
|
||||
if (!productToMatch) {
|
||||
if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
|
||||
// We didn't specify a product and this is not a default product, so skip.
|
||||
@@ -229,20 +209,20 @@ static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& c
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool ResourceParser::parseResources(XmlPullParser* parser) {
|
||||
bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
|
||||
std::set<ResourceName> strippedResources;
|
||||
|
||||
bool error = false;
|
||||
std::u16string comment;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
const XmlPullParser::Event event = parser->getEvent();
|
||||
if (event == XmlPullParser::Event::kComment) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
const xml::XmlPullParser::Event event = parser->getEvent();
|
||||
if (event == xml::XmlPullParser::Event::kComment) {
|
||||
comment = parser->getComment();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event == XmlPullParser::Event::kText) {
|
||||
if (event == xml::XmlPullParser::Event::kText) {
|
||||
if (!util::trimWhitespace(parser->getText()).empty()) {
|
||||
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
|
||||
<< "plain text not allowed here");
|
||||
@@ -251,7 +231,7 @@ bool ResourceParser::parseResources(XmlPullParser* parser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(event == XmlPullParser::Event::kStartElement);
|
||||
assert(event == xml::XmlPullParser::Event::kStartElement);
|
||||
|
||||
if (!parser->getElementNamespace().empty()) {
|
||||
// Skip unknown namespace.
|
||||
@@ -266,7 +246,7 @@ bool ResourceParser::parseResources(XmlPullParser* parser) {
|
||||
|
||||
if (elementName == u"item") {
|
||||
// Items simply have their type encoded in the type attribute.
|
||||
if (Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type")) {
|
||||
if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) {
|
||||
elementName = maybeType.value().toString();
|
||||
} else {
|
||||
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
|
||||
@@ -280,7 +260,7 @@ bool ResourceParser::parseResources(XmlPullParser* parser) {
|
||||
parsedResource.source = mSource.withLine(parser->getLineNumber());
|
||||
parsedResource.comment = std::move(comment);
|
||||
|
||||
if (Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name")) {
|
||||
if (Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name")) {
|
||||
parsedResource.name.entry = maybeName.value().toString();
|
||||
|
||||
} else if (elementName != u"public-group") {
|
||||
@@ -403,7 +383,7 @@ enum {
|
||||
* an Item. If allowRawValue is false, nullptr is returned in this
|
||||
* case.
|
||||
*/
|
||||
std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint32_t typeMask,
|
||||
std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
|
||||
const bool allowRawValue) {
|
||||
const size_t beginXmlLine = parser->getLineNumber();
|
||||
|
||||
@@ -432,10 +412,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint
|
||||
if (processedItem) {
|
||||
// Fix up the reference.
|
||||
if (Reference* ref = valueCast<Reference>(processedItem.get())) {
|
||||
if (Maybe<ResourceName> transformedName =
|
||||
parser->transformPackage(ref->name.value(), u"")) {
|
||||
ref->name = std::move(transformedName);
|
||||
}
|
||||
transformReferenceFromNamespace(parser, u"", ref);
|
||||
}
|
||||
return processedItem;
|
||||
}
|
||||
@@ -456,11 +433,11 @@ std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
bool formatted = true;
|
||||
if (Maybe<StringPiece16> formattedAttr = findAttribute(parser, u"formatted")) {
|
||||
if (Maybe<StringPiece16> formattedAttr = xml::findAttribute(parser, u"formatted")) {
|
||||
if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) {
|
||||
mDiag->error(DiagMessage(source) << "invalid value for 'formatted'. Must be a boolean");
|
||||
return false;
|
||||
@@ -468,7 +445,7 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou
|
||||
}
|
||||
|
||||
bool translateable = mOptions.translatable;
|
||||
if (Maybe<StringPiece16> translateableAttr = findAttribute(parser, u"translatable")) {
|
||||
if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) {
|
||||
if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) {
|
||||
mDiag->error(DiagMessage(source)
|
||||
<< "invalid value for 'translatable'. Must be a boolean");
|
||||
@@ -495,7 +472,7 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parseColor(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
|
||||
@@ -506,7 +483,7 @@ bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResour
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
uint32_t typeMask = 0;
|
||||
@@ -540,10 +517,10 @@ bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outRe
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
|
||||
Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
|
||||
if (!maybeType) {
|
||||
mDiag->error(DiagMessage(source) << "<public> must have a 'type' attribute");
|
||||
return false;
|
||||
@@ -558,7 +535,7 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResou
|
||||
|
||||
outResource->name.type = *parsedType;
|
||||
|
||||
if (Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"id")) {
|
||||
if (Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"id")) {
|
||||
android::Res_value val;
|
||||
bool result = android::ResTable::stringToInt(maybeId.value().data(),
|
||||
maybeId.value().size(), &val);
|
||||
@@ -580,10 +557,10 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResou
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
|
||||
Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
|
||||
if (!maybeType) {
|
||||
mDiag->error(DiagMessage(source) << "<public-group> must have a 'type' attribute");
|
||||
return false;
|
||||
@@ -596,7 +573,7 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"first-id");
|
||||
Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"first-id");
|
||||
if (!maybeId) {
|
||||
mDiag->error(DiagMessage(source) << "<public-group> must have a 'first-id' attribute");
|
||||
return false;
|
||||
@@ -615,11 +592,11 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out
|
||||
std::u16string comment;
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() == XmlPullParser::Event::kComment) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
|
||||
comment = util::trimWhitespace(parser->getComment()).toString();
|
||||
continue;
|
||||
} else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
} else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Skip text.
|
||||
continue;
|
||||
}
|
||||
@@ -628,20 +605,20 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out
|
||||
const std::u16string& elementNamespace = parser->getElementNamespace();
|
||||
const std::u16string& elementName = parser->getElementName();
|
||||
if (elementNamespace.empty() && elementName == u"public") {
|
||||
Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
|
||||
Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
|
||||
if (!maybeName) {
|
||||
mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (findNonEmptyAttribute(parser, u"id")) {
|
||||
if (xml::findNonEmptyAttribute(parser, u"id")) {
|
||||
mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (findNonEmptyAttribute(parser, u"type")) {
|
||||
if (xml::findNonEmptyAttribute(parser, u"type")) {
|
||||
mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>");
|
||||
error = true;
|
||||
continue;
|
||||
@@ -666,10 +643,10 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool ResourceParser::parseSymbol(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
|
||||
Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
|
||||
if (!maybeType) {
|
||||
mDiag->error(DiagMessage(source) << "<" << parser->getElementName() << "> must have a "
|
||||
"'type' attribute");
|
||||
@@ -715,17 +692,16 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
outResource->source = mSource.withLine(parser->getLineNumber());
|
||||
return parseAttrImpl(parser, outResource, false);
|
||||
}
|
||||
|
||||
bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) {
|
||||
bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
|
||||
bool weak) {
|
||||
uint32_t typeMask = 0;
|
||||
|
||||
Maybe<StringPiece16> maybeFormat = findAttribute(parser, u"format");
|
||||
Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format");
|
||||
if (maybeFormat) {
|
||||
typeMask = parseFormatAttribute(maybeFormat.value());
|
||||
if (typeMask == 0) {
|
||||
@@ -735,18 +711,6 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a declaration, the package name may be in the name. Separate these out.
|
||||
// Eg. <attr name="android:text" />
|
||||
// No format attribute is allowed.
|
||||
if (weak && !maybeFormat) {
|
||||
StringPiece16 package, type, name;
|
||||
ResourceUtils::extractResourceName(outResource->name.entry, &package, &type, &name);
|
||||
if (type.empty() && !package.empty()) {
|
||||
outResource->name.package = package.toString();
|
||||
outResource->name.entry = name.toString();
|
||||
}
|
||||
}
|
||||
|
||||
struct SymbolComparator {
|
||||
bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
|
||||
return a.symbol.name.value() < b.symbol.name.value();
|
||||
@@ -758,11 +722,11 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes
|
||||
std::u16string comment;
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() == XmlPullParser::Event::kComment) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
|
||||
comment = util::trimWhitespace(parser->getComment()).toString();
|
||||
continue;
|
||||
} else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
} else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Skip text.
|
||||
continue;
|
||||
}
|
||||
@@ -834,17 +798,17 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser,
|
||||
Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser,
|
||||
const StringPiece16& tag) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
|
||||
Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
|
||||
if (!maybeName) {
|
||||
mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
|
||||
return {};
|
||||
}
|
||||
|
||||
Maybe<StringPiece16> maybeValue = findNonEmptyAttribute(parser, u"value");
|
||||
Maybe<StringPiece16> maybeValue = xml::findNonEmptyAttribute(parser, u"value");
|
||||
if (!maybeValue) {
|
||||
mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
|
||||
return {};
|
||||
@@ -863,12 +827,19 @@ Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* pars
|
||||
val.data };
|
||||
}
|
||||
|
||||
static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) {
|
||||
static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) {
|
||||
str = util::trimWhitespace(str);
|
||||
const char16_t* const start = str.data();
|
||||
const char16_t* start = str.data();
|
||||
const char16_t* const end = start + str.size();
|
||||
const char16_t* p = start;
|
||||
|
||||
Reference ref;
|
||||
if (p != end && *p == u'*') {
|
||||
ref.privateReference = true;
|
||||
start++;
|
||||
p++;
|
||||
}
|
||||
|
||||
StringPiece16 package;
|
||||
StringPiece16 name;
|
||||
while (p != end) {
|
||||
@@ -880,28 +851,27 @@ static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) {
|
||||
p++;
|
||||
}
|
||||
|
||||
return ResourceName(package.toString(), ResourceType::kAttr,
|
||||
ref.name = ResourceName(package.toString(), ResourceType::kAttr,
|
||||
name.empty() ? str.toString() : name.toString());
|
||||
return Maybe<Reference>(std::move(ref));
|
||||
}
|
||||
|
||||
bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) {
|
||||
bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
|
||||
Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
|
||||
Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
|
||||
if (!maybeName) {
|
||||
mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<ResourceName> maybeKey = parseXmlAttributeName(maybeName.value());
|
||||
Maybe<Reference> maybeKey = parseXmlAttributeName(maybeName.value());
|
||||
if (!maybeKey) {
|
||||
mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Maybe<ResourceName> transformedName = parser->transformPackage(maybeKey.value(), u"")) {
|
||||
maybeKey = std::move(transformedName);
|
||||
}
|
||||
transformReferenceFromNamespace(parser, u"", &maybeKey.value());
|
||||
|
||||
std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
|
||||
if (!value) {
|
||||
@@ -909,15 +879,15 @@ bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
style->entries.push_back(Style::Entry{ Reference(maybeKey.value()), std::move(value) });
|
||||
style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
std::unique_ptr<Style> style = util::make_unique<Style>();
|
||||
|
||||
Maybe<StringPiece16> maybeParent = findAttribute(parser, u"parent");
|
||||
Maybe<StringPiece16> maybeParent = xml::findAttribute(parser, u"parent");
|
||||
if (maybeParent) {
|
||||
// If the parent is empty, we don't have a parent, but we also don't infer either.
|
||||
if (!maybeParent.value().empty()) {
|
||||
@@ -928,10 +898,9 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResour
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Maybe<ResourceName> transformedName =
|
||||
parser->transformPackage(style->parent.value().name.value(), u"")) {
|
||||
style->parent.value().name = std::move(transformedName);
|
||||
}
|
||||
// Transform the namespace prefix to the actual package name, and mark the reference as
|
||||
// private if appropriate.
|
||||
transformReferenceFromNamespace(parser, u"", &style->parent.value());
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -940,15 +909,15 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResour
|
||||
size_t pos = styleName.find_last_of(u'.');
|
||||
if (pos != std::string::npos) {
|
||||
style->parentInferred = true;
|
||||
style->parent = Reference(
|
||||
ResourceName({}, ResourceType::kStyle, styleName.substr(0, pos)));
|
||||
style->parent = Reference(ResourceName({}, ResourceType::kStyle,
|
||||
styleName.substr(0, pos)));
|
||||
}
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Skip text and comments.
|
||||
continue;
|
||||
}
|
||||
@@ -973,15 +942,15 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResour
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResource,
|
||||
bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource,
|
||||
uint32_t typeMask) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
std::unique_ptr<Array> array = util::make_unique<Array>();
|
||||
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Skip text and comments.
|
||||
continue;
|
||||
}
|
||||
@@ -1014,14 +983,14 @@ bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResour
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
|
||||
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Skip text and comments.
|
||||
continue;
|
||||
}
|
||||
@@ -1030,16 +999,15 @@ bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResou
|
||||
const std::u16string& elementNamespace = parser->getElementNamespace();
|
||||
const std::u16string& elementName = parser->getElementName();
|
||||
if (elementNamespace.empty() && elementName == u"item") {
|
||||
const auto endAttrIter = parser->endAttributes();
|
||||
auto attrIter = parser->findAttribute(u"", u"quantity");
|
||||
if (attrIter == endAttrIter || attrIter->value.empty()) {
|
||||
Maybe<StringPiece16> maybeQuantity = xml::findNonEmptyAttribute(parser, u"quantity");
|
||||
if (!maybeQuantity) {
|
||||
mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
|
||||
<< "'quantity'");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value);
|
||||
StringPiece16 trimmedQuantity = util::trimWhitespace(maybeQuantity.value());
|
||||
size_t index = 0;
|
||||
if (trimmedQuantity == u"zero") {
|
||||
index = Plural::Zero;
|
||||
@@ -1089,7 +1057,7 @@ bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResou
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource) {
|
||||
bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource) {
|
||||
const Source source = mSource.withLine(parser->getLineNumber());
|
||||
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
|
||||
|
||||
@@ -1099,11 +1067,11 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource
|
||||
std::u16string comment;
|
||||
bool error = false;
|
||||
const size_t depth = parser->getDepth();
|
||||
while (XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() == XmlPullParser::Event::kComment) {
|
||||
while (xml::XmlPullParser::nextChildNode(parser, depth)) {
|
||||
if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
|
||||
comment = util::trimWhitespace(parser->getComment()).toString();
|
||||
continue;
|
||||
} else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
|
||||
} else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
|
||||
// Ignore text.
|
||||
continue;
|
||||
}
|
||||
@@ -1112,17 +1080,29 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource
|
||||
const std::u16string& elementNamespace = parser->getElementNamespace();
|
||||
const std::u16string& elementName = parser->getElementName();
|
||||
if (elementNamespace.empty() && elementName == u"attr") {
|
||||
const auto endAttrIter = parser->endAttributes();
|
||||
auto attrIter = parser->findAttribute(u"", u"name");
|
||||
if (attrIter == endAttrIter || attrIter->value.empty()) {
|
||||
Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
|
||||
if (!maybeName) {
|
||||
mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is a declaration, the package name may be in the name. Separate these out.
|
||||
// Eg. <attr name="android:text" />
|
||||
Maybe<Reference> maybeRef = parseXmlAttributeName(maybeName.value());
|
||||
if (!maybeRef) {
|
||||
mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
|
||||
<< maybeName.value() << "'");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference& childRef = maybeRef.value();
|
||||
xml::transformReferenceFromNamespace(parser, u"", &childRef);
|
||||
|
||||
// Create the ParsedResource that will add the attribute to the table.
|
||||
ParsedResource childResource;
|
||||
childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value);
|
||||
childResource.name = childRef.name.value();
|
||||
childResource.source = itemSource;
|
||||
childResource.comment = std::move(comment);
|
||||
|
||||
@@ -1132,7 +1112,6 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource
|
||||
}
|
||||
|
||||
// Create the reference to this attribute.
|
||||
Reference childRef(childResource.name);
|
||||
childRef.setComment(childResource.comment);
|
||||
childRef.setSource(itemSource);
|
||||
styleable->entries.push_back(std::move(childRef));
|
||||
|
||||
@@ -22,10 +22,9 @@
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "StringPool.h"
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include "util/Maybe.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -57,7 +56,7 @@ public:
|
||||
|
||||
ResourceParser(const ResourceParser&) = delete; // No copy.
|
||||
|
||||
bool parse(XmlPullParser* parser);
|
||||
bool parse(xml::XmlPullParser* parser);
|
||||
|
||||
private:
|
||||
/*
|
||||
@@ -66,7 +65,7 @@ private:
|
||||
* contains the escaped and whitespace trimmed text, while `outRawString`
|
||||
* contains the unescaped text. Returns true on success.
|
||||
*/
|
||||
bool flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
|
||||
bool flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString,
|
||||
StyleString* outStyleString);
|
||||
|
||||
/*
|
||||
@@ -75,24 +74,25 @@ private:
|
||||
* If `allowRawValue` is true and the subtree can not be parsed as a regular Item, then a
|
||||
* RawString is returned. Otherwise this returns false;
|
||||
*/
|
||||
std::unique_ptr<Item> parseXml(XmlPullParser* parser, const uint32_t typeMask,
|
||||
std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
|
||||
const bool allowRawValue);
|
||||
|
||||
bool parseResources(XmlPullParser* parser);
|
||||
bool parseString(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseColor(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parsePrimitive(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parsePublic(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseSymbol(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseAttr(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak);
|
||||
Maybe<Attribute::Symbol> parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag);
|
||||
bool parseStyle(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseStyleItem(XmlPullParser* parser, Style* style);
|
||||
bool parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseArray(XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
|
||||
bool parsePlural(XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseResources(xml::XmlPullParser* parser);
|
||||
bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseColor(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak);
|
||||
Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
|
||||
const StringPiece16& tag);
|
||||
bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
|
||||
bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
|
||||
bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
|
||||
|
||||
IDiagnostics* mDiag;
|
||||
ResourceTable* mTable;
|
||||
|
||||
@@ -18,9 +18,8 @@
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include "test/Context.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sstream>
|
||||
@@ -36,7 +35,7 @@ TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
|
||||
input << "<attr name=\"foo\"/>" << std::endl;
|
||||
ResourceTable table;
|
||||
ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
|
||||
XmlPullParser xmlParser(input);
|
||||
xml::XmlPullParser xmlParser(input);
|
||||
ASSERT_FALSE(parser.parse(&xmlParser));
|
||||
}
|
||||
|
||||
@@ -56,7 +55,7 @@ struct ResourceParserTest : public ::testing::Test {
|
||||
parserOptions.product = product;
|
||||
ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
|
||||
parserOptions);
|
||||
XmlPullParser xmlParser(input);
|
||||
xml::XmlPullParser xmlParser(input);
|
||||
if (parser.parse(&xmlParser)) {
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
@@ -360,6 +359,25 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
|
||||
EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value());
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
|
||||
std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
|
||||
" <attr name=\"*android:bar\" />\n"
|
||||
" <attr name=\"privAndroid:bat\" />\n"
|
||||
"</declare-styleable>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
|
||||
ASSERT_NE(nullptr, styleable);
|
||||
ASSERT_EQ(2u, styleable->entries.size());
|
||||
|
||||
EXPECT_TRUE(styleable->entries[0].privateReference);
|
||||
AAPT_ASSERT_TRUE(styleable->entries[0].name);
|
||||
EXPECT_EQ(std::u16string(u"android"), styleable->entries[0].name.value().package);
|
||||
|
||||
EXPECT_TRUE(styleable->entries[1].privateReference);
|
||||
AAPT_ASSERT_TRUE(styleable->entries[1].name);
|
||||
EXPECT_EQ(std::u16string(u"android"), styleable->entries[1].name.value().package);
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseArray) {
|
||||
std::string input = "<array name=\"foo\">\n"
|
||||
" <item>@string/ref</item>\n"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "ResourceUtils.h"
|
||||
#include "flatten/ResourceTypeExtensions.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
@@ -47,6 +48,42 @@ bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
|
||||
return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
|
||||
}
|
||||
|
||||
bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) {
|
||||
size_t offset = 0;
|
||||
bool priv = false;
|
||||
if (str.data()[0] == u'*') {
|
||||
priv = true;
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
StringPiece16 package;
|
||||
StringPiece16 type;
|
||||
StringPiece16 entry;
|
||||
if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ResourceType* parsedType = parseResourceType(type);
|
||||
if (!parsedType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outRef) {
|
||||
outRef->package = package;
|
||||
outRef->type = *parsedType;
|
||||
outRef->entry = entry;
|
||||
}
|
||||
|
||||
if (outPrivate) {
|
||||
*outPrivate = priv;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate,
|
||||
bool* outPrivate) {
|
||||
StringPiece16 trimmedStr(util::trimWhitespace(str));
|
||||
@@ -61,35 +98,24 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool*
|
||||
if (trimmedStr.data()[1] == u'+') {
|
||||
create = true;
|
||||
offset += 1;
|
||||
} else if (trimmedStr.data()[1] == u'*') {
|
||||
priv = true;
|
||||
offset += 1;
|
||||
}
|
||||
StringPiece16 package;
|
||||
StringPiece16 type;
|
||||
StringPiece16 entry;
|
||||
if (!extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
|
||||
&package, &type, &entry)) {
|
||||
|
||||
ResourceNameRef name;
|
||||
if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
|
||||
&name, &priv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ResourceType* parsedType = parseResourceType(type);
|
||||
if (!parsedType) {
|
||||
if (create && priv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (create && *parsedType != ResourceType::kId) {
|
||||
if (create && name.type != ResourceType::kId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outRef) {
|
||||
outRef->package = package;
|
||||
outRef->type = *parsedType;
|
||||
outRef->entry = entry;
|
||||
*outRef = name;
|
||||
}
|
||||
|
||||
if (outCreate) {
|
||||
|
||||
@@ -39,6 +39,13 @@ namespace ResourceUtils {
|
||||
bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
|
||||
StringPiece16* outType, StringPiece16* outEntry);
|
||||
|
||||
/**
|
||||
* Returns true if the string was parsed as a resource name ([*][package:]type/name), with
|
||||
* `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix
|
||||
* was present.
|
||||
*/
|
||||
bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, bool* outPrivate);
|
||||
|
||||
/*
|
||||
* Returns true if the string was parsed as a reference (@[+][package:]type/name), with
|
||||
* `outReference` set to the parsed reference.
|
||||
|
||||
@@ -16,15 +16,30 @@
|
||||
|
||||
#include "Resource.h"
|
||||
#include "ResourceUtils.h"
|
||||
|
||||
#include "test/Common.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
TEST(ResourceUtilsTest, ParseResourceName) {
|
||||
ResourceNameRef actual;
|
||||
bool actualPriv = false;
|
||||
EXPECT_TRUE(ResourceUtils::parseResourceName(u"android:color/foo", &actual, &actualPriv));
|
||||
EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual);
|
||||
EXPECT_FALSE(actualPriv);
|
||||
|
||||
EXPECT_TRUE(ResourceUtils::parseResourceName(u"color/foo", &actual, &actualPriv));
|
||||
EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, u"foo"), actual);
|
||||
EXPECT_FALSE(actualPriv);
|
||||
|
||||
EXPECT_TRUE(ResourceUtils::parseResourceName(u"*android:color/foo", &actual, &actualPriv));
|
||||
EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual);
|
||||
EXPECT_TRUE(actualPriv);
|
||||
}
|
||||
|
||||
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
|
||||
ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
|
||||
ResourceNameRef expected({}, ResourceType::kColor, u"foo");
|
||||
ResourceNameRef actual;
|
||||
bool create = false;
|
||||
bool privateRef = false;
|
||||
@@ -35,7 +50,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
|
||||
}
|
||||
|
||||
TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
|
||||
ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
|
||||
ResourceNameRef expected(u"android", ResourceType::kColor, u"foo");
|
||||
ResourceNameRef actual;
|
||||
bool create = false;
|
||||
bool privateRef = false;
|
||||
@@ -47,7 +62,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
|
||||
}
|
||||
|
||||
TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
|
||||
ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
|
||||
ResourceNameRef expected(u"android", ResourceType::kColor, u"foo");
|
||||
ResourceNameRef actual;
|
||||
bool create = false;
|
||||
bool privateRef = false;
|
||||
@@ -59,7 +74,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
|
||||
}
|
||||
|
||||
TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
|
||||
ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
|
||||
ResourceNameRef expected(u"android", ResourceType::kId, u"foo");
|
||||
ResourceNameRef actual;
|
||||
bool create = false;
|
||||
bool privateRef = false;
|
||||
@@ -71,7 +86,7 @@ TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
|
||||
}
|
||||
|
||||
TEST(ResourceUtilsTest, ParsePrivateReference) {
|
||||
ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
|
||||
ResourceNameRef expected(u"android", ResourceType::kId, u"foo");
|
||||
ResourceNameRef actual;
|
||||
bool create = false;
|
||||
bool privateRef = false;
|
||||
@@ -111,8 +126,8 @@ TEST(ResourceUtilsTest, FailParseIncompleteReference) {
|
||||
}
|
||||
|
||||
TEST(ResourceUtilsTest, ParseStyleParentReference) {
|
||||
const ResourceName kAndroidStyleFooName = { u"android", ResourceType::kStyle, u"foo" };
|
||||
const ResourceName kStyleFooName = { {}, ResourceType::kStyle, u"foo" };
|
||||
const ResourceName kAndroidStyleFooName(u"android", ResourceType::kStyle, u"foo");
|
||||
const ResourceName kStyleFooName({}, ResourceType::kStyle, u"foo");
|
||||
|
||||
std::string errStr;
|
||||
Maybe<Reference> ref = ResourceUtils::parseStyleParentReference(u"@android:style/foo", &errStr);
|
||||
|
||||
@@ -71,27 +71,23 @@ Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type
|
||||
}
|
||||
|
||||
bool Reference::flatten(android::Res_value* outValue) const {
|
||||
outValue->dataType = (referenceType == Reference::Type::kResource)
|
||||
? android::Res_value::TYPE_REFERENCE
|
||||
: android::Res_value::TYPE_ATTRIBUTE;
|
||||
outValue->dataType = (referenceType == Reference::Type::kResource) ?
|
||||
android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE;
|
||||
outValue->data = util::hostToDevice32(id ? id.value().id : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference* Reference::clone(StringPool* /*newPool*/) const {
|
||||
Reference* ref = new Reference();
|
||||
ref->mComment = mComment;
|
||||
ref->mSource = mSource;
|
||||
ref->referenceType = referenceType;
|
||||
ref->name = name;
|
||||
ref->id = id;
|
||||
return ref;
|
||||
return new Reference(*this);
|
||||
}
|
||||
|
||||
void Reference::print(std::ostream* out) const {
|
||||
*out << "(reference) ";
|
||||
if (referenceType == Reference::Type::kResource) {
|
||||
*out << "@";
|
||||
if (privateReference) {
|
||||
*out << "*";
|
||||
}
|
||||
} else {
|
||||
*out << "?";
|
||||
}
|
||||
@@ -116,10 +112,7 @@ bool Id::flatten(android::Res_value* out) const {
|
||||
}
|
||||
|
||||
Id* Id::clone(StringPool* /*newPool*/) const {
|
||||
Id* id = new Id();
|
||||
id->mComment = mComment;
|
||||
id->mSource = mSource;
|
||||
return id;
|
||||
return new Id(*this);
|
||||
}
|
||||
|
||||
void Id::print(std::ostream* out) const {
|
||||
@@ -214,10 +207,7 @@ bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
|
||||
}
|
||||
|
||||
BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
|
||||
BinaryPrimitive* bp = new BinaryPrimitive(value);
|
||||
bp->mComment = mComment;
|
||||
bp->mSource = mSource;
|
||||
return bp;
|
||||
return new BinaryPrimitive(*this);
|
||||
}
|
||||
|
||||
void BinaryPrimitive::print(std::ostream* out) const {
|
||||
@@ -255,12 +245,7 @@ bool Attribute::isWeak() const {
|
||||
}
|
||||
|
||||
Attribute* Attribute::clone(StringPool* /*newPool*/) const {
|
||||
Attribute* attr = new Attribute(weak);
|
||||
attr->mComment = mComment;
|
||||
attr->mSource = mSource;
|
||||
attr->typeMask = typeMask;
|
||||
std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols));
|
||||
return attr;
|
||||
return new Attribute(*this);
|
||||
}
|
||||
|
||||
void Attribute::printMask(std::ostream* out) const {
|
||||
@@ -450,11 +435,7 @@ static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Ite
|
||||
}
|
||||
|
||||
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
|
||||
Styleable* styleable = new Styleable();
|
||||
styleable->mComment = mComment;
|
||||
styleable->mSource = mSource;
|
||||
std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries));
|
||||
return styleable;
|
||||
return new Styleable(*this);
|
||||
}
|
||||
|
||||
void Styleable::print(std::ostream* out) const {
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#include "Flags.h"
|
||||
#include "ResourceParser.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "XmlDom.h"
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include "compile/IdAssigner.h"
|
||||
#include "compile/Png.h"
|
||||
#include "compile/XmlIdCollector.h"
|
||||
@@ -31,6 +28,8 @@
|
||||
#include "util/Files.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
@@ -131,7 +130,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
|
||||
|
||||
|
||||
// Parse the values file from XML.
|
||||
XmlPullParser xmlParser(fin);
|
||||
xml::XmlPullParser xmlParser(fin);
|
||||
|
||||
ResourceParserOptions parserOptions;
|
||||
parserOptions.product = options.product;
|
||||
@@ -191,7 +190,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
|
||||
static bool compileXml(IAaptContext* context, const CompileOptions& options,
|
||||
const ResourcePathData& pathData, const std::string& outputPath) {
|
||||
|
||||
std::unique_ptr<XmlResource> xmlRes;
|
||||
std::unique_ptr<xml::XmlResource> xmlRes;
|
||||
|
||||
{
|
||||
std::ifstream fin(pathData.source.path, std::ifstream::binary);
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "compile/XmlIdCollector.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
@@ -61,7 +60,7 @@ struct IdCollector : public xml::Visitor {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool XmlIdCollector::consume(IAaptContext* context, XmlResource* xmlRes) {
|
||||
bool XmlIdCollector::consume(IAaptContext* context, xml::XmlResource* xmlRes) {
|
||||
xmlRes->file.exportedSymbols.clear();
|
||||
IdCollector collector(&xmlRes->file.exportedSymbols);
|
||||
xmlRes->root->accept(&collector);
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
#define AAPT_XMLIDCOLLECTOR_H
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
struct XmlIdCollector : public IXmlResourceConsumer {
|
||||
bool consume(IAaptContext* context, XmlResource* xmlRes) override;
|
||||
bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace aapt {
|
||||
TEST(XmlIdCollectorTest, CollectsIds) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
|
||||
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/foo"
|
||||
text="@+id/bar">
|
||||
@@ -50,7 +50,7 @@ TEST(XmlIdCollectorTest, CollectsIds) {
|
||||
TEST(XmlIdCollectorTest, DontCollectNonIds) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
|
||||
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
|
||||
|
||||
XmlIdCollector collector;
|
||||
ASSERT_TRUE(collector.consume(context.get(), doc.get()));
|
||||
|
||||
@@ -62,7 +62,7 @@ struct ExtendedTypes {
|
||||
* A raw string value that hasn't had its escape sequences
|
||||
* processed nor whitespace removed.
|
||||
*/
|
||||
TYPE_RAW_STRING = 0xfe
|
||||
TYPE_RAW_STRING = 0xfe,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -78,10 +78,18 @@ public:
|
||||
explicit SymbolWriter(StringPool* pool) : mPool(pool) {
|
||||
}
|
||||
|
||||
void addSymbol(const ResourceNameRef& name, size_t offset) {
|
||||
symbols.push_back(Entry{ mPool->makeRef(name.package.toString() + u":" +
|
||||
toString(name.type).toString() + u"/" +
|
||||
name.entry.toString()), offset });
|
||||
void addSymbol(const Reference& ref, size_t offset) {
|
||||
const ResourceName& name = ref.name.value();
|
||||
std::u16string fullName;
|
||||
if (ref.privateReference) {
|
||||
fullName += u"*";
|
||||
}
|
||||
|
||||
if (!name.package.empty()) {
|
||||
fullName += name.package + u":";
|
||||
}
|
||||
fullName += toString(name.type).toString() + u"/" + name.entry;
|
||||
symbols.push_back(Entry{ mPool->makeRef(fullName), offset });
|
||||
}
|
||||
|
||||
void shiftAllOffsets(size_t offset) {
|
||||
@@ -100,20 +108,23 @@ struct MapFlattenVisitor : public RawValueVisitor {
|
||||
SymbolWriter* mSymbols;
|
||||
FlatEntry* mEntry;
|
||||
BigBuffer* mBuffer;
|
||||
bool mUseExtendedChunks;
|
||||
size_t mEntryCount = 0;
|
||||
Maybe<uint32_t> mParentIdent;
|
||||
Maybe<ResourceNameRef> mParentName;
|
||||
|
||||
MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer) :
|
||||
mSymbols(symbols), mEntry(entry), mBuffer(buffer) {
|
||||
MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer,
|
||||
bool useExtendedChunks) :
|
||||
mSymbols(symbols), mEntry(entry), mBuffer(buffer),
|
||||
mUseExtendedChunks(useExtendedChunks) {
|
||||
}
|
||||
|
||||
void flattenKey(Reference* key, ResTable_map* outEntry) {
|
||||
if (!key->id) {
|
||||
if (!key->id || (key->privateReference && mUseExtendedChunks)) {
|
||||
assert(key->name && "reference must have a name");
|
||||
|
||||
outEntry->name.ident = util::hostToDevice32(0);
|
||||
mSymbols->addSymbol(key->name.value(), (mBuffer->size() - sizeof(ResTable_map)) +
|
||||
mSymbols->addSymbol(*key, (mBuffer->size() - sizeof(ResTable_map)) +
|
||||
offsetof(ResTable_map, name));
|
||||
} else {
|
||||
outEntry->name.ident = util::hostToDevice32(key->id.value().id);
|
||||
@@ -121,16 +132,21 @@ struct MapFlattenVisitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
void flattenValue(Item* value, ResTable_map* outEntry) {
|
||||
bool privateRef = false;
|
||||
if (Reference* ref = valueCast<Reference>(value)) {
|
||||
if (!ref->id) {
|
||||
privateRef = ref->privateReference && mUseExtendedChunks;
|
||||
if (!ref->id || privateRef) {
|
||||
assert(ref->name && "reference must have a name");
|
||||
|
||||
mSymbols->addSymbol(ref->name.value(), (mBuffer->size() - sizeof(ResTable_map)) +
|
||||
mSymbols->addSymbol(*ref, (mBuffer->size() - sizeof(ResTable_map)) +
|
||||
offsetof(ResTable_map, value) + offsetof(Res_value, data));
|
||||
}
|
||||
}
|
||||
|
||||
bool result = value->flatten(&outEntry->value);
|
||||
if (privateRef) {
|
||||
outEntry->value.data = 0;
|
||||
}
|
||||
assert(result && "flatten failed");
|
||||
}
|
||||
|
||||
@@ -169,7 +185,8 @@ struct MapFlattenVisitor : public RawValueVisitor {
|
||||
|
||||
void visit(Style* style) override {
|
||||
if (style->parent) {
|
||||
if (!style->parent.value().id) {
|
||||
bool privateRef = style->parent.value().privateReference && mUseExtendedChunks;
|
||||
if (!style->parent.value().id || privateRef) {
|
||||
assert(style->parent.value().name && "reference must have a name");
|
||||
mParentName = style->parent.value().name;
|
||||
} else {
|
||||
@@ -339,21 +356,28 @@ private:
|
||||
bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
|
||||
if (Item* item = valueCast<Item>(entry->value)) {
|
||||
writeEntry<ResTable_entry, true>(entry, buffer);
|
||||
bool privateRef = false;
|
||||
if (Reference* ref = valueCast<Reference>(entry->value)) {
|
||||
if (!ref->id) {
|
||||
// If there is no ID or the reference is private and we allow extended chunks,
|
||||
// write out a 0 and mark the symbol table with the name of the reference.
|
||||
privateRef = (ref->privateReference && mOptions.useExtendedChunks);
|
||||
if (!ref->id || privateRef) {
|
||||
assert(ref->name && "reference must have at least a name");
|
||||
mSymbols->addSymbol(ref->name.value(),
|
||||
buffer->size() + offsetof(Res_value, data));
|
||||
mSymbols->addSymbol(*ref, buffer->size() + offsetof(Res_value, data));
|
||||
}
|
||||
}
|
||||
Res_value* outValue = buffer->nextBlock<Res_value>();
|
||||
bool result = item->flatten(outValue);
|
||||
assert(result && "flatten failed");
|
||||
if (privateRef) {
|
||||
// Force the value of 0 so we look up the symbol at unflatten time.
|
||||
outValue->data = 0;
|
||||
}
|
||||
outValue->size = util::hostToDevice16(sizeof(*outValue));
|
||||
} else {
|
||||
const size_t beforeEntry = buffer->size();
|
||||
ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
|
||||
MapFlattenVisitor visitor(mSymbols, entry, buffer);
|
||||
MapFlattenVisitor visitor(mSymbols, entry, buffer, mOptions.useExtendedChunks);
|
||||
entry->value->accept(&visitor);
|
||||
outEntry->count = util::hostToDevice32(visitor.mEntryCount);
|
||||
if (visitor.mParentName) {
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "flatten/TableFlattener.h"
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
#include "unflatten/BinaryResourceParser.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
@@ -15,15 +15,14 @@
|
||||
*/
|
||||
|
||||
#include "SdkConstants.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "flatten/ChunkWriter.h"
|
||||
#include "flatten/ResourceTypeExtensions.h"
|
||||
#include "flatten/XmlFlattener.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <vector>
|
||||
#include <utils/misc.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace android;
|
||||
|
||||
@@ -306,7 +305,7 @@ bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XmlFlattener::consume(IAaptContext* context, XmlResource* resource) {
|
||||
bool XmlFlattener::consume(IAaptContext* context, xml::XmlResource* resource) {
|
||||
if (!resource->root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -17,16 +17,12 @@
|
||||
#ifndef AAPT_FLATTEN_XMLFLATTENER_H
|
||||
#define AAPT_FLATTEN_XMLFLATTENER_H
|
||||
|
||||
#include "util/BigBuffer.h"
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "util/BigBuffer.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
namespace xml {
|
||||
struct Node;
|
||||
}
|
||||
|
||||
struct XmlFlattenerOptions {
|
||||
/**
|
||||
* Keep attribute raw string values along with typed values.
|
||||
@@ -45,7 +41,7 @@ public:
|
||||
mBuffer(buffer), mOptions(options) {
|
||||
}
|
||||
|
||||
bool consume(IAaptContext* context, XmlResource* resource) override;
|
||||
bool consume(IAaptContext* context, xml::XmlResource* resource) override;
|
||||
|
||||
private:
|
||||
BigBuffer* mBuffer;
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
#include "flatten/XmlFlattener.h"
|
||||
#include "link/Linkers.h"
|
||||
#include "util/BigBuffer.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
#include "util/BigBuffer.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <gtest/gtest.h>
|
||||
@@ -45,7 +44,7 @@ public:
|
||||
.build();
|
||||
}
|
||||
|
||||
::testing::AssertionResult flatten(XmlResource* doc, android::ResXMLTree* outTree,
|
||||
::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
|
||||
XmlFlattenerOptions options = {}) {
|
||||
BigBuffer buffer(1024);
|
||||
XmlFlattener flattener(&buffer, options);
|
||||
@@ -65,7 +64,7 @@ protected:
|
||||
};
|
||||
|
||||
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
<View xmlns:test="http://com.test"
|
||||
attr="hey">
|
||||
<Layout test:hello="hi" />
|
||||
@@ -144,7 +143,7 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingStart="1dp"
|
||||
android:colorAccent="#ffffff"/>)EOF");
|
||||
@@ -169,7 +168,7 @@ TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@id/id"
|
||||
class="str"
|
||||
@@ -192,7 +191,7 @@ TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
|
||||
* namespace.
|
||||
*/
|
||||
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
|
||||
|
||||
android::ResXMLTree tree;
|
||||
ASSERT_TRUE(flatten(doc.get(), &tree));
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
#include "ResourceParser.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include "java/AnnotationProcessor.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -42,7 +40,7 @@ struct AnnotationProcessorTest : public ::testing::Test {
|
||||
options);
|
||||
std::stringstream in;
|
||||
in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
|
||||
XmlPullParser xmlParser(in);
|
||||
xml::XmlPullParser xmlParser(in);
|
||||
if (parser.parse(&xmlParser)) {
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef AAPT_JAVA_CLASSDEFINITION_H
|
||||
#define AAPT_JAVA_CLASSDEFINITION_H
|
||||
|
||||
#include "Resource.h"
|
||||
#include "java/AnnotationProcessor.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "Source.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "java/AnnotationProcessor.h"
|
||||
#include "java/ClassDefinitionWriter.h"
|
||||
#include "java/ManifestClassGenerator.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -80,7 +79,7 @@ static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef,
|
||||
}
|
||||
|
||||
bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
|
||||
XmlResource* res, std::ostream* out) {
|
||||
xml::XmlResource* res, std::ostream* out) {
|
||||
xml::Element* el = xml::findRootElement(res->root.get());
|
||||
if (!el) {
|
||||
return false;
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
struct ManifestClassGenerator {
|
||||
bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res,
|
||||
bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res,
|
||||
std::ostream* out);
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "java/ManifestClassGenerator.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
@@ -25,7 +24,7 @@ namespace aapt {
|
||||
|
||||
TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
|
||||
std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<permission android:name="android.permission.ACCESS_INTERNET" />
|
||||
<permission android:name="android.DO_DANGEROUS_THINGS" />
|
||||
@@ -75,7 +74,7 @@ TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
|
||||
|
||||
TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
|
||||
std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Required to access the internet.
|
||||
Added in API 1. -->
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "java/ProguardRules.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -40,11 +39,11 @@ public:
|
||||
|
||||
virtual void visit(xml::Element* node) override {
|
||||
if (!node->namespaceUri.empty()) {
|
||||
Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
|
||||
Maybe<xml::ExtractedPackage> maybePackage = xml::extractPackageFromNamespace(
|
||||
node->namespaceUri);
|
||||
if (maybePackage) {
|
||||
// This is a custom view, let's figure out the class name from this.
|
||||
std::u16string package = maybePackage.value() + u"." + node->name;
|
||||
std::u16string package = maybePackage.value().package + u"." + node->name;
|
||||
if (util::isJavaClassName(package)) {
|
||||
addClass(node->lineNumber, package);
|
||||
}
|
||||
@@ -185,7 +184,8 @@ struct ManifestVisitor : public BaseVisitor {
|
||||
std::u16string mPackage;
|
||||
};
|
||||
|
||||
bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
|
||||
bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res,
|
||||
KeepSet* keepSet) {
|
||||
ManifestVisitor visitor(source, keepSet);
|
||||
if (res->root) {
|
||||
res->root->accept(&visitor);
|
||||
@@ -194,7 +194,7 @@ bool collectProguardRulesForManifest(const Source& source, XmlResource* res, Kee
|
||||
return false;
|
||||
}
|
||||
|
||||
bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
|
||||
bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) {
|
||||
if (!res->root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
#include "Resource.h"
|
||||
#include "Source.h"
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
@@ -47,8 +46,8 @@ private:
|
||||
std::map<std::u16string, std::set<Source>> mKeepMethodSet;
|
||||
};
|
||||
|
||||
bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet);
|
||||
bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet);
|
||||
bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet);
|
||||
bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet);
|
||||
|
||||
bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#include "Debug.h"
|
||||
#include "Flags.h"
|
||||
#include "NameMangler.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "compile/IdAssigner.h"
|
||||
#include "flatten/Archive.h"
|
||||
#include "flatten/TableFlattener.h"
|
||||
@@ -28,6 +26,7 @@
|
||||
#include "java/ManifestClassGenerator.h"
|
||||
#include "java/ProguardRules.h"
|
||||
#include "link/Linkers.h"
|
||||
#include "link/ReferenceLinker.h"
|
||||
#include "link/ManifestFixer.h"
|
||||
#include "link/TableMerger.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
@@ -36,6 +35,7 @@
|
||||
#include "unflatten/FileExportHeaderReader.h"
|
||||
#include "util/Files.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
@@ -159,7 +159,7 @@ public:
|
||||
/**
|
||||
* Inflates an XML file from the source path.
|
||||
*/
|
||||
std::unique_ptr<XmlResource> loadXml(const std::string& path) {
|
||||
std::unique_ptr<xml::XmlResource> loadXml(const std::string& path) {
|
||||
std::ifstream fin(path, std::ifstream::binary);
|
||||
if (!fin) {
|
||||
mContext.getDiagnostics()->error(DiagMessage(path) << strerror(errno));
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
/**
|
||||
* Inflates a binary XML file from the source path.
|
||||
*/
|
||||
std::unique_ptr<XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) {
|
||||
std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) {
|
||||
// Read header for symbol info and export info.
|
||||
std::string errorStr;
|
||||
Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
|
||||
@@ -188,7 +188,7 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlResource> xmlRes = xml::inflate(
|
||||
std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
|
||||
(const uint8_t*) maybeF.value().getDataPtr() + (size_t) offset,
|
||||
maybeF.value().getDataLength() - offset,
|
||||
mContext.getDiagnostics(), Source(path));
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<AppInfo> extractAppInfoFromManifest(XmlResource* xmlRes) {
|
||||
Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
|
||||
// Make sure the first element is <manifest> with package attribute.
|
||||
if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
|
||||
if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
|
||||
@@ -309,7 +309,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flattenXml(XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
|
||||
bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
|
||||
IArchiveWriter* writer) {
|
||||
BigBuffer buffer(1024);
|
||||
XmlFlattenerOptions options = {};
|
||||
@@ -354,7 +354,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeManifestJavaFile(XmlResource* manifestXml) {
|
||||
bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
|
||||
if (!mOptions.generateJavaClassPath) {
|
||||
return true;
|
||||
}
|
||||
@@ -502,7 +502,7 @@ public:
|
||||
|
||||
int run(const std::vector<std::string>& inputFiles) {
|
||||
// Load the AndroidManifest.xml
|
||||
std::unique_ptr<XmlResource> manifestXml = loadXml(mOptions.manifestPath);
|
||||
std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath);
|
||||
if (!manifestXml) {
|
||||
return 1;
|
||||
}
|
||||
@@ -545,25 +545,21 @@ public:
|
||||
<< "with package ID " << std::hex << (int) mContext.mPackageId);
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
|
||||
for (const std::string& input : inputFiles) {
|
||||
if (!processFile(input, false)) {
|
||||
error = true;
|
||||
mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& input : mOptions.overlayFiles) {
|
||||
if (!processFile(input, true)) {
|
||||
error = true;
|
||||
mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!verifyNoExternalPackages()) {
|
||||
return 1;
|
||||
}
|
||||
@@ -608,6 +604,7 @@ public:
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
{
|
||||
ManifestFixerOptions manifestFixerOptions;
|
||||
manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
|
||||
@@ -617,6 +614,11 @@ public:
|
||||
error = true;
|
||||
}
|
||||
|
||||
// AndroidManifest.xml has no resource name, but the CallSite is built from the name
|
||||
// (aka, which package the AndroidManifest.xml is coming from).
|
||||
// So we give it a package name so it can see local resources.
|
||||
manifestXml->file.name.package = mContext.getCompilationPackage().toString();
|
||||
|
||||
XmlReferenceLinker manifestLinker;
|
||||
if (manifestLinker.consume(&mContext, manifestXml.get())) {
|
||||
if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
|
||||
@@ -640,14 +642,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const FileToProcess& file : mFilesToProcess) {
|
||||
if (file.file.name.type != ResourceType::kRaw &&
|
||||
util::stringEndsWith<char>(file.source.path, ".xml.flat")) {
|
||||
if (mOptions.verbose) {
|
||||
mContext.getDiagnostics()->note(DiagMessage() << "linking " << file.source.path);
|
||||
mContext.getDiagnostics()->note(DiagMessage()
|
||||
<< "linking " << file.source.path);
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(file.source.path);
|
||||
std::unique_ptr<xml::XmlResource> xmlRes = loadBinaryXmlSkipFileExport(
|
||||
file.source.path);
|
||||
if (!xmlRes) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
#ifndef AAPT_LINKER_LINKERS_H
|
||||
#define AAPT_LINKER_LINKERS_H
|
||||
|
||||
#include "Resource.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -27,6 +29,14 @@ class ResourceTable;
|
||||
struct ResourceEntry;
|
||||
struct ConfigDescription;
|
||||
|
||||
/**
|
||||
* Defines the location in which a value exists. This determines visibility of other
|
||||
* package's private symbols.
|
||||
*/
|
||||
struct CallSite {
|
||||
ResourceNameRef resource;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether a versioned resource should be created. If a versioned resource already
|
||||
* exists, it takes precedence.
|
||||
@@ -39,7 +49,7 @@ struct AutoVersioner : public IResourceTableConsumer {
|
||||
};
|
||||
|
||||
struct XmlAutoVersioner : public IXmlResourceConsumer {
|
||||
bool consume(IAaptContext* context, XmlResource* resource) override;
|
||||
bool consume(IAaptContext* context, xml::XmlResource* resource) override;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -68,15 +78,6 @@ struct PrivateAttributeMover : public IResourceTableConsumer {
|
||||
bool consume(IAaptContext* context, ResourceTable* table) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves all references to resources in the ResourceTable and assigns them IDs.
|
||||
* The ResourceTable must already have IDs assigned to each resource.
|
||||
* Once the ResourceTable is processed by this linker, it is ready to be flattened.
|
||||
*/
|
||||
struct ReferenceLinker : public IResourceTableConsumer {
|
||||
bool consume(IAaptContext* context, ResourceTable* table) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves attributes in the XmlResource and compiles string values to resource values.
|
||||
* Once an XmlResource is processed by this linker, it is ready to be flattened.
|
||||
@@ -86,7 +87,7 @@ private:
|
||||
std::set<int> mSdkLevelsFound;
|
||||
|
||||
public:
|
||||
bool consume(IAaptContext* context, XmlResource* resource) override;
|
||||
bool consume(IAaptContext* context, xml::XmlResource* resource) override;
|
||||
|
||||
/**
|
||||
* Once the XmlResource has been consumed, this returns the various SDK levels in which
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
|
||||
#include "ResourceUtils.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "link/ManifestFixer.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -63,7 +62,7 @@ static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ManifestFixer::consume(IAaptContext* context, XmlResource* doc) {
|
||||
bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
|
||||
xml::Element* root = xml::findRootElement(doc->root.get());
|
||||
if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
|
||||
context->getDiagnostics()->error(DiagMessage(doc->file.source)
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#define AAPT_LINK_MANIFESTFIXER_H
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -36,7 +40,7 @@ struct ManifestFixer : public IXmlResourceConsumer {
|
||||
ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
|
||||
}
|
||||
|
||||
bool consume(IAaptContext* context, XmlResource* doc) override;
|
||||
bool consume(IAaptContext* context, xml::XmlResource* doc) override;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "link/ManifestFixer.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
@@ -51,13 +50,13 @@ struct ManifestFixerTest : public ::testing::Test {
|
||||
.build();
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlResource> verify(const StringPiece& str) {
|
||||
std::unique_ptr<xml::XmlResource> verify(const StringPiece& str) {
|
||||
return verifyWithOptions(str, {});
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlResource> verifyWithOptions(const StringPiece& str,
|
||||
const ManifestFixerOptions& options) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(str);
|
||||
std::unique_ptr<xml::XmlResource> verifyWithOptions(const StringPiece& str,
|
||||
const ManifestFixerOptions& options) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(str);
|
||||
ManifestFixer fixer(options);
|
||||
if (fixer.consume(mContext.get(), doc.get())) {
|
||||
return doc;
|
||||
@@ -88,7 +87,7 @@ TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
|
||||
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
|
||||
ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
|
||||
|
||||
std::unique_ptr<XmlResource> doc = verifyWithOptions(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android">
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "ResourceTable.h"
|
||||
|
||||
#include "link/Linkers.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -14,17 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ReferenceLinker.h"
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "util/Util.h"
|
||||
#include "ValueVisitor.h"
|
||||
|
||||
#include "link/Linkers.h"
|
||||
#include "link/ReferenceLinkerVisitor.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "process/SymbolTable.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <cassert>
|
||||
@@ -41,52 +42,15 @@ namespace {
|
||||
*
|
||||
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
|
||||
*/
|
||||
class StyleAndReferenceLinkerVisitor : public ValueVisitor {
|
||||
class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
private:
|
||||
ReferenceLinkerVisitor mReferenceVisitor;
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
IPackageDeclStack* mPackageDecls;
|
||||
xml::IPackageDeclStack* mPackageDecls;
|
||||
StringPool* mStringPool;
|
||||
CallSite* mCallSite;
|
||||
bool mError = false;
|
||||
|
||||
const ISymbolTable::Symbol* findAttributeSymbol(Reference* reference) {
|
||||
assert(reference);
|
||||
assert(reference->name || reference->id);
|
||||
|
||||
if (reference->name) {
|
||||
// Transform the package name if it is an alias.
|
||||
Maybe<ResourceName> realName = mPackageDecls->transformPackage(
|
||||
reference->name.value(), mContext->getCompilationPackage());
|
||||
|
||||
// Mangle the reference name if it should be mangled.
|
||||
Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
|
||||
realName ? realName.value() : reference->name.value());
|
||||
|
||||
const ISymbolTable::Symbol* s = nullptr;
|
||||
if (mangledName) {
|
||||
s = mSymbols->findByName(mangledName.value());
|
||||
} else if (realName) {
|
||||
s = mSymbols->findByName(realName.value());
|
||||
} else {
|
||||
s = mSymbols->findByName(reference->name.value());
|
||||
}
|
||||
|
||||
if (s && s->attribute) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (reference->id) {
|
||||
if (const ISymbolTable::Symbol* s = mSymbols->findById(reference->id.value())) {
|
||||
if (s->attribute) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a RawString value into a more specific, appropriate value, based on the
|
||||
* Attribute. If a non RawString value is passed in, this is an identity transform.
|
||||
@@ -94,8 +58,8 @@ private:
|
||||
std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
|
||||
const Attribute* attr) {
|
||||
if (RawString* rawString = valueCast<RawString>(value.get())) {
|
||||
std::unique_ptr<Item> transformed = ResourceUtils::parseItemForAttribute(
|
||||
*rawString->value, attr);
|
||||
std::unique_ptr<Item> transformed =
|
||||
ResourceUtils::parseItemForAttribute(*rawString->value, attr);
|
||||
|
||||
// If we could not parse as any specific type, try a basic STRING.
|
||||
if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
|
||||
@@ -163,14 +127,16 @@ private:
|
||||
public:
|
||||
using ValueVisitor::visit;
|
||||
|
||||
StyleAndReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
|
||||
StringPool* stringPool, IPackageDeclStack* decl) :
|
||||
mReferenceVisitor(context, symbols, decl), mContext(context), mSymbols(symbols),
|
||||
mPackageDecls(decl), mStringPool(stringPool) {
|
||||
ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool,
|
||||
xml::IPackageDeclStack* decl,CallSite* callSite) :
|
||||
mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
|
||||
mCallSite(callSite) {
|
||||
}
|
||||
|
||||
void visit(Reference* reference) override {
|
||||
mReferenceVisitor.visit(reference);
|
||||
void visit(Reference* ref) override {
|
||||
if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
|
||||
mError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,13 +150,26 @@ public:
|
||||
}
|
||||
|
||||
for (Style::Entry& entry : style->entries) {
|
||||
if (const ISymbolTable::Symbol* s = findAttributeSymbol(&entry.key)) {
|
||||
std::string errStr;
|
||||
|
||||
// Transform the attribute reference so that it is using the fully qualified package
|
||||
// name. This will also mark the reference as being able to see private resources if
|
||||
// there was a '*' in the reference or if the package came from the private namespace.
|
||||
Reference transformedReference = entry.key;
|
||||
transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
|
||||
&transformedReference);
|
||||
|
||||
// Find the attribute in the symbol table and check if it is visible from this callsite.
|
||||
const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
|
||||
if (symbol) {
|
||||
// Assign our style key the correct ID.
|
||||
entry.key.id = s->id;
|
||||
entry.key.id = symbol->id;
|
||||
|
||||
// Try to convert the value to a more specific, typed value based on the
|
||||
// attribute it is set to.
|
||||
entry.value = parseValueWithAttribute(std::move(entry.value), s->attribute.get());
|
||||
entry.value = parseValueWithAttribute(std::move(entry.value),
|
||||
symbol->attribute.get());
|
||||
|
||||
// Link/resolve the final value (mostly if it's a reference).
|
||||
entry.value->accept(this);
|
||||
@@ -201,13 +180,13 @@ public:
|
||||
entry.value->flatten(&val);
|
||||
|
||||
// Always allow references.
|
||||
const uint32_t typeMask = s->attribute->typeMask |
|
||||
const uint32_t typeMask = symbol->attribute->typeMask |
|
||||
android::ResTable_map::TYPE_REFERENCE;
|
||||
|
||||
if (!(typeMask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
|
||||
// The actual type of this item is incompatible with the attribute.
|
||||
DiagMessage msg(style->getSource());
|
||||
buildAttributeMismatchMessage(&msg, s->attribute.get(), entry.value.get());
|
||||
buildAttributeMismatchMessage(&msg, symbol->attribute.get(), entry.value.get());
|
||||
mContext->getDiagnostics()->error(msg);
|
||||
mError = true;
|
||||
}
|
||||
@@ -219,23 +198,151 @@ public:
|
||||
} else {
|
||||
msg << entry.key.id.value();
|
||||
}
|
||||
msg << "' not found";
|
||||
msg << "' " << errStr;
|
||||
mContext->getDiagnostics()->error(msg);
|
||||
mContext->getDiagnostics()->note(DiagMessage(style->getSource()) << entry.key);
|
||||
mError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool hasError() {
|
||||
return mError || mReferenceVisitor.hasError();
|
||||
bool hasError() {
|
||||
return mError;
|
||||
}
|
||||
};
|
||||
|
||||
struct EmptyDeclStack : public IPackageDeclStack {
|
||||
Maybe<ResourceName> transformPackage(const ResourceName& name,
|
||||
const StringPiece16& localPackage) const override {
|
||||
if (name.package.empty()) {
|
||||
return ResourceName{ localPackage.toString(), name.type, name.entry };
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* The symbol is visible if it is public, or if the reference to it is requesting private access
|
||||
* or if the callsite comes from the same package.
|
||||
*/
|
||||
bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
|
||||
const CallSite& callSite) {
|
||||
if (!symbol.isPublic && !ref.privateReference) {
|
||||
if (ref.name) {
|
||||
return callSite.resource.package == ref.name.value().package;
|
||||
} else if (ref.id) {
|
||||
return ref.id.value().packageId() == symbol.id.packageId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
|
||||
NameMangler* mangler,
|
||||
ISymbolTable* symbols) {
|
||||
if (reference.name) {
|
||||
Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
|
||||
return symbols->findByName(mangled ? mangled.value() : reference.name.value());
|
||||
} else if (reference.id) {
|
||||
return symbols->findById(reference.id.value());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
|
||||
const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
|
||||
CallSite* callSite, std::string* outError) {
|
||||
const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
|
||||
if (!symbol) {
|
||||
std::stringstream errStr;
|
||||
errStr << "not found";
|
||||
if (outError) *outError = errStr.str();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!isSymbolVisible(*symbol, reference, *callSite)) {
|
||||
std::stringstream errStr;
|
||||
errStr << "is private";
|
||||
if (outError) *outError = errStr.str();
|
||||
return nullptr;
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
|
||||
CallSite* callSite, std::string* outError) {
|
||||
const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
|
||||
symbols, callSite,
|
||||
outError);
|
||||
if (!symbol) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!symbol->attribute) {
|
||||
std::stringstream errStr;
|
||||
errStr << "is not an attribute";
|
||||
if (outError) *outError = errStr.str();
|
||||
return nullptr;
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError) {
|
||||
const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
|
||||
if (!symbol) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!symbol->attribute) {
|
||||
std::stringstream errStr;
|
||||
errStr << "is not an attribute";
|
||||
if (outError) *outError = errStr.str();
|
||||
return {};
|
||||
}
|
||||
return xml::AaptAttribute{ symbol->id, *symbol->attribute };
|
||||
}
|
||||
|
||||
bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
|
||||
ISymbolTable* symbols, xml::IPackageDeclStack* decls,
|
||||
CallSite* callSite) {
|
||||
assert(reference);
|
||||
assert(reference->name || reference->id);
|
||||
|
||||
Reference transformedReference = *reference;
|
||||
transformReferenceFromNamespace(decls, context->getCompilationPackage(),
|
||||
&transformedReference);
|
||||
|
||||
std::string errStr;
|
||||
const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
|
||||
transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
|
||||
if (s) {
|
||||
reference->id = s->id;
|
||||
return true;
|
||||
}
|
||||
|
||||
DiagMessage errorMsg(reference->getSource());
|
||||
errorMsg << "resource ";
|
||||
if (reference->name) {
|
||||
errorMsg << reference->name.value();
|
||||
if (transformedReference.name.value() != reference->name.value()) {
|
||||
errorMsg << " (aka " << transformedReference.name.value() << ")";
|
||||
}
|
||||
} else {
|
||||
errorMsg << reference->id.value();
|
||||
}
|
||||
|
||||
errorMsg << " " << errStr;
|
||||
context->getDiagnostics()->error(errorMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct EmptyDeclStack : public xml::IPackageDeclStack {
|
||||
Maybe<xml::ExtractedPackage> transformPackageAlias(
|
||||
const StringPiece16& alias, const StringPiece16& localPackage) const override {
|
||||
if (alias.empty()) {
|
||||
return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -259,14 +366,16 @@ bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
|
||||
ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
|
||||
&table->stringPool, &declStack, &callSite);
|
||||
|
||||
for (auto& configValue : entry->values) {
|
||||
StyleAndReferenceLinkerVisitor visitor(context,
|
||||
context->getExternalSymbols(),
|
||||
&table->stringPool, &declStack);
|
||||
configValue.value->accept(&visitor);
|
||||
if (visitor.hasError()) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (visitor.hasError()) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
99
tools/aapt2/link/ReferenceLinker.h
Normal file
99
tools/aapt2/link/ReferenceLinker.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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_LINKER_REFERENCELINKER_H
|
||||
#define AAPT_LINKER_REFERENCELINKER_H
|
||||
|
||||
#include "Resource.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "ValueVisitor.h"
|
||||
#include "link/Linkers.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "process/SymbolTable.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
/**
|
||||
* Resolves all references to resources in the ResourceTable and assigns them IDs.
|
||||
* The ResourceTable must already have IDs assigned to each resource.
|
||||
* Once the ResourceTable is processed by this linker, it is ready to be flattened.
|
||||
*/
|
||||
struct ReferenceLinker : public IResourceTableConsumer {
|
||||
/**
|
||||
* Returns true if the symbol is visible by the reference and from the callsite.
|
||||
*/
|
||||
static bool isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
|
||||
const CallSite& callSite);
|
||||
|
||||
/**
|
||||
* Performs name mangling and looks up the resource in the symbol table. Returns nullptr
|
||||
* if the symbol was not found.
|
||||
*/
|
||||
static const ISymbolTable::Symbol* resolveSymbol(const Reference& reference,
|
||||
NameMangler* mangler, ISymbolTable* symbols);
|
||||
|
||||
/**
|
||||
* Performs name mangling and looks up the resource in the symbol table. If the symbol is
|
||||
* not visible by the reference at the callsite, nullptr is returned. outError holds
|
||||
* the error message.
|
||||
*/
|
||||
static const ISymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
|
||||
/**
|
||||
* Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
|
||||
* That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
|
||||
*/
|
||||
static const ISymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
|
||||
/**
|
||||
* Resolves the attribute reference and returns an xml::AaptAttribute if successful.
|
||||
* If resolution fails, outError holds the error message.
|
||||
*/
|
||||
static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
|
||||
/**
|
||||
* Transforms the package name of the reference to the fully qualified package name using
|
||||
* the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
|
||||
* to the reference at the callsite, the reference is updated with an ID.
|
||||
* Returns false on failure, and an error message is logged to the IDiagnostics in the context.
|
||||
*/
|
||||
static bool linkReference(Reference* reference, IAaptContext* context, ISymbolTable* symbols,
|
||||
xml::IPackageDeclStack* decls, CallSite* callSite);
|
||||
|
||||
/**
|
||||
* Links all references in the ResourceTable.
|
||||
*/
|
||||
bool consume(IAaptContext* context, ResourceTable* table) override;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
#endif /* AAPT_LINKER_REFERENCELINKER_H */
|
||||
@@ -1,109 +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_LINKER_REFERENCELINKERVISITOR_H
|
||||
#define AAPT_LINKER_REFERENCELINKERVISITOR_H
|
||||
|
||||
#include "Resource.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "ValueVisitor.h"
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "process/SymbolTable.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
/**
|
||||
* The ReferenceLinkerVisitor will follow all references and make sure they point
|
||||
* to resources that actually exist in the given ISymbolTable.
|
||||
* Once the target resource has been found, the ID of the resource will be assigned
|
||||
* to the reference object.
|
||||
*/
|
||||
class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
using ValueVisitor::visit;
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
IPackageDeclStack* mPackageDecls;
|
||||
bool mError = false;
|
||||
|
||||
public:
|
||||
ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, IPackageDeclStack* decls) :
|
||||
mContext(context), mSymbols(symbols), mPackageDecls(decls) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup a reference and ensure it exists, either in our local table, or as an external
|
||||
* symbol. Once found, assign the ID of the target resource to this reference object.
|
||||
*/
|
||||
void visit(Reference* reference) override {
|
||||
assert(reference);
|
||||
assert(reference->name || reference->id);
|
||||
|
||||
// We prefer to lookup by name if the name is set. Otherwise it could be
|
||||
// an out-of-date ID.
|
||||
if (reference->name) {
|
||||
// Transform the package name if it is an alias.
|
||||
Maybe<ResourceName> realName = mPackageDecls->transformPackage(
|
||||
reference->name.value(), mContext->getCompilationPackage());
|
||||
|
||||
// Mangle the reference name if it should be mangled.
|
||||
Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
|
||||
realName ? realName.value() : reference->name.value());
|
||||
|
||||
const ISymbolTable::Symbol* s = nullptr;
|
||||
if (mangledName) {
|
||||
s = mSymbols->findByName(mangledName.value());
|
||||
} else if (realName) {
|
||||
s = mSymbols->findByName(realName.value());
|
||||
} else {
|
||||
s = mSymbols->findByName(reference->name.value());
|
||||
}
|
||||
|
||||
if (s) {
|
||||
reference->id = s->id;
|
||||
return;
|
||||
}
|
||||
|
||||
DiagMessage errorMsg(reference->getSource());
|
||||
errorMsg << "reference to " << reference->name.value();
|
||||
if (realName) {
|
||||
errorMsg << " (aka " << realName.value() << ")";
|
||||
}
|
||||
errorMsg << " was not found";
|
||||
mContext->getDiagnostics()->error(errorMsg);
|
||||
mError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSymbols->findById(reference->id.value())) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(reference->getSource())
|
||||
<< "reference to " << reference->id.value()
|
||||
<< " was not found");
|
||||
mError = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool hasError() {
|
||||
return mError;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
#endif /* AAPT_LINKER_REFERENCELINKERVISITOR_H */
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "link/Linkers.h"
|
||||
#include "link/ReferenceLinker.h"
|
||||
#include "process/SymbolTable.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
@@ -44,7 +44,7 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) {
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:string/ok", ResourceId(0x01040034))
|
||||
.addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034))
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
@@ -92,12 +92,12 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) {
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
|
||||
.addSymbol(u"@android:attr/foo", ResourceId(0x01010001),
|
||||
.addPublicSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
|
||||
.addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.addSymbol(u"@android:attr/bar", ResourceId(0x01010002),
|
||||
.addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_FLAGS)
|
||||
.addItem(u"one", 0x01)
|
||||
@@ -132,7 +132,7 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@com.app.test:attr/com.android.support$foo",
|
||||
.addPublicSymbol(u"@com.app.test:attr/com.android.support$foo",
|
||||
ResourceId(0x7f010000), test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
|
||||
.build())
|
||||
@@ -156,4 +156,78 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
|
||||
EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
|
||||
}
|
||||
|
||||
TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
|
||||
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
|
||||
.setPackageId(u"com.app.test", 0x7f)
|
||||
.addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000),
|
||||
u"@android:string/hidden")
|
||||
.build();
|
||||
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:string/hidden", ResourceId(0x01040034))
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
ASSERT_FALSE(linker.consume(context.get(), table.get()));
|
||||
}
|
||||
|
||||
TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
|
||||
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
|
||||
.setPackageId(u"com.app.test", 0x7f)
|
||||
.addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000),
|
||||
u"@com.app.lib:string/hidden")
|
||||
.build();
|
||||
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@com.app.test:string/com.app.lib$hidden",
|
||||
ResourceId(0x7f040034))
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
ASSERT_FALSE(linker.consume(context.get(), table.get()));
|
||||
}
|
||||
|
||||
TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
|
||||
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
|
||||
.setPackageId(u"com.app.test", 0x7f)
|
||||
.addValue(u"@com.app.test:style/Theme", test::StyleBuilder()
|
||||
.addItem(u"@android:attr/hidden", ResourceUtils::tryParseColor(u"#ff00ff"))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:attr/hidden", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
ASSERT_FALSE(linker.consume(context.get(), table.get()));
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -17,51 +17,91 @@
|
||||
#include "Diagnostics.h"
|
||||
#include "ResourceUtils.h"
|
||||
#include "SdkConstants.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include "link/Linkers.h"
|
||||
#include "link/ReferenceLinkerVisitor.h"
|
||||
#include "link/ReferenceLinker.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "process/SymbolTable.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
namespace {
|
||||
|
||||
class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor {
|
||||
/**
|
||||
* Visits all references (including parents of styles, references in styles, arrays, etc) and
|
||||
* links their symbolic name to their Resource ID, performing mangling and package aliasing
|
||||
* as needed.
|
||||
*/
|
||||
class ReferenceVisitor : public ValueVisitor {
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
xml::IPackageDeclStack* mDecls;
|
||||
CallSite* mCallSite;
|
||||
bool mError;
|
||||
|
||||
public:
|
||||
using ValueVisitor::visit;
|
||||
|
||||
ReferenceVisitor(IAaptContext* context, ISymbolTable* symbols, xml::IPackageDeclStack* decls,
|
||||
CallSite* callSite) :
|
||||
mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
|
||||
mError(false) {
|
||||
}
|
||||
|
||||
void visit(Reference* ref) override {
|
||||
if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
|
||||
mError = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasError() const {
|
||||
return mError;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visits each xml Element and compiles the attributes within.
|
||||
*/
|
||||
class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
Source mSource;
|
||||
std::set<int>* mSdkLevelsFound;
|
||||
ReferenceLinkerVisitor mReferenceLinkerVisitor;
|
||||
CallSite* mCallSite;
|
||||
ReferenceVisitor mReferenceVisitor;
|
||||
bool mError = false;
|
||||
|
||||
public:
|
||||
using xml::PackageAwareVisitor::visit;
|
||||
|
||||
XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source,
|
||||
std::set<int>* sdkLevelsFound) :
|
||||
XmlVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source,
|
||||
std::set<int>* sdkLevelsFound, CallSite* callSite) :
|
||||
mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
|
||||
mReferenceLinkerVisitor(context, symbols, this) {
|
||||
mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
|
||||
}
|
||||
|
||||
void visit(xml::Element* el) override {
|
||||
const Source source = mSource.withLine(el->lineNumber);
|
||||
for (xml::Attribute& attr : el->attributes) {
|
||||
Maybe<std::u16string> maybePackage =
|
||||
util::extractPackageFromNamespace(attr.namespaceUri);
|
||||
Maybe<xml::ExtractedPackage> maybePackage =
|
||||
xml::extractPackageFromNamespace(attr.namespaceUri);
|
||||
if (maybePackage) {
|
||||
// There is a valid package name for this attribute. We will look this up.
|
||||
StringPiece16 package = maybePackage.value();
|
||||
StringPiece16 package = maybePackage.value().package;
|
||||
if (package.empty()) {
|
||||
// Empty package means the 'current' or 'local' package.
|
||||
package = mContext->getCompilationPackage();
|
||||
}
|
||||
|
||||
attr.compiledAttribute = compileAttribute(
|
||||
ResourceName{ package.toString(), ResourceType::kAttr, attr.name });
|
||||
Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name));
|
||||
attrRef.privateReference = maybePackage.value().privateNamespace;
|
||||
|
||||
std::string errStr;
|
||||
attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
|
||||
attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
|
||||
|
||||
// Convert the string value into a compiled Value if this is a valid attribute.
|
||||
if (attr.compiledAttribute) {
|
||||
@@ -87,7 +127,7 @@ public:
|
||||
} else {
|
||||
mContext->getDiagnostics()->error(DiagMessage(source)
|
||||
<< "attribute '" << package << ":"
|
||||
<< attr.name << "' was not found");
|
||||
<< attr.name << "' " << errStr);
|
||||
mError = true;
|
||||
|
||||
}
|
||||
@@ -99,7 +139,7 @@ public:
|
||||
if (attr.compiledValue) {
|
||||
// With a compiledValue, we must resolve the reference and assign it an ID.
|
||||
attr.compiledValue->setSource(source);
|
||||
attr.compiledValue->accept(&mReferenceLinkerVisitor);
|
||||
attr.compiledValue->accept(&mReferenceVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,28 +147,18 @@ public:
|
||||
xml::PackageAwareVisitor::visit(el);
|
||||
}
|
||||
|
||||
Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) {
|
||||
Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name);
|
||||
if (const ISymbolTable::Symbol* symbol = mSymbols->findByName(
|
||||
mangledName ? mangledName.value() : name)) {
|
||||
if (symbol->attribute) {
|
||||
return xml::AaptAttribute{ symbol->id, *symbol->attribute };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
inline bool hasError() {
|
||||
return mError || mReferenceLinkerVisitor.hasError();
|
||||
bool hasError() {
|
||||
return mError || mReferenceVisitor.hasError();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) {
|
||||
bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) {
|
||||
mSdkLevelsFound.clear();
|
||||
XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
|
||||
&mSdkLevelsFound);
|
||||
CallSite callSite = { resource->file.name };
|
||||
XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
|
||||
&mSdkLevelsFound, &callSite);
|
||||
if (resource->root) {
|
||||
resource->root->accept(&visitor);
|
||||
return !visitor.hasError();
|
||||
|
||||
@@ -31,37 +31,40 @@ public:
|
||||
.setNameManglerPolicy(
|
||||
NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
|
||||
.addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_ENUM |
|
||||
android::ResTable_map::TYPE_DIMENSION)
|
||||
.addItem(u"match_parent", 0xffffffff)
|
||||
.build())
|
||||
.addSymbol(u"@android:attr/background", ResourceId(0x01010001),
|
||||
.addPublicSymbol(u"@android:attr/background", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
|
||||
.addSymbol(u"@android:attr/attr", ResourceId(0x01010002),
|
||||
.addPublicSymbol(u"@android:attr/attr", ResourceId(0x01010002),
|
||||
test::AttributeBuilder().build())
|
||||
.addSymbol(u"@android:attr/text", ResourceId(0x01010003),
|
||||
.addPublicSymbol(u"@android:attr/text", ResourceId(0x01010003),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_STRING)
|
||||
.build())
|
||||
|
||||
// Add one real symbol that was introduces in v21
|
||||
.addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
|
||||
.addPublicSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
|
||||
test::AttributeBuilder().build())
|
||||
|
||||
.addSymbol(u"@android:id/id", ResourceId(0x01030000))
|
||||
// Private symbol.
|
||||
.addSymbol(u"@android:color/hidden", ResourceId(0x01020001))
|
||||
|
||||
.addPublicSymbol(u"@android:id/id", ResourceId(0x01030000))
|
||||
.addSymbol(u"@com.app.test:id/id", ResourceId(0x7f030000))
|
||||
.addSymbol(u"@com.app.test:color/green", ResourceId(0x7f020000))
|
||||
.addSymbol(u"@com.app.test:color/red", ResourceId(0x7f020001))
|
||||
.addSymbol(u"@com.app.test:attr/colorAccent", ResourceId(0x7f010000),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
|
||||
.addSymbol(u"@com.app.test:attr/com.android.support$colorAccent",
|
||||
.addPublicSymbol(u"@com.app.test:attr/com.android.support$colorAccent",
|
||||
ResourceId(0x7f010001), test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
|
||||
.addSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002),
|
||||
.addPublicSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002),
|
||||
test::AttributeBuilder().build())
|
||||
.build())
|
||||
.build();
|
||||
@@ -71,23 +74,8 @@ protected:
|
||||
std::unique_ptr<IAaptContext> mContext;
|
||||
};
|
||||
|
||||
static xml::Element* getRootElement(XmlResource* doc) {
|
||||
xml::Node* node = doc->root.get();
|
||||
while (xml::nodeCast<xml::Namespace>(node)) {
|
||||
if (node->children.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
node = node->children.front().get();
|
||||
}
|
||||
|
||||
if (xml::Element* el = xml::nodeCast<xml::Element>(node)) {
|
||||
return el;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/green"
|
||||
@@ -97,7 +85,7 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
|
||||
|
||||
xml::Element* viewEl = getRootElement(doc.get());
|
||||
xml::Element* viewEl = xml::findRootElement(doc.get());
|
||||
ASSERT_NE(viewEl, nullptr);
|
||||
|
||||
xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android",
|
||||
@@ -132,8 +120,26 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
|
||||
ASSERT_EQ(xmlAttr->compiledValue, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="@android:color/hidden" />)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_FALSE(linker.consume(mContext.get(), doc.get()));
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="@*android:color/hidden" />)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:colorAccent="#ffffff" />)EOF");
|
||||
|
||||
@@ -143,14 +149,14 @@ TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
|
||||
support:colorAccent="#ff0000" />)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
|
||||
|
||||
xml::Element* viewEl = getRootElement(doc.get());
|
||||
xml::Element* viewEl = xml::findRootElement(doc.get());
|
||||
ASSERT_NE(viewEl, nullptr);
|
||||
|
||||
xml::Attribute* xmlAttr = viewEl->findAttribute(
|
||||
@@ -162,14 +168,14 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:colorAccent="@app:color/red" />)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
|
||||
|
||||
xml::Element* viewEl = getRootElement(doc.get());
|
||||
xml::Element* viewEl = xml::findRootElement(doc.get());
|
||||
ASSERT_NE(viewEl, nullptr);
|
||||
|
||||
xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res-auto",
|
||||
@@ -185,7 +191,7 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:app="http://schemas.android.com/apk/res/android"
|
||||
app:attr="@app:id/id">
|
||||
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
|
||||
@@ -195,7 +201,7 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
|
||||
|
||||
xml::Element* viewEl = getRootElement(doc.get());
|
||||
xml::Element* viewEl = xml::findRootElement(doc.get());
|
||||
ASSERT_NE(viewEl, nullptr);
|
||||
|
||||
// All attributes and references in this element should be referring to "android" (0x01).
|
||||
@@ -225,14 +231,14 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
}
|
||||
|
||||
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
|
||||
std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
|
||||
std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
|
||||
android:attr="@id/id"/>)EOF");
|
||||
|
||||
XmlReferenceLinker linker;
|
||||
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
|
||||
|
||||
xml::Element* viewEl = getRootElement(doc.get());
|
||||
xml::Element* viewEl = xml::findRootElement(doc.get());
|
||||
ASSERT_NE(viewEl, nullptr);
|
||||
|
||||
// All attributes and references in this element should be referring to "com.app.test" (0x7f).
|
||||
|
||||
@@ -49,25 +49,13 @@ struct IResourceTableConsumer {
|
||||
};
|
||||
|
||||
namespace xml {
|
||||
struct Node;
|
||||
struct XmlResource;
|
||||
}
|
||||
|
||||
struct XmlResource {
|
||||
ResourceFile file;
|
||||
std::unique_ptr<xml::Node> root;
|
||||
};
|
||||
|
||||
struct IXmlResourceConsumer {
|
||||
virtual ~IXmlResourceConsumer() = default;
|
||||
|
||||
virtual bool consume(IAaptContext* context, XmlResource* resource) = 0;
|
||||
};
|
||||
|
||||
struct IPackageDeclStack {
|
||||
virtual ~IPackageDeclStack() = default;
|
||||
|
||||
virtual Maybe<ResourceName> transformPackage(const ResourceName& name,
|
||||
const StringPiece16& localPackage) const = 0;
|
||||
virtual bool consume(IAaptContext* context, xml::XmlResource* resource) = 0;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -51,6 +51,7 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n
|
||||
|
||||
std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
|
||||
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
|
||||
symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
|
||||
|
||||
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
|
||||
const ConfigDescription kDefaultConfig;
|
||||
@@ -158,6 +159,7 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa
|
||||
}
|
||||
|
||||
if (s) {
|
||||
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
mCache.put(name, s);
|
||||
return s.get();
|
||||
}
|
||||
@@ -165,6 +167,44 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
|
||||
android::ResTable::resource_name resName;
|
||||
if (!table.getResourceName(id.id, true, &resName)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ResourceName name;
|
||||
if (resName.package) {
|
||||
name.package = StringPiece16(resName.package, resName.packageLen).toString();
|
||||
}
|
||||
|
||||
const ResourceType* type;
|
||||
if (resName.type) {
|
||||
type = parseResourceType(StringPiece16(resName.type, resName.typeLen));
|
||||
|
||||
} else if (resName.type8) {
|
||||
type = parseResourceType(util::utf8ToUtf16(StringPiece(resName.type8, resName.typeLen)));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return {};
|
||||
}
|
||||
|
||||
name.type = *type;
|
||||
|
||||
if (resName.name) {
|
||||
name.entry = StringPiece16(resName.name, resName.nameLen).toString();
|
||||
} else if (resName.name8) {
|
||||
name.entry = util::utf8ToUtf16(StringPiece(resName.name8, resName.nameLen));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
|
||||
ResourceId id) {
|
||||
if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
|
||||
@@ -174,22 +214,16 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa
|
||||
for (const auto& asset : mAssets) {
|
||||
const android::ResTable& table = asset->getResources(false);
|
||||
|
||||
android::ResTable::resource_name name;
|
||||
if (!table.getResourceName(id.id, true, &name)) {
|
||||
Maybe<ResourceName> maybeName = getResourceName(table, id);
|
||||
if (!maybeName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isAttr = false;
|
||||
if (name.type) {
|
||||
if (const ResourceType* t = parseResourceType(StringPiece16(name.type, name.typeLen))) {
|
||||
isAttr = (*t == ResourceType::kAttr);
|
||||
}
|
||||
} else if (name.type8) {
|
||||
isAttr = (StringPiece(name.type8, name.typeLen) == "attr");
|
||||
}
|
||||
uint32_t typeSpecFlags = 0;
|
||||
table.getResourceFlags(id.id, &typeSpecFlags);
|
||||
|
||||
std::shared_ptr<Symbol> s;
|
||||
if (isAttr) {
|
||||
if (maybeName.value().type == ResourceType::kAttr) {
|
||||
s = lookupAttributeInTable(table, id);
|
||||
} else {
|
||||
s = std::make_shared<Symbol>();
|
||||
@@ -197,6 +231,7 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa
|
||||
}
|
||||
|
||||
if (s) {
|
||||
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
mIdCache.put(id, s);
|
||||
return s.get();
|
||||
}
|
||||
|
||||
@@ -19,10 +19,9 @@
|
||||
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "XmlDom.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include "test/Common.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -212,15 +211,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
inline std::unique_ptr<XmlResource> buildXmlDom(const StringPiece& str) {
|
||||
inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) {
|
||||
std::stringstream in;
|
||||
in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
|
||||
StdErrDiagnostics diag;
|
||||
std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, {});
|
||||
std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, {});
|
||||
assert(doc);
|
||||
return doc;
|
||||
}
|
||||
|
||||
inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context,
|
||||
const StringPiece& str) {
|
||||
std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
|
||||
doc->file.name.package = context->getCompilationPackage().toString();
|
||||
return doc;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace aapt
|
||||
|
||||
|
||||
@@ -135,8 +135,19 @@ private:
|
||||
std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>();
|
||||
|
||||
public:
|
||||
StaticSymbolTableBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
|
||||
id, std::move(attr));
|
||||
symbol->isPublic = true;
|
||||
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
|
||||
mSymbolTable->mIdMap[id] = symbol.get();
|
||||
mSymbolTable->mSymbols.push_back(std::move(symbol));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id,
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
|
||||
id, std::move(attr));
|
||||
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
|
||||
|
||||
@@ -97,19 +97,19 @@ bool BinaryResourceParser::parse() {
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
|
||||
Maybe<Reference> BinaryResourceParser::getSymbol(const void* data) {
|
||||
if (!mSymbolEntries || mSymbolEntryCount == 0) {
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
if ((uintptr_t) data < (uintptr_t) mData) {
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
// We only support 32 bit offsets right now.
|
||||
const uintptr_t offset = (uintptr_t) data - (uintptr_t) mData;
|
||||
if (offset > std::numeric_limits<uint32_t>::max()) {
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mSymbolEntryCount; i++) {
|
||||
@@ -118,24 +118,23 @@ bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbo
|
||||
const StringPiece16 str = util::getString(
|
||||
mSymbolPool, util::deviceToHost32(mSymbolEntries[i].name.index));
|
||||
|
||||
StringPiece16 typeStr;
|
||||
ResourceUtils::extractResourceName(str, &outSymbol->package, &typeStr,
|
||||
&outSymbol->entry);
|
||||
const ResourceType* type = parseResourceType(typeStr);
|
||||
if (!type) {
|
||||
return false;
|
||||
ResourceNameRef nameRef;
|
||||
bool privateRef = false;
|
||||
if (!ResourceUtils::parseResourceName(str, &nameRef, &privateRef)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
outSymbol->type = *type;
|
||||
|
||||
// Since we scan the symbol table in order, we can start looking for the
|
||||
// next symbol from this point.
|
||||
mSymbolEntryCount -= i + 1;
|
||||
mSymbolEntries += i + 1;
|
||||
return true;
|
||||
|
||||
Reference ref(nameRef);
|
||||
ref.privateReference = privateRef;
|
||||
return Maybe<Reference>(std::move(ref));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -566,7 +565,13 @@ bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
|
||||
resourceValue = parseValue(name, config, value, entry->flags);
|
||||
}
|
||||
|
||||
assert(resourceValue && "failed to interpret valid resource");
|
||||
if (!resourceValue) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(mSource)
|
||||
<< "failed to parse value for resource " << name
|
||||
<< " (" << resId << ") with configuration '"
|
||||
<< config << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
Source source = mSource;
|
||||
if (sourceBlock) {
|
||||
@@ -657,7 +662,7 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na
|
||||
if (value->dataType == Res_value::TYPE_REFERENCE ||
|
||||
value->dataType == Res_value::TYPE_ATTRIBUTE) {
|
||||
const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
|
||||
Reference::Type::kResource : Reference::Type::kAttribute;
|
||||
Reference::Type::kResource : Reference::Type::kAttribute;
|
||||
|
||||
if (data != 0) {
|
||||
// This is a normal reference.
|
||||
@@ -665,9 +670,9 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na
|
||||
}
|
||||
|
||||
// This reference has an invalid ID. Check if it is an unresolved symbol.
|
||||
ResourceNameRef symbol;
|
||||
if (getSymbol(&value->data, &symbol)) {
|
||||
return util::make_unique<Reference>(symbol, type);
|
||||
if (Maybe<Reference> ref = getSymbol(&value->data)) {
|
||||
ref.value().referenceType = type;
|
||||
return util::make_unique<Reference>(std::move(ref.value()));
|
||||
}
|
||||
|
||||
// This is not an unresolved symbol, so it must be the magic @null reference.
|
||||
@@ -715,10 +720,8 @@ std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& n
|
||||
if (util::deviceToHost32(map->parent.ident) == 0) {
|
||||
// The parent is either not set or it is an unresolved symbol.
|
||||
// Check to see if it is a symbol.
|
||||
ResourceNameRef symbol;
|
||||
if (getSymbol(&map->parent.ident, &symbol)) {
|
||||
style->parent = Reference(symbol.toResourceName());
|
||||
}
|
||||
style->parent = getSymbol(&map->parent.ident);
|
||||
|
||||
} else {
|
||||
// The parent is a regular reference to a resource.
|
||||
style->parent = Reference(util::deviceToHost32(map->parent.ident));
|
||||
@@ -731,10 +734,14 @@ std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& n
|
||||
if (util::deviceToHost32(mapEntry.name.ident) == 0) {
|
||||
// The map entry's key (attribute) is not set. This must be
|
||||
// a symbol reference, so resolve it.
|
||||
ResourceNameRef symbol;
|
||||
bool result = getSymbol(&mapEntry.name.ident, &symbol);
|
||||
assert(result);
|
||||
styleEntry.key.name = symbol.toResourceName();
|
||||
Maybe<Reference> symbol = getSymbol(&mapEntry.name.ident);
|
||||
if (!symbol) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(mSource)
|
||||
<< "unresolved style attribute");
|
||||
return {};
|
||||
}
|
||||
styleEntry.key = std::move(symbol.value());
|
||||
|
||||
} else {
|
||||
// The map entry's key (attribute) is a regular reference.
|
||||
styleEntry.key.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
|
||||
@@ -742,7 +749,9 @@ std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& n
|
||||
|
||||
// Parse the attribute's value.
|
||||
styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
|
||||
assert(styleEntry.value);
|
||||
if (!styleEntry.value) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
@@ -773,10 +782,14 @@ std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef
|
||||
if (util::deviceToHost32(mapEntry.name.ident) == 0) {
|
||||
// The map entry's key (id) is not set. This must be
|
||||
// a symbol reference, so resolve it.
|
||||
ResourceNameRef symbolName;
|
||||
bool result = getSymbol(&mapEntry.name.ident, &symbolName);
|
||||
assert(result);
|
||||
symbol.symbol.name = symbolName.toResourceName();
|
||||
Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
|
||||
if (!ref) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(mSource)
|
||||
<< "unresolved attribute symbol");
|
||||
return {};
|
||||
}
|
||||
symbol.symbol = std::move(ref.value());
|
||||
|
||||
} else {
|
||||
// The map entry's key (id) is a regular reference.
|
||||
symbol.symbol.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
|
||||
@@ -808,10 +821,14 @@ std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNa
|
||||
if (util::deviceToHost32(mapEntry.name.ident) == 0) {
|
||||
// The map entry's key (attribute) is not set. This must be
|
||||
// a symbol reference, so resolve it.
|
||||
ResourceNameRef symbol;
|
||||
bool result = getSymbol(&mapEntry.name.ident, &symbol);
|
||||
assert(result);
|
||||
styleable->entries.emplace_back(symbol);
|
||||
Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
|
||||
if (!ref) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(mSource)
|
||||
<< "unresolved styleable symbol");
|
||||
return {};
|
||||
}
|
||||
styleable->entries.emplace_back(std::move(ref.value()));
|
||||
|
||||
} else {
|
||||
// The map entry's key (attribute) is a regular reference.
|
||||
styleable->entries.emplace_back(util::deviceToHost32(mapEntry.name.ident));
|
||||
@@ -826,6 +843,9 @@ std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef&
|
||||
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
|
||||
for (const ResTable_map& mapEntry : map) {
|
||||
std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
|
||||
if (!item) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (util::deviceToHost32(mapEntry.name.ident)) {
|
||||
case android::ResTable_map::ATTR_ZERO:
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
private:
|
||||
// Helper method to retrieve the symbol name for a given table offset specified
|
||||
// as a pointer.
|
||||
bool getSymbol(const void* data, ResourceNameRef* outSymbol);
|
||||
Maybe<Reference> getSymbol(const void* data);
|
||||
|
||||
bool parseTable(const android::ResChunk_header* chunk);
|
||||
bool parseSymbolTable(const android::ResChunk_header* chunk);
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
namespace aapt {
|
||||
namespace util {
|
||||
|
||||
constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
|
||||
constexpr const char16_t* kSchemaPrefix = u"http://schemas.android.com/apk/res/";
|
||||
|
||||
static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep,
|
||||
const std::function<char(char)>& f) {
|
||||
std::vector<std::string> parts;
|
||||
@@ -467,18 +464,6 @@ std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) {
|
||||
return data;
|
||||
}
|
||||
|
||||
Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri) {
|
||||
if (stringStartsWith<char16_t>(namespaceUri, kSchemaPrefix)) {
|
||||
StringPiece16 schemaPrefix = kSchemaPrefix;
|
||||
StringPiece16 package = namespaceUri;
|
||||
return package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size())
|
||||
.toString();
|
||||
} else if (namespaceUri == kSchemaAuto) {
|
||||
return std::u16string();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix,
|
||||
StringPiece16* outEntry, StringPiece16* outSuffix) {
|
||||
if (!stringStartsWith<char16_t>(path, u"res/")) {
|
||||
|
||||
@@ -330,15 +330,6 @@ inline uint32_t deviceToHost32(uint32_t value) {
|
||||
return dtohl(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a package name if the namespace URI is of the form:
|
||||
* http://schemas.android.com/apk/res/<package>
|
||||
*
|
||||
* Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
|
||||
* returns an empty package name.
|
||||
*/
|
||||
Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri);
|
||||
|
||||
/**
|
||||
* Given a path like: res/xml-sw600dp/foo.xml
|
||||
*
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "util/Util.h"
|
||||
#include "XmlDom.h"
|
||||
#include "XmlPullParser.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <expat.h>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
@@ -317,6 +318,10 @@ std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnost
|
||||
return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
|
||||
}
|
||||
|
||||
Element* findRootElement(XmlResource* doc) {
|
||||
return findRootElement(doc->root.get());
|
||||
}
|
||||
|
||||
Element* findRootElement(Node* node) {
|
||||
if (!node) {
|
||||
return nullptr;
|
||||
@@ -397,5 +402,39 @@ std::vector<Element*> Element::getChildElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
void PackageAwareVisitor::visit(Namespace* ns) {
|
||||
bool added = false;
|
||||
if (Maybe<ExtractedPackage> maybePackage = extractPackageFromNamespace(ns->namespaceUri)) {
|
||||
ExtractedPackage& package = maybePackage.value();
|
||||
mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, std::move(package) });
|
||||
added = true;
|
||||
}
|
||||
|
||||
Visitor::visit(ns);
|
||||
|
||||
if (added) {
|
||||
mPackageDecls.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<ExtractedPackage> PackageAwareVisitor::transformPackageAlias(
|
||||
const StringPiece16& alias, const StringPiece16& localPackage) const {
|
||||
if (alias.empty()) {
|
||||
return ExtractedPackage{ localPackage.toString(), false /* private */ };
|
||||
}
|
||||
|
||||
const auto rend = mPackageDecls.rend();
|
||||
for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
|
||||
if (alias == iter->prefix) {
|
||||
if (iter->package.package.empty()) {
|
||||
return ExtractedPackage{ localPackage.toString(),
|
||||
iter->package.privateNamespace };
|
||||
}
|
||||
return iter->package;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
@@ -22,11 +22,9 @@
|
||||
#include "ResourceValues.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
#include <istream>
|
||||
#include <expat.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -34,20 +32,8 @@
|
||||
namespace aapt {
|
||||
namespace xml {
|
||||
|
||||
constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
|
||||
|
||||
struct RawVisitor;
|
||||
|
||||
/**
|
||||
* The type of node. Can be used to downcast to the concrete XML node
|
||||
* class.
|
||||
*/
|
||||
enum class NodeType {
|
||||
kNamespace,
|
||||
kElement,
|
||||
kText,
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for all XML nodes.
|
||||
*/
|
||||
@@ -58,9 +44,10 @@ struct Node {
|
||||
std::u16string comment;
|
||||
std::vector<std::unique_ptr<Node>> children;
|
||||
|
||||
virtual ~Node() = default;
|
||||
|
||||
void addChild(std::unique_ptr<Node> child);
|
||||
virtual void accept(RawVisitor* visitor) = 0;
|
||||
virtual ~Node() {}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -121,6 +108,14 @@ struct Text : public BaseNode<Text> {
|
||||
std::u16string text;
|
||||
};
|
||||
|
||||
/**
|
||||
* An XML resource with a source, name, and XML tree.
|
||||
*/
|
||||
struct XmlResource {
|
||||
ResourceFile file;
|
||||
std::unique_ptr<xml::Node> root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inflates an XML DOM from a text stream, logging errors to the logger.
|
||||
* Returns the root node on success, or nullptr on failure.
|
||||
@@ -134,6 +129,7 @@ std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const
|
||||
std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
|
||||
const Source& source);
|
||||
|
||||
Element* findRootElement(XmlResource* doc);
|
||||
Element* findRootElement(Node* node);
|
||||
|
||||
/**
|
||||
@@ -180,7 +176,7 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
|
||||
private:
|
||||
struct PackageDecl {
|
||||
std::u16string prefix;
|
||||
std::u16string package;
|
||||
ExtractedPackage package;
|
||||
};
|
||||
|
||||
std::vector<PackageDecl> mPackageDecls;
|
||||
@@ -188,44 +184,9 @@ private:
|
||||
public:
|
||||
using Visitor::visit;
|
||||
|
||||
void visit(Namespace* ns) override {
|
||||
bool added = false;
|
||||
{
|
||||
Maybe<std::u16string> package = util::extractPackageFromNamespace(ns->namespaceUri);
|
||||
if (package) {
|
||||
mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, package.value() });
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
Visitor::visit(ns);
|
||||
|
||||
if (added) {
|
||||
mPackageDecls.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<ResourceName> transformPackage(const ResourceName& name,
|
||||
const StringPiece16& localPackage) const override {
|
||||
if (name.package.empty()) {
|
||||
return ResourceName{ localPackage.toString(), name.type, name.entry };
|
||||
}
|
||||
|
||||
const auto rend = mPackageDecls.rend();
|
||||
for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
|
||||
if (name.package == iter->prefix) {
|
||||
if (iter->package.empty()) {
|
||||
if (localPackage != name.package) {
|
||||
return ResourceName{ localPackage.toString(), name.type, name.entry };
|
||||
}
|
||||
} else if (iter->package != name.package) {
|
||||
return ResourceName{ iter->package, name.type, name.entry };
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
void visit(Namespace* ns) override;
|
||||
Maybe<ExtractedPackage> transformPackageAlias(
|
||||
const StringPiece16& alias, const StringPiece16& localPackage) const override;
|
||||
};
|
||||
|
||||
// Implementations
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "XmlDom.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sstream>
|
||||
@@ -38,7 +38,7 @@ TEST(XmlDomTest, Inflate) {
|
||||
|
||||
const Source source = { "test.xml" };
|
||||
StdErrDiagnostics diag;
|
||||
std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, source);
|
||||
std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, source);
|
||||
ASSERT_NE(doc, nullptr);
|
||||
|
||||
xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get());
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
#include "util/Maybe.h"
|
||||
#include "util/Util.h"
|
||||
#include "XmlPullParser.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
namespace xml {
|
||||
|
||||
constexpr char kXmlNamespaceSep = 1;
|
||||
|
||||
@@ -72,14 +74,14 @@ XmlPullParser::Event XmlPullParser::next() {
|
||||
// Record namespace prefixes and package names so that we can do our own
|
||||
// handling of references that use namespace aliases.
|
||||
if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
|
||||
Maybe<std::u16string> result = util::extractPackageFromNamespace(getNamespaceUri());
|
||||
Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri());
|
||||
if (event == Event::kStartNamespace) {
|
||||
if (result) {
|
||||
mPackageAliases.emplace_back(getNamespacePrefix(), result.value());
|
||||
mPackageAliases.emplace_back(
|
||||
PackageDecl{ getNamespacePrefix(), std::move(result.value()) });
|
||||
}
|
||||
} else {
|
||||
if (result) {
|
||||
assert(mPackageAliases.back().second == result.value());
|
||||
mPackageAliases.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -131,20 +133,20 @@ const std::u16string& XmlPullParser::getNamespaceUri() const {
|
||||
return mEventQueue.front().data2;
|
||||
}
|
||||
|
||||
Maybe<ResourceName> XmlPullParser::transformPackage(
|
||||
const ResourceName& name, const StringPiece16& localPackage) const {
|
||||
if (name.package.empty()) {
|
||||
return ResourceName{ localPackage.toString(), name.type, name.entry };
|
||||
Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias(
|
||||
const StringPiece16& alias, const StringPiece16& localPackage) const {
|
||||
if (alias.empty()) {
|
||||
return ExtractedPackage{ localPackage.toString(), false /* private */ };
|
||||
}
|
||||
|
||||
const auto endIter = mPackageAliases.rend();
|
||||
for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
|
||||
if (name.package == iter->first) {
|
||||
if (iter->second.empty()) {
|
||||
return ResourceName{ localPackage.toString(), name.type, name.entry };
|
||||
} else {
|
||||
return ResourceName{ iter->second, name.type, name.entry };
|
||||
if (alias == iter->prefix) {
|
||||
if (iter->package.package.empty()) {
|
||||
return ExtractedPackage{ localPackage.toString(),
|
||||
iter->package.privateNamespace };
|
||||
}
|
||||
return iter->package;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@@ -283,4 +285,24 @@ void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comme
|
||||
});
|
||||
}
|
||||
|
||||
Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) {
|
||||
auto iter = parser->findAttribute(u"", name);
|
||||
if (iter != parser->endAttributes()) {
|
||||
return StringPiece16(util::trimWhitespace(iter->value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) {
|
||||
auto iter = parser->findAttribute(u"", name);
|
||||
if (iter != parser->endAttributes()) {
|
||||
StringPiece16 trimmed = util::trimWhitespace(iter->value);
|
||||
if (!trimmed.empty()) {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
@@ -17,11 +17,11 @@
|
||||
#ifndef AAPT_XML_PULL_PARSER_H
|
||||
#define AAPT_XML_PULL_PARSER_H
|
||||
|
||||
#include "util/Maybe.h"
|
||||
#include "Resource.h"
|
||||
#include "util/StringPiece.h"
|
||||
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <expat.h>
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <vector>
|
||||
|
||||
namespace aapt {
|
||||
namespace xml {
|
||||
|
||||
class XmlPullParser : public IPackageDeclStack {
|
||||
public:
|
||||
@@ -60,7 +61,7 @@ public:
|
||||
static bool isGoodEvent(Event event);
|
||||
|
||||
XmlPullParser(std::istream& in);
|
||||
virtual ~XmlPullParser();
|
||||
~XmlPullParser();
|
||||
|
||||
/**
|
||||
* Returns the current event that is being processed.
|
||||
@@ -95,6 +96,13 @@ public:
|
||||
const std::u16string& getNamespacePrefix() const;
|
||||
const std::u16string& getNamespaceUri() const;
|
||||
|
||||
//
|
||||
// These are available for StartElement and EndElement.
|
||||
//
|
||||
|
||||
const std::u16string& getElementNamespace() const;
|
||||
const std::u16string& getElementName() const;
|
||||
|
||||
/*
|
||||
* Uses the current stack of namespaces to resolve the package. Eg:
|
||||
* xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
|
||||
@@ -106,17 +114,8 @@ public:
|
||||
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
|
||||
* 'package' will be set to 'defaultPackage'.
|
||||
*/
|
||||
//
|
||||
|
||||
//
|
||||
// These are available for StartElement and EndElement.
|
||||
//
|
||||
|
||||
const std::u16string& getElementNamespace() const;
|
||||
const std::u16string& getElementName() const;
|
||||
|
||||
Maybe<ResourceName> transformPackage(const ResourceName& name,
|
||||
const StringPiece16& localPackage) const override;
|
||||
Maybe<ExtractedPackage> transformPackageAlias(
|
||||
const StringPiece16& alias, const StringPiece16& localPackage) const override;
|
||||
|
||||
//
|
||||
// Remaining methods are for retrieving information about attributes
|
||||
@@ -169,9 +168,25 @@ private:
|
||||
const std::u16string mEmpty;
|
||||
size_t mDepth;
|
||||
std::stack<std::u16string> mNamespaceUris;
|
||||
std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
|
||||
|
||||
struct PackageDecl {
|
||||
std::u16string prefix;
|
||||
ExtractedPackage package;
|
||||
};
|
||||
std::vector<PackageDecl> mPackageAliases;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds the attribute in the current element within the global namespace.
|
||||
*/
|
||||
Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name);
|
||||
|
||||
/**
|
||||
* Finds the attribute in the current element within the global namespace. The attribute's value
|
||||
* must not be the empty string.
|
||||
*/
|
||||
Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name);
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
@@ -277,6 +292,7 @@ inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16
|
||||
return endIter;
|
||||
}
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_XML_PULL_PARSER_H
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "util/StringPiece.h"
|
||||
#include "XmlPullParser.h"
|
||||
#include "xml/XmlPullParser.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sstream>
|
||||
@@ -26,30 +26,30 @@ TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
|
||||
std::stringstream str;
|
||||
str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
|
||||
XmlPullParser parser(str);
|
||||
xml::XmlPullParser parser(str);
|
||||
|
||||
const size_t depthOuter = parser.getDepth();
|
||||
ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthOuter));
|
||||
ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
|
||||
|
||||
EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
EXPECT_EQ(StringPiece16(u"a"), StringPiece16(parser.getElementName()));
|
||||
|
||||
const size_t depthA = parser.getDepth();
|
||||
ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthA));
|
||||
EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthA));
|
||||
EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
EXPECT_EQ(StringPiece16(u"b"), StringPiece16(parser.getElementName()));
|
||||
|
||||
const size_t depthB = parser.getDepth();
|
||||
ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB));
|
||||
EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
|
||||
EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
EXPECT_EQ(StringPiece16(u"c"), StringPiece16(parser.getElementName()));
|
||||
|
||||
ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB));
|
||||
EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
|
||||
EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
|
||||
EXPECT_EQ(StringPiece16(u"e"), StringPiece16(parser.getElementName()));
|
||||
|
||||
ASSERT_FALSE(XmlPullParser::nextChildNode(&parser, depthOuter));
|
||||
EXPECT_EQ(XmlPullParser::Event::kEndDocument, parser.getEvent());
|
||||
ASSERT_FALSE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
|
||||
EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.getEvent());
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
67
tools/aapt2/xml/XmlUtil.cpp
Normal file
67
tools/aapt2/xml/XmlUtil.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "util/Maybe.h"
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
namespace xml {
|
||||
|
||||
Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri) {
|
||||
if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPublicPrefix)) {
|
||||
StringPiece16 schemaPrefix = kSchemaPublicPrefix;
|
||||
StringPiece16 package = namespaceUri;
|
||||
package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
|
||||
if (package.empty()) {
|
||||
return {};
|
||||
}
|
||||
return ExtractedPackage{ package.toString(), false /* isPrivate */ };
|
||||
|
||||
} else if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPrivatePrefix)) {
|
||||
StringPiece16 schemaPrefix = kSchemaPrivatePrefix;
|
||||
StringPiece16 package = namespaceUri;
|
||||
package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
|
||||
if (package.empty()) {
|
||||
return {};
|
||||
}
|
||||
return ExtractedPackage{ package.toString(), true /* isPrivate */ };
|
||||
|
||||
} else if (namespaceUri == kSchemaAuto) {
|
||||
return ExtractedPackage{ std::u16string(), true /* isPrivate */ };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void transformReferenceFromNamespace(IPackageDeclStack* declStack,
|
||||
const StringPiece16& localPackage, Reference* inRef) {
|
||||
if (inRef->name) {
|
||||
if (Maybe<ExtractedPackage> transformedPackage =
|
||||
declStack->transformPackageAlias(inRef->name.value().package, localPackage)) {
|
||||
ExtractedPackage& extractedPackage = transformedPackage.value();
|
||||
inRef->name.value().package = std::move(extractedPackage.package);
|
||||
|
||||
// If the reference was already private (with a * prefix) and the namespace is public,
|
||||
// we keep the reference private.
|
||||
inRef->privateReference |= extractedPackage.privateNamespace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
85
tools/aapt2/xml/XmlUtil.h
Normal file
85
tools/aapt2/xml/XmlUtil.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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_XML_XMLUTIL_H
|
||||
#define AAPT_XML_XMLUTIL_H
|
||||
|
||||
#include "ResourceValues.h"
|
||||
#include "util/Maybe.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
namespace xml {
|
||||
|
||||
constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
|
||||
constexpr const char16_t* kSchemaPublicPrefix = u"http://schemas.android.com/apk/res/";
|
||||
constexpr const char16_t* kSchemaPrivatePrefix = u"http://schemas.android.com/apk/prv/res/";
|
||||
constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
|
||||
|
||||
/**
|
||||
* Result of extracting a package name from a namespace URI declaration.
|
||||
*/
|
||||
struct ExtractedPackage {
|
||||
/**
|
||||
* The name of the package. This can be the empty string, which means that the package
|
||||
* should be assumed to be the package being compiled.
|
||||
*/
|
||||
std::u16string package;
|
||||
|
||||
/**
|
||||
* True if the package's private namespace was declared. This means that private resources
|
||||
* are made visible.
|
||||
*/
|
||||
bool privateNamespace;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an ExtractedPackage struct if the namespace URI is of the form:
|
||||
* http://schemas.android.com/apk/res/<package> or
|
||||
* http://schemas.android.com/apk/prv/res/<package>
|
||||
*
|
||||
* Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
|
||||
* returns an empty package name.
|
||||
*/
|
||||
Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri);
|
||||
|
||||
/**
|
||||
* Interface representing a stack of XML namespace declarations. When looking up the package
|
||||
* for a namespace prefix, the stack is checked from top to bottom.
|
||||
*/
|
||||
struct IPackageDeclStack {
|
||||
virtual ~IPackageDeclStack() = default;
|
||||
|
||||
/**
|
||||
* Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
|
||||
*/
|
||||
virtual Maybe<ExtractedPackage> transformPackageAlias(
|
||||
const StringPiece16& alias, const StringPiece16& localPackage) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for transforming the original Reference inRef to a fully qualified reference
|
||||
* via the IPackageDeclStack. This will also mark the Reference as private if the namespace of
|
||||
* the package declaration was private.
|
||||
*/
|
||||
void transformReferenceFromNamespace(IPackageDeclStack* declStack,
|
||||
const StringPiece16& localPackage, Reference* inRef);
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
|
||||
#endif /* AAPT_XML_XMLUTIL_H */
|
||||
54
tools/aapt2/xml/XmlUtil_test.cpp
Normal file
54
tools/aapt2/xml/XmlUtil_test.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "test/Common.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
TEST(XmlUtilTest, ExtractPackageFromNamespace) {
|
||||
AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"com.android"));
|
||||
AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk"));
|
||||
AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res"));
|
||||
AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/"));
|
||||
AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(
|
||||
u"http://schemas.android.com/apk/prv/res/"));
|
||||
|
||||
Maybe<xml::ExtractedPackage> p =
|
||||
xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/a");
|
||||
AAPT_ASSERT_TRUE(p);
|
||||
EXPECT_EQ(std::u16string(u"a"), p.value().package);
|
||||
EXPECT_EQ(false, p.value().privateNamespace);
|
||||
|
||||
p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/android");
|
||||
AAPT_ASSERT_TRUE(p);
|
||||
EXPECT_EQ(std::u16string(u"android"), p.value().package);
|
||||
EXPECT_EQ(true, p.value().privateNamespace);
|
||||
|
||||
p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/com.test");
|
||||
AAPT_ASSERT_TRUE(p);
|
||||
EXPECT_EQ(std::u16string(u"com.test"), p.value().package);
|
||||
EXPECT_EQ(true, p.value().privateNamespace);
|
||||
|
||||
p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res-auto");
|
||||
AAPT_ASSERT_TRUE(p);
|
||||
EXPECT_EQ(std::u16string(), p.value().package);
|
||||
EXPECT_EQ(true, p.value().privateNamespace);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
Reference in New Issue
Block a user