blob: 786494b6ad1c36724a33ac2794a7a0f3cadc0439 [file] [log] [blame]
Adam Lesinski5eeaadd2016-08-25 12:26:56 -07001/*
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
Adam Lesinskicacb28f2016-10-19 12:18:14 -070017#include "compile/InlineXmlFormatParser.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070018
19#include <sstream>
20#include <string>
21
22#include "android-base/macros.h"
23
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070024#include "Debug.h"
25#include "ResourceUtils.h"
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070026#include "util/Util.h"
27#include "xml/XmlDom.h"
28#include "xml/XmlUtil.h"
29
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070030namespace aapt {
31
32namespace {
33
34/**
35 * XML Visitor that will find all <aapt:attr> elements for extraction.
36 */
37class Visitor : public xml::PackageAwareVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070038 public:
Adam Lesinskice5e56e2016-10-21 17:56:45 -070039 using xml::PackageAwareVisitor::Visit;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070040
Adam Lesinskicacb28f2016-10-19 12:18:14 -070041 struct InlineDeclaration {
42 xml::Element* el;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070043 std::string attr_namespace_uri;
44 std::string attr_name;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070045 };
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070046
Adam Lesinskice5e56e2016-10-21 17:56:45 -070047 explicit Visitor(IAaptContext* context, xml::XmlResource* xml_resource)
48 : context_(context), xml_resource_(xml_resource) {}
Adam Lesinskicacb28f2016-10-19 12:18:14 -070049
Adam Lesinskice5e56e2016-10-21 17:56:45 -070050 void Visit(xml::Element* el) override {
51 if (el->namespace_uri != xml::kSchemaAapt || el->name != "attr") {
52 xml::PackageAwareVisitor::Visit(el);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070053 return;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070054 }
55
Adam Lesinskice5e56e2016-10-21 17:56:45 -070056 const Source& src = xml_resource_->file.source.WithLine(el->line_number);
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070057
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058 xml::Attribute* attr = el->FindAttribute({}, "name");
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 if (!attr) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070060 context_->GetDiagnostics()->Error(DiagMessage(src)
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061 << "missing 'name' attribute");
Adam Lesinskice5e56e2016-10-21 17:56:45 -070062 error_ = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070063 return;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070064 }
65
Adam Lesinskice5e56e2016-10-21 17:56:45 -070066 Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070067 if (!ref) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 context_->GetDiagnostics()->Error(
Adam Lesinskicacb28f2016-10-19 12:18:14 -070069 DiagMessage(src) << "invalid XML attribute '" << attr->value << "'");
Adam Lesinskice5e56e2016-10-21 17:56:45 -070070 error_ = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070071 return;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070072 }
73
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 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.
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 Maybe<xml::ExtractedPackage> maybe_pkg =
82 TransformPackageAlias(name.package, {});
83 if (!maybe_pkg) {
84 context_->GetDiagnostics()->Error(DiagMessage(src)
Adam Lesinskicacb28f2016-10-19 12:18:14 -070085 << "invalid namespace prefix '"
86 << name.package << "'");
Adam Lesinskice5e56e2016-10-21 17:56:45 -070087 error_ = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 return;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070089 }
90
Adam Lesinskice5e56e2016-10-21 17:56:45 -070091 const xml::ExtractedPackage& pkg = maybe_pkg.value();
92 const bool private_namespace =
93 pkg.private_namespace || ref.value().private_reference;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070094
Adam Lesinskicacb28f2016-10-19 12:18:14 -070095 InlineDeclaration decl;
96 decl.el = el;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070097 decl.attr_name = name.entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070098 if (!pkg.package.empty()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070099 decl.attr_namespace_uri =
100 xml::BuildPackageNamespace(pkg.package, private_namespace);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700101 }
102
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103 inline_declarations_.push_back(std::move(decl));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700104 }
105
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700106 const std::vector<InlineDeclaration>& GetInlineDeclarations() const {
107 return inline_declarations_;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700108 }
109
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700110 bool HasError() const { return error_; }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700111
112 private:
113 DISALLOW_COPY_AND_ASSIGN(Visitor);
114
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700115 IAaptContext* context_;
116 xml::XmlResource* xml_resource_;
117 std::vector<InlineDeclaration> inline_declarations_;
118 bool error_ = false;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700119};
120
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121} // namespace
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700122
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700123bool InlineXmlFormatParser::Consume(IAaptContext* context,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700124 xml::XmlResource* doc) {
125 Visitor visitor(context, doc);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700126 doc->root->Accept(&visitor);
127 if (visitor.HasError()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700128 return false;
129 }
130
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700131 size_t name_suffix_counter = 0;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 for (const Visitor::InlineDeclaration& decl :
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133 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;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700138
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
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700145 new_doc->file.name.entry =
146 NameMangler::MangleEntry({}, new_doc->file.name.entry + "__" +
147 std::to_string(name_suffix_counter));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148
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) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700152 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)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 << "can't extract text into its own resource");
158 return false;
159 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700160 } else if (new_doc->root) {
161 context->GetDiagnostics()->Error(
162 DiagMessage(child_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700163 << "inline XML resources must have a single root");
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700164 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700165 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700166 new_doc->root = std::move(child);
167 new_doc->root->parent = nullptr;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700168 }
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700169 }
170
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700171 // Walk up and find the parent element.
172 xml::Node* node = decl.el;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700173 xml::Element* parent_el = nullptr;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700174 while (node->parent &&
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700175 (parent_el = xml::NodeCast<xml::Element>(node->parent)) == nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700176 node = node->parent;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700177 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700178
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700179 if (!parent_el) {
180 context->GetDiagnostics()->Error(
181 DiagMessage(new_doc->file.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700182 << "no suitable parent for inheriting attribute");
183 return false;
184 }
185
186 // Add the inline attribute to the parent.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700187 parent_el->attributes.push_back(
188 xml::Attribute{decl.attr_namespace_uri, decl.attr_name,
189 "@" + new_doc->file.name.ToString()});
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700190
191 // Delete the subtree.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700192 for (auto iter = parent_el->children.begin();
193 iter != parent_el->children.end(); ++iter) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 if (iter->get() == node) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700195 parent_el->children.erase(iter);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700196 break;
197 }
198 }
199
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700200 queue_.push_back(std::move(new_doc));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700201
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700202 name_suffix_counter++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700203 }
204 return true;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700205}
206
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207} // namespace aapt