1/* 2 * Copyright (C) 2016 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 "compile/InlineXmlFormatParser.h" 18 19#include <sstream> 20#include <string> 21 22#include "android-base/macros.h" 23 24#include "Debug.h" 25#include "ResourceUtils.h" 26#include "util/Util.h" 27#include "xml/XmlDom.h" 28#include "xml/XmlUtil.h" 29 30namespace aapt { 31 32namespace { 33 34/** 35 * XML Visitor that will find all <aapt:attr> elements for extraction. 36 */ 37class Visitor : public xml::PackageAwareVisitor { 38 public: 39 using xml::PackageAwareVisitor::Visit; 40 41 struct InlineDeclaration { 42 xml::Element* el; 43 std::string attr_namespace_uri; 44 std::string attr_name; 45 }; 46 47 explicit Visitor(IAaptContext* context, xml::XmlResource* xml_resource) 48 : context_(context), xml_resource_(xml_resource) {} 49 50 void Visit(xml::Element* el) override { 51 if (el->namespace_uri != xml::kSchemaAapt || el->name != "attr") { 52 xml::PackageAwareVisitor::Visit(el); 53 return; 54 } 55 56 const Source& src = xml_resource_->file.source.WithLine(el->line_number); 57 58 xml::Attribute* attr = el->FindAttribute({}, "name"); 59 if (!attr) { 60 context_->GetDiagnostics()->Error(DiagMessage(src) 61 << "missing 'name' attribute"); 62 error_ = true; 63 return; 64 } 65 66 Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value); 67 if (!ref) { 68 context_->GetDiagnostics()->Error( 69 DiagMessage(src) << "invalid XML attribute '" << attr->value << "'"); 70 error_ = true; 71 return; 72 } 73 74 const ResourceName& name = ref.value().name.value(); 75 76 // Use an empty string for the compilation package because we don't want to 77 // default to 78 // the local package if the user specified name="style" or something. This 79 // should just 80 // be the default namespace. 81 Maybe<xml::ExtractedPackage> maybe_pkg = 82 TransformPackageAlias(name.package, {}); 83 if (!maybe_pkg) { 84 context_->GetDiagnostics()->Error(DiagMessage(src) 85 << "invalid namespace prefix '" 86 << name.package << "'"); 87 error_ = true; 88 return; 89 } 90 91 const xml::ExtractedPackage& pkg = maybe_pkg.value(); 92 const bool private_namespace = 93 pkg.private_namespace || ref.value().private_reference; 94 95 InlineDeclaration decl; 96 decl.el = el; 97 decl.attr_name = name.entry; 98 if (!pkg.package.empty()) { 99 decl.attr_namespace_uri = 100 xml::BuildPackageNamespace(pkg.package, private_namespace); 101 } 102 103 inline_declarations_.push_back(std::move(decl)); 104 } 105 106 const std::vector<InlineDeclaration>& GetInlineDeclarations() const { 107 return inline_declarations_; 108 } 109 110 bool HasError() const { return error_; } 111 112 private: 113 DISALLOW_COPY_AND_ASSIGN(Visitor); 114 115 IAaptContext* context_; 116 xml::XmlResource* xml_resource_; 117 std::vector<InlineDeclaration> inline_declarations_; 118 bool error_ = false; 119}; 120 121} // namespace 122 123bool InlineXmlFormatParser::Consume(IAaptContext* context, 124 xml::XmlResource* doc) { 125 Visitor visitor(context, doc); 126 doc->root->Accept(&visitor); 127 if (visitor.HasError()) { 128 return false; 129 } 130 131 size_t name_suffix_counter = 0; 132 for (const Visitor::InlineDeclaration& decl : 133 visitor.GetInlineDeclarations()) { 134 auto new_doc = util::make_unique<xml::XmlResource>(); 135 new_doc->file.config = doc->file.config; 136 new_doc->file.source = doc->file.source.WithLine(decl.el->line_number); 137 new_doc->file.name = doc->file.name; 138 139 // Modify the new entry name. We need to suffix the entry with a number to 140 // avoid 141 // local collisions, then mangle it with the empty package, such that it 142 // won't show up 143 // in R.java. 144 145 new_doc->file.name.entry = 146 NameMangler::MangleEntry({}, new_doc->file.name.entry + "__" + 147 std::to_string(name_suffix_counter)); 148 149 // Extracted elements must be the only child of <aapt:attr>. 150 // Make sure there is one root node in the children (ignore empty text). 151 for (auto& child : decl.el->children) { 152 const Source child_source = doc->file.source.WithLine(child->line_number); 153 if (xml::Text* t = xml::NodeCast<xml::Text>(child.get())) { 154 if (!util::TrimWhitespace(t->text).empty()) { 155 context->GetDiagnostics()->Error( 156 DiagMessage(child_source) 157 << "can't extract text into its own resource"); 158 return false; 159 } 160 } else if (new_doc->root) { 161 context->GetDiagnostics()->Error( 162 DiagMessage(child_source) 163 << "inline XML resources must have a single root"); 164 return false; 165 } else { 166 new_doc->root = std::move(child); 167 new_doc->root->parent = nullptr; 168 } 169 } 170 171 // Walk up and find the parent element. 172 xml::Node* node = decl.el; 173 xml::Element* parent_el = nullptr; 174 while (node->parent && 175 (parent_el = xml::NodeCast<xml::Element>(node->parent)) == nullptr) { 176 node = node->parent; 177 } 178 179 if (!parent_el) { 180 context->GetDiagnostics()->Error( 181 DiagMessage(new_doc->file.source) 182 << "no suitable parent for inheriting attribute"); 183 return false; 184 } 185 186 // Add the inline attribute to the parent. 187 parent_el->attributes.push_back( 188 xml::Attribute{decl.attr_namespace_uri, decl.attr_name, 189 "@" + new_doc->file.name.ToString()}); 190 191 // Delete the subtree. 192 for (auto iter = parent_el->children.begin(); 193 iter != parent_el->children.end(); ++iter) { 194 if (iter->get() == node) { 195 parent_el->children.erase(iter); 196 break; 197 } 198 } 199 200 queue_.push_back(std::move(new_doc)); 201 202 name_suffix_counter++; 203 } 204 return true; 205} 206 207} // namespace aapt 208