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