blob: caab9b8a4684c1ad7fb2d72298b81acc75da6031 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Diagnostics.h"
18#include "ResourceUtils.h"
19#include "SdkConstants.h"
20#include "XmlDom.h"
21
22#include "link/Linkers.h"
23#include "link/ReferenceLinkerVisitor.h"
24#include "process/IResourceTableConsumer.h"
25#include "process/SymbolTable.h"
26#include "util/Util.h"
27
28namespace aapt {
29
30namespace {
31
32class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor {
33private:
34 IAaptContext* mContext;
35 ISymbolTable* mSymbols;
Adam Lesinskie352b992015-11-16 11:59:14 -080036 Source mSource;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037 std::set<int>* mSdkLevelsFound;
38 ReferenceLinkerVisitor mReferenceLinkerVisitor;
39 bool mError = false;
40
41public:
42 using xml::PackageAwareVisitor::visit;
43
Adam Lesinskie352b992015-11-16 11:59:14 -080044 XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source,
Adam Lesinski1ab598f2015-08-14 14:26:04 -070045 std::set<int>* sdkLevelsFound) :
Adam Lesinskie352b992015-11-16 11:59:14 -080046 mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
Adam Lesinski1ab598f2015-08-14 14:26:04 -070047 mReferenceLinkerVisitor(context, symbols, this) {
48 }
49
50 void visit(xml::Element* el) override {
Adam Lesinskie352b992015-11-16 11:59:14 -080051 const Source source = mSource.withLine(el->lineNumber);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070052 for (xml::Attribute& attr : el->attributes) {
53 Maybe<std::u16string> maybePackage =
54 util::extractPackageFromNamespace(attr.namespaceUri);
55 if (maybePackage) {
56 // There is a valid package name for this attribute. We will look this up.
57 StringPiece16 package = maybePackage.value();
58 if (package.empty()) {
59 // Empty package means the 'current' or 'local' package.
60 package = mContext->getCompilationPackage();
61 }
62
63 attr.compiledAttribute = compileAttribute(
64 ResourceName{ package.toString(), ResourceType::kAttr, attr.name });
65
66 // Convert the string value into a compiled Value if this is a valid attribute.
67 if (attr.compiledAttribute) {
68 // Record all SDK levels from which the attributes were defined.
69 const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
70 if (sdkLevel > 1) {
71 mSdkLevelsFound->insert(sdkLevel);
72 }
73
74 const Attribute* attribute = &attr.compiledAttribute.value().attribute;
75 attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value,
76 attribute);
77 if (!attr.compiledValue &&
78 !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
79 // We won't be able to encode this as a string.
80 mContext->getDiagnostics()->error(
Adam Lesinskie352b992015-11-16 11:59:14 -080081 DiagMessage(source) << "'" << attr.value << "' "
82 << "is incompatible with attribute "
83 << package << ":" << attr.name << " "
84 << *attribute);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070085 mError = true;
86 }
87 } else {
Adam Lesinskie352b992015-11-16 11:59:14 -080088 mContext->getDiagnostics()->error(DiagMessage(source)
89 << "attribute '" << package << ":"
90 << attr.name << "' was not found");
Adam Lesinski1ab598f2015-08-14 14:26:04 -070091 mError = true;
92
93 }
94 } else {
95 // We still encode references.
96 attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
97 }
98
99 if (attr.compiledValue) {
100 // With a compiledValue, we must resolve the reference and assign it an ID.
Adam Lesinskie352b992015-11-16 11:59:14 -0800101 attr.compiledValue->setSource(source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700102 attr.compiledValue->accept(&mReferenceLinkerVisitor);
103 }
104 }
105
106 // Call the super implementation.
107 xml::PackageAwareVisitor::visit(el);
108 }
109
110 Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) {
111 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name);
112 if (const ISymbolTable::Symbol* symbol = mSymbols->findByName(
113 mangledName ? mangledName.value() : name)) {
114 if (symbol->attribute) {
115 return xml::AaptAttribute{ symbol->id, *symbol->attribute };
116 }
117 }
118 return {};
119 }
120
121 inline bool hasError() {
122 return mError || mReferenceLinkerVisitor.hasError();
123 }
124};
125
126} // namespace
127
128bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) {
129 mSdkLevelsFound.clear();
Adam Lesinskie352b992015-11-16 11:59:14 -0800130 XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
131 &mSdkLevelsFound);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700132 if (resource->root) {
133 resource->root->accept(&visitor);
134 return !visitor.hasError();
135 }
136 return false;
137}
138
139} // namespace aapt