| /* | 
 |  * 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 "link/Linkers.h" | 
 |  | 
 | #include "androidfw/ResourceTypes.h" | 
 |  | 
 | #include "Diagnostics.h" | 
 | #include "ResourceUtils.h" | 
 | #include "SdkConstants.h" | 
 | #include "ValueVisitor.h" | 
 | #include "link/ReferenceLinker.h" | 
 | #include "process/IResourceTableConsumer.h" | 
 | #include "process/SymbolTable.h" | 
 | #include "trace/TraceBuffer.h" | 
 | #include "util/Util.h" | 
 | #include "xml/XmlDom.h" | 
 |  | 
 | namespace aapt { | 
 |  | 
 | namespace { | 
 |  | 
 | // Visits all references (including parents of styles, references in styles, arrays, etc) and | 
 | // links their symbolic name to their Resource ID, performing mangling and package aliasing | 
 | // as needed. | 
 | class ReferenceVisitor : public DescendingValueVisitor { | 
 |  public: | 
 |   using DescendingValueVisitor::Visit; | 
 |  | 
 |   ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols, | 
 |                    xml::IPackageDeclStack* decls) | 
 |       : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {} | 
 |  | 
 |   void Visit(Reference* ref) override { | 
 |     if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) { | 
 |       error_ = true; | 
 |     } | 
 |   } | 
 |  | 
 |   bool HasError() const { | 
 |     return error_; | 
 |   } | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor); | 
 |  | 
 |   const CallSite& callsite_; | 
 |   IAaptContext* context_; | 
 |   SymbolTable* symbols_; | 
 |   xml::IPackageDeclStack* decls_; | 
 |   bool error_; | 
 | }; | 
 |  | 
 | // Visits each xml Element and compiles the attributes within. | 
 | class XmlVisitor : public xml::PackageAwareVisitor { | 
 |  public: | 
 |   using xml::PackageAwareVisitor::Visit; | 
 |  | 
 |   XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context, | 
 |              SymbolTable* symbols) | 
 |       : source_(source), | 
 |         callsite_(callsite), | 
 |         context_(context), | 
 |         symbols_(symbols), | 
 |         reference_visitor_(callsite, context, symbols, this) { | 
 |   } | 
 |  | 
 |   void Visit(xml::Element* el) override { | 
 |     // The default Attribute allows everything except enums or flags. | 
 |     Attribute default_attribute(android::ResTable_map::TYPE_ANY); | 
 |     default_attribute.SetWeak(true); | 
 |  | 
 |     const Source source = source_.WithLine(el->line_number); | 
 |     for (xml::Attribute& attr : el->attributes) { | 
 |       // If the attribute has no namespace, interpret values as if | 
 |       // they were assigned to the default Attribute. | 
 |  | 
 |       const Attribute* attribute = &default_attribute; | 
 |  | 
 |       if (Maybe<xml::ExtractedPackage> maybe_package = | 
 |               xml::ExtractPackageFromNamespace(attr.namespace_uri)) { | 
 |         // There is a valid package name for this attribute. We will look this up. | 
 |         Reference attr_ref( | 
 |             ResourceNameRef(maybe_package.value().package, ResourceType::kAttr, attr.name)); | 
 |         attr_ref.private_reference = maybe_package.value().private_namespace; | 
 |  | 
 |         std::string err_str; | 
 |         attr.compiled_attribute = | 
 |             ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str); | 
 |  | 
 |         if (!attr.compiled_attribute) { | 
 |           DiagMessage error_msg(source); | 
 |           error_msg << "attribute "; | 
 |           ReferenceLinker::WriteAttributeName(attr_ref, callsite_, this, &error_msg); | 
 |           error_msg << " " << err_str; | 
 |           context_->GetDiagnostics()->Error(error_msg); | 
 |           error_ = true; | 
 |           continue; | 
 |         } | 
 |  | 
 |         attribute = &attr.compiled_attribute.value().attribute; | 
 |       } | 
 |  | 
 |       attr.compiled_value = ResourceUtils::TryParseItemForAttribute(attr.value, attribute); | 
 |       if (attr.compiled_value) { | 
 |         // With a compiledValue, we must resolve the reference and assign it an ID. | 
 |         attr.compiled_value->SetSource(source); | 
 |         attr.compiled_value->Accept(&reference_visitor_); | 
 |       } else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) { | 
 |         // We won't be able to encode this as a string. | 
 |         DiagMessage msg(source); | 
 |         msg << "'" << attr.value << "' is incompatible with attribute " << attr.name << " " | 
 |             << *attribute; | 
 |         context_->GetDiagnostics()->Error(msg); | 
 |         error_ = true; | 
 |       } | 
 |     } | 
 |  | 
 |     // Call the super implementation. | 
 |     xml::PackageAwareVisitor::Visit(el); | 
 |   } | 
 |  | 
 |   bool HasError() { | 
 |     return error_ || reference_visitor_.HasError(); | 
 |   } | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(XmlVisitor); | 
 |  | 
 |   Source source_; | 
 |   const CallSite& callsite_; | 
 |   IAaptContext* context_; | 
 |   SymbolTable* symbols_; | 
 |  | 
 |   ReferenceVisitor reference_visitor_; | 
 |   bool error_ = false; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) { | 
 |   TRACE_NAME("XmlReferenceLinker::Consume"); | 
 |   CallSite callsite{resource->file.name.package}; | 
 |  | 
 |   std::string out_name = resource->file.name.entry; | 
 |   NameMangler::Unmangle(&out_name, &callsite.package); | 
 |  | 
 |   if (callsite.package.empty()) { | 
 |     // Assume an empty package means that the XML file is local. This is true of AndroidManifest.xml | 
 |     // for example. | 
 |     callsite.package = context->GetCompilationPackage(); | 
 |   } | 
 |  | 
 |   XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols()); | 
 |   if (resource->root) { | 
 |     resource->root->Accept(&visitor); | 
 |     return !visitor.HasError(); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace aapt |