XmlReferenceLinker.cpp revision 1ab598f46c3ff520a67f9d80194847741f3467ab
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 "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;
36    std::set<int>* mSdkLevelsFound;
37    ReferenceLinkerVisitor mReferenceLinkerVisitor;
38    bool mError = false;
39
40public:
41    using xml::PackageAwareVisitor::visit;
42
43    XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
44                              std::set<int>* sdkLevelsFound) :
45            mContext(context), mSymbols(symbols), mSdkLevelsFound(sdkLevelsFound),
46            mReferenceLinkerVisitor(context, symbols, this) {
47    }
48
49    void visit(xml::Element* el) override {
50        for (xml::Attribute& attr : el->attributes) {
51            Maybe<std::u16string> maybePackage =
52                    util::extractPackageFromNamespace(attr.namespaceUri);
53            if (maybePackage) {
54                // There is a valid package name for this attribute. We will look this up.
55                StringPiece16 package = maybePackage.value();
56                if (package.empty()) {
57                    // Empty package means the 'current' or 'local' package.
58                    package = mContext->getCompilationPackage();
59                }
60
61                attr.compiledAttribute = compileAttribute(
62                        ResourceName{ package.toString(), ResourceType::kAttr, attr.name });
63
64                // Convert the string value into a compiled Value if this is a valid attribute.
65                if (attr.compiledAttribute) {
66                    // Record all SDK levels from which the attributes were defined.
67                    const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
68                    if (sdkLevel > 1) {
69                        mSdkLevelsFound->insert(sdkLevel);
70                    }
71
72                    const Attribute* attribute = &attr.compiledAttribute.value().attribute;
73                    attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value,
74                                                                              attribute);
75                    if (!attr.compiledValue &&
76                            !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
77                        // We won't be able to encode this as a string.
78                        mContext->getDiagnostics()->error(
79                                DiagMessage() << "'" << attr.value << "' "
80                                              << "is incompatible with attribute "
81                                              << package << ":" << attr.name << " " << *attribute);
82                        mError = true;
83                    }
84                } else {
85                    mContext->getDiagnostics()->error(
86                            DiagMessage() << "attribute '" << package << ":" << attr.name
87                                          << "' was not found");
88                    mError = true;
89
90                }
91            } else {
92                // We still encode references.
93                attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
94            }
95
96            if (attr.compiledValue) {
97                // With a compiledValue, we must resolve the reference and assign it an ID.
98                attr.compiledValue->accept(&mReferenceLinkerVisitor);
99            }
100        }
101
102        // Call the super implementation.
103        xml::PackageAwareVisitor::visit(el);
104    }
105
106    Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) {
107        Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name);
108        if (const ISymbolTable::Symbol* symbol = mSymbols->findByName(
109                mangledName ? mangledName.value() : name)) {
110            if (symbol->attribute) {
111                return xml::AaptAttribute{ symbol->id, *symbol->attribute };
112            }
113        }
114        return {};
115    }
116
117    inline bool hasError() {
118        return mError || mReferenceLinkerVisitor.hasError();
119    }
120};
121
122} // namespace
123
124bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) {
125    mSdkLevelsFound.clear();
126    XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), &mSdkLevelsFound);
127    if (resource->root) {
128        resource->root->accept(&visitor);
129        return !visitor.hasError();
130    }
131    return false;
132}
133
134} // namespace aapt
135