blob: 24b28dd7d9702a7bbfe2938f82661b19d016306d [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080017#include "ResourceParser.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070018
19#include <functional>
20#include <sstream>
21
22#include "android-base/logging.h"
23
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024#include "ResourceTable.h"
25#include "ResourceUtils.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080026#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027#include "ValueVisitor.h"
Adam Lesinski7ff3ee12015-12-14 16:08:50 -080028#include "util/ImmutableMap.h"
Adam Lesinski75421622017-01-06 15:20:04 -080029#include "util/Maybe.h"
Adam Lesinski9e10ac72015-10-16 14:37:48 -070030#include "util/Util.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080031#include "xml/XmlPullParser.h"
Adam Lesinski9e10ac72015-10-16 14:37:48 -070032
Adam Lesinski71be7052017-12-12 16:48:07 -080033using ::android::StringPiece;
Adam Lesinskid5083f62017-01-16 15:07:21 -080034
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080035namespace aapt {
36
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070037constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080038
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070039// Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
40static bool ShouldIgnoreElement(const StringPiece& ns, const StringPiece& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070041 return ns.empty() && (name == "skip" || name == "eat-comment");
Adam Lesinski27afb9e2015-11-06 18:25:04 -080042}
43
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070044static uint32_t ParseFormatTypeNoEnumsOrFlags(const StringPiece& piece) {
45 if (piece == "reference") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070046 return android::ResTable_map::TYPE_REFERENCE;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070047 } else if (piece == "string") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070048 return android::ResTable_map::TYPE_STRING;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070049 } else if (piece == "integer") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070050 return android::ResTable_map::TYPE_INTEGER;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070051 } else if (piece == "boolean") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 return android::ResTable_map::TYPE_BOOLEAN;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070053 } else if (piece == "color") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070054 return android::ResTable_map::TYPE_COLOR;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070055 } else if (piece == "float") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 return android::ResTable_map::TYPE_FLOAT;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070057 } else if (piece == "dimension") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058 return android::ResTable_map::TYPE_DIMENSION;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070059 } else if (piece == "fraction") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070060 return android::ResTable_map::TYPE_FRACTION;
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070061 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070062 return 0;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -080063}
64
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070065static uint32_t ParseFormatType(const StringPiece& piece) {
66 if (piece == "enum") {
67 return android::ResTable_map::TYPE_ENUM;
68 } else if (piece == "flags") {
69 return android::ResTable_map::TYPE_FLAGS;
70 }
71 return ParseFormatTypeNoEnumsOrFlags(piece);
72}
73
Adam Lesinskice5e56e2016-10-21 17:56:45 -070074static uint32_t ParseFormatAttribute(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070075 uint32_t mask = 0;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070076 for (StringPiece part : util::Tokenize(str, '|')) {
77 StringPiece trimmed_part = util::TrimWhitespace(part);
78 uint32_t type = ParseFormatType(trimmed_part);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070079 if (type == 0) {
80 return 0;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -080081 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070082 mask |= type;
83 }
84 return mask;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -080085}
86
Adam Lesinskid5fd76a2017-05-16 12:18:08 -070087// A parsed resource ready to be added to the ResourceTable.
Adam Lesinski7ff3ee12015-12-14 16:08:50 -080088struct ParsedResource {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070089 ResourceName name;
90 ConfigDescription config;
91 std::string product;
92 Source source;
Adam Lesinski71be7052017-12-12 16:48:07 -080093
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 ResourceId id;
Adam Lesinski71be7052017-12-12 16:48:07 -080095 Visibility::Level visibility_level = Visibility::Level::kUndefined;
Adam Lesinski4488f1c2017-05-26 17:33:38 -070096 bool allow_new = false;
Adam Lesinski71be7052017-12-12 16:48:07 -080097 bool overlayable = false;
98
Adam Lesinskicacb28f2016-10-19 12:18:14 -070099 std::string comment;
100 std::unique_ptr<Value> value;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700101 std::list<ParsedResource> child_resources;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800102};
103
104// Recursively adds resources to the ResourceTable.
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700105static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700106 StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
107 if (trimmed_comment.size() != res->comment.size()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700108 // Only if there was a change do we re-assign.
Adam Lesinskid5083f62017-01-16 15:07:21 -0800109 res->comment = trimmed_comment.to_string();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700110 }
Adam Lesinski7656554f2016-03-10 21:55:04 -0800111
Adam Lesinski71be7052017-12-12 16:48:07 -0800112 if (res->visibility_level != Visibility::Level::kUndefined) {
113 Visibility visibility;
114 visibility.level = res->visibility_level;
115 visibility.source = res->source;
116 visibility.comment = res->comment;
117 if (!table->SetVisibilityWithId(res->name, visibility, res->id, diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700118 return false;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800119 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700120 }
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800121
Adam Lesinski71be7052017-12-12 16:48:07 -0800122 if (res->allow_new) {
123 AllowNew allow_new;
124 allow_new.source = res->source;
125 allow_new.comment = res->comment;
126 if (!table->SetAllowNew(res->name, allow_new, diag)) {
127 return false;
128 }
129 }
130
131 if (res->overlayable) {
132 Overlayable overlayable;
133 overlayable.source = res->source;
134 overlayable.comment = res->comment;
135 if (!table->SetOverlayable(res->name, overlayable, diag)) {
136 return false;
137 }
138 }
139
140 if (res->value != nullptr) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 // Attach the comment, source and config to the value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700142 res->value->SetComment(std::move(res->comment));
143 res->value->SetSource(std::move(res->source));
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800144
Adam Lesinski71be7052017-12-12 16:48:07 -0800145 if (!table->AddResourceWithId(res->name, res->id, res->config, res->product,
146 std::move(res->value), diag)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700147 return false;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800148 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700149 }
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800150
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700151 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700152 for (ParsedResource& child : res->child_resources) {
153 error |= !AddResourcesToTable(table, diag, &child);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700154 }
155 return !error;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800156}
157
158// Convenient aliases for more readable function calls.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159enum { kAllowRawString = true, kNoRawString = false };
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800160
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table,
162 const Source& source,
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700163 const ConfigDescription& config,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700164 const ResourceParserOptions& options)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700165 : diag_(diag),
166 table_(table),
167 source_(source),
168 config_(config),
169 options_(options) {}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800170
171/**
172 * Build a string from XML that converts nested elements into Span objects.
173 */
Adam Lesinski75421622017-01-06 15:20:04 -0800174bool ResourceParser::FlattenXmlSubtree(
175 xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string,
176 std::vector<UntranslatableSection>* out_untranslatable_sections) {
177 // Keeps track of formatting tags (<b>, <i>) and the range of characters for which they apply.
Adam Lesinski8049f3d2017-03-31 18:28:14 -0700178 // The stack elements refer to the indices in out_style_string->spans.
179 // By first adding to the out_style_string->spans vector, and then using the stack to refer
180 // to this vector, the original order of tags is preserved in cases such as <b><i>hello</b></i>.
181 std::vector<size_t> span_stack;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800182
Adam Lesinski75421622017-01-06 15:20:04 -0800183 // Clear the output variables.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700184 out_raw_string->clear();
185 out_style_string->spans.clear();
Adam Lesinski75421622017-01-06 15:20:04 -0800186 out_untranslatable_sections->clear();
187
188 // The StringBuilder will concatenate the various segments of text which are initially
189 // separated by tags. It also handles unicode escape codes and quotations.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700190 util::StringBuilder builder;
Adam Lesinski75421622017-01-06 15:20:04 -0800191
192 // The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
193 Maybe<size_t> untranslatable_start_depth;
194
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700195 size_t depth = 1;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700196 while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
197 const xml::XmlPullParser::Event event = parser->event();
Adam Lesinski75421622017-01-06 15:20:04 -0800198
199 if (event == xml::XmlPullParser::Event::kStartElement) {
200 if (parser->element_namespace().empty()) {
201 // This is an HTML tag which we encode as a span. Add it to the span stack.
202 std::string span_name = parser->element_name();
203 const auto end_attr_iter = parser->end_attributes();
204 for (auto attr_iter = parser->begin_attributes(); attr_iter != end_attr_iter; ++attr_iter) {
205 span_name += ";";
206 span_name += attr_iter->name;
207 span_name += "=";
208 span_name += attr_iter->value;
209 }
210
211 // Make sure the string is representable in our binary format.
212 if (builder.Utf16Len() > std::numeric_limits<uint32_t>::max()) {
213 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
214 << "style string '" << builder.ToString() << "' is too long");
215 return false;
216 }
217
Adam Lesinski8049f3d2017-03-31 18:28:14 -0700218 out_style_string->spans.push_back(
219 Span{std::move(span_name), static_cast<uint32_t>(builder.Utf16Len())});
220 span_stack.push_back(out_style_string->spans.size() - 1);
Adam Lesinski75421622017-01-06 15:20:04 -0800221 } else if (parser->element_namespace() == sXliffNamespaceUri) {
222 if (parser->element_name() == "g") {
223 if (untranslatable_start_depth) {
224 // We've already encountered an <xliff:g> tag, and nested <xliff:g> tags are illegal.
225 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
226 << "illegal nested XLIFF 'g' tag");
227 return false;
228 } else {
229 // Mark the start of an untranslatable section. Use UTF8 indices/lengths.
230 untranslatable_start_depth = depth;
231 const size_t current_idx = builder.ToString().size();
232 out_untranslatable_sections->push_back(UntranslatableSection{current_idx, current_idx});
233 }
234 }
235 // Ignore other xliff tags, they get handled by other tools.
236
237 } else {
238 // Besides XLIFF, any other namespaced tag is unsupported and ignored.
239 diag_->Warn(DiagMessage(source_.WithLine(parser->line_number()))
240 << "ignoring element '" << parser->element_name()
241 << "' with unknown namespace '" << parser->element_namespace() << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700242 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700243
Adam Lesinski75421622017-01-06 15:20:04 -0800244 // Enter one level inside the element.
245 depth++;
246 } else if (event == xml::XmlPullParser::Event::kText) {
247 // Record both the raw text and append to the builder to deal with escape sequences
248 // and quotations.
249 out_raw_string->append(parser->text());
250 builder.Append(parser->text());
251 } else if (event == xml::XmlPullParser::Event::kEndElement) {
252 // Return one level from within the element.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700253 depth--;
254 if (depth == 0) {
255 break;
256 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800257
Adam Lesinski75421622017-01-06 15:20:04 -0800258 if (parser->element_namespace().empty()) {
259 // This is an HTML tag which we encode as a span. Update the span
260 // stack and pop the top entry.
Adam Lesinski8049f3d2017-03-31 18:28:14 -0700261 Span& top_span = out_style_string->spans[span_stack.back()];
Adam Lesinski75421622017-01-06 15:20:04 -0800262 top_span.last_char = builder.Utf16Len() - 1;
Adam Lesinski75421622017-01-06 15:20:04 -0800263 span_stack.pop_back();
264 } else if (untranslatable_start_depth == make_value(depth)) {
265 // This is the end of an untranslatable section. Use UTF8 indices/lengths.
266 UntranslatableSection& untranslatable_section = out_untranslatable_sections->back();
267 untranslatable_section.end = builder.ToString().size();
268 untranslatable_start_depth = {};
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700269 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700270 } else if (event == xml::XmlPullParser::Event::kComment) {
Adam Lesinski75421622017-01-06 15:20:04 -0800271 // Ignore.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700272 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700273 LOG(FATAL) << "unhandled XML event";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700274 }
275 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700276
Adam Lesinski75421622017-01-06 15:20:04 -0800277 CHECK(span_stack.empty()) << "spans haven't been fully processed";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700278 out_style_string->str = builder.ToString();
Adam Lesinski75421622017-01-06 15:20:04 -0800279 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800280}
281
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700282bool ResourceParser::Parse(xml::XmlPullParser* parser) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700283 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700284 const size_t depth = parser->depth();
285 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
286 if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700287 // Skip comments and text.
288 continue;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800289 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700290
Adam Lesinski060b53d2017-07-28 17:10:35 -0700291 if (!parser->element_namespace().empty() || parser->element_name() != "resources") {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700292 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700293 << "root element must be <resources>");
294 return false;
295 }
296
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700297 error |= !ParseResources(parser);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700298 break;
299 };
300
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700301 if (parser->event() == xml::XmlPullParser::Event::kBadDocument) {
302 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
303 << "xml parser error: " << parser->error());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700304 return false;
305 }
306 return !error;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800307}
308
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700309bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
310 std::set<ResourceName> stripped_resources;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700311
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700312 bool error = false;
313 std::string comment;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700314 const size_t depth = parser->depth();
315 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
316 const xml::XmlPullParser::Event event = parser->event();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700317 if (event == xml::XmlPullParser::Event::kComment) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700318 comment = parser->comment();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700319 continue;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800320 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700321
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700322 if (event == xml::XmlPullParser::Event::kText) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700323 if (!util::TrimWhitespace(parser->text()).empty()) {
324 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700325 << "plain text not allowed here");
326 error = true;
327 }
328 continue;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700329 }
330
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700331 CHECK(event == xml::XmlPullParser::Event::kStartElement);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700332
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700333 if (!parser->element_namespace().empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700334 // Skip unknown namespace.
335 continue;
336 }
337
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700338 std::string element_name = parser->element_name();
339 if (element_name == "skip" || element_name == "eat-comment") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700340 comment = "";
341 continue;
342 }
343
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700344 ParsedResource parsed_resource;
345 parsed_resource.config = config_;
346 parsed_resource.source = source_.WithLine(parser->line_number());
347 parsed_resource.comment = std::move(comment);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700348
349 // Extract the product name if it exists.
Adam Lesinski060b53d2017-07-28 17:10:35 -0700350 if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
Adam Lesinskid5083f62017-01-16 15:07:21 -0800351 parsed_resource.product = maybe_product.value().to_string();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700352 }
353
354 // Parse the resource regardless of product.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355 if (!ParseResource(parser, &parsed_resource)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700356 error = true;
357 continue;
358 }
359
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700360 if (!AddResourcesToTable(table_, diag_, &parsed_resource)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700361 error = true;
362 }
363 }
364
365 // Check that we included at least one variant of each stripped resource.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700366 for (const ResourceName& stripped_resource : stripped_resources) {
367 if (!table_->FindResource(stripped_resource)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700368 // Failed to find the resource.
Adam Lesinski060b53d2017-07-28 17:10:35 -0700369 diag_->Error(DiagMessage(source_) << "resource '" << stripped_resource
370 << "' was filtered out but no product variant remains");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700371 error = true;
372 }
373 }
374
375 return !error;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800376}
377
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700378bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
379 ParsedResource* out_resource) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700380 struct ItemTypeFormat {
381 ResourceType type;
382 uint32_t format;
383 };
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800384
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700385 using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
386 ParsedResource*)>;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800387
Adam Lesinski86d67df2017-01-31 13:47:27 -0800388 static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({
389 {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}},
390 {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}},
391 {"configVarying", {ResourceType::kConfigVarying, android::ResTable_map::TYPE_ANY}},
392 {"dimen",
393 {ResourceType::kDimen,
394 android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
395 android::ResTable_map::TYPE_DIMENSION}},
396 {"drawable", {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}},
397 {"fraction",
398 {ResourceType::kFraction,
399 android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION |
400 android::ResTable_map::TYPE_DIMENSION}},
401 {"integer", {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}},
402 {"string", {ResourceType::kString, android::ResTable_map::TYPE_STRING}},
403 });
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800404
Adam Lesinski86d67df2017-01-31 13:47:27 -0800405 static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({
406 {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)},
407 {"array", std::mem_fn(&ResourceParser::ParseArray)},
408 {"attr", std::mem_fn(&ResourceParser::ParseAttr)},
409 {"configVarying",
410 std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kConfigVarying,
411 std::placeholders::_2, std::placeholders::_3)},
412 {"declare-styleable", std::mem_fn(&ResourceParser::ParseDeclareStyleable)},
413 {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)},
414 {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
Adam Lesinski46c4d722017-08-23 13:03:56 -0700415 {"overlayable", std::mem_fn(&ResourceParser::ParseOverlayable)},
Adam Lesinski86d67df2017-01-31 13:47:27 -0800416 {"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
417 {"public", std::mem_fn(&ResourceParser::ParsePublic)},
418 {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
419 {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
420 {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
421 std::placeholders::_2, std::placeholders::_3)},
422 {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
423 });
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800424
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700425 std::string resource_type = parser->element_name();
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800426
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700427 // The value format accepted for this resource.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700428 uint32_t resource_format = 0u;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800429
Adam Lesinski86d67df2017-01-31 13:47:27 -0800430 bool can_be_item = true;
431 bool can_be_bag = true;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700432 if (resource_type == "item") {
Adam Lesinski86d67df2017-01-31 13:47:27 -0800433 can_be_bag = false;
434
Adam Lesinskie597d682017-06-01 17:16:44 -0700435 // The default format for <item> is any. If a format attribute is present, that one will
436 // override the default.
437 resource_format = android::ResTable_map::TYPE_ANY;
438
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700439 // Items have their type encoded in the type attribute.
Adam Lesinskid5fd76a2017-05-16 12:18:08 -0700440 if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
Adam Lesinskid5083f62017-01-16 15:07:21 -0800441 resource_type = maybe_type.value().to_string();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700442 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700443 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700444 << "<item> must have a 'type' attribute");
445 return false;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800446 }
447
Adam Lesinskid5fd76a2017-05-16 12:18:08 -0700448 if (Maybe<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700449 // An explicit format for this resource was specified. The resource will
Adam Lesinskid5fd76a2017-05-16 12:18:08 -0700450 // retain its type in its name, but the accepted value for this type is
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700451 // overridden.
Adam Lesinskid5fd76a2017-05-16 12:18:08 -0700452 resource_format = ParseFormatTypeNoEnumsOrFlags(maybe_format.value());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700453 if (!resource_format) {
454 diag_->Error(DiagMessage(out_resource->source)
455 << "'" << maybe_format.value()
456 << "' is an invalid format");
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800457 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700458 }
459 }
Adam Lesinski86d67df2017-01-31 13:47:27 -0800460 } else if (resource_type == "bag") {
461 can_be_item = false;
462
463 // Bags have their type encoded in the type attribute.
464 if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
465 resource_type = maybe_type.value().to_string();
466 } else {
467 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
468 << "<bag> must have a 'type' attribute");
469 return false;
470 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700471 }
472
473 // Get the name of the resource. This will be checked later, because not all
474 // XML elements require a name.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700475 Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700476
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700477 if (resource_type == "id") {
478 if (!maybe_name) {
479 diag_->Error(DiagMessage(out_resource->source)
480 << "<" << parser->element_name()
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700481 << "> missing 'name' attribute");
482 return false;
483 }
484
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700485 out_resource->name.type = ResourceType::kId;
Adam Lesinskid5083f62017-01-16 15:07:21 -0800486 out_resource->name.entry = maybe_name.value().to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700487 out_resource->value = util::make_unique<Id>();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700488 return true;
489 }
490
Adam Lesinski86d67df2017-01-31 13:47:27 -0800491 if (can_be_item) {
492 const auto item_iter = elToItemMap.find(resource_type);
493 if (item_iter != elToItemMap.end()) {
494 // This is an item, record its type and format and start parsing.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700495
Adam Lesinski86d67df2017-01-31 13:47:27 -0800496 if (!maybe_name) {
497 diag_->Error(DiagMessage(out_resource->source)
498 << "<" << parser->element_name() << "> missing 'name' attribute");
499 return false;
500 }
501
502 out_resource->name.type = item_iter->second.type;
503 out_resource->name.entry = maybe_name.value().to_string();
504
Adam Lesinskie597d682017-06-01 17:16:44 -0700505 // Only use the implied format of the type when there is no explicit format.
506 if (resource_format == 0u) {
Adam Lesinski86d67df2017-01-31 13:47:27 -0800507 resource_format = item_iter->second.format;
508 }
509
510 if (!ParseItem(parser, out_resource, resource_format)) {
511 return false;
512 }
513 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700514 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700515 }
516
517 // This might be a bag or something.
Adam Lesinski86d67df2017-01-31 13:47:27 -0800518 if (can_be_bag) {
519 const auto bag_iter = elToBagMap.find(resource_type);
520 if (bag_iter != elToBagMap.end()) {
521 // Ensure we have a name (unless this is a <public-group>).
Adam Lesinski46c4d722017-08-23 13:03:56 -0700522 if (resource_type != "public-group" && resource_type != "overlayable") {
Adam Lesinski86d67df2017-01-31 13:47:27 -0800523 if (!maybe_name) {
524 diag_->Error(DiagMessage(out_resource->source)
525 << "<" << parser->element_name() << "> missing 'name' attribute");
526 return false;
527 }
528
529 out_resource->name.entry = maybe_name.value().to_string();
530 }
531
532 // Call the associated parse method. The type will be filled in by the
533 // parse func.
534 if (!bag_iter->second(this, parser, out_resource)) {
535 return false;
536 }
537 return true;
538 }
539 }
540
541 if (can_be_item) {
542 // Try parsing the elementName (or type) as a resource. These shall only be
543 // resources like 'layout' or 'xml' and they can only be references.
544 const ResourceType* parsed_type = ParseResourceType(resource_type);
545 if (parsed_type) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700546 if (!maybe_name) {
547 diag_->Error(DiagMessage(out_resource->source)
548 << "<" << parser->element_name()
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700549 << "> missing 'name' attribute");
550 return false;
551 }
552
Adam Lesinski86d67df2017-01-31 13:47:27 -0800553 out_resource->name.type = *parsed_type;
Adam Lesinskid5083f62017-01-16 15:07:21 -0800554 out_resource->name.entry = maybe_name.value().to_string();
Adam Lesinski86d67df2017-01-31 13:47:27 -0800555 out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
556 if (!out_resource->value) {
557 diag_->Error(DiagMessage(out_resource->source)
558 << "invalid value for type '" << *parsed_type << "'. Expected a reference");
559 return false;
560 }
561 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700562 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700563 }
564
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700565 diag_->Warn(DiagMessage(out_resource->source)
566 << "unknown resource type '" << parser->element_name() << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700567 return false;
568}
569
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700570bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
571 ParsedResource* out_resource,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700572 const uint32_t format) {
573 if (format == android::ResTable_map::TYPE_STRING) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700574 return ParseString(parser, out_resource);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700575 }
576
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700577 out_resource->value = ParseXml(parser, format, kNoRawString);
578 if (!out_resource->value) {
579 diag_->Error(DiagMessage(out_resource->source) << "invalid "
580 << out_resource->name.type);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700581 return false;
582 }
583 return true;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -0800584}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800585
586/**
587 * Reads the entire XML subtree and attempts to parse it as some Item,
588 * with typeMask denoting which items it can be. If allowRawValue is
589 * true, a RawString is returned if the XML couldn't be parsed as
590 * an Item. If allowRawValue is false, nullptr is returned in this
591 * case.
592 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700593std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
594 const uint32_t type_mask,
595 const bool allow_raw_value) {
596 const size_t begin_xml_line = parser->line_number();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800597
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700598 std::string raw_value;
599 StyleString style_string;
Adam Lesinski75421622017-01-06 15:20:04 -0800600 std::vector<UntranslatableSection> untranslatable_sections;
601 if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800602 return {};
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700603 }
604
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700605 if (!style_string.spans.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700606 // This can only be a StyledString.
Adam Lesinski75421622017-01-06 15:20:04 -0800607 std::unique_ptr<StyledString> styled_string =
608 util::make_unique<StyledString>(table_->string_pool.MakeRef(
Adam Lesinski060b53d2017-07-28 17:10:35 -0700609 style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
Adam Lesinski75421622017-01-06 15:20:04 -0800610 styled_string->untranslatable_sections = std::move(untranslatable_sections);
611 return std::move(styled_string);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700612 }
613
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700614 auto on_create_reference = [&](const ResourceName& name) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700615 // name.package can be empty here, as it will assume the package name of the
616 // table.
617 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700618 id->SetSource(source_.WithLine(begin_xml_line));
619 table_->AddResource(name, {}, {}, std::move(id), diag_);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700620 };
621
622 // Process the raw value.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700623 std::unique_ptr<Item> processed_item =
Adam Lesinski71be7052017-12-12 16:48:07 -0800624 ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700625 if (processed_item) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700626 // Fix up the reference.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700627 if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
Adam Lesinski1ef0fa92017-08-15 21:32:49 -0700628 ResolvePackage(parser, ref);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700629 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700630 return processed_item;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700631 }
632
633 // Try making a regular string.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700634 if (type_mask & android::ResTable_map::TYPE_STRING) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700635 // Use the trimmed, escaped string.
Adam Lesinski75421622017-01-06 15:20:04 -0800636 std::unique_ptr<String> string = util::make_unique<String>(
637 table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
638 string->untranslatable_sections = std::move(untranslatable_sections);
639 return std::move(string);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700640 }
641
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700642 // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
643 if (util::TrimWhitespace(raw_value).empty()) {
644 return ResourceUtils::MakeNull();
645 }
646
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700647 if (allow_raw_value) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700648 // We can't parse this so return a RawString if we are allowed.
649 return util::make_unique<RawString>(
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700650 table_->string_pool.MakeRef(raw_value, StringPool::Context(config_)));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700651 }
652 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800653}
654
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700655bool ResourceParser::ParseString(xml::XmlPullParser* parser,
656 ParsedResource* out_resource) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700657 bool formatted = true;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700658 if (Maybe<StringPiece> formatted_attr =
659 xml::FindAttribute(parser, "formatted")) {
660 Maybe<bool> maybe_formatted =
661 ResourceUtils::ParseBool(formatted_attr.value());
662 if (!maybe_formatted) {
663 diag_->Error(DiagMessage(out_resource->source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700664 << "invalid value for 'formatted'. Must be a boolean");
665 return false;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800666 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700667 formatted = maybe_formatted.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700668 }
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800669
Adam Lesinski75421622017-01-06 15:20:04 -0800670 bool translatable = options_.translatable;
671 if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
672 Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
673 if (!maybe_translatable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700674 diag_->Error(DiagMessage(out_resource->source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700675 << "invalid value for 'translatable'. Must be a boolean");
676 return false;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800677 }
Adam Lesinski75421622017-01-06 15:20:04 -0800678 translatable = maybe_translatable.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700679 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800680
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700681 out_resource->value =
682 ParseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
683 if (!out_resource->value) {
684 diag_->Error(DiagMessage(out_resource->source) << "not a valid string");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700685 return false;
686 }
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800687
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700688 if (String* string_value = ValueCast<String>(out_resource->value.get())) {
Adam Lesinski75421622017-01-06 15:20:04 -0800689 string_value->SetTranslatable(translatable);
Adam Lesinski393b5f02015-12-17 13:03:11 -0800690
Adam Lesinski75421622017-01-06 15:20:04 -0800691 if (formatted && translatable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700692 if (!util::VerifyJavaStringFormat(*string_value->value)) {
693 DiagMessage msg(out_resource->source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700694 msg << "multiple substitutions specified in non-positional format; "
695 "did you mean to add the formatted=\"false\" attribute?";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700696 if (options_.error_on_positional_arguments) {
697 diag_->Error(msg);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700698 return false;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800699 }
Adam Lesinski393b5f02015-12-17 13:03:11 -0800700
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700701 diag_->Warn(msg);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700702 }
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800703 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700704
Adam Lesinski75421622017-01-06 15:20:04 -0800705 } else if (StyledString* string_value = ValueCast<StyledString>(out_resource->value.get())) {
706 string_value->SetTranslatable(translatable);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700707 }
708 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800709}
710
Adam Lesinski71be7052017-12-12 16:48:07 -0800711bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
Adam Lesinski46c4d722017-08-23 13:03:56 -0700712 if (out_resource->config != ConfigDescription::DefaultConfig()) {
713 diag_->Warn(DiagMessage(out_resource->source)
714 << "ignoring configuration '" << out_resource->config << "' for <public> tag");
715 }
716
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700717 Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
718 if (!maybe_type) {
719 diag_->Error(DiagMessage(out_resource->source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700720 << "<public> must have a 'type' attribute");
721 return false;
722 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800723
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700724 const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
725 if (!parsed_type) {
726 diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
727 << maybe_type.value()
728 << "' in <public>");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700729 return false;
730 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800731
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700732 out_resource->name.type = *parsed_type;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800733
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800734 if (Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
735 Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700736 if (!maybe_id) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800737 diag_->Error(DiagMessage(out_resource->source)
738 << "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700739 return false;
Adam Lesinski27afb9e2015-11-06 18:25:04 -0800740 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700741 out_resource->id = maybe_id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700742 }
Adam Lesinski27afb9e2015-11-06 18:25:04 -0800743
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700744 if (*parsed_type == ResourceType::kId) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700745 // An ID marked as public is also the definition of an ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700746 out_resource->value = util::make_unique<Id>();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700747 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700748
Adam Lesinski71be7052017-12-12 16:48:07 -0800749 out_resource->visibility_level = Visibility::Level::kPublic;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700750 return true;
Adam Lesinski27afb9e2015-11-06 18:25:04 -0800751}
752
Adam Lesinski46c4d722017-08-23 13:03:56 -0700753bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
754 if (out_resource->config != ConfigDescription::DefaultConfig()) {
755 diag_->Warn(DiagMessage(out_resource->source)
756 << "ignoring configuration '" << out_resource->config
757 << "' for <public-group> tag");
758 }
759
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700760 Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
761 if (!maybe_type) {
762 diag_->Error(DiagMessage(out_resource->source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700763 << "<public-group> must have a 'type' attribute");
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800764 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700765 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800766
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700767 const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
768 if (!parsed_type) {
769 diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
770 << maybe_type.value()
771 << "' in <public-group>");
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800772 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700773 }
774
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700775 Maybe<StringPiece> maybe_id_str =
776 xml::FindNonEmptyAttribute(parser, "first-id");
777 if (!maybe_id_str) {
778 diag_->Error(DiagMessage(out_resource->source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700779 << "<public-group> must have a 'first-id' attribute");
780 return false;
781 }
782
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700783 Maybe<ResourceId> maybe_id =
784 ResourceUtils::ParseResourceId(maybe_id_str.value());
785 if (!maybe_id) {
786 diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
787 << maybe_id_str.value()
788 << "' in <public-group>");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700789 return false;
790 }
791
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700792 ResourceId next_id = maybe_id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700793
794 std::string comment;
795 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700796 const size_t depth = parser->depth();
797 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
798 if (parser->event() == xml::XmlPullParser::Event::kComment) {
Adam Lesinskid5083f62017-01-16 15:07:21 -0800799 comment = util::TrimWhitespace(parser->comment()).to_string();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700800 continue;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700801 } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700802 // Skip text.
803 continue;
804 }
805
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700806 const Source item_source = source_.WithLine(parser->line_number());
807 const std::string& element_namespace = parser->element_namespace();
808 const std::string& element_name = parser->element_name();
809 if (element_namespace.empty() && element_name == "public") {
810 Maybe<StringPiece> maybe_name =
811 xml::FindNonEmptyAttribute(parser, "name");
812 if (!maybe_name) {
813 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700814 << "<public> must have a 'name' attribute");
815 error = true;
816 continue;
817 }
818
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700819 if (xml::FindNonEmptyAttribute(parser, "id")) {
820 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700821 << "'id' is ignored within <public-group>");
822 error = true;
823 continue;
824 }
825
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700826 if (xml::FindNonEmptyAttribute(parser, "type")) {
827 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700828 << "'type' is ignored within <public-group>");
829 error = true;
830 continue;
831 }
832
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700833 ParsedResource child_resource;
834 child_resource.name.type = *parsed_type;
Adam Lesinskid5083f62017-01-16 15:07:21 -0800835 child_resource.name.entry = maybe_name.value().to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700836 child_resource.id = next_id;
837 child_resource.comment = std::move(comment);
838 child_resource.source = item_source;
Adam Lesinski71be7052017-12-12 16:48:07 -0800839 child_resource.visibility_level = Visibility::Level::kPublic;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700840 out_resource->child_resources.push_back(std::move(child_resource));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700841
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700842 next_id.id += 1;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700843
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700844 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
845 diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700846 error = true;
847 }
848 }
849 return !error;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800850}
851
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700852bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
853 ParsedResource* out_resource) {
854 Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
855 if (!maybe_type) {
856 diag_->Error(DiagMessage(out_resource->source)
857 << "<" << parser->element_name()
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700858 << "> must have a 'type' attribute");
859 return false;
860 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800861
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700862 const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
863 if (!parsed_type) {
864 diag_->Error(DiagMessage(out_resource->source)
865 << "invalid resource type '" << maybe_type.value() << "' in <"
866 << parser->element_name() << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700867 return false;
868 }
869
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700870 out_resource->name.type = *parsed_type;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700871 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800872}
873
Adam Lesinski46c4d722017-08-23 13:03:56 -0700874bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) {
875 if (out_resource->config != ConfigDescription::DefaultConfig()) {
876 diag_->Warn(DiagMessage(out_resource->source)
877 << "ignoring configuration '" << out_resource->config << "' for <"
878 << parser->element_name() << "> tag");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700879 }
Adam Lesinski46c4d722017-08-23 13:03:56 -0700880
881 if (!ParseSymbolImpl(parser, out_resource)) {
882 return false;
883 }
884
Adam Lesinski71be7052017-12-12 16:48:07 -0800885 out_resource->visibility_level = Visibility::Level::kPrivate;
Adam Lesinski46c4d722017-08-23 13:03:56 -0700886 return true;
887}
888
889bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
890 if (out_resource->config != ConfigDescription::DefaultConfig()) {
891 diag_->Warn(DiagMessage(out_resource->source)
892 << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
893 }
894
895 if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) {
896 const StringPiece& policy = maybe_policy.value();
897 if (policy != "system") {
898 diag_->Error(DiagMessage(out_resource->source)
899 << "<overlayable> has invalid policy '" << policy << "'");
900 return false;
901 }
902 }
903
904 bool error = false;
905 const size_t depth = parser->depth();
906 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
907 if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
908 // Skip text/comments.
909 continue;
910 }
911
912 const Source item_source = source_.WithLine(parser->line_number());
913 const std::string& element_namespace = parser->element_namespace();
914 const std::string& element_name = parser->element_name();
915 if (element_namespace.empty() && element_name == "item") {
916 Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
917 if (!maybe_name) {
918 diag_->Error(DiagMessage(item_source)
919 << "<item> within an <overlayable> tag must have a 'name' attribute");
920 error = true;
921 continue;
922 }
923
924 Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
925 if (!maybe_type) {
926 diag_->Error(DiagMessage(item_source)
927 << "<item> within an <overlayable> tag must have a 'type' attribute");
928 error = true;
929 continue;
930 }
931
932 const ResourceType* type = ParseResourceType(maybe_type.value());
933 if (type == nullptr) {
934 diag_->Error(DiagMessage(out_resource->source)
935 << "invalid resource type '" << maybe_type.value()
936 << "' in <item> within an <overlayable>");
937 error = true;
938 continue;
939 }
940
Adam Lesinski71be7052017-12-12 16:48:07 -0800941 ParsedResource child_resource;
942 child_resource.name.type = *type;
943 child_resource.name.entry = maybe_name.value().to_string();
944 child_resource.source = item_source;
945 child_resource.overlayable = true;
946 out_resource->child_resources.push_back(std::move(child_resource));
Adam Lesinski46c4d722017-08-23 13:03:56 -0700947
948 xml::XmlPullParser::SkipCurrentElement(parser);
949 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
950 diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
951 error = true;
952 }
953 }
954 return !error;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800955}
956
Adam Lesinski71be7052017-12-12 16:48:07 -0800957bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700958 if (ParseSymbolImpl(parser, out_resource)) {
Adam Lesinski71be7052017-12-12 16:48:07 -0800959 out_resource->visibility_level = Visibility::Level::kUndefined;
Adam Lesinski4488f1c2017-05-26 17:33:38 -0700960 out_resource->allow_new = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700961 return true;
962 }
963 return false;
964}
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700965
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700966bool ResourceParser::ParseAttr(xml::XmlPullParser* parser,
967 ParsedResource* out_resource) {
968 return ParseAttrImpl(parser, out_resource, false);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700969}
970
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700971bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
972 ParsedResource* out_resource, bool weak) {
973 out_resource->name.type = ResourceType::kAttr;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700974
975 // Attributes only end up in default configuration.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700976 if (out_resource->config != ConfigDescription::DefaultConfig()) {
977 diag_->Warn(DiagMessage(out_resource->source)
978 << "ignoring configuration '" << out_resource->config
979 << "' for attribute " << out_resource->name);
980 out_resource->config = ConfigDescription::DefaultConfig();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700981 }
982
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700983 uint32_t type_mask = 0;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700984
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700985 Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
986 if (maybe_format) {
987 type_mask = ParseFormatAttribute(maybe_format.value());
988 if (type_mask == 0) {
989 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
990 << "invalid attribute format '" << maybe_format.value()
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700991 << "'");
992 return false;
993 }
994 }
995
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700996 Maybe<int32_t> maybe_min, maybe_max;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700997
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700998 if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
999 StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
1000 if (!min_str.empty()) {
1001 std::u16string min_str16 = util::Utf8ToUtf16(min_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001002 android::Res_value value;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001003 if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(),
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001004 &value)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001005 maybe_min = static_cast<int32_t>(value.data);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001006 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001007 }
1008
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001009 if (!maybe_min) {
1010 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1011 << "invalid 'min' value '" << min_str << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001012 return false;
1013 }
1014 }
1015
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001016 if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
1017 StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
1018 if (!max_str.empty()) {
1019 std::u16string max_str16 = util::Utf8ToUtf16(max_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001020 android::Res_value value;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001021 if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(),
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001022 &value)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001023 maybe_max = static_cast<int32_t>(value.data);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001024 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001025 }
1026
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001027 if (!maybe_max) {
1028 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1029 << "invalid 'max' value '" << max_str << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001030 return false;
1031 }
1032 }
1033
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001034 if ((maybe_min || maybe_max) &&
1035 (type_mask & android::ResTable_map::TYPE_INTEGER) == 0) {
1036 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001037 << "'min' and 'max' can only be used when format='integer'");
1038 return false;
1039 }
1040
1041 struct SymbolComparator {
Yi Kong087e2d42017-05-02 12:49:25 -07001042 bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001043 return a.symbol.name.value() < b.symbol.name.value();
1044 }
1045 };
1046
1047 std::set<Attribute::Symbol, SymbolComparator> items;
1048
1049 std::string comment;
1050 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001051 const size_t depth = parser->depth();
1052 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1053 if (parser->event() == xml::XmlPullParser::Event::kComment) {
Adam Lesinskid5083f62017-01-16 15:07:21 -08001054 comment = util::TrimWhitespace(parser->comment()).to_string();
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001055 continue;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001056 } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001057 // Skip text.
1058 continue;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001059 }
1060
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001061 const Source item_source = source_.WithLine(parser->line_number());
1062 const std::string& element_namespace = parser->element_namespace();
1063 const std::string& element_name = parser->element_name();
1064 if (element_namespace.empty() &&
1065 (element_name == "flag" || element_name == "enum")) {
1066 if (element_name == "enum") {
1067 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
1068 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001069 << "can not define an <enum>; already defined a <flag>");
1070 error = true;
1071 continue;
1072 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001073 type_mask |= android::ResTable_map::TYPE_ENUM;
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001074
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001075 } else if (element_name == "flag") {
1076 if (type_mask & android::ResTable_map::TYPE_ENUM) {
1077 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001078 << "can not define a <flag>; already defined an <enum>");
1079 error = true;
1080 continue;
1081 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001082 type_mask |= android::ResTable_map::TYPE_FLAGS;
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001083 }
1084
1085 if (Maybe<Attribute::Symbol> s =
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001086 ParseEnumOrFlagItem(parser, element_name)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001087 Attribute::Symbol& symbol = s.value();
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001088 ParsedResource child_resource;
1089 child_resource.name = symbol.symbol.name.value();
1090 child_resource.source = item_source;
1091 child_resource.value = util::make_unique<Id>();
1092 out_resource->child_resources.push_back(std::move(child_resource));
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001093
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001094 symbol.symbol.SetComment(std::move(comment));
1095 symbol.symbol.SetSource(item_source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001096
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001097 auto insert_result = items.insert(std::move(symbol));
1098 if (!insert_result.second) {
1099 const Attribute::Symbol& existing_symbol = *insert_result.first;
1100 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001101 << "duplicate symbol '"
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001102 << existing_symbol.symbol.name.value().entry << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001103
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001104 diag_->Note(DiagMessage(existing_symbol.symbol.GetSource())
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001105 << "first defined here");
1106 error = true;
1107 }
1108 } else {
1109 error = true;
1110 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001111 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1112 diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001113 error = true;
1114 }
1115
1116 comment = {};
1117 }
1118
1119 if (error) {
1120 return false;
1121 }
1122
1123 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
1124 attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001125 attr->type_mask =
1126 type_mask ? type_mask : uint32_t(android::ResTable_map::TYPE_ANY);
1127 if (maybe_min) {
1128 attr->min_int = maybe_min.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001129 }
1130
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001131 if (maybe_max) {
1132 attr->max_int = maybe_max.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001133 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001134 out_resource->value = std::move(attr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001135 return true;
1136}
1137
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001138Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001139 xml::XmlPullParser* parser, const StringPiece& tag) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001140 const Source source = source_.WithLine(parser->line_number());
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001141
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001142 Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1143 if (!maybe_name) {
1144 diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001145 << tag << ">");
1146 return {};
1147 }
1148
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001149 Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
1150 if (!maybe_value) {
1151 diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001152 << tag << ">");
1153 return {};
1154 }
1155
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001156 std::u16string value16 = util::Utf8ToUtf16(maybe_value.value());
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001157 android::Res_value val;
1158 if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001159 diag_->Error(DiagMessage(source) << "invalid value '" << maybe_value.value()
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001160 << "' for <" << tag
1161 << ">; must be an integer");
1162 return {};
1163 }
1164
1165 return Attribute::Symbol{
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001166 Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001167 val.data};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001168}
1169
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001170bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
1171 const Source source = source_.WithLine(parser->line_number());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001172
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001173 Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
1174 if (!maybe_name) {
1175 diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001176 return false;
1177 }
1178
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001179 Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001180 if (!maybe_key) {
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001181 diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001182 return false;
1183 }
1184
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001185 ResolvePackage(parser, &maybe_key.value());
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001186 maybe_key.value().SetSource(source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001187
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001188 std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001189 if (!value) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001190 diag_->Error(DiagMessage(source) << "could not parse style item");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001191 return false;
1192 }
1193
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001194 style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001195 return true;
1196}
1197
Adam Lesinski86d67df2017-01-31 13:47:27 -08001198bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001199 ParsedResource* out_resource) {
Adam Lesinski86d67df2017-01-31 13:47:27 -08001200 out_resource->name.type = type;
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001201
1202 std::unique_ptr<Style> style = util::make_unique<Style>();
1203
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001204 Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
1205 if (maybe_parent) {
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001206 // If the parent is empty, we don't have a parent, but we also don't infer either.
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001207 if (!maybe_parent.value().empty()) {
1208 std::string err_str;
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001209 style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001210 if (!style->parent) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001211 diag_->Error(DiagMessage(out_resource->source) << err_str);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001212 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001213 }
1214
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001215 // Transform the namespace prefix to the actual package name, and mark the reference as
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001216 // private if appropriate.
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001217 ResolvePackage(parser, &style->parent.value());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001218 }
1219
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001220 } else {
1221 // No parent was specified, so try inferring it from the style name.
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001222 std::string style_name = out_resource->name.entry;
1223 size_t pos = style_name.find_last_of(u'.');
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001224 if (pos != std::string::npos) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001225 style->parent_inferred = true;
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001226 style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001227 }
1228 }
1229
1230 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001231 const size_t depth = parser->depth();
1232 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1233 if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001234 // Skip text and comments.
1235 continue;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001236 }
1237
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001238 const std::string& element_namespace = parser->element_namespace();
1239 const std::string& element_name = parser->element_name();
1240 if (element_namespace == "" && element_name == "item") {
1241 error |= !ParseStyleItem(parser, style.get());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001242
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001243 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1244 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1245 << ":" << element_name << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001246 error = true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001247 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001248 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001249
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001250 if (error) {
1251 return false;
1252 }
1253
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001254 out_resource->value = std::move(style);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001255 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001256}
1257
Adam Lesinskid5fd76a2017-05-16 12:18:08 -07001258bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1259 uint32_t resource_format = android::ResTable_map::TYPE_ANY;
1260 if (Maybe<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
1261 resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
1262 if (resource_format == 0u) {
1263 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1264 << "'" << format_attr.value() << "' is an invalid format");
1265 return false;
1266 }
1267 }
1268 return ParseArrayImpl(parser, out_resource, resource_format);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001269}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001270
Adam Lesinskid5fd76a2017-05-16 12:18:08 -07001271bool ResourceParser::ParseIntegerArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1272 return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_INTEGER);
Adam Lesinski7ff3ee12015-12-14 16:08:50 -08001273}
1274
Adam Lesinskid5fd76a2017-05-16 12:18:08 -07001275bool ResourceParser::ParseStringArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
1276 return ParseArrayImpl(parser, out_resource, android::ResTable_map::TYPE_STRING);
Adam Lesinski7ff3ee12015-12-14 16:08:50 -08001277}
1278
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001279bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
1280 ParsedResource* out_resource,
Adam Lesinski7ff3ee12015-12-14 16:08:50 -08001281 const uint32_t typeMask) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001282 out_resource->name.type = ResourceType::kArray;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -08001283
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001284 std::unique_ptr<Array> array = util::make_unique<Array>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001285
Adam Lesinski75421622017-01-06 15:20:04 -08001286 bool translatable = options_.translatable;
1287 if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
1288 Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
1289 if (!maybe_translatable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001290 diag_->Error(DiagMessage(out_resource->source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001291 << "invalid value for 'translatable'. Must be a boolean");
1292 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -07001293 }
Adam Lesinski75421622017-01-06 15:20:04 -08001294 translatable = maybe_translatable.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001295 }
Adam Lesinski75421622017-01-06 15:20:04 -08001296 array->SetTranslatable(translatable);
Adam Lesinski458b8772016-04-25 14:20:21 -07001297
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001298 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001299 const size_t depth = parser->depth();
1300 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1301 if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001302 // Skip text and comments.
1303 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001304 }
1305
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001306 const Source item_source = source_.WithLine(parser->line_number());
1307 const std::string& element_namespace = parser->element_namespace();
1308 const std::string& element_name = parser->element_name();
1309 if (element_namespace.empty() && element_name == "item") {
1310 std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001311 if (!item) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001312 diag_->Error(DiagMessage(item_source) << "could not parse array item");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001313 error = true;
1314 continue;
1315 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001316 item->SetSource(item_source);
Adam Lesinski4ffea042017-08-10 15:37:28 -07001317 array->elements.emplace_back(std::move(item));
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001318
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001319 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1320 diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
1321 << "unknown tag <" << element_namespace << ":"
1322 << element_name << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001323 error = true;
1324 }
1325 }
1326
1327 if (error) {
1328 return false;
1329 }
1330
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001331 out_resource->value = std::move(array);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001332 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001333}
1334
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001335bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
1336 ParsedResource* out_resource) {
1337 out_resource->name.type = ResourceType::kPlurals;
Adam Lesinski7ff3ee12015-12-14 16:08:50 -08001338
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001339 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001340
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001341 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001342 const size_t depth = parser->depth();
1343 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1344 if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001345 // Skip text and comments.
1346 continue;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001347 }
1348
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001349 const Source item_source = source_.WithLine(parser->line_number());
1350 const std::string& element_namespace = parser->element_namespace();
1351 const std::string& element_name = parser->element_name();
1352 if (element_namespace.empty() && element_name == "item") {
1353 Maybe<StringPiece> maybe_quantity =
1354 xml::FindNonEmptyAttribute(parser, "quantity");
1355 if (!maybe_quantity) {
1356 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001357 << "<item> in <plurals> requires attribute "
1358 << "'quantity'");
1359 error = true;
1360 continue;
1361 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001362
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001363 StringPiece trimmed_quantity =
1364 util::TrimWhitespace(maybe_quantity.value());
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001365 size_t index = 0;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001366 if (trimmed_quantity == "zero") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001367 index = Plural::Zero;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001368 } else if (trimmed_quantity == "one") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001369 index = Plural::One;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001370 } else if (trimmed_quantity == "two") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001371 index = Plural::Two;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001372 } else if (trimmed_quantity == "few") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001373 index = Plural::Few;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001374 } else if (trimmed_quantity == "many") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001375 index = Plural::Many;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001376 } else if (trimmed_quantity == "other") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001377 index = Plural::Other;
1378 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001379 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001380 << "<item> in <plural> has invalid value '"
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001381 << trimmed_quantity << "' for attribute 'quantity'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001382 error = true;
1383 continue;
1384 }
1385
1386 if (plural->values[index]) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001387 diag_->Error(DiagMessage(item_source) << "duplicate quantity '"
1388 << trimmed_quantity << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001389 error = true;
1390 continue;
1391 }
1392
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001393 if (!(plural->values[index] = ParseXml(
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001394 parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
1395 error = true;
1396 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001397 plural->values[index]->SetSource(item_source);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001398
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001399 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1400 diag_->Error(DiagMessage(item_source) << "unknown tag <"
1401 << element_namespace << ":"
1402 << element_name << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001403 error = true;
1404 }
1405 }
1406
1407 if (error) {
1408 return false;
1409 }
1410
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001411 out_resource->value = std::move(plural);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001412 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001413}
1414
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001415bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
1416 ParsedResource* out_resource) {
1417 out_resource->name.type = ResourceType::kStyleable;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001418
Adam Lesinski71be7052017-12-12 16:48:07 -08001419 // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
1420 out_resource->visibility_level = Visibility::Level::kPublic;
Adam Lesinski9f222042015-11-04 13:51:45 -08001421
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001422 // Declare-styleable only ends up in default config;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001423 if (out_resource->config != ConfigDescription::DefaultConfig()) {
1424 diag_->Warn(DiagMessage(out_resource->source)
1425 << "ignoring configuration '" << out_resource->config
1426 << "' for styleable " << out_resource->name.entry);
1427 out_resource->config = ConfigDescription::DefaultConfig();
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001428 }
1429
1430 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
1431
1432 std::string comment;
1433 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001434 const size_t depth = parser->depth();
1435 while (xml::XmlPullParser::NextChildNode(parser, depth)) {
1436 if (parser->event() == xml::XmlPullParser::Event::kComment) {
Adam Lesinskid5083f62017-01-16 15:07:21 -08001437 comment = util::TrimWhitespace(parser->comment()).to_string();
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001438 continue;
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001439 } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001440 // Ignore text.
1441 continue;
Adam Lesinski52364f72016-01-11 13:10:24 -08001442 }
1443
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001444 const Source item_source = source_.WithLine(parser->line_number());
1445 const std::string& element_namespace = parser->element_namespace();
1446 const std::string& element_name = parser->element_name();
1447 if (element_namespace.empty() && element_name == "attr") {
1448 Maybe<StringPiece> maybe_name =
1449 xml::FindNonEmptyAttribute(parser, "name");
1450 if (!maybe_name) {
1451 diag_->Error(DiagMessage(item_source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001452 << "<attr> tag must have a 'name' attribute");
1453 error = true;
1454 continue;
1455 }
Adam Lesinski7ff3ee12015-12-14 16:08:50 -08001456
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001457 // If this is a declaration, the package name may be in the name. Separate
1458 // these out.
1459 // Eg. <attr name="android:text" />
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001460 Maybe<Reference> maybe_ref =
1461 ResourceUtils::ParseXmlAttributeName(maybe_name.value());
1462 if (!maybe_ref) {
1463 diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
1464 << maybe_name.value() << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001465 error = true;
1466 continue;
1467 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001468
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001469 Reference& child_ref = maybe_ref.value();
Adam Lesinski1ef0fa92017-08-15 21:32:49 -07001470 xml::ResolvePackage(parser, &child_ref);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001471
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001472 // Create the ParsedResource that will add the attribute to the table.
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001473 ParsedResource child_resource;
1474 child_resource.name = child_ref.name.value();
1475 child_resource.source = item_source;
1476 child_resource.comment = std::move(comment);
Adam Lesinski467f1712015-11-16 17:35:44 -08001477
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001478 if (!ParseAttrImpl(parser, &child_resource, true)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001479 error = true;
1480 continue;
1481 }
Adam Lesinski467f1712015-11-16 17:35:44 -08001482
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001483 // Create the reference to this attribute.
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001484 child_ref.SetComment(child_resource.comment);
1485 child_ref.SetSource(item_source);
1486 styleable->entries.push_back(std::move(child_ref));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001487
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001488 out_resource->child_resources.push_back(std::move(child_resource));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001489
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001490 } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
1491 diag_->Error(DiagMessage(item_source) << "unknown tag <"
1492 << element_namespace << ":"
1493 << element_name << ">");
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001494 error = true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001495 }
1496
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001497 comment = {};
1498 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001499
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001500 if (error) {
1501 return false;
1502 }
1503
Adam Lesinskice5e56e2016-10-21 17:56:45 -07001504 out_resource->value = std::move(styleable);
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001505 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001506}
1507
Adam Lesinskicacb28f2016-10-19 12:18:14 -07001508} // namespace aapt