Merge "Add namespace handling in attribute values" into mnc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2f519532ee
@@ -40,10 +40,10 @@ sources := \
|
||||
ManifestValidator.cpp \
|
||||
Png.cpp \
|
||||
ResChunkPullParser.cpp \
|
||||
Resolver.cpp \
|
||||
Resource.cpp \
|
||||
ResourceParser.cpp \
|
||||
ResourceTable.cpp \
|
||||
ResourceTableResolver.cpp \
|
||||
ResourceValues.cpp \
|
||||
SdkConstants.cpp \
|
||||
StringPool.cpp \
|
||||
|
||||
@@ -39,7 +39,7 @@ using namespace android;
|
||||
* given a mapping from resource ID to resource name.
|
||||
*/
|
||||
struct ReferenceIdToNameVisitor : ValueVisitor {
|
||||
ReferenceIdToNameVisitor(const std::shared_ptr<Resolver>& resolver,
|
||||
ReferenceIdToNameVisitor(const std::shared_ptr<IResolver>& resolver,
|
||||
std::map<ResourceId, ResourceName>* cache) :
|
||||
mResolver(resolver), mCache(cache) {
|
||||
}
|
||||
@@ -96,30 +96,25 @@ private:
|
||||
reference.name = cacheIter->second;
|
||||
reference.id = 0;
|
||||
} else {
|
||||
const android::ResTable& table = mResolver->getResTable();
|
||||
android::ResTable::resource_name resourceName;
|
||||
if (table.getResourceName(reference.id.id, false, &resourceName)) {
|
||||
const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
|
||||
resourceName.typeLen));
|
||||
assert(type);
|
||||
reference.name.package.assign(resourceName.package, resourceName.packageLen);
|
||||
reference.name.type = *type;
|
||||
reference.name.entry.assign(resourceName.name, resourceName.nameLen);
|
||||
reference.id = 0;
|
||||
Maybe<ResourceName> result = mResolver->findName(reference.id);
|
||||
if (result) {
|
||||
reference.name = result.value();
|
||||
|
||||
// Add to cache.
|
||||
mCache->insert({reference.id, reference.name});
|
||||
|
||||
reference.id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Resolver> mResolver;
|
||||
std::shared_ptr<IResolver> mResolver;
|
||||
std::map<ResourceId, ResourceName>* mCache;
|
||||
};
|
||||
|
||||
|
||||
BinaryResourceParser::BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
|
||||
const std::shared_ptr<Resolver>& resolver,
|
||||
const std::shared_ptr<IResolver>& resolver,
|
||||
const Source& source,
|
||||
const void* data,
|
||||
size_t len) :
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
* add any resources parsed to `table`. `source` is for logging purposes.
|
||||
*/
|
||||
BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
|
||||
const std::shared_ptr<Resolver>& resolver,
|
||||
const std::shared_ptr<IResolver>& resolver,
|
||||
const Source& source,
|
||||
const void* data, size_t len);
|
||||
|
||||
@@ -92,7 +92,7 @@ private:
|
||||
|
||||
std::shared_ptr<ResourceTable> mTable;
|
||||
|
||||
std::shared_ptr<Resolver> mResolver;
|
||||
std::shared_ptr<IResolver> mResolver;
|
||||
|
||||
const Source mSource;
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
#include "BinaryXmlPullParser.h"
|
||||
#include "Maybe.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <memory>
|
||||
@@ -77,12 +79,31 @@ XmlPullParser::Event BinaryXmlPullParser::next() {
|
||||
mEvent = codeToEvent(code);
|
||||
switch (mEvent) {
|
||||
case Event::kStartNamespace:
|
||||
case Event::kEndNamespace:
|
||||
case Event::kEndNamespace: {
|
||||
data = mParser->getNamespacePrefix(&len);
|
||||
mStr1.assign(data, len);
|
||||
if (data) {
|
||||
mStr1.assign(data, len);
|
||||
} else {
|
||||
mStr1.clear();
|
||||
}
|
||||
data = mParser->getNamespaceUri(&len);
|
||||
mStr2.assign(data, len);
|
||||
if (data) {
|
||||
mStr2.assign(data, len);
|
||||
} else {
|
||||
mStr2.clear();
|
||||
}
|
||||
|
||||
Maybe<std::u16string> result = util::extractPackageFromNamespace(mStr2);
|
||||
if (result) {
|
||||
if (mEvent == Event::kStartNamespace) {
|
||||
mPackageAliases.emplace_back(mStr1, result.value());
|
||||
} else {
|
||||
assert(mPackageAliases.back().second == result.value());
|
||||
mPackageAliases.pop_back();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Event::kStartElement:
|
||||
copyAttributes();
|
||||
@@ -90,14 +111,26 @@ XmlPullParser::Event BinaryXmlPullParser::next() {
|
||||
|
||||
case Event::kEndElement:
|
||||
data = mParser->getElementNamespace(&len);
|
||||
mStr1.assign(data, len);
|
||||
if (data) {
|
||||
mStr1.assign(data, len);
|
||||
} else {
|
||||
mStr1.clear();
|
||||
}
|
||||
data = mParser->getElementName(&len);
|
||||
mStr2.assign(data, len);
|
||||
if (data) {
|
||||
mStr2.assign(data, len);
|
||||
} else {
|
||||
mStr2.clear();
|
||||
}
|
||||
break;
|
||||
|
||||
case Event::kText:
|
||||
data = mParser->getText(&len);
|
||||
mStr1.assign(data, len);
|
||||
if (data) {
|
||||
mStr1.assign(data, len);
|
||||
} else {
|
||||
mStr1.clear();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -155,6 +188,22 @@ const std::u16string& BinaryXmlPullParser::getNamespaceUri() const {
|
||||
return sEmpty;
|
||||
}
|
||||
|
||||
bool BinaryXmlPullParser::applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const {
|
||||
const auto endIter = mPackageAliases.rend();
|
||||
for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
|
||||
if (iter->first == *package) {
|
||||
if (iter->second.empty()) {
|
||||
*package = defaultPackage;
|
||||
} else {
|
||||
*package = iter->second;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::u16string& BinaryXmlPullParser::getElementNamespace() const {
|
||||
if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
|
||||
mEvent == XmlPullParser::Event::kEndElement)) {
|
||||
@@ -191,11 +240,17 @@ void BinaryXmlPullParser::copyAttributes() {
|
||||
XmlPullParser::Attribute attr;
|
||||
size_t len;
|
||||
const char16_t* str = mParser->getAttributeNamespace(i, &len);
|
||||
attr.namespaceUri.assign(str, len);
|
||||
if (str) {
|
||||
attr.namespaceUri.assign(str, len);
|
||||
}
|
||||
str = mParser->getAttributeName(i, &len);
|
||||
attr.name.assign(str, len);
|
||||
if (str) {
|
||||
attr.name.assign(str, len);
|
||||
}
|
||||
str = mParser->getAttributeStringValue(i, &len);
|
||||
attr.value.assign(str, len);
|
||||
if (str) {
|
||||
attr.value.assign(str, len);
|
||||
}
|
||||
mAttributes.push_back(std::move(attr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,25 +34,27 @@ public:
|
||||
BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser);
|
||||
BinaryXmlPullParser(const BinaryXmlPullParser& rhs) = delete;
|
||||
|
||||
Event getEvent() const;
|
||||
const std::string& getLastError() const;
|
||||
Event next();
|
||||
Event getEvent() const override;
|
||||
const std::string& getLastError() const override;
|
||||
Event next() override;
|
||||
|
||||
const std::u16string& getComment() const;
|
||||
size_t getLineNumber() const;
|
||||
size_t getDepth() const;
|
||||
const std::u16string& getComment() const override;
|
||||
size_t getLineNumber() const override;
|
||||
size_t getDepth() const override;
|
||||
|
||||
const std::u16string& getText() const;
|
||||
const std::u16string& getText() const override;
|
||||
|
||||
const std::u16string& getNamespacePrefix() const;
|
||||
const std::u16string& getNamespaceUri() const;
|
||||
const std::u16string& getNamespacePrefix() const override;
|
||||
const std::u16string& getNamespaceUri() const override;
|
||||
bool applyPackageAlias(std::u16string* package, const std::u16string& defaultpackage)
|
||||
const override;
|
||||
|
||||
const std::u16string& getElementNamespace() const;
|
||||
const std::u16string& getElementName() const;
|
||||
const std::u16string& getElementNamespace() const override;
|
||||
const std::u16string& getElementName() const override;
|
||||
|
||||
const_iterator beginAttributes() const;
|
||||
const_iterator endAttributes() const;
|
||||
size_t getAttributeCount() const;
|
||||
const_iterator beginAttributes() const override;
|
||||
const_iterator endAttributes() const override;
|
||||
size_t getAttributeCount() const override;
|
||||
|
||||
private:
|
||||
void copyAttributes();
|
||||
@@ -66,6 +68,7 @@ private:
|
||||
const std::u16string sEmpty;
|
||||
const std::string sEmpty8;
|
||||
size_t mDepth;
|
||||
std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -252,6 +252,11 @@ const std::u16string& BindingXmlPullParser::getNamespaceUri() const {
|
||||
return mParser->getNamespaceUri();
|
||||
}
|
||||
|
||||
bool BindingXmlPullParser::applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const {
|
||||
return mParser->applyPackageAlias(package, defaultPackage);
|
||||
}
|
||||
|
||||
const std::u16string& BindingXmlPullParser::getElementNamespace() const {
|
||||
return mParser->getElementNamespace();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ public:
|
||||
|
||||
const std::u16string& getNamespacePrefix() const override;
|
||||
const std::u16string& getNamespaceUri() const override;
|
||||
bool applyPackageAlias(std::u16string* package, const std::u16string& defaultPackage)
|
||||
const override;
|
||||
|
||||
const std::u16string& getElementNamespace() const override;
|
||||
const std::u16string& getElementName() const override;
|
||||
|
||||
@@ -65,7 +65,7 @@ void parse(int argc, char** argv, const StringPiece& command) {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const StringPiece arg(argv[i]);
|
||||
if (*arg.data() != '-') {
|
||||
sArgs.emplace_back(arg.toString());
|
||||
sArgs.push_back(arg.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ TEST_F(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
|
||||
output.find("public static final int hey_dude_cool_attr = 0;"));
|
||||
}
|
||||
|
||||
/*
|
||||
TEST_F(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
|
||||
ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
|
||||
ResourceId{ 0x01, 0x02, 0x0000 }));
|
||||
@@ -111,6 +112,6 @@ TEST_F(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
|
||||
output = out.str();
|
||||
EXPECT_NE(std::string::npos, output.find("int test ="));
|
||||
EXPECT_EQ(std::string::npos, output.find("int foo ="));
|
||||
}
|
||||
}*/
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "Linker.h"
|
||||
#include "Logger.h"
|
||||
#include "NameMangler.h"
|
||||
#include "Resolver.h"
|
||||
#include "ResourceParser.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
@@ -38,7 +40,7 @@ namespace aapt {
|
||||
Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
|
||||
}
|
||||
|
||||
Linker::Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver) :
|
||||
Linker::Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<IResolver> resolver) :
|
||||
mTable(table), mResolver(resolver), mError(false) {
|
||||
}
|
||||
|
||||
@@ -100,11 +102,15 @@ bool Linker::linkAndValidate() {
|
||||
}
|
||||
entry->entryId = nextIndex++;
|
||||
|
||||
std::u16string unmangledPackage = mTable->getPackage();
|
||||
std::u16string unmangledName = entry->name;
|
||||
NameMangler::unmangle(&unmangledName, &unmangledPackage);
|
||||
|
||||
// Update callers of this resource with the right ID.
|
||||
auto callersIter = mGraph.find(ResourceNameRef{
|
||||
mTable->getPackage(),
|
||||
unmangledPackage,
|
||||
type->type,
|
||||
entry->name
|
||||
unmangledName
|
||||
});
|
||||
|
||||
if (callersIter != std::end(mGraph)) {
|
||||
@@ -175,13 +181,14 @@ void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine
|
||||
// we called through the original value.
|
||||
|
||||
auto onCreateReference = [&](const ResourceName& name) {
|
||||
mTable->addResource(name, ConfigDescription{},
|
||||
source, util::make_unique<Id>());
|
||||
// We should never get here. All references would have been
|
||||
// parsed in the parser phase.
|
||||
assert(false);
|
||||
//mTable->addResource(name, ConfigDescription{}, source, util::make_unique<Id>());
|
||||
};
|
||||
|
||||
convertedValue = ResourceParser::parseItemForAttribute(
|
||||
*str.value, attr, mResolver->getDefaultPackage(),
|
||||
onCreateReference);
|
||||
convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
|
||||
onCreateReference);
|
||||
if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
|
||||
// Last effort is to parse as a string.
|
||||
util::StringBuilder builder;
|
||||
@@ -225,13 +232,13 @@ void Linker::visit(Style& style, ValueVisitorArgs& a) {
|
||||
}
|
||||
|
||||
for (Style::Entry& styleEntry : style.entries) {
|
||||
Maybe<Resolver::Entry> result = mResolver->findAttribute(styleEntry.key.name);
|
||||
Maybe<IResolver::Entry> result = mResolver->findAttribute(styleEntry.key.name);
|
||||
if (!result || !result.value().attr) {
|
||||
addUnresolvedSymbol(styleEntry.key.name, args.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
const Resolver::Entry& entry = result.value();
|
||||
const IResolver::Entry& entry = result.value();
|
||||
if (entry.id.isValid()) {
|
||||
styleEntry.key.id = entry.id;
|
||||
} else {
|
||||
|
||||
@@ -52,9 +52,9 @@ class Linker : ValueVisitor {
|
||||
public:
|
||||
/**
|
||||
* Create a Linker for the given resource table with the sources available in
|
||||
* Resolver. Resolver should contain the ResourceTable as a source too.
|
||||
* IResolver. IResolver should contain the ResourceTable as a source too.
|
||||
*/
|
||||
Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver);
|
||||
Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<IResolver> resolver);
|
||||
|
||||
Linker(const Linker&) = delete;
|
||||
|
||||
@@ -117,7 +117,7 @@ private:
|
||||
friend ::std::ostream& operator<<(::std::ostream&, const Node&);
|
||||
|
||||
std::shared_ptr<ResourceTable> mTable;
|
||||
std::shared_ptr<Resolver> mResolver;
|
||||
std::shared_ptr<IResolver> mResolver;
|
||||
std::map<ResourceNameRef, std::vector<Node>> mGraph;
|
||||
std::map<ResourceName, std::vector<SourceLine>> mUnresolvedSymbols;
|
||||
bool mError;
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
#include "Linker.h"
|
||||
#include "Resolver.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceTableResolver.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "Util.h"
|
||||
|
||||
@@ -31,7 +31,7 @@ struct LinkerTest : public ::testing::Test {
|
||||
mTable = std::make_shared<ResourceTable>();
|
||||
mTable->setPackage(u"android");
|
||||
mTable->setPackageId(0x01);
|
||||
mLinker = std::make_shared<Linker>(mTable, std::make_shared<Resolver>(
|
||||
mLinker = std::make_shared<Linker>(mTable, std::make_shared<ResourceTableResolver>(
|
||||
mTable, std::make_shared<android::AssetManager>()));
|
||||
|
||||
// Create a few attributes for use in the tests.
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "Png.h"
|
||||
#include "ResourceParser.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceTableResolver.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "SdkConstants.h"
|
||||
#include "SourceXmlPullParser.h"
|
||||
@@ -271,6 +272,7 @@ struct LinkItem {
|
||||
ConfigDescription config;
|
||||
std::string originalPath;
|
||||
ZipFile* apk;
|
||||
std::u16string originalPackage;
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
@@ -371,6 +373,8 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
|
||||
XmlFlattener flattener(table, {});
|
||||
|
||||
XmlFlattener::Options xmlOptions;
|
||||
xmlOptions.defaultPackage = table->getPackage();
|
||||
|
||||
if (options.versionStylesAndLayouts) {
|
||||
// We strip attributes that do not belong in this version of the resource.
|
||||
// Non-version qualified resources have an implicit version 1 requirement.
|
||||
@@ -432,7 +436,7 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
|
||||
return true;
|
||||
}
|
||||
|
||||
bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolver,
|
||||
bool linkXml(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
|
||||
const LinkItem& item, const void* data, size_t dataLen, ZipFile* outApk) {
|
||||
std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
|
||||
if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
|
||||
@@ -443,7 +447,10 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolv
|
||||
|
||||
BigBuffer outBuffer(1024);
|
||||
XmlFlattener flattener({}, resolver);
|
||||
if (!flattener.flatten(item.source, xmlParser, &outBuffer, {})) {
|
||||
|
||||
XmlFlattener::Options xmlOptions;
|
||||
xmlOptions.defaultPackage = item.originalPackage;
|
||||
if (!flattener.flatten(item.source, xmlParser, &outBuffer, xmlOptions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -490,8 +497,8 @@ bool copyFile(const AaptOptions& options, const CompileItem& item, ZipFile* outA
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver>& resolver,
|
||||
ZipFile* outApk) {
|
||||
bool compileManifest(const AaptOptions& options,
|
||||
const std::shared_ptr<ResourceTableResolver>& resolver, ZipFile* outApk) {
|
||||
if (options.verbose) {
|
||||
Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
|
||||
}
|
||||
@@ -506,7 +513,9 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver>
|
||||
std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
|
||||
XmlFlattener flattener({}, resolver);
|
||||
|
||||
if (!flattener.flatten(options.manifest, xmlParser, &outBuffer, {})) {
|
||||
XmlFlattener::Options xmlOptions;
|
||||
xmlOptions.defaultPackage = options.appInfo.package;
|
||||
if (!flattener.flatten(options.manifest, xmlParser, &outBuffer, xmlOptions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -640,8 +649,12 @@ static void addApkFilesToLinkQueue(const std::u16string& package, const Source&
|
||||
for (auto& value : entry->values) {
|
||||
visitFunc<FileReference>(*value.value, [&](FileReference& ref) {
|
||||
std::string pathUtf8 = util::utf16ToUtf8(*ref.path);
|
||||
Source newSource = source;
|
||||
newSource.path += "/";
|
||||
newSource.path += pathUtf8;
|
||||
outLinkQueue->push(LinkItem{
|
||||
source, name, value.config, pathUtf8, apk.get() });
|
||||
newSource, name, value.config, pathUtf8, apk.get(),
|
||||
table->getPackage() });
|
||||
// Now rewrite the file path.
|
||||
if (mangle) {
|
||||
ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16(
|
||||
@@ -657,9 +670,20 @@ static void addApkFilesToLinkQueue(const std::u16string& package, const Source&
|
||||
static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate |
|
||||
ZipFile::kOpenReadWrite;
|
||||
|
||||
struct DeleteMalloc {
|
||||
void operator()(void* ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct StaticLibraryData {
|
||||
Source source;
|
||||
std::unique_ptr<ZipFile> apk;
|
||||
};
|
||||
|
||||
bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outTable,
|
||||
const std::shared_ptr<Resolver>& resolver) {
|
||||
std::map<std::shared_ptr<ResourceTable>, std::unique_ptr<ZipFile>> apkFiles;
|
||||
const std::shared_ptr<ResourceTableResolver>& resolver) {
|
||||
std::map<std::shared_ptr<ResourceTable>, StaticLibraryData> apkFiles;
|
||||
std::unordered_set<std::u16string> linkedPackages;
|
||||
|
||||
// Populate the linkedPackages with our own.
|
||||
@@ -681,19 +705,18 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
return false;
|
||||
}
|
||||
|
||||
void* uncompressedData = zipFile->uncompress(entry);
|
||||
std::unique_ptr<void, DeleteMalloc> uncompressedData = std::unique_ptr<void, DeleteMalloc>(
|
||||
zipFile->uncompress(entry));
|
||||
assert(uncompressedData);
|
||||
|
||||
BinaryResourceParser parser(table, resolver, source, uncompressedData,
|
||||
BinaryResourceParser parser(table, resolver, source, uncompressedData.get(),
|
||||
entry->getUncompressedLen());
|
||||
if (!parser.parse()) {
|
||||
free(uncompressedData);
|
||||
return false;
|
||||
}
|
||||
free(uncompressedData);
|
||||
|
||||
// Keep track of where this table came from.
|
||||
apkFiles[table] = std::move(zipFile);
|
||||
apkFiles[table] = StaticLibraryData{ source, std::move(zipFile) };
|
||||
|
||||
// Add the package to the set of linked packages.
|
||||
linkedPackages.insert(table->getPackage());
|
||||
@@ -704,7 +727,8 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
const std::shared_ptr<ResourceTable>& inTable = p.first;
|
||||
|
||||
// Collect all FileReferences and add them to the queue for processing.
|
||||
addApkFilesToLinkQueue(options.appInfo.package, Source{}, inTable, p.second, &linkQueue);
|
||||
addApkFilesToLinkQueue(options.appInfo.package, p.second.source, inTable, p.second.apk,
|
||||
&linkQueue);
|
||||
|
||||
// Merge the tables.
|
||||
if (!outTable->merge(std::move(*inTable))) {
|
||||
@@ -746,6 +770,7 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
for (; !linkQueue.empty(); linkQueue.pop()) {
|
||||
const LinkItem& item = linkQueue.front();
|
||||
|
||||
assert(!item.originalPackage.empty());
|
||||
ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data());
|
||||
if (!entry) {
|
||||
Logger::error(item.source) << "failed to find '" << item.originalPath << "'."
|
||||
@@ -811,6 +836,20 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
}
|
||||
}
|
||||
|
||||
outTable->getValueStringPool().prune();
|
||||
outTable->getValueStringPool().sort(
|
||||
[](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
|
||||
if (a.context.priority < b.context.priority) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a.context.priority > b.context.priority) {
|
||||
return false;
|
||||
}
|
||||
return a.value < b.value;
|
||||
});
|
||||
|
||||
|
||||
// Flatten the resource table.
|
||||
TableFlattener::Options flattenerOptions;
|
||||
flattenerOptions.useExtendedChunks = false;
|
||||
@@ -823,7 +862,7 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
}
|
||||
|
||||
bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
|
||||
const std::shared_ptr<Resolver>& resolver) {
|
||||
const std::shared_ptr<IResolver>& resolver) {
|
||||
std::queue<CompileItem> compileQueue;
|
||||
bool error = false;
|
||||
|
||||
@@ -1073,7 +1112,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Make the resolver that will cache IDs for us.
|
||||
std::shared_ptr<Resolver> resolver = std::make_shared<Resolver>(table, libraries);
|
||||
std::shared_ptr<ResourceTableResolver> resolver = std::make_shared<ResourceTableResolver>(
|
||||
table, libraries);
|
||||
|
||||
if (options.phase == AaptOptions::Phase::Compile) {
|
||||
if (!compile(options, table, resolver)) {
|
||||
|
||||
@@ -190,18 +190,26 @@ bool ManifestValidator::validateManifest(const Source& source, android::ResXMLPa
|
||||
bool error = false;
|
||||
SourceLogger logger(source);
|
||||
|
||||
const size_t attrCount = parser->getAttributeCount();
|
||||
for (size_t i = 0; i < attrCount; i++) {
|
||||
size_t len = 0;
|
||||
StringPiece16 attrNamespace(parser->getAttributeNamespace(i, &len), len);
|
||||
StringPiece16 attrName(parser->getAttributeName(i, &len), len);
|
||||
if (attrNamespace.empty() && attrName == u"package") {
|
||||
error |= !validateInlineAttribute(parser, i, logger, kPackageIdentSet);
|
||||
} else if (attrNamespace == u"android") {
|
||||
if (attrName == u"sharedUserId") {
|
||||
error |= !validateInlineAttribute(parser, i, logger, kPackageIdentSet);
|
||||
}
|
||||
}
|
||||
const StringPiece16 kAndroid = u"android";
|
||||
const StringPiece16 kPackage = u"package";
|
||||
const StringPiece16 kSharedUserId = u"sharedUserId";
|
||||
|
||||
ssize_t idx;
|
||||
|
||||
idx = parser->indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
|
||||
if (idx < 0) {
|
||||
logger.error(parser->getLineNumber())
|
||||
<< "missing package attribute."
|
||||
<< std::endl;
|
||||
error = true;
|
||||
} else {
|
||||
error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
|
||||
}
|
||||
|
||||
idx = parser->indexOfAttribute(kAndroid.data(), kAndroid.size(),
|
||||
kSharedUserId.data(), kSharedUserId.size());
|
||||
if (idx >= 0) {
|
||||
error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
@@ -34,54 +34,68 @@ public:
|
||||
/**
|
||||
* Construct Nothing.
|
||||
*/
|
||||
inline Maybe();
|
||||
Maybe();
|
||||
|
||||
inline ~Maybe();
|
||||
~Maybe();
|
||||
|
||||
Maybe(const Maybe& rhs);
|
||||
|
||||
template <typename U>
|
||||
inline Maybe(const Maybe<U>& rhs);
|
||||
Maybe(const Maybe<U>& rhs);
|
||||
|
||||
Maybe(Maybe&& rhs);
|
||||
|
||||
template <typename U>
|
||||
inline Maybe(Maybe<U>&& rhs);
|
||||
Maybe(Maybe<U>&& rhs);
|
||||
|
||||
Maybe& operator=(const Maybe& rhs);
|
||||
|
||||
template <typename U>
|
||||
inline Maybe& operator=(const Maybe<U>& rhs);
|
||||
Maybe& operator=(const Maybe<U>& rhs);
|
||||
|
||||
Maybe& operator=(Maybe&& rhs);
|
||||
|
||||
template <typename U>
|
||||
inline Maybe& operator=(Maybe<U>&& rhs);
|
||||
Maybe& operator=(Maybe<U>&& rhs);
|
||||
|
||||
/**
|
||||
* Construct a Maybe holding a value.
|
||||
*/
|
||||
inline Maybe(const T& value);
|
||||
Maybe(const T& value);
|
||||
|
||||
/**
|
||||
* Construct a Maybe holding a value.
|
||||
*/
|
||||
inline Maybe(T&& value);
|
||||
Maybe(T&& value);
|
||||
|
||||
/**
|
||||
* True if this holds a value, false if
|
||||
* it holds Nothing.
|
||||
*/
|
||||
inline operator bool() const;
|
||||
operator bool() const;
|
||||
|
||||
/**
|
||||
* Gets the value if one exists, or else
|
||||
* panics.
|
||||
*/
|
||||
inline T& value();
|
||||
T& value();
|
||||
|
||||
/**
|
||||
* Gets the value if one exists, or else
|
||||
* panics.
|
||||
*/
|
||||
inline const T& value() const;
|
||||
const T& value() const;
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
friend class Maybe;
|
||||
|
||||
template <typename U>
|
||||
Maybe& copy(const Maybe<U>& rhs);
|
||||
|
||||
template <typename U>
|
||||
Maybe& move(Maybe<U>&& rhs);
|
||||
|
||||
void destroy();
|
||||
|
||||
bool mNothing;
|
||||
@@ -101,6 +115,14 @@ Maybe<T>::~Maybe() {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Maybe<T>::Maybe(const Maybe& rhs)
|
||||
: mNothing(rhs.mNothing) {
|
||||
if (!rhs.mNothing) {
|
||||
new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
Maybe<T>::Maybe(const Maybe<U>& rhs)
|
||||
@@ -110,6 +132,18 @@ Maybe<T>::Maybe(const Maybe<U>& rhs)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Maybe<T>::Maybe(Maybe&& rhs)
|
||||
: mNothing(rhs.mNothing) {
|
||||
if (!rhs.mNothing) {
|
||||
rhs.mNothing = true;
|
||||
|
||||
// Move the value from rhs.
|
||||
new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
|
||||
rhs.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
Maybe<T>::Maybe(Maybe<U>&& rhs)
|
||||
@@ -119,16 +153,25 @@ Maybe<T>::Maybe(Maybe<U>&& rhs)
|
||||
|
||||
// Move the value from rhs.
|
||||
new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
|
||||
|
||||
// Since the value in rhs is now Nothing,
|
||||
// run the destructor for the value.
|
||||
rhs.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
|
||||
// Delegate to the actual assignment.
|
||||
return copy(rhs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
|
||||
inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
|
||||
return copy(rhs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
|
||||
if (mNothing && rhs.mNothing) {
|
||||
// Both are nothing, nothing to do.
|
||||
return *this;
|
||||
@@ -149,9 +192,21 @@ Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
|
||||
// Delegate to the actual assignment.
|
||||
return move(std::forward<Maybe<T>>(rhs));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
|
||||
inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
|
||||
return move(std::forward<Maybe<U>>(rhs));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
|
||||
if (mNothing && rhs.mNothing) {
|
||||
// Both are nothing, nothing to do.
|
||||
return *this;
|
||||
@@ -162,14 +217,15 @@ Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
|
||||
rhs.destroy();
|
||||
} else if (mNothing) {
|
||||
// We are nothing but rhs is something.
|
||||
mNothing = rhs.mNothing;
|
||||
mNothing = false;
|
||||
rhs.mNothing = true;
|
||||
|
||||
// Move the value from rhs.
|
||||
new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
|
||||
rhs.destroy();
|
||||
} else {
|
||||
// We are something but rhs is nothing, so destroy our value.
|
||||
mNothing = rhs.mNothing;
|
||||
mNothing = true;
|
||||
destroy();
|
||||
}
|
||||
return *this;
|
||||
|
||||
@@ -23,20 +23,64 @@ namespace aapt {
|
||||
|
||||
struct Dummy {
|
||||
Dummy() {
|
||||
std::cerr << "Constructing Dummy " << (void *) this << std::endl;
|
||||
data = new int;
|
||||
*data = 1;
|
||||
std::cerr << "Construct Dummy{0x" << (void *) this
|
||||
<< "} with data=0x" << (void*) data
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
Dummy(const Dummy& rhs) {
|
||||
std::cerr << "Copying Dummy " << (void *) this << " from " << (const void*) &rhs << std::endl;
|
||||
data = nullptr;
|
||||
if (rhs.data) {
|
||||
data = new int;
|
||||
*data = *rhs.data;
|
||||
}
|
||||
std::cerr << "CopyConstruct Dummy{0x" << (void *) this
|
||||
<< "} from Dummy{0x" << (const void*) &rhs
|
||||
<< "}" << std::endl;
|
||||
}
|
||||
|
||||
Dummy(Dummy&& rhs) {
|
||||
std::cerr << "Moving Dummy " << (void *) this << " from " << (void*) &rhs << std::endl;
|
||||
data = rhs.data;
|
||||
rhs.data = nullptr;
|
||||
std::cerr << "MoveConstruct Dummy{0x" << (void *) this
|
||||
<< "} from Dummy{0x" << (const void*) &rhs
|
||||
<< "}" << std::endl;
|
||||
}
|
||||
|
||||
Dummy& operator=(const Dummy& rhs) {
|
||||
delete data;
|
||||
data = nullptr;
|
||||
|
||||
if (rhs.data) {
|
||||
data = new int;
|
||||
*data = *rhs.data;
|
||||
}
|
||||
std::cerr << "CopyAssign Dummy{0x" << (void *) this
|
||||
<< "} from Dummy{0x" << (const void*) &rhs
|
||||
<< "}" << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dummy& operator=(Dummy&& rhs) {
|
||||
delete data;
|
||||
data = rhs.data;
|
||||
rhs.data = nullptr;
|
||||
std::cerr << "MoveAssign Dummy{0x" << (void *) this
|
||||
<< "} from Dummy{0x" << (const void*) &rhs
|
||||
<< "}" << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Dummy() {
|
||||
std::cerr << "Destroying Dummy " << (void *) this << std::endl;
|
||||
std::cerr << "Destruct Dummy{0x" << (void *) this
|
||||
<< "} with data=0x" << (void*) data
|
||||
<< std::endl;
|
||||
delete data;
|
||||
}
|
||||
|
||||
int* data;
|
||||
};
|
||||
|
||||
TEST(MaybeTest, MakeNothing) {
|
||||
@@ -66,4 +110,12 @@ TEST(MaybeTest, Lifecycle) {
|
||||
Maybe<Dummy> val2 = make_value(Dummy());
|
||||
}
|
||||
|
||||
TEST(MaybeTest, MoveAssign) {
|
||||
Maybe<Dummy> val;
|
||||
{
|
||||
Maybe<Dummy> val2 = Dummy();
|
||||
val = std::move(val2);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -19,31 +19,18 @@
|
||||
|
||||
#include "Maybe.h"
|
||||
#include "Resource.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
|
||||
#include <androidfw/AssetManager.h>
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
/**
|
||||
* Resolves symbolic references (package:type/entry) into resource IDs/objects.
|
||||
* Encapsulates the search of library sources as well as the local ResourceTable.
|
||||
*/
|
||||
class Resolver {
|
||||
class IResolver {
|
||||
public:
|
||||
/**
|
||||
* Creates a resolver with a local ResourceTable and an AssetManager
|
||||
* loaded with library packages.
|
||||
*/
|
||||
Resolver(std::shared_ptr<const ResourceTable> table,
|
||||
std::shared_ptr<const android::AssetManager> sources);
|
||||
|
||||
Resolver(const Resolver&) = delete; // Not copyable.
|
||||
virtual ~IResolver() {};
|
||||
|
||||
/**
|
||||
* Holds the result of a resource name lookup.
|
||||
@@ -68,44 +55,27 @@ public:
|
||||
* Return the package to use when none is specified. This
|
||||
* is the package name of the app being built.
|
||||
*/
|
||||
const std::u16string& getDefaultPackage() const;
|
||||
virtual const std::u16string& getDefaultPackage() const = 0;
|
||||
|
||||
/**
|
||||
* Returns a ResourceID if the name is found. The ResourceID
|
||||
* may not be valid if the resource was not assigned an ID.
|
||||
*/
|
||||
Maybe<ResourceId> findId(const ResourceName& name);
|
||||
virtual Maybe<ResourceId> findId(const ResourceName& name) = 0;
|
||||
|
||||
/**
|
||||
* Returns an Entry if the name is found. Entry::attr
|
||||
* may be nullptr if the resource is not an attribute.
|
||||
*/
|
||||
Maybe<Entry> findAttribute(const ResourceName& name);
|
||||
virtual Maybe<Entry> findAttribute(const ResourceName& name) = 0;
|
||||
|
||||
const android::ResTable& getResTable() const;
|
||||
|
||||
private:
|
||||
struct CacheEntry {
|
||||
ResourceId id;
|
||||
std::unique_ptr<Attribute> attr;
|
||||
};
|
||||
|
||||
const CacheEntry* buildCacheEntry(const ResourceName& name);
|
||||
|
||||
std::shared_ptr<const ResourceTable> mTable;
|
||||
std::shared_ptr<const android::AssetManager> mSources;
|
||||
std::map<ResourceName, CacheEntry> mCache;
|
||||
std::unordered_set<std::u16string> mIncludedPackages;
|
||||
/**
|
||||
* Find a resource by ID. Resolvers may contain resources without
|
||||
* resource IDs assigned to them.
|
||||
*/
|
||||
virtual Maybe<ResourceName> findName(ResourceId resId) = 0;
|
||||
};
|
||||
|
||||
inline const std::u16string& Resolver::getDefaultPackage() const {
|
||||
return mTable->getPackage();
|
||||
}
|
||||
|
||||
inline const android::ResTable& Resolver::getResTable() const {
|
||||
return mSources->getResources(false);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_RESOLVER_H
|
||||
|
||||
@@ -130,6 +130,7 @@ struct ResourceId {
|
||||
uint8_t typeId() const;
|
||||
uint16_t entryId() const;
|
||||
bool operator<(const ResourceId& rhs) const;
|
||||
bool operator==(const ResourceId& rhs) const;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -178,6 +179,10 @@ inline bool ResourceId::operator<(const ResourceId& rhs) const {
|
||||
return id < rhs.id;
|
||||
}
|
||||
|
||||
inline bool ResourceId::operator==(const ResourceId& rhs) const {
|
||||
return id == rhs.id;
|
||||
}
|
||||
|
||||
inline ::std::ostream& operator<<(::std::ostream& out,
|
||||
const ResourceId& resId) {
|
||||
std::ios_base::fmtflags oldFlags = out.flags();
|
||||
|
||||
@@ -175,23 +175,16 @@ bool ResourceParser::parseStyleParentReference(const StringPiece16& str, Referen
|
||||
}
|
||||
|
||||
std::unique_ptr<Reference> ResourceParser::tryParseReference(const StringPiece16& str,
|
||||
const StringPiece16& defaultPackage,
|
||||
bool* outCreate) {
|
||||
ResourceNameRef ref;
|
||||
bool privateRef = false;
|
||||
if (tryParseReference(str, &ref, outCreate, &privateRef)) {
|
||||
if (ref.package.empty()) {
|
||||
ref.package = defaultPackage;
|
||||
}
|
||||
std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
|
||||
value->privateReference = privateRef;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (tryParseAttributeReference(str, &ref)) {
|
||||
if (ref.package.empty()) {
|
||||
ref.package = defaultPackage;
|
||||
}
|
||||
*outCreate = false;
|
||||
return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
|
||||
}
|
||||
@@ -330,7 +323,7 @@ std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseBool(const StringPiece1
|
||||
StringPiece16 trimmedStr(util::trimWhitespace(str));
|
||||
uint32_t data = 0;
|
||||
if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
|
||||
data = 1;
|
||||
data = 0xffffffffu;
|
||||
} else if (trimmedStr != u"false" && trimmedStr != u"FALSE") {
|
||||
return {};
|
||||
}
|
||||
@@ -397,7 +390,7 @@ uint32_t ResourceParser::androidTypeToAttributeTypeMask(uint16_t type) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
|
||||
const StringPiece16& value, uint32_t typeMask, const StringPiece16& defaultPackage,
|
||||
const StringPiece16& value, uint32_t typeMask,
|
||||
std::function<void(const ResourceName&)> onCreateReference) {
|
||||
std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
|
||||
if (nullOrEmpty) {
|
||||
@@ -405,7 +398,7 @@ std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
|
||||
}
|
||||
|
||||
bool create = false;
|
||||
std::unique_ptr<Reference> reference = tryParseReference(value, defaultPackage, &create);
|
||||
std::unique_ptr<Reference> reference = tryParseReference(value, &create);
|
||||
if (reference) {
|
||||
if (create && onCreateReference) {
|
||||
onCreateReference(reference->name);
|
||||
@@ -457,11 +450,10 @@ std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
|
||||
* allows.
|
||||
*/
|
||||
std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
|
||||
const StringPiece16& str, const Attribute& attr, const StringPiece16& defaultPackage,
|
||||
const StringPiece16& str, const Attribute& attr,
|
||||
std::function<void(const ResourceName&)> onCreateReference) {
|
||||
const uint32_t typeMask = attr.typeMask;
|
||||
std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, defaultPackage,
|
||||
onCreateReference);
|
||||
std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
@@ -770,14 +762,25 @@ std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, uint32_t t
|
||||
}
|
||||
|
||||
auto onCreateReference = [&](const ResourceName& name) {
|
||||
// name.package can be empty here, as it will assume the package name of the table.
|
||||
mTable->addResource(name, {}, mSource.line(beginXmlLine), util::make_unique<Id>());
|
||||
};
|
||||
|
||||
// Process the raw value.
|
||||
std::unique_ptr<Item> processedItem = parseItemForAttribute(rawValue, typeMask,
|
||||
mTable->getPackage(),
|
||||
onCreateReference);
|
||||
if (processedItem) {
|
||||
// Fix up the reference.
|
||||
visitFunc<Reference>(*processedItem, [&](Reference& ref) {
|
||||
if (!ref.name.package.empty()) {
|
||||
// The package name was set, so lookup its alias.
|
||||
parser->applyPackageAlias(&ref.name.package, mTable->getPackage());
|
||||
} else {
|
||||
// The package name was left empty, so it assumes the default package
|
||||
// without alias lookup.
|
||||
ref.name.package = mTable->getPackage();
|
||||
}
|
||||
});
|
||||
return processedItem;
|
||||
}
|
||||
|
||||
@@ -1093,7 +1096,7 @@ bool ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, const StringPiec
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseXmlAttributeName(StringPiece16 str, ResourceNameRef* outRef) {
|
||||
static bool parseXmlAttributeName(StringPiece16 str, ResourceName* outName) {
|
||||
str = util::trimWhitespace(str);
|
||||
const char16_t* const start = str.data();
|
||||
const char16_t* const end = start + str.size();
|
||||
@@ -1110,12 +1113,12 @@ static bool parseXmlAttributeName(StringPiece16 str, ResourceNameRef* outRef) {
|
||||
p++;
|
||||
}
|
||||
|
||||
outRef->package = package;
|
||||
outRef->type = ResourceType::kAttr;
|
||||
outName->package = package.toString();
|
||||
outName->type = ResourceType::kAttr;
|
||||
if (name.size() == 0) {
|
||||
outRef->entry = str;
|
||||
outName->entry = str.toString();
|
||||
} else {
|
||||
outRef->entry = name;
|
||||
outName->entry = name.toString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1130,8 +1133,8 @@ bool ResourceParser::parseUntypedItem(XmlPullParser* parser, Style& style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceNameRef keyRef;
|
||||
if (!parseXmlAttributeName(nameAttrIter->value, &keyRef)) {
|
||||
ResourceName key;
|
||||
if (!parseXmlAttributeName(nameAttrIter->value, &key)) {
|
||||
mLogger.error(parser->getLineNumber())
|
||||
<< "invalid attribute name '"
|
||||
<< nameAttrIter->value
|
||||
@@ -1140,14 +1143,15 @@ bool ResourceParser::parseUntypedItem(XmlPullParser* parser, Style& style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyRef.package.empty()) {
|
||||
keyRef.package = mTable->getPackage();
|
||||
if (!key.package.empty()) {
|
||||
// We have a package name set, so lookup its alias.
|
||||
parser->applyPackageAlias(&key.package, mTable->getPackage());
|
||||
} else {
|
||||
// The package name was omitted, so use the default package name with
|
||||
// no alias lookup.
|
||||
key.package = mTable->getPackage();
|
||||
}
|
||||
|
||||
// Create a copy instead of a reference because we
|
||||
// are about to invalidate keyRef when advancing the parser.
|
||||
ResourceName key = keyRef.toResourceName();
|
||||
|
||||
std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
|
||||
if (!value) {
|
||||
return false;
|
||||
@@ -1170,7 +1174,11 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& re
|
||||
return false;
|
||||
}
|
||||
|
||||
if (style->parent.name.package.empty()) {
|
||||
if (!style->parent.name.package.empty()) {
|
||||
// Try to interpret the package name as an alias. These take precedence.
|
||||
parser->applyPackageAlias(&style->parent.name.package, mTable->getPackage());
|
||||
} else {
|
||||
// If no package is specified, this can not be an alias and is the local package.
|
||||
style->parent.name.package = mTable->getPackage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +76,10 @@ public:
|
||||
|
||||
/*
|
||||
* Returns a Reference object if the string was parsed as a resource or attribute reference,
|
||||
* ( @[+][package:]type/name | ?[package:]type/name )
|
||||
* assigning defaultPackage if the package was not present in the string, and setting
|
||||
* outCreate to true if the '+' was present in the string.
|
||||
* ( @[+][package:]type/name | ?[package:]type/name ) setting outCreate to true if
|
||||
* the '+' was present in the string.
|
||||
*/
|
||||
static std::unique_ptr<Reference> tryParseReference(const StringPiece16& str,
|
||||
const StringPiece16& defaultPackage,
|
||||
bool* outCreate);
|
||||
|
||||
/*
|
||||
@@ -127,20 +125,18 @@ public:
|
||||
*/
|
||||
static std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute& enumAttr,
|
||||
const StringPiece16& str);
|
||||
|
||||
/*
|
||||
* Try to convert a string to an Item for the given attribute. The attribute will
|
||||
* restrict what values the string can be converted to.
|
||||
* The defaultPackage is used when the string is a reference with no defined package.
|
||||
* The callback function onCreateReference is called when the parsed item is a
|
||||
* reference to an ID that must be created (@+id/foo).
|
||||
*/
|
||||
static std::unique_ptr<Item> parseItemForAttribute(
|
||||
const StringPiece16& value, const Attribute& attr, const StringPiece16& defaultPackage,
|
||||
const StringPiece16& value, const Attribute& attr,
|
||||
std::function<void(const ResourceName&)> onCreateReference = {});
|
||||
|
||||
static std::unique_ptr<Item> parseItemForAttribute(
|
||||
const StringPiece16& value, uint32_t typeMask, const StringPiece16& defaultPackage,
|
||||
const StringPiece16& value, uint32_t typeMask,
|
||||
std::function<void(const ResourceName&)> onCreateReference = {});
|
||||
|
||||
static uint32_t androidTypeToAttributeTypeMask(uint16_t type);
|
||||
|
||||
@@ -125,11 +125,9 @@ struct ResourceParserTest : public ::testing::Test {
|
||||
mTable->setPackage(u"android");
|
||||
}
|
||||
|
||||
::testing::AssertionResult testParse(std::istream& in) {
|
||||
::testing::AssertionResult testParse(const StringPiece& str) {
|
||||
std::stringstream input(kXmlPreamble);
|
||||
input << "<resources>" << std::endl
|
||||
<< in.rdbuf() << std::endl
|
||||
<< "</resources>" << std::endl;
|
||||
input << "<resources>\n" << str << "\n</resources>" << std::endl;
|
||||
ResourceParser parser(mTable, Source{ "test" }, {},
|
||||
std::make_shared<SourceXmlPullParser>(input));
|
||||
if (parser.parse()) {
|
||||
@@ -174,7 +172,7 @@ TEST_F(ResourceParserTest, FailToParseWithNoRootResourcesElement) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseQuotedString) {
|
||||
std::stringstream input("<string name=\"foo\"> \" hey there \" </string>");
|
||||
std::string input = "<string name=\"foo\"> \" hey there \" </string>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const String* str = findResource<String>(ResourceName{
|
||||
@@ -184,7 +182,7 @@ TEST_F(ResourceParserTest, ParseQuotedString) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseEscapedString) {
|
||||
std::stringstream input("<string name=\"foo\">\\?123</string>");
|
||||
std::string input = "<string name=\"foo\">\\?123</string>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const String* str = findResource<String>(ResourceName{
|
||||
@@ -194,9 +192,8 @@ TEST_F(ResourceParserTest, ParseEscapedString) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseAttr) {
|
||||
std::stringstream input;
|
||||
input << "<attr name=\"foo\" format=\"string\"/>" << std::endl
|
||||
<< "<attr name=\"bar\"/>" << std::endl;
|
||||
std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
|
||||
"<attr name=\"bar\"/>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Attribute* attr = findResource<Attribute>(ResourceName{
|
||||
@@ -211,11 +208,10 @@ TEST_F(ResourceParserTest, ParseAttr) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
|
||||
std::stringstream input;
|
||||
input << "<declare-styleable name=\"Styleable\">" << std::endl
|
||||
<< " <attr name=\"foo\" />" << std::endl
|
||||
<< "</declare-styleable>" << std::endl
|
||||
<< "<attr name=\"foo\" format=\"string\"/>" << std::endl;
|
||||
std::string input = "<declare-styleable name=\"Styleable\">\n"
|
||||
" <attr name=\"foo\" />\n"
|
||||
"</declare-styleable>\n"
|
||||
"<attr name=\"foo\" format=\"string\"/>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Attribute* attr = findResource<Attribute>(ResourceName{
|
||||
@@ -225,14 +221,12 @@ TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
|
||||
std::stringstream input;
|
||||
input << "<declare-styleable name=\"Theme\">" << std::endl
|
||||
<< " <attr name=\"foo\" />" << std::endl
|
||||
<< "</declare-styleable>" << std::endl
|
||||
<< "<declare-styleable name=\"Window\">" << std::endl
|
||||
<< " <attr name=\"foo\" format=\"boolean\"/>" << std::endl
|
||||
<< "</declare-styleable>" << std::endl;
|
||||
|
||||
std::string input = "<declare-styleable name=\"Theme\">"
|
||||
" <attr name=\"foo\" />\n"
|
||||
"</declare-styleable>\n"
|
||||
"<declare-styleable name=\"Window\">\n"
|
||||
" <attr name=\"foo\" format=\"boolean\"/>\n"
|
||||
"</declare-styleable>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Attribute* attr = findResource<Attribute>(ResourceName{
|
||||
@@ -242,12 +236,11 @@ TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseEnumAttr) {
|
||||
std::stringstream input;
|
||||
input << "<attr name=\"foo\">" << std::endl
|
||||
<< " <enum name=\"bar\" value=\"0\"/>" << std::endl
|
||||
<< " <enum name=\"bat\" value=\"1\"/>" << std::endl
|
||||
<< " <enum name=\"baz\" value=\"2\"/>" << std::endl
|
||||
<< "</attr>" << std::endl;
|
||||
std::string input = "<attr name=\"foo\">\n"
|
||||
" <enum name=\"bar\" value=\"0\"/>\n"
|
||||
" <enum name=\"bat\" value=\"1\"/>\n"
|
||||
" <enum name=\"baz\" value=\"2\"/>\n"
|
||||
"</attr>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Attribute* enumAttr = findResource<Attribute>(ResourceName{
|
||||
@@ -267,12 +260,11 @@ TEST_F(ResourceParserTest, ParseEnumAttr) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseFlagAttr) {
|
||||
std::stringstream input;
|
||||
input << "<attr name=\"foo\">" << std::endl
|
||||
<< " <flag name=\"bar\" value=\"0\"/>" << std::endl
|
||||
<< " <flag name=\"bat\" value=\"1\"/>" << std::endl
|
||||
<< " <flag name=\"baz\" value=\"2\"/>" << std::endl
|
||||
<< "</attr>" << std::endl;
|
||||
std::string input = "<attr name=\"foo\">\n"
|
||||
" <flag name=\"bar\" value=\"0\"/>\n"
|
||||
" <flag name=\"bat\" value=\"1\"/>\n"
|
||||
" <flag name=\"baz\" value=\"2\"/>\n"
|
||||
"</attr>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Attribute* flagAttr = findResource<Attribute>(ResourceName{
|
||||
@@ -297,22 +289,20 @@ TEST_F(ResourceParserTest, ParseFlagAttr) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
|
||||
std::stringstream input;
|
||||
input << "<attr name=\"foo\">" << std::endl
|
||||
<< " <enum name=\"bar\" value=\"0\"/>" << std::endl
|
||||
<< " <enum name=\"bat\" value=\"1\"/>" << std::endl
|
||||
<< " <enum name=\"bat\" value=\"2\"/>" << std::endl
|
||||
<< "</attr>" << std::endl;
|
||||
std::string input = "<attr name=\"foo\">\n"
|
||||
" <enum name=\"bar\" value=\"0\"/>\n"
|
||||
" <enum name=\"bat\" value=\"1\"/>\n"
|
||||
" <enum name=\"bat\" value=\"2\"/>\n"
|
||||
"</attr>";
|
||||
ASSERT_FALSE(testParse(input));
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseStyle) {
|
||||
std::stringstream input;
|
||||
input << "<style name=\"foo\" parent=\"@style/fu\">" << std::endl
|
||||
<< " <item name=\"bar\">#ffffffff</item>" << std::endl
|
||||
<< " <item name=\"bat\">@string/hey</item>" << std::endl
|
||||
<< " <item name=\"baz\"><b>hey</b></item>" << std::endl
|
||||
<< "</style>" << std::endl;
|
||||
std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n"
|
||||
" <item name=\"bar\">#ffffffff</item>\n"
|
||||
" <item name=\"bat\">@string/hey</item>\n"
|
||||
" <item name=\"baz\"><b>hey</b></item>\n"
|
||||
"</style>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Style* style = findResource<Style>(ResourceName{
|
||||
@@ -330,8 +320,7 @@ TEST_F(ResourceParserTest, ParseStyle) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
|
||||
std::stringstream input;
|
||||
input << "<style name=\"foo\" parent=\"com.app:Theme\"/>" << std::endl;
|
||||
std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Style* style = findResource<Style>(
|
||||
@@ -340,9 +329,34 @@ TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
|
||||
EXPECT_EQ(ResourceNameRef(u"com.app", ResourceType::kStyle, u"Theme"), style->parent.name);
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
|
||||
std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
|
||||
" name=\"foo\" parent=\"app:Theme\"/>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Style* style = findResource<Style>(ResourceName{
|
||||
u"android", ResourceType::kStyle, u"foo" });
|
||||
ASSERT_NE(style, nullptr);
|
||||
EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"Theme"), style->parent.name);
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
|
||||
std::string input =
|
||||
"<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n"
|
||||
" <item name=\"app:bar\">0</item>\n"
|
||||
"</style>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Style* style = findResource<Style>(ResourceName{
|
||||
u"android", ResourceType::kStyle, u"foo" });
|
||||
ASSERT_NE(style, nullptr);
|
||||
ASSERT_EQ(1u, style->entries.size());
|
||||
EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kAttr, u"bar"),
|
||||
style->entries[0].key.name);
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
|
||||
std::stringstream input;
|
||||
input << "<string name=\"foo\">@+id/bar</string>" << std::endl;
|
||||
std::string input = "<string name=\"foo\">@+id/bar</string>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"bar"});
|
||||
@@ -350,11 +364,10 @@ TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
|
||||
std::stringstream input;
|
||||
input << "<declare-styleable name=\"foo\">" << std::endl
|
||||
<< " <attr name=\"bar\" />" << std::endl
|
||||
<< " <attr name=\"bat\" format=\"string|reference\"/>" << std::endl
|
||||
<< "</declare-styleable>" << std::endl;
|
||||
std::string input = "<declare-styleable name=\"foo\">\n"
|
||||
" <attr name=\"bar\" />\n"
|
||||
" <attr name=\"bat\" format=\"string|reference\"/>\n"
|
||||
"</declare-styleable>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Attribute* attr = findResource<Attribute>(ResourceName{
|
||||
@@ -376,12 +389,11 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseArray) {
|
||||
std::stringstream input;
|
||||
input << "<array name=\"foo\">" << std::endl
|
||||
<< " <item>@string/ref</item>" << std::endl
|
||||
<< " <item>hey</item>" << std::endl
|
||||
<< " <item>23</item>" << std::endl
|
||||
<< "</array>" << std::endl;
|
||||
std::string input = "<array name=\"foo\">\n"
|
||||
" <item>@string/ref</item>\n"
|
||||
" <item>hey</item>\n"
|
||||
" <item>23</item>\n"
|
||||
"</array>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Array* array = findResource<Array>(ResourceName{
|
||||
@@ -395,19 +407,16 @@ TEST_F(ResourceParserTest, ParseArray) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParsePlural) {
|
||||
std::stringstream input;
|
||||
input << "<plurals name=\"foo\">" << std::endl
|
||||
<< " <item quantity=\"other\">apples</item>" << std::endl
|
||||
<< " <item quantity=\"one\">apple</item>" << std::endl
|
||||
<< "</plurals>" << std::endl
|
||||
<< std::endl;
|
||||
std::string input = "<plurals name=\"foo\">\n"
|
||||
" <item quantity=\"other\">apples</item>\n"
|
||||
" <item quantity=\"one\">apple</item>\n"
|
||||
"</plurals>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
}
|
||||
|
||||
TEST_F(ResourceParserTest, ParseCommentsWithResource) {
|
||||
std::stringstream input;
|
||||
input << "<!-- This is a comment -->" << std::endl
|
||||
<< "<string name=\"foo\">Hi</string>" << std::endl;
|
||||
std::string input = "<!-- This is a comment -->\n"
|
||||
"<string name=\"foo\">Hi</string>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const ResourceTableType* type;
|
||||
@@ -425,7 +434,7 @@ TEST_F(ResourceParserTest, ParseCommentsWithResource) {
|
||||
* (as an ID has no value).
|
||||
*/
|
||||
TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
|
||||
std::stringstream input("<public type=\"id\" name=\"foo\"/>");
|
||||
std::string input = "<public type=\"id\" name=\"foo\"/>";
|
||||
ASSERT_TRUE(testParse(input));
|
||||
|
||||
const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"foo" });
|
||||
|
||||
@@ -318,12 +318,16 @@ bool ResourceTable::merge(ResourceTable&& other) {
|
||||
|
||||
for (auto& otherType : other) {
|
||||
std::unique_ptr<ResourceTableType>& type = findOrCreateType(otherType->type);
|
||||
if (type->publicStatus.isPublic && otherType->publicStatus.isPublic &&
|
||||
type->typeId != otherType->typeId) {
|
||||
Logger::error() << "can not merge type '" << type->type << "': conflicting public IDs "
|
||||
<< "(" << type->typeId << " vs " << otherType->typeId << ")."
|
||||
<< std::endl;
|
||||
return false;
|
||||
if (otherType->publicStatus.isPublic) {
|
||||
if (type->publicStatus.isPublic && type->typeId != otherType->typeId) {
|
||||
Logger::error() << "can not merge type '" << type->type
|
||||
<< "': conflicting public IDs "
|
||||
<< "(" << type->typeId << " vs " << otherType->typeId << ")."
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
type->publicStatus = std::move(otherType->publicStatus);
|
||||
type->typeId = otherType->typeId;
|
||||
}
|
||||
|
||||
for (auto& otherEntry : otherType->entries) {
|
||||
@@ -335,13 +339,16 @@ bool ResourceTable::merge(ResourceTable&& other) {
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, *nameToAdd);
|
||||
if (entry->publicStatus.isPublic && otherEntry->publicStatus.isPublic &&
|
||||
entry->entryId != otherEntry->entryId) {
|
||||
Logger::error() << "can not merge entry '" << type->type << "/" << entry->name
|
||||
<< "': conflicting public IDs "
|
||||
<< "(" << entry->entryId << " vs " << entry->entryId << ")."
|
||||
<< std::endl;
|
||||
return false;
|
||||
if (otherEntry->publicStatus.isPublic) {
|
||||
if (entry->publicStatus.isPublic && entry->entryId != otherEntry->entryId) {
|
||||
Logger::error() << "can not merge entry '" << type->type << "/" << entry->name
|
||||
<< "': conflicting public IDs "
|
||||
<< "(" << entry->entryId << " vs " << entry->entryId << ")."
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
entry->publicStatus = std::move(otherEntry->publicStatus);
|
||||
entry->entryId = otherEntry->entryId;
|
||||
}
|
||||
|
||||
for (ResourceConfigValue& otherValue : otherEntry->values) {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
#include "Maybe.h"
|
||||
#include "NameMangler.h"
|
||||
#include "Resolver.h"
|
||||
#include "Resource.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceTableResolver.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "Util.h"
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
|
||||
namespace aapt {
|
||||
|
||||
Resolver::Resolver(std::shared_ptr<const ResourceTable> table,
|
||||
std::shared_ptr<const android::AssetManager> sources) :
|
||||
ResourceTableResolver::ResourceTableResolver(
|
||||
std::shared_ptr<const ResourceTable> table,
|
||||
std::shared_ptr<const android::AssetManager> sources) :
|
||||
mTable(table), mSources(sources) {
|
||||
const android::ResTable& resTable = mSources->getResources(false);
|
||||
const size_t packageCount = resTable.getBasePackageCount();
|
||||
@@ -40,7 +41,7 @@ Resolver::Resolver(std::shared_ptr<const ResourceTable> table,
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<ResourceId> Resolver::findId(const ResourceName& name) {
|
||||
Maybe<ResourceId> ResourceTableResolver::findId(const ResourceName& name) {
|
||||
Maybe<Entry> result = findAttribute(name);
|
||||
if (result) {
|
||||
return result.value().id;
|
||||
@@ -48,7 +49,7 @@ Maybe<ResourceId> Resolver::findId(const ResourceName& name) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Maybe<Resolver::Entry> Resolver::findAttribute(const ResourceName& name) {
|
||||
Maybe<IResolver::Entry> ResourceTableResolver::findAttribute(const ResourceName& name) {
|
||||
auto cacheIter = mCache.find(name);
|
||||
if (cacheIter != std::end(mCache)) {
|
||||
return Entry{ cacheIter->second.id, cacheIter->second.attr.get() };
|
||||
@@ -97,12 +98,30 @@ Maybe<Resolver::Entry> Resolver::findAttribute(const ResourceName& name) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) {
|
||||
const android::ResTable& table = mSources->getResources(false);
|
||||
|
||||
android::ResTable::resource_name resourceName;
|
||||
if (!table.getResourceName(resId.id, false, &resourceName)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
|
||||
resourceName.typeLen));
|
||||
assert(type);
|
||||
return ResourceName{
|
||||
{ resourceName.package, resourceName.packageLen },
|
||||
*type,
|
||||
{ resourceName.name, resourceName.nameLen } };
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when we need to lookup a resource name in the AssetManager.
|
||||
* Since the values in the AssetManager are not parsed like in a ResourceTable,
|
||||
* we must create Attribute objects here if we find them.
|
||||
*/
|
||||
const Resolver::CacheEntry* Resolver::buildCacheEntry(const ResourceName& name) {
|
||||
const ResourceTableResolver::CacheEntry* ResourceTableResolver::buildCacheEntry(
|
||||
const ResourceName& name) {
|
||||
const android::ResTable& table = mSources->getResources(false);
|
||||
|
||||
const StringPiece16 type16 = toString(name.type);
|
||||
82
tools/aapt2/ResourceTableResolver.h
Normal file
82
tools/aapt2/ResourceTableResolver.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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_RESOURCE_TABLE_RESOLVER_H
|
||||
#define AAPT_RESOURCE_TABLE_RESOLVER_H
|
||||
|
||||
#include "Maybe.h"
|
||||
#include "Resolver.h"
|
||||
#include "Resource.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceValues.h"
|
||||
|
||||
#include <androidfw/AssetManager.h>
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
/**
|
||||
* Encapsulates the search of library sources as well as the local ResourceTable.
|
||||
*/
|
||||
class ResourceTableResolver : public IResolver {
|
||||
public:
|
||||
/**
|
||||
* Creates a resolver with a local ResourceTable and an AssetManager
|
||||
* loaded with library packages.
|
||||
*/
|
||||
ResourceTableResolver(std::shared_ptr<const ResourceTable> table,
|
||||
std::shared_ptr<const android::AssetManager> sources);
|
||||
|
||||
ResourceTableResolver(const ResourceTableResolver&) = delete; // Not copyable.
|
||||
|
||||
virtual const std::u16string& getDefaultPackage() const override;
|
||||
|
||||
virtual Maybe<ResourceId> findId(const ResourceName& name) override;
|
||||
|
||||
virtual Maybe<Entry> findAttribute(const ResourceName& name) override;
|
||||
|
||||
virtual Maybe<ResourceName> findName(ResourceId resId) override;
|
||||
|
||||
const android::ResTable& getResTable() const;
|
||||
|
||||
private:
|
||||
struct CacheEntry {
|
||||
ResourceId id;
|
||||
std::unique_ptr<Attribute> attr;
|
||||
};
|
||||
|
||||
const CacheEntry* buildCacheEntry(const ResourceName& name);
|
||||
|
||||
std::shared_ptr<const ResourceTable> mTable;
|
||||
std::shared_ptr<const android::AssetManager> mSources;
|
||||
std::map<ResourceName, CacheEntry> mCache;
|
||||
std::unordered_set<std::u16string> mIncludedPackages;
|
||||
};
|
||||
|
||||
inline const std::u16string& ResourceTableResolver::getDefaultPackage() const {
|
||||
return mTable->getPackage();
|
||||
}
|
||||
|
||||
inline const android::ResTable& ResourceTableResolver::getResTable() const {
|
||||
return mSources->getResources(false);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_RESOURCE_TABLE_RESOLVER_H
|
||||
@@ -76,6 +76,11 @@ const std::u16string& ScopedXmlPullParser::getNamespaceUri() const {
|
||||
return mParser->getNamespaceUri();
|
||||
}
|
||||
|
||||
bool ScopedXmlPullParser::applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const {
|
||||
return mParser->applyPackageAlias(package, defaultPackage);
|
||||
}
|
||||
|
||||
const std::u16string& ScopedXmlPullParser::getElementNamespace() const {
|
||||
return mParser->getElementNamespace();
|
||||
}
|
||||
|
||||
@@ -52,25 +52,27 @@ public:
|
||||
ScopedXmlPullParser& operator=(const ScopedXmlPullParser&) = delete;
|
||||
~ScopedXmlPullParser();
|
||||
|
||||
Event getEvent() const;
|
||||
const std::string& getLastError() const;
|
||||
Event next();
|
||||
Event getEvent() const override;
|
||||
const std::string& getLastError() const override;
|
||||
Event next() override;
|
||||
|
||||
const std::u16string& getComment() const;
|
||||
size_t getLineNumber() const;
|
||||
size_t getDepth() const;
|
||||
const std::u16string& getComment() const override;
|
||||
size_t getLineNumber() const override;
|
||||
size_t getDepth() const override;
|
||||
|
||||
const std::u16string& getText() const;
|
||||
const std::u16string& getText() const override;
|
||||
|
||||
const std::u16string& getNamespacePrefix() const;
|
||||
const std::u16string& getNamespaceUri() const;
|
||||
const std::u16string& getNamespacePrefix() const override;
|
||||
const std::u16string& getNamespaceUri() const override;
|
||||
bool applyPackageAlias(std::u16string* package, const std::u16string& defaultPackage)
|
||||
const override;
|
||||
|
||||
const std::u16string& getElementNamespace() const;
|
||||
const std::u16string& getElementName() const;
|
||||
const std::u16string& getElementNamespace() const override;
|
||||
const std::u16string& getElementName() const override;
|
||||
|
||||
const_iterator beginAttributes() const;
|
||||
const_iterator endAttributes() const;
|
||||
size_t getAttributeCount() const;
|
||||
const_iterator beginAttributes() const override;
|
||||
const_iterator endAttributes() const override;
|
||||
size_t getAttributeCount() const override;
|
||||
|
||||
private:
|
||||
XmlPullParser* mParser;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "Maybe.h"
|
||||
#include "SourceXmlPullParser.h"
|
||||
#include "Util.h"
|
||||
|
||||
@@ -66,7 +67,25 @@ SourceXmlPullParser::Event SourceXmlPullParser::next() {
|
||||
}
|
||||
}
|
||||
|
||||
return getEvent();
|
||||
Event event = getEvent();
|
||||
|
||||
// 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());
|
||||
if (event == Event::kStartNamespace) {
|
||||
if (result) {
|
||||
mPackageAliases.emplace_back(getNamespacePrefix(), result.value());
|
||||
}
|
||||
} else {
|
||||
if (result) {
|
||||
assert(mPackageAliases.back().second == result.value());
|
||||
mPackageAliases.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
SourceXmlPullParser::Event SourceXmlPullParser::getEvent() const {
|
||||
@@ -112,6 +131,22 @@ const std::u16string& SourceXmlPullParser::getNamespaceUri() const {
|
||||
return mEventQueue.front().data2;
|
||||
}
|
||||
|
||||
bool SourceXmlPullParser::applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const {
|
||||
const auto endIter = mPackageAliases.rend();
|
||||
for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
|
||||
if (iter->first == *package) {
|
||||
if (iter->second.empty()) {
|
||||
*package = defaultPackage;
|
||||
} else {
|
||||
*package = iter->second;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::u16string& SourceXmlPullParser::getElementNamespace() const {
|
||||
const Event currentEvent = getEvent();
|
||||
if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef AAPT_SOURCE_XML_PULL_PARSER_H
|
||||
#define AAPT_SOURCE_XML_PULL_PARSER_H
|
||||
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include <istream>
|
||||
#include <libexpat/expat.h>
|
||||
#include <queue>
|
||||
@@ -24,8 +26,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
class SourceXmlPullParser : public XmlPullParser {
|
||||
@@ -34,25 +34,28 @@ public:
|
||||
SourceXmlPullParser(const SourceXmlPullParser& rhs) = delete;
|
||||
~SourceXmlPullParser();
|
||||
|
||||
Event getEvent() const;
|
||||
const std::string& getLastError() const;
|
||||
Event next();
|
||||
Event getEvent() const override;
|
||||
const std::string& getLastError() const override ;
|
||||
Event next() override ;
|
||||
|
||||
const std::u16string& getComment() const;
|
||||
size_t getLineNumber() const;
|
||||
size_t getDepth() const;
|
||||
const std::u16string& getComment() const override;
|
||||
size_t getLineNumber() const override;
|
||||
size_t getDepth() const override;
|
||||
|
||||
const std::u16string& getText() const;
|
||||
const std::u16string& getText() const override;
|
||||
|
||||
const std::u16string& getNamespacePrefix() const;
|
||||
const std::u16string& getNamespaceUri() const;
|
||||
const std::u16string& getNamespacePrefix() const override;
|
||||
const std::u16string& getNamespaceUri() const override;
|
||||
bool applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const override;
|
||||
|
||||
const std::u16string& getElementNamespace() const;
|
||||
const std::u16string& getElementName() const;
|
||||
|
||||
const_iterator beginAttributes() const;
|
||||
const_iterator endAttributes() const;
|
||||
size_t getAttributeCount() const;
|
||||
const std::u16string& getElementNamespace() const override;
|
||||
const std::u16string& getElementName() const override;
|
||||
|
||||
const_iterator beginAttributes() const override;
|
||||
const_iterator endAttributes() const override;
|
||||
size_t getAttributeCount() const override;
|
||||
|
||||
private:
|
||||
static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
|
||||
@@ -80,6 +83,7 @@ private:
|
||||
const std::u16string mEmpty;
|
||||
size_t mDepth;
|
||||
std::stack<std::u16string> mNamespaceUris;
|
||||
std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -265,25 +265,38 @@ void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp
|
||||
);
|
||||
}
|
||||
|
||||
static uint8_t* encodeLength(uint8_t* data, size_t length) {
|
||||
if (length > 0x7fu) {
|
||||
*data++ = 0x80u | (0x000000ffu & (length >> 8));
|
||||
template <typename T>
|
||||
static T* encodeLength(T* data, size_t length) {
|
||||
static_assert(std::is_integral<T>::value, "wat.");
|
||||
|
||||
constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
|
||||
constexpr size_t kMaxSize = kMask - 1;
|
||||
if (length > kMaxSize) {
|
||||
*data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
|
||||
}
|
||||
*data++ = 0x000000ffu & length;
|
||||
*data++ = length;
|
||||
return data;
|
||||
}
|
||||
|
||||
static size_t encodedLengthByteCount(size_t length) {
|
||||
return length > 0x7fu ? 2 : 1;
|
||||
template <typename T>
|
||||
static size_t encodedLengthUnits(size_t length) {
|
||||
static_assert(std::is_integral<T>::value, "wat.");
|
||||
|
||||
constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
|
||||
constexpr size_t kMaxSize = kMask - 1;
|
||||
return length > kMaxSize ? 2 : 1;
|
||||
}
|
||||
|
||||
bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
|
||||
|
||||
bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
|
||||
const size_t startIndex = out->size();
|
||||
android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
|
||||
header->header.type = android::RES_STRING_POOL_TYPE;
|
||||
header->header.headerSize = sizeof(*header);
|
||||
header->stringCount = pool.size();
|
||||
header->flags |= android::ResStringPool_header::UTF8_FLAG;
|
||||
if (utf8) {
|
||||
header->flags |= android::ResStringPool_header::UTF8_FLAG;
|
||||
}
|
||||
|
||||
uint32_t* indices = pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
|
||||
|
||||
@@ -300,25 +313,31 @@ bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
|
||||
*indices = out->size() - beforeStringsIndex;
|
||||
indices++;
|
||||
|
||||
std::string encoded = util::utf16ToUtf8(entry->value);
|
||||
if (utf8) {
|
||||
std::string encoded = util::utf16ToUtf8(entry->value);
|
||||
|
||||
const size_t stringByteLength = sizeof(char) * encoded.length();
|
||||
const size_t totalSize = encodedLengthByteCount(entry->value.size())
|
||||
+ encodedLengthByteCount(encoded.length())
|
||||
+ stringByteLength
|
||||
+ sizeof(char);
|
||||
const size_t totalSize = encodedLengthUnits<char>(entry->value.size())
|
||||
+ encodedLengthUnits<char>(encoded.length())
|
||||
+ encoded.size() + 1;
|
||||
|
||||
uint8_t* data = out->nextBlock<uint8_t>(totalSize);
|
||||
char* data = out->nextBlock<char>(totalSize);
|
||||
|
||||
// First encode the actual UTF16 string length.
|
||||
data = encodeLength(data, entry->value.size());
|
||||
// First encode the actual UTF16 string length.
|
||||
data = encodeLength(data, entry->value.size());
|
||||
|
||||
// Now encode the size of the converted UTF8 string.
|
||||
data = encodeLength(data, encoded.length());
|
||||
// Now encode the size of the converted UTF8 string.
|
||||
data = encodeLength(data, encoded.length());
|
||||
strncpy(data, encoded.data(), encoded.size());
|
||||
} else {
|
||||
const size_t totalSize = encodedLengthUnits<char16_t>(entry->value.size())
|
||||
+ entry->value.size() + 1;
|
||||
|
||||
memcpy(data, encoded.data(), stringByteLength);
|
||||
data += stringByteLength;
|
||||
*data = 0;
|
||||
char16_t* data = out->nextBlock<char16_t>(totalSize);
|
||||
|
||||
// Encode the actual UTF16 string length.
|
||||
data = encodeLength(data, entry->value.size());
|
||||
strncpy16(data, entry->value.data(), entry->value.size());
|
||||
}
|
||||
}
|
||||
|
||||
out->align4();
|
||||
@@ -364,4 +383,12 @@ bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
|
||||
return flatten(out, pool, true);
|
||||
}
|
||||
|
||||
bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
|
||||
return flatten(out, pool, false);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
|
||||
|
||||
static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
|
||||
static bool flatten(BigBuffer* out, const StringPool& pool);
|
||||
static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
|
||||
|
||||
StringPool() = default;
|
||||
StringPool(const StringPool&) = delete;
|
||||
@@ -193,6 +193,8 @@ private:
|
||||
friend const_iterator begin(const StringPool& pool);
|
||||
friend const_iterator end(const StringPool& pool);
|
||||
|
||||
static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
|
||||
|
||||
Ref makeRefImpl(const StringPiece16& str, const Context& context, bool unique);
|
||||
|
||||
std::vector<std::unique_ptr<Entry>> mStrings;
|
||||
|
||||
@@ -211,6 +211,7 @@ struct ValueFlattener : ConstValueVisitor {
|
||||
|
||||
virtual void visitItem(const Item& item, ValueVisitorArgs&) override {
|
||||
result = item.flatten(*mOutValue);
|
||||
mOutValue->res0 = 0;
|
||||
mOutValue->size = sizeof(*mOutValue);
|
||||
}
|
||||
|
||||
@@ -508,9 +509,9 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
|
||||
package->name[table.getPackage().length()] = 0;
|
||||
|
||||
package->typeStrings = package->header.headerSize;
|
||||
StringPool::flattenUtf8(out, typePool);
|
||||
StringPool::flattenUtf16(out, typePool);
|
||||
package->keyStrings = out->size() - beforePackageIndex;
|
||||
StringPool::flattenUtf8(out, keyPool);
|
||||
StringPool::flattenUtf16(out, keyPool);
|
||||
|
||||
if (symbolEntryData != nullptr) {
|
||||
for (size_t i = 0; i < symbolEntries.size(); i++) {
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
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;
|
||||
@@ -279,5 +282,17 @@ 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 {};
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace aapt
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define AAPT_UTIL_H
|
||||
|
||||
#include "BigBuffer.h"
|
||||
#include "Maybe.h"
|
||||
#include "StringPiece.h"
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
@@ -277,6 +278,15 @@ inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) :
|
||||
mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
} // namespace util
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,6 +85,11 @@ const std::u16string& XliffXmlPullParser::getNamespaceUri() const {
|
||||
return mParser->getNamespaceUri();
|
||||
}
|
||||
|
||||
bool XliffXmlPullParser::applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const {
|
||||
return mParser->applyPackageAlias(package, defaultPackage);
|
||||
}
|
||||
|
||||
const std::u16string& XliffXmlPullParser::getElementNamespace() const {
|
||||
return mParser->getElementNamespace();
|
||||
}
|
||||
|
||||
@@ -33,25 +33,27 @@ public:
|
||||
XliffXmlPullParser(const std::shared_ptr<XmlPullParser>& parser);
|
||||
XliffXmlPullParser(const XliffXmlPullParser& rhs) = delete;
|
||||
|
||||
Event getEvent() const;
|
||||
const std::string& getLastError() const;
|
||||
Event next();
|
||||
Event getEvent() const override;
|
||||
const std::string& getLastError() const override;
|
||||
Event next() override;
|
||||
|
||||
const std::u16string& getComment() const;
|
||||
size_t getLineNumber() const;
|
||||
size_t getDepth() const;
|
||||
const std::u16string& getComment() const override;
|
||||
size_t getLineNumber() const override;
|
||||
size_t getDepth() const override;
|
||||
|
||||
const std::u16string& getText() const;
|
||||
const std::u16string& getText() const override;
|
||||
|
||||
const std::u16string& getNamespacePrefix() const;
|
||||
const std::u16string& getNamespaceUri() const;
|
||||
const std::u16string& getNamespacePrefix() const override;
|
||||
const std::u16string& getNamespaceUri() const override;
|
||||
bool applyPackageAlias(std::u16string* package, const std::u16string& defaultPackage)
|
||||
const override;
|
||||
|
||||
const std::u16string& getElementNamespace() const;
|
||||
const std::u16string& getElementName() const;
|
||||
const std::u16string& getElementNamespace() const override;
|
||||
const std::u16string& getElementName() const override;
|
||||
|
||||
const_iterator beginAttributes() const;
|
||||
const_iterator endAttributes() const;
|
||||
size_t getAttributeCount() const;
|
||||
const_iterator beginAttributes() const override;
|
||||
const_iterator endAttributes() const override;
|
||||
size_t getAttributeCount() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<XmlPullParser> mParser;
|
||||
|
||||
@@ -36,56 +36,67 @@
|
||||
namespace aapt {
|
||||
|
||||
constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
|
||||
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/";
|
||||
|
||||
struct AttributeValueFlattener : ValueVisitor {
|
||||
struct Args : ValueVisitorArgs {
|
||||
Args(std::shared_ptr<Resolver> r, SourceLogger& s, android::Res_value& oV,
|
||||
std::shared_ptr<XmlPullParser> p, bool& e, StringPool::Ref& rV,
|
||||
std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>& sR) :
|
||||
resolver(r), logger(s), outValue(oV), parser(p), error(e), rawValue(rV),
|
||||
stringRefs(sR) {
|
||||
AttributeValueFlattener(
|
||||
std::shared_ptr<IResolver> resolver, SourceLogger* logger,
|
||||
android::Res_value* outValue, std::shared_ptr<XmlPullParser> parser, bool* outError,
|
||||
StringPool::Ref rawValue, std::u16string* defaultPackage,
|
||||
std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>* outStringRefs) :
|
||||
mResolver(resolver), mLogger(logger), mOutValue(outValue), mParser(parser),
|
||||
mError(outError), mRawValue(rawValue), mDefaultPackage(defaultPackage),
|
||||
mStringRefs(outStringRefs) {
|
||||
}
|
||||
|
||||
void visit(Reference& reference, ValueVisitorArgs&) override {
|
||||
// First see if we can convert the package name from a prefix to a real
|
||||
// package name.
|
||||
ResourceName aliasedName = reference.name;
|
||||
|
||||
if (!reference.name.package.empty()) {
|
||||
// Only if we specified a package do we look for its alias.
|
||||
mParser->applyPackageAlias(&reference.name.package, *mDefaultPackage);
|
||||
} else {
|
||||
reference.name.package = *mDefaultPackage;
|
||||
}
|
||||
|
||||
std::shared_ptr<Resolver> resolver;
|
||||
SourceLogger& logger;
|
||||
android::Res_value& outValue;
|
||||
std::shared_ptr<XmlPullParser> parser;
|
||||
bool& error;
|
||||
StringPool::Ref& rawValue;
|
||||
std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>& stringRefs;
|
||||
};
|
||||
|
||||
void visit(Reference& reference, ValueVisitorArgs& a) override {
|
||||
Args& args = static_cast<Args&>(a);
|
||||
|
||||
Maybe<ResourceId> result = args.resolver->findId(reference.name);
|
||||
Maybe<ResourceId> result = mResolver->findId(reference.name);
|
||||
if (!result || !result.value().isValid()) {
|
||||
args.logger.error(args.parser->getLineNumber())
|
||||
std::ostream& out = mLogger->error(mParser->getLineNumber())
|
||||
<< "unresolved reference '"
|
||||
<< reference.name
|
||||
<< "'."
|
||||
<< std::endl;
|
||||
args.error = true;
|
||||
<< aliasedName
|
||||
<< "'";
|
||||
if (aliasedName != reference.name) {
|
||||
out << " (aka '" << reference.name << "')";
|
||||
}
|
||||
out << "'." << std::endl;
|
||||
*mError = true;
|
||||
} else {
|
||||
reference.id = result.value();
|
||||
reference.flatten(args.outValue);
|
||||
reference.flatten(*mOutValue);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(String& string, ValueVisitorArgs& a) override {
|
||||
Args& args = static_cast<Args&>(a);
|
||||
|
||||
args.outValue.dataType = android::Res_value::TYPE_STRING;
|
||||
args.stringRefs.emplace_back(args.rawValue,
|
||||
reinterpret_cast<android::ResStringPool_ref*>(&args.outValue.data));
|
||||
void visit(String& string, ValueVisitorArgs&) override {
|
||||
mOutValue->dataType = android::Res_value::TYPE_STRING;
|
||||
mStringRefs->emplace_back(
|
||||
mRawValue,
|
||||
reinterpret_cast<android::ResStringPool_ref*>(mOutValue->data));
|
||||
}
|
||||
|
||||
void visitItem(Item& item, ValueVisitorArgs& a) override {
|
||||
Args& args = static_cast<Args&>(a);
|
||||
item.flatten(args.outValue);
|
||||
void visitItem(Item& item, ValueVisitorArgs&) override {
|
||||
item.flatten(*mOutValue);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<IResolver> mResolver;
|
||||
SourceLogger* mLogger;
|
||||
android::Res_value* mOutValue;
|
||||
std::shared_ptr<XmlPullParser> mParser;
|
||||
bool* mError;
|
||||
StringPool::Ref mRawValue;
|
||||
std::u16string* mDefaultPackage;
|
||||
std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>* mStringRefs;
|
||||
};
|
||||
|
||||
struct XmlAttribute {
|
||||
@@ -100,7 +111,7 @@ static bool lessAttributeId(const XmlAttribute& a, uint32_t id) {
|
||||
}
|
||||
|
||||
XmlFlattener::XmlFlattener(const std::shared_ptr<ResourceTable>& table,
|
||||
const std::shared_ptr<Resolver>& resolver) :
|
||||
const std::shared_ptr<IResolver>& resolver) :
|
||||
mTable(table), mResolver(resolver) {
|
||||
}
|
||||
|
||||
@@ -184,8 +195,13 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
|
||||
node->comment.index = -1;
|
||||
|
||||
android::ResXMLTree_attrExt* elem = out.nextBlock<android::ResXMLTree_attrExt>();
|
||||
stringRefs.emplace_back(
|
||||
pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
|
||||
if (!parser->getElementNamespace().empty()) {
|
||||
stringRefs.emplace_back(
|
||||
pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
|
||||
} else {
|
||||
// The device doesn't think a string of size 0 is the same as null.
|
||||
elem->ns.index = -1;
|
||||
}
|
||||
stringRefs.emplace_back(
|
||||
pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
|
||||
elem->attributeStart = sizeof(*elem);
|
||||
@@ -222,28 +238,25 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
|
||||
}
|
||||
|
||||
|
||||
StringPiece16 package;
|
||||
if (util::stringStartsWith<char16_t>(attrIter->namespaceUri, kSchemaPrefix)) {
|
||||
StringPiece16 schemaPrefix = kSchemaPrefix;
|
||||
package = attrIter->namespaceUri;
|
||||
package = package.substr(schemaPrefix.size(),
|
||||
package.size() - schemaPrefix.size());
|
||||
} else if (attrIter->namespaceUri == kSchemaAuto && mResolver) {
|
||||
package = mResolver->getDefaultPackage();
|
||||
}
|
||||
|
||||
if (package.empty() || !mResolver) {
|
||||
Maybe<std::u16string> package = util::extractPackageFromNamespace(
|
||||
attrIter->namespaceUri);
|
||||
if (!package || !mResolver) {
|
||||
// Attributes that have no resource ID (because they don't belong to a
|
||||
// package) should appear after those that do have resource IDs. Assign
|
||||
// them some/ integer value that will appear after.
|
||||
// them some integer value that will appear after.
|
||||
id = 0x80000000u | nextAttributeId++;
|
||||
nameRef = pool.makeRef(attrIter->name, StringPool::Context{ id });
|
||||
|
||||
} else {
|
||||
// Find the Attribute object via our Resolver.
|
||||
ResourceName attrName = {
|
||||
package.toString(), ResourceType::kAttr, attrIter->name };
|
||||
Maybe<Resolver::Entry> result = mResolver->findAttribute(attrName);
|
||||
package.value(), ResourceType::kAttr, attrIter->name };
|
||||
|
||||
if (attrName.package.empty()) {
|
||||
attrName.package = options.defaultPackage;
|
||||
}
|
||||
|
||||
Maybe<IResolver::Entry> result = mResolver->findAttribute(attrName);
|
||||
if (!result || !result.value().id.isValid()) {
|
||||
logger.error(parser->getLineNumber())
|
||||
<< "unresolved attribute '"
|
||||
@@ -269,7 +282,7 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
|
||||
|
||||
// Put the attribute name into a package specific pool, since we don't
|
||||
// want to collapse names from different packages.
|
||||
nameRef = packagePools[package.toString()].makeRef(
|
||||
nameRef = packagePools[package.value()].makeRef(
|
||||
attrIter->name, StringPool::Context{ id });
|
||||
}
|
||||
|
||||
@@ -290,26 +303,32 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
|
||||
for (auto entry : sortedAttributes) {
|
||||
android::ResXMLTree_attribute* attr =
|
||||
out.nextBlock<android::ResXMLTree_attribute>();
|
||||
stringRefs.emplace_back(
|
||||
pool.makeRef(entry.xmlAttr->namespaceUri, lowPriority), &attr->ns);
|
||||
StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
|
||||
stringRefs.emplace_back(rawValueRef, &attr->rawValue);
|
||||
if (!entry.xmlAttr->namespaceUri.empty()) {
|
||||
stringRefs.emplace_back(
|
||||
pool.makeRef(entry.xmlAttr->namespaceUri, lowPriority), &attr->ns);
|
||||
} else {
|
||||
attr->ns.index = -1;
|
||||
}
|
||||
|
||||
stringRefs.emplace_back(entry.nameRef, &attr->name);
|
||||
attr->rawValue.index = -1;
|
||||
|
||||
StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
|
||||
|
||||
if (entry.attr) {
|
||||
std::unique_ptr<Item> value = ResourceParser::parseItemForAttribute(
|
||||
entry.xmlAttr->value, *entry.attr, mResolver->getDefaultPackage());
|
||||
entry.xmlAttr->value, *entry.attr);
|
||||
if (value) {
|
||||
AttributeValueFlattener flattener;
|
||||
value->accept(flattener, AttributeValueFlattener::Args{
|
||||
AttributeValueFlattener flattener(
|
||||
mResolver,
|
||||
logger,
|
||||
attr->typedValue,
|
||||
&logger,
|
||||
&attr->typedValue,
|
||||
parser,
|
||||
error,
|
||||
&error,
|
||||
rawValueRef,
|
||||
stringRefs
|
||||
});
|
||||
&options.defaultPackage,
|
||||
&stringRefs);
|
||||
value->accept(flattener, {});
|
||||
} else if (!(entry.attr->typeMask & android::ResTable_map::TYPE_STRING)) {
|
||||
logger.error(parser->getLineNumber())
|
||||
<< "'"
|
||||
@@ -321,12 +340,14 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
|
||||
error = true;
|
||||
} else {
|
||||
attr->typedValue.dataType = android::Res_value::TYPE_STRING;
|
||||
stringRefs.emplace_back(rawValueRef, &attr->rawValue);
|
||||
stringRefs.emplace_back(rawValueRef,
|
||||
reinterpret_cast<android::ResStringPool_ref*>(
|
||||
&attr->typedValue.data));
|
||||
}
|
||||
} else {
|
||||
attr->typedValue.dataType = android::Res_value::TYPE_STRING;
|
||||
stringRefs.emplace_back(rawValueRef, &attr->rawValue);
|
||||
stringRefs.emplace_back(rawValueRef,
|
||||
reinterpret_cast<android::ResStringPool_ref*>(
|
||||
&attr->typedValue.data));
|
||||
@@ -438,7 +459,7 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
|
||||
resIdMapChunk->size = outBuffer->size() - beforeResIdMapIndex;
|
||||
|
||||
// Flatten the StringPool.
|
||||
StringPool::flattenUtf8(outBuffer, pool);
|
||||
StringPool::flattenUtf16(outBuffer, pool);
|
||||
|
||||
// Move the temporary BigBuffer into outBuffer->
|
||||
outBuffer->appendBuffer(std::move(out));
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "Source.h"
|
||||
#include "XmlPullParser.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
/**
|
||||
@@ -33,6 +35,12 @@ namespace aapt {
|
||||
class XmlFlattener {
|
||||
public:
|
||||
struct Options {
|
||||
/**
|
||||
* The package to use when a reference has no package specified
|
||||
* (or a namespace URI equals "http://schemas.android.com/apk/res-auto").
|
||||
*/
|
||||
std::u16string defaultPackage;
|
||||
|
||||
/**
|
||||
* If set, tells the XmlFlattener to strip out
|
||||
* attributes that have been introduced after
|
||||
@@ -46,7 +54,7 @@ public:
|
||||
* and attributes.
|
||||
*/
|
||||
XmlFlattener(const std::shared_ptr<ResourceTable>& table,
|
||||
const std::shared_ptr<Resolver>& resolver);
|
||||
const std::shared_ptr<IResolver>& resolver);
|
||||
|
||||
XmlFlattener(const XmlFlattener&) = delete; // Not copyable.
|
||||
|
||||
@@ -62,7 +70,7 @@ public:
|
||||
|
||||
private:
|
||||
std::shared_ptr<ResourceTable> mTable;
|
||||
std::shared_ptr<Resolver> mResolver;
|
||||
std::shared_ptr<IResolver> mResolver;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -33,30 +33,76 @@ namespace aapt {
|
||||
|
||||
constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||
|
||||
struct MockResolver : public IResolver {
|
||||
MockResolver(const StringPiece16& defaultPackage,
|
||||
const std::map<ResourceName, ResourceId>& items) :
|
||||
mPackage(defaultPackage.toString()), mAttr(false, ResTable_map::TYPE_ANY),
|
||||
mItems(items) {
|
||||
}
|
||||
|
||||
virtual const std::u16string& getDefaultPackage() const override {
|
||||
return mPackage;
|
||||
}
|
||||
|
||||
virtual Maybe<ResourceId> findId(const ResourceName& name) override {
|
||||
const auto iter = mItems.find(name);
|
||||
if (iter != mItems.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual Maybe<Entry> findAttribute(const ResourceName& name) override {
|
||||
Maybe<ResourceId> result = findId(name);
|
||||
if (result) {
|
||||
if (name.type == ResourceType::kAttr) {
|
||||
return Entry{ result.value(), &mAttr };
|
||||
} else {
|
||||
return Entry{ result.value() };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual Maybe<ResourceName> findName(ResourceId resId) override {
|
||||
for (auto& p : mItems) {
|
||||
if (p.second == resId) {
|
||||
return p.first;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::u16string mPackage;
|
||||
Attribute mAttr;
|
||||
std::map<ResourceName, ResourceId> mItems;
|
||||
};
|
||||
|
||||
class XmlFlattenerTest : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
|
||||
table->setPackage(u"android");
|
||||
table->setPackageId(0x01);
|
||||
std::shared_ptr<IResolver> resolver = std::make_shared<MockResolver>(u"android",
|
||||
std::map<ResourceName, ResourceId>({
|
||||
{ ResourceName{ u"android", ResourceType::kAttr, u"attr" },
|
||||
ResourceId{ 0x01010000u } },
|
||||
{ ResourceName{ u"android", ResourceType::kId, u"id" },
|
||||
ResourceId{ 0x01020000u } },
|
||||
{ ResourceName{ u"com.lib", ResourceType::kAttr, u"attr" },
|
||||
ResourceId{ 0x01010001u } },
|
||||
{ ResourceName{ u"com.lib", ResourceType::kId, u"id" },
|
||||
ResourceId{ 0x01020001u } }}));
|
||||
|
||||
table->addResource(ResourceName{ {}, ResourceType::kAttr, u"id" },
|
||||
ResourceId{ 0x01010000 }, {}, {},
|
||||
util::make_unique<Attribute>(false, ResTable_map::TYPE_ANY));
|
||||
|
||||
table->addResource(ResourceName{ {}, ResourceType::kId, u"test" },
|
||||
ResourceId{ 0x01020000 }, {}, {}, util::make_unique<Id>());
|
||||
|
||||
mFlattener = std::make_shared<XmlFlattener>(nullptr,
|
||||
std::make_shared<Resolver>(table, std::make_shared<AssetManager>()));
|
||||
mFlattener = std::make_shared<XmlFlattener>(nullptr, resolver);
|
||||
}
|
||||
|
||||
::testing::AssertionResult testFlatten(std::istream& in, ResXMLTree* outTree) {
|
||||
::testing::AssertionResult testFlatten(const std::string& in, ResXMLTree* outTree) {
|
||||
std::stringstream input(kXmlPreamble);
|
||||
input << in.rdbuf() << std::endl;
|
||||
input << in << std::endl;
|
||||
std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
|
||||
BigBuffer outBuffer(1024);
|
||||
if (!mFlattener->flatten(Source{ "test" }, xmlParser, &outBuffer, {})) {
|
||||
XmlFlattener::Options xmlOptions;
|
||||
xmlOptions.defaultPackage = u"android";
|
||||
if (!mFlattener->flatten(Source{ "test" }, xmlParser, &outBuffer, xmlOptions)) {
|
||||
return ::testing::AssertionFailure();
|
||||
}
|
||||
|
||||
@@ -71,11 +117,9 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(XmlFlattenerTest, ParseSimpleView) {
|
||||
std::stringstream input;
|
||||
input << "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"" << std::endl
|
||||
<< " android:id=\"@id/test\">" << std::endl
|
||||
<< "</View>" << std::endl;
|
||||
|
||||
std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
|
||||
" android:attr=\"@id/id\">\n"
|
||||
"</View>";
|
||||
ResXMLTree tree;
|
||||
ASSERT_TRUE(testFlatten(input, &tree));
|
||||
|
||||
@@ -84,4 +128,113 @@ TEST_F(XmlFlattenerTest, ParseSimpleView) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, ParseViewWithPackageAlias) {
|
||||
std::string input = "<View xmlns:ns1=\"http://schemas.android.com/apk/res/android\"\n"
|
||||
" xmlns:ns2=\"http://schemas.android.com/apk/res/android\"\n"
|
||||
" ns1:attr=\"@ns2:id/id\">\n"
|
||||
"</View>";
|
||||
ResXMLTree tree;
|
||||
ASSERT_TRUE(testFlatten(input, &tree));
|
||||
|
||||
while (tree.next() != ResXMLTree::END_DOCUMENT) {
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
|
||||
}
|
||||
}
|
||||
|
||||
::testing::AssertionResult attributeNameAndValueEquals(ResXMLTree* tree, size_t index,
|
||||
ResourceId nameId, ResourceId valueId) {
|
||||
if (index >= tree->getAttributeCount()) {
|
||||
return ::testing::AssertionFailure() << "index " << index << " is out of bounds ("
|
||||
<< tree->getAttributeCount() << ")";
|
||||
}
|
||||
|
||||
if (tree->getAttributeNameResID(index) != nameId.id) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "attribute at index " << index << " has ID "
|
||||
<< ResourceId{ (uint32_t) tree->getAttributeNameResID(index) }
|
||||
<< ". Expected ID " << nameId;
|
||||
}
|
||||
|
||||
if (tree->getAttributeDataType(index) != Res_value::TYPE_REFERENCE) {
|
||||
return ::testing::AssertionFailure() << "attribute at index " << index << " has value of "
|
||||
<< "type " << std::hex
|
||||
<< tree->getAttributeDataType(index) << std::dec
|
||||
<< ". Expected reference (" << std::hex
|
||||
<< Res_value::TYPE_REFERENCE << std::dec << ")";
|
||||
}
|
||||
|
||||
if ((uint32_t) tree->getAttributeData(index) != valueId.id) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "attribute at index " << index << " has value " << "with ID "
|
||||
<< ResourceId{ (uint32_t) tree->getAttributeData(index) }
|
||||
<< ". Expected ID " << valueId;
|
||||
}
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, ParseViewWithShadowedPackageAlias) {
|
||||
std::string input = "<View xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
|
||||
" app:attr=\"@app:id/id\">\n"
|
||||
" <View xmlns:app=\"http://schemas.android.com/apk/res/com.lib\"\n"
|
||||
" app:attr=\"@app:id/id\"/>\n"
|
||||
"</View>";
|
||||
ResXMLTree tree;
|
||||
ASSERT_TRUE(testFlatten(input, &tree));
|
||||
|
||||
while (tree.next() != ResXMLTree::START_TAG) {
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010000u },
|
||||
ResourceId{ 0x01020000u }));
|
||||
|
||||
while (tree.next() != ResXMLTree::START_TAG) {
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u },
|
||||
ResourceId{ 0x01020001u }));
|
||||
}
|
||||
|
||||
TEST_F(XmlFlattenerTest, ParseViewWithLocalPackageAndAliasOfTheSameName) {
|
||||
std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/com.lib\"\n"
|
||||
" android:attr=\"@id/id\"/>";
|
||||
ResXMLTree tree;
|
||||
ASSERT_TRUE(testFlatten(input, &tree));
|
||||
|
||||
while (tree.next() != ResXMLTree::START_TAG) {
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
|
||||
}
|
||||
|
||||
// We expect the 'android:attr' to be converted to 'com.lib:attr' due to the namespace
|
||||
// assignment.
|
||||
// However, we didn't give '@id/id' a package, so it should use the default package
|
||||
// 'android', and not be converted from 'android' to 'com.lib'.
|
||||
ASSERT_TRUE(attributeNameAndValueEquals(&tree, 0u, ResourceId{ 0x01010001u },
|
||||
ResourceId{ 0x01020000u }));
|
||||
}
|
||||
|
||||
/*
|
||||
* The device ResXMLParser in libandroidfw differentiates between empty namespace and null
|
||||
* namespace.
|
||||
*/
|
||||
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
|
||||
std::string input = "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
|
||||
" package=\"android\"/>";
|
||||
|
||||
ResXMLTree tree;
|
||||
ASSERT_TRUE(testFlatten(input, &tree));
|
||||
|
||||
while (tree.next() != ResXMLTree::START_TAG) {
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::BAD_DOCUMENT);
|
||||
ASSERT_NE(tree.getEventType(), ResXMLTree::END_DOCUMENT);
|
||||
}
|
||||
|
||||
const StringPiece16 kPackage = u"package";
|
||||
EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -72,16 +72,27 @@ public:
|
||||
*/
|
||||
virtual const std::u16string& getText() const = 0;
|
||||
|
||||
/**
|
||||
* Namespace prefix is available for StartNamespace and EndNamespace.
|
||||
*/
|
||||
virtual const std::u16string& getNamespacePrefix() const = 0;
|
||||
//
|
||||
// Namespace prefix and URI are available for StartNamespace and EndNamespace.
|
||||
//
|
||||
|
||||
/**
|
||||
* Namespace URI is available for StartNamespace.
|
||||
*/
|
||||
virtual const std::u16string& getNamespacePrefix() const = 0;
|
||||
virtual const std::u16string& getNamespaceUri() const = 0;
|
||||
|
||||
/*
|
||||
* Uses the current stack of namespaces to resolve the package. Eg:
|
||||
* xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
|
||||
* ...
|
||||
* android:text="@app:string/message"
|
||||
*
|
||||
* In this case, 'app' will be converted to 'com.android.app'.
|
||||
*
|
||||
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
|
||||
* 'package' will be set to 'defaultPackage'.
|
||||
*/
|
||||
virtual bool applyPackageAlias(std::u16string* package,
|
||||
const std::u16string& defaultPackage) const = 0;
|
||||
|
||||
//
|
||||
// These are available for StartElement and EndElement.
|
||||
//
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
<style name="Platform.AppCompat" parent="@android:style/Theme">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<bool name="allow">true</bool>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
|
||||
android:id="@+id/view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -11,7 +12,7 @@
|
||||
android:layout_width="1dp"
|
||||
android:text="@{user.name}"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_width="false"
|
||||
app:layout_width="@support:bool/allow"
|
||||
app:flags="complex|weak"
|
||||
android:colorAccent="#ffffff"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
|
||||
<style name="App" parent="android.appcompat:Platform.AppCompat">
|
||||
<item name="android:background">@color/primary</item>
|
||||
<item name="android:colorPrimary">@color/primary</item>
|
||||
@@ -9,6 +9,7 @@
|
||||
<attr name="custom" format="reference" />
|
||||
<style name="Pop">
|
||||
<item name="custom">@drawable/image</item>
|
||||
<item name="android:focusable">@lib:bool/allow</item>
|
||||
</style>
|
||||
<string name="yo">@string/wow</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user