1/*
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 "link/Linkers.h"
21#include "link/ReferenceLinker.h"
22#include "process/IResourceTableConsumer.h"
23#include "process/SymbolTable.h"
24#include "util/Util.h"
25#include "xml/XmlDom.h"
26
27namespace aapt {
28
29namespace {
30
31/**
32 * Visits all references (including parents of styles, references in styles, arrays, etc) and
33 * links their symbolic name to their Resource ID, performing mangling and package aliasing
34 * as needed.
35 */
36class ReferenceVisitor : public ValueVisitor {
37public:
38    using ValueVisitor::visit;
39
40    ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
41                     CallSite* callSite) :
42             mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
43             mError(false) {
44    }
45
46    void visit(Reference* ref) override {
47        if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
48            mError = true;
49        }
50    }
51
52    bool hasError() const {
53        return mError;
54    }
55
56private:
57    IAaptContext* mContext;
58    SymbolTable* mSymbols;
59    xml::IPackageDeclStack* mDecls;
60    CallSite* mCallSite;
61    bool mError;
62};
63
64/**
65 * Visits each xml Element and compiles the attributes within.
66 */
67class XmlVisitor : public xml::PackageAwareVisitor {
68public:
69    using xml::PackageAwareVisitor::visit;
70
71    XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
72               std::set<int>* sdkLevelsFound, CallSite* callSite) :
73            mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
74            mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
75    }
76
77    void visit(xml::Element* el) override {
78        const Source source = mSource.withLine(el->lineNumber);
79        for (xml::Attribute& attr : el->attributes) {
80            Maybe<xml::ExtractedPackage> maybePackage =
81                    xml::extractPackageFromNamespace(attr.namespaceUri);
82            if (maybePackage) {
83                // There is a valid package name for this attribute. We will look this up.
84                StringPiece16 package = maybePackage.value().package;
85                if (package.empty()) {
86                    // Empty package means the 'current' or 'local' package.
87                    package = mContext->getCompilationPackage();
88                }
89
90                Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name));
91                attrRef.privateReference = maybePackage.value().privateNamespace;
92
93                std::string errStr;
94                attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
95                        attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
96
97                // Convert the string value into a compiled Value if this is a valid attribute.
98                if (attr.compiledAttribute) {
99                    if (attr.compiledAttribute.value().id) {
100                        // Record all SDK levels from which the attributes were defined.
101                        const size_t sdkLevel = findAttributeSdkLevel(
102                                attr.compiledAttribute.value().id.value());
103                        if (sdkLevel > 1) {
104                            mSdkLevelsFound->insert(sdkLevel);
105                        }
106                    }
107
108                    const Attribute* attribute = &attr.compiledAttribute.value().attribute;
109                    attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value,
110                                                                              attribute);
111                    if (!attr.compiledValue &&
112                            !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
113                        // We won't be able to encode this as a string.
114                        mContext->getDiagnostics()->error(
115                                DiagMessage(source) << "'" << attr.value << "' "
116                                                    << "is incompatible with attribute "
117                                                    << package << ":" << attr.name << " "
118                                                    << *attribute);
119                        mError = true;
120                    }
121
122                } else {
123                    mContext->getDiagnostics()->error(DiagMessage(source)
124                                                      << "attribute '" << package << ":"
125                                                      << attr.name << "' " << errStr);
126                    mError = true;
127
128                }
129            } else {
130                // We still encode references.
131                attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
132            }
133
134            if (attr.compiledValue) {
135                // With a compiledValue, we must resolve the reference and assign it an ID.
136                attr.compiledValue->setSource(source);
137                attr.compiledValue->accept(&mReferenceVisitor);
138            }
139        }
140
141        // Call the super implementation.
142        xml::PackageAwareVisitor::visit(el);
143    }
144
145    bool hasError() {
146        return mError || mReferenceVisitor.hasError();
147    }
148
149private:
150    IAaptContext* mContext;
151    SymbolTable* mSymbols;
152    Source mSource;
153    std::set<int>* mSdkLevelsFound;
154    CallSite* mCallSite;
155    ReferenceVisitor mReferenceVisitor;
156    bool mError = false;
157};
158
159} // namespace
160
161bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) {
162    mSdkLevelsFound.clear();
163    CallSite callSite = { resource->file.name };
164    XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
165                       &mSdkLevelsFound, &callSite);
166    if (resource->root) {
167        resource->root->accept(&visitor);
168        return !visitor.hasError();
169    }
170    return false;
171}
172
173} // namespace aapt
174