AAPT2: Separate out the various steps
An early refactor. Some ideas became clearer as
development continued. Now the various phases are much
clearer and more easily reusable.
Also added a ton of tests!
Change-Id: Ic8f0a70c8222370352e63533b329c40457c0903e
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
new file mode 100644
index 0000000..147b9bf
--- /dev/null
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Diagnostics.h"
+#include "ResourceUtils.h"
+#include "SdkConstants.h"
+#include "XmlDom.h"
+
+#include "link/Linkers.h"
+#include "link/ReferenceLinkerVisitor.h"
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+#include "util/Util.h"
+
+namespace aapt {
+
+namespace {
+
+class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor {
+private:
+ IAaptContext* mContext;
+ ISymbolTable* mSymbols;
+ std::set<int>* mSdkLevelsFound;
+ ReferenceLinkerVisitor mReferenceLinkerVisitor;
+ bool mError = false;
+
+public:
+ using xml::PackageAwareVisitor::visit;
+
+ XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
+ std::set<int>* sdkLevelsFound) :
+ mContext(context), mSymbols(symbols), mSdkLevelsFound(sdkLevelsFound),
+ mReferenceLinkerVisitor(context, symbols, this) {
+ }
+
+ void visit(xml::Element* el) override {
+ for (xml::Attribute& attr : el->attributes) {
+ Maybe<std::u16string> maybePackage =
+ util::extractPackageFromNamespace(attr.namespaceUri);
+ if (maybePackage) {
+ // There is a valid package name for this attribute. We will look this up.
+ StringPiece16 package = maybePackage.value();
+ if (package.empty()) {
+ // Empty package means the 'current' or 'local' package.
+ package = mContext->getCompilationPackage();
+ }
+
+ attr.compiledAttribute = compileAttribute(
+ ResourceName{ package.toString(), ResourceType::kAttr, attr.name });
+
+ // Convert the string value into a compiled Value if this is a valid attribute.
+ if (attr.compiledAttribute) {
+ // Record all SDK levels from which the attributes were defined.
+ const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
+ if (sdkLevel > 1) {
+ mSdkLevelsFound->insert(sdkLevel);
+ }
+
+ const Attribute* attribute = &attr.compiledAttribute.value().attribute;
+ attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value,
+ attribute);
+ if (!attr.compiledValue &&
+ !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
+ // We won't be able to encode this as a string.
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "'" << attr.value << "' "
+ << "is incompatible with attribute "
+ << package << ":" << attr.name << " " << *attribute);
+ mError = true;
+ }
+ } else {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "attribute '" << package << ":" << attr.name
+ << "' was not found");
+ mError = true;
+
+ }
+ } else {
+ // We still encode references.
+ attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
+ }
+
+ if (attr.compiledValue) {
+ // With a compiledValue, we must resolve the reference and assign it an ID.
+ attr.compiledValue->accept(&mReferenceLinkerVisitor);
+ }
+ }
+
+ // Call the super implementation.
+ xml::PackageAwareVisitor::visit(el);
+ }
+
+ Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) {
+ Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name);
+ if (const ISymbolTable::Symbol* symbol = mSymbols->findByName(
+ mangledName ? mangledName.value() : name)) {
+ if (symbol->attribute) {
+ return xml::AaptAttribute{ symbol->id, *symbol->attribute };
+ }
+ }
+ return {};
+ }
+
+ inline bool hasError() {
+ return mError || mReferenceLinkerVisitor.hasError();
+ }
+};
+
+} // namespace
+
+bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) {
+ mSdkLevelsFound.clear();
+ XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), &mSdkLevelsFound);
+ if (resource->root) {
+ resource->root->accept(&visitor);
+ return !visitor.hasError();
+ }
+ return false;
+}
+
+} // namespace aapt