blob: 424e9be3ef097ad965d54fcdcab34da34dd4607f [file] [log] [blame]
Shane Farmer74cdea32017-05-12 16:22:36 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "configuration/ConfigurationParser.h"
18
19#include <algorithm>
20#include <functional>
Shane Farmer57669432017-06-19 12:52:04 -070021#include <map>
Shane Farmer74cdea32017-05-12 16:22:36 -070022#include <memory>
23#include <utility>
24
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070025#include "android-base/file.h"
26#include "android-base/logging.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070027
28#include "ConfigDescription.h"
29#include "Diagnostics.h"
Shane Farmerb1027272017-06-14 09:10:28 -070030#include "io/File.h"
31#include "io/FileSystem.h"
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070032#include "io/StringInputStream.h"
Shane Farmer9f0e7f12017-06-22 12:26:44 -070033#include "util/Maybe.h"
Shane Farmer74cdea32017-05-12 16:22:36 -070034#include "util/Util.h"
35#include "xml/XmlActionExecutor.h"
36#include "xml/XmlDom.h"
37#include "xml/XmlUtil.h"
38
39namespace aapt {
40
41namespace {
42
43using ::aapt::configuration::Abi;
44using ::aapt::configuration::AndroidManifest;
45using ::aapt::configuration::AndroidSdk;
46using ::aapt::configuration::Artifact;
Shane Farmer280be342017-06-21 15:20:15 -070047using ::aapt::configuration::PostProcessingConfiguration;
Shane Farmer74cdea32017-05-12 16:22:36 -070048using ::aapt::configuration::GlTexture;
49using ::aapt::configuration::Group;
50using ::aapt::configuration::Locale;
Shane Farmerb1027272017-06-14 09:10:28 -070051using ::aapt::io::IFile;
52using ::aapt::io::RegularFile;
Adam Lesinskiefeb7af2017-08-02 14:57:43 -070053using ::aapt::io::StringInputStream;
Shane Farmer74cdea32017-05-12 16:22:36 -070054using ::aapt::util::TrimWhitespace;
55using ::aapt::xml::Element;
Shane Farmer74cdea32017-05-12 16:22:36 -070056using ::aapt::xml::NodeCast;
57using ::aapt::xml::XmlActionExecutor;
58using ::aapt::xml::XmlActionExecutorPolicy;
59using ::aapt::xml::XmlNodeAction;
Shane Farmerb1027272017-06-14 09:10:28 -070060using ::android::base::ReadFileToString;
Shane Farmer0a5b2012017-06-22 12:24:12 -070061using ::android::StringPiece;
Shane Farmer74cdea32017-05-12 16:22:36 -070062
Shane Farmer57669432017-06-19 12:52:04 -070063const std::unordered_map<std::string, Abi> kStringToAbiMap = {
64 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
65 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
66 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
67};
68const std::map<Abi, std::string> kAbiToStringMap = {
69 {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"},
70 {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"},
71 {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"},
Shane Farmer74cdea32017-05-12 16:22:36 -070072};
73
74constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
75
76/** A default noop diagnostics context. */
77class NoopDiagnostics : public IDiagnostics {
78 public:
79 void Log(Level level, DiagMessageActual& actualMsg) override {}
80};
81NoopDiagnostics noop_;
82
83std::string GetLabel(const Element* element, IDiagnostics* diag) {
84 std::string label;
85 for (const auto& attr : element->attributes) {
86 if (attr.name == "label") {
87 label = attr.value;
88 break;
89 }
90 }
91
92 if (label.empty()) {
93 diag->Error(DiagMessage() << "No label found for element " << element->name);
94 }
95 return label;
96}
97
98/** XML node visitor that removes all of the namespace URIs from the node and all children. */
99class NamespaceVisitor : public xml::Visitor {
100 public:
101 void Visit(xml::Element* node) override {
102 node->namespace_uri.clear();
103 VisitChildren(node);
104 }
105};
106
107} // namespace
108
Shane Farmer57669432017-06-19 12:52:04 -0700109namespace configuration {
Shane Farmerb1027272017-06-14 09:10:28 -0700110
Shane Farmer57669432017-06-19 12:52:04 -0700111const std::string& AbiToString(Abi abi) {
112 return kAbiToStringMap.find(abi)->second;
113}
114
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700115/**
116 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
117 * success, or false if the either the placeholder is not found in the name, or the value is not
118 * present and the placeholder was.
119 */
Shane Farmer0a5b2012017-06-22 12:24:12 -0700120static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700121 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700122 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700123 bool found = (offset != std::string::npos);
124
125 // Make sure the placeholder was present if the desired value is present.
126 if (!found) {
127 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700128 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
129 return false;
130 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700131 return true;
132 }
133
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700134 DCHECK(found) << "Missing return path for placeholder not found";
135
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700136 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700137 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700138 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700139 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700140 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700141
Shane Farmer0a5b2012017-06-22 12:24:12 -0700142 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700143
144 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700145 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700146 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
147 return false;
148 }
149 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700150}
151
Shane Farmer0a5b2012017-06-22 12:24:12 -0700152Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, IDiagnostics* diag,
153 const StringPiece& base_name,
154 const StringPiece& ext) const {
155 std::string result = format.to_string();
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700156
Shane Farmer0a5b2012017-06-22 12:24:12 -0700157 Maybe<StringPiece> maybe_base_name =
158 base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
159 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700160 return {};
161 }
162
Shane Farmer0a5b2012017-06-22 12:24:12 -0700163 // Extension is optional.
164 if (result.find("${ext}") != std::string::npos) {
165 if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
166 return {};
167 }
168 }
169
170 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700171 return {};
172 }
173
Shane Farmer0a5b2012017-06-22 12:24:12 -0700174 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700175 return {};
176 }
177
Shane Farmer0a5b2012017-06-22 12:24:12 -0700178 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700179 return {};
180 }
181
Shane Farmer0a5b2012017-06-22 12:24:12 -0700182 if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700183 return {};
184 }
185
Shane Farmer0a5b2012017-06-22 12:24:12 -0700186 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700187 return {};
188 }
189
Shane Farmer0a5b2012017-06-22 12:24:12 -0700190 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
191 return {};
192 }
193
194 return result;
195}
196
197Maybe<std::string> Artifact::Name(const StringPiece& base_name, const StringPiece& ext,
198 IDiagnostics* diag) const {
199 if (!name) {
200 return {};
201 }
202
203 std::string result = name.value();
204
205 // Base name is optional.
206 if (result.find("${basename}") != std::string::npos) {
207 if (!ReplacePlaceholder("${basename}", {base_name}, &result, diag)) {
208 return {};
209 }
210 }
211
212 // Extension is optional.
213 if (result.find("${ext}") != std::string::npos) {
214 if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
215 return {};
216 }
217 }
218
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700219 return result;
220}
221
Shane Farmer57669432017-06-19 12:52:04 -0700222} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700223
224/** Returns a ConfigurationParser for the file located at the provided path. */
225Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
226 std::string contents;
227 if (!ReadFileToString(path, &contents, true)) {
228 return {};
229 }
230 return ConfigurationParser(contents);
231}
232
Shane Farmer74cdea32017-05-12 16:22:36 -0700233ConfigurationParser::ConfigurationParser(std::string contents)
234 : contents_(std::move(contents)),
235 diag_(&noop_) {
236}
237
Shane Farmer280be342017-06-21 15:20:15 -0700238Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
Adam Lesinskiefeb7af2017-08-02 14:57:43 -0700239 StringInputStream in(contents_);
Adam Lesinski6b372992017-08-09 10:54:23 -0700240 std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag_, Source("config.xml"));
Shane Farmer74cdea32017-05-12 16:22:36 -0700241 if (!doc) {
242 return {};
243 }
244
245 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
Adam Lesinski6b372992017-08-09 10:54:23 -0700246 Element* root = doc->root.get();
Shane Farmer74cdea32017-05-12 16:22:36 -0700247 if (root == nullptr) {
248 diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
249 return {};
250 }
251
252 std::string& xml_ns = root->namespace_uri;
253 if (!xml_ns.empty()) {
254 if (xml_ns != kAaptXmlNs) {
255 diag_->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
256 return {};
257 }
258
259 xml_ns.clear();
260 NamespaceVisitor visitor;
261 root->Accept(&visitor);
262 }
263
264 XmlActionExecutor executor;
265 XmlNodeAction& root_action = executor["post-process"];
266 XmlNodeAction& artifacts_action = root_action["artifacts"];
267 XmlNodeAction& groups_action = root_action["groups"];
268
Shane Farmer280be342017-06-21 15:20:15 -0700269 PostProcessingConfiguration config;
Shane Farmer74cdea32017-05-12 16:22:36 -0700270
271 // Helper to bind a static method to an action handler in the DOM executor.
Shane Farmer280be342017-06-21 15:20:15 -0700272 auto bind_handler =
273 [&config](std::function<bool(PostProcessingConfiguration*, Element*, IDiagnostics*)> h)
Shane Farmer74cdea32017-05-12 16:22:36 -0700274 -> XmlNodeAction::ActionFuncWithDiag {
275 return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
276 };
277
278 // Parse the artifact elements.
279 artifacts_action["artifact"].Action(bind_handler(artifact_handler_));
280 artifacts_action["artifact-format"].Action(bind_handler(artifact_format_handler_));
281
282 // Parse the different configuration groups.
283 groups_action["abi-group"].Action(bind_handler(abi_group_handler_));
284 groups_action["screen-density-group"].Action(bind_handler(screen_density_group_handler_));
285 groups_action["locale-group"].Action(bind_handler(locale_group_handler_));
286 groups_action["android-sdk-group"].Action(bind_handler(android_sdk_group_handler_));
287 groups_action["gl-texture-group"].Action(bind_handler(gl_texture_group_handler_));
288 groups_action["device-feature-group"].Action(bind_handler(device_feature_group_handler_));
289
290 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag_, doc.get())) {
291 diag_->Error(DiagMessage() << "Could not process XML document");
292 return {};
293 }
294
Shane Farmer57669432017-06-19 12:52:04 -0700295 // TODO: Validate all references in the configuration are valid. It should be safe to assume from
296 // this point on that any references from one section to another will be present.
297
Shane Farmer74cdea32017-05-12 16:22:36 -0700298 return {config};
299}
300
301ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700302 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
303 Artifact artifact{};
304 for (const auto& attr : root_element->attributes) {
305 if (attr.name == "name") {
306 artifact.name = attr.value;
307 } else if (attr.name == "abi-group") {
308 artifact.abi_group = {attr.value};
309 } else if (attr.name == "screen-density-group") {
310 artifact.screen_density_group = {attr.value};
311 } else if (attr.name == "locale-group") {
312 artifact.locale_group = {attr.value};
313 } else if (attr.name == "android-sdk-group") {
314 artifact.android_sdk_group = {attr.value};
315 } else if (attr.name == "gl-texture-group") {
316 artifact.gl_texture_group = {attr.value};
317 } else if (attr.name == "device-feature-group") {
318 artifact.device_feature_group = {attr.value};
319 } else {
320 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
321 << attr.value);
322 }
323 }
324 config->artifacts.push_back(artifact);
325 return true;
326};
Shane Farmer74cdea32017-05-12 16:22:36 -0700327
328ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700329 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
330 for (auto& node : root_element->children) {
331 xml::Text* t;
332 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
333 config->artifact_format = TrimWhitespace(t->text).to_string();
334 break;
335 }
336 }
337 return true;
338};
339
340ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
341 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
342 std::string label = GetLabel(root_element, diag);
343 if (label.empty()) {
344 return false;
345 }
346
347 auto& group = config->abi_groups[label];
348 bool valid = true;
349
350 for (auto* child : root_element->GetChildElements()) {
351 if (child->name != "abi") {
352 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
353 valid = false;
354 } else {
355 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700356 xml::Text* t;
357 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer280be342017-06-21 15:20:15 -0700358 group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
Shane Farmer74cdea32017-05-12 16:22:36 -0700359 break;
360 }
361 }
Shane Farmer280be342017-06-21 15:20:15 -0700362 }
363 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700364
Shane Farmer280be342017-06-21 15:20:15 -0700365 return valid;
366};
Shane Farmer74cdea32017-05-12 16:22:36 -0700367
368ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700369 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
370 std::string label = GetLabel(root_element, diag);
371 if (label.empty()) {
372 return false;
373 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700374
Shane Farmer280be342017-06-21 15:20:15 -0700375 auto& group = config->screen_density_groups[label];
376 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700377
Shane Farmer280be342017-06-21 15:20:15 -0700378 for (auto* child : root_element->GetChildElements()) {
379 if (child->name != "screen-density") {
380 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
381 << child->name);
382 valid = false;
383 } else {
384 for (auto& node : child->children) {
385 xml::Text* t;
386 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
387 ConfigDescription config_descriptor;
388 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700389 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
390 if (parsed &&
391 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
392 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700393 // Copy the density with the minimum SDK version stripped out.
394 group.push_back(config_descriptor.CopyWithoutSdkVersion());
395 } else {
396 diag->Error(DiagMessage()
397 << "Could not parse config descriptor for screen-density: " << text);
398 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700399 }
Shane Farmer280be342017-06-21 15:20:15 -0700400 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700401 }
402 }
Shane Farmer280be342017-06-21 15:20:15 -0700403 }
404 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700405
Shane Farmer280be342017-06-21 15:20:15 -0700406 return valid;
407};
Shane Farmer74cdea32017-05-12 16:22:36 -0700408
409ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700410 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
411 std::string label = GetLabel(root_element, diag);
412 if (label.empty()) {
413 return false;
414 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700415
Shane Farmer280be342017-06-21 15:20:15 -0700416 auto& group = config->locale_groups[label];
417 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700418
Shane Farmer280be342017-06-21 15:20:15 -0700419 for (auto* child : root_element->GetChildElements()) {
420 if (child->name != "locale") {
421 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
422 << child->name);
423 valid = false;
424 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700425 for (auto& node : child->children) {
426 xml::Text* t;
427 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
428 ConfigDescription config_descriptor;
429 const android::StringPiece& text = TrimWhitespace(t->text);
430 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
431 if (parsed &&
432 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
433 android::ResTable_config::CONFIG_LOCALE)) {
434 // Copy the locale with the minimum SDK version stripped out.
435 group.push_back(config_descriptor.CopyWithoutSdkVersion());
436 } else {
437 diag->Error(DiagMessage()
438 << "Could not parse config descriptor for screen-density: " << text);
439 valid = false;
440 }
441 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700442 }
443 }
Shane Farmer280be342017-06-21 15:20:15 -0700444 }
445 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700446
Shane Farmer280be342017-06-21 15:20:15 -0700447 return valid;
448};
Shane Farmer74cdea32017-05-12 16:22:36 -0700449
450ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700451 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
452 std::string label = GetLabel(root_element, diag);
453 if (label.empty()) {
454 return false;
455 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700456
Shane Farmer280be342017-06-21 15:20:15 -0700457 auto& group = config->android_sdk_groups[label];
458 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700459
Shane Farmer280be342017-06-21 15:20:15 -0700460 for (auto* child : root_element->GetChildElements()) {
461 if (child->name != "android-sdk") {
462 diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
463 valid = false;
464 } else {
465 AndroidSdk entry;
466 for (const auto& attr : child->attributes) {
467 if (attr.name == "minSdkVersion") {
468 entry.min_sdk_version = {attr.value};
469 } else if (attr.name == "targetSdkVersion") {
470 entry.target_sdk_version = {attr.value};
471 } else if (attr.name == "maxSdkVersion") {
472 entry.max_sdk_version = {attr.value};
Shane Farmer74cdea32017-05-12 16:22:36 -0700473 } else {
Shane Farmer280be342017-06-21 15:20:15 -0700474 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700475 }
476 }
477
Shane Farmer280be342017-06-21 15:20:15 -0700478 // TODO: Fill in the manifest details when they are finalised.
479 for (auto node : child->GetChildElements()) {
480 if (node->name == "manifest") {
481 if (entry.manifest) {
482 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
483 continue;
484 }
485 entry.manifest = {AndroidManifest()};
486 }
487 }
488
489 group.push_back(entry);
490 }
491 }
492
493 return valid;
494};
Shane Farmer74cdea32017-05-12 16:22:36 -0700495
496ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700497 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
498 std::string label = GetLabel(root_element, diag);
499 if (label.empty()) {
500 return false;
501 }
502
503 auto& group = config->gl_texture_groups[label];
504 bool valid = true;
505
506 GlTexture result;
507 for (auto* child : root_element->GetChildElements()) {
508 if (child->name != "gl-texture") {
509 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
510 valid = false;
511 } else {
512 for (const auto& attr : child->attributes) {
513 if (attr.name == "name") {
514 result.name = attr.value;
515 break;
516 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700517 }
518
Shane Farmer280be342017-06-21 15:20:15 -0700519 for (auto* element : child->GetChildElements()) {
520 if (element->name != "texture-path") {
521 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700522 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700523 continue;
524 }
525 for (auto& node : element->children) {
526 xml::Text* t;
527 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
528 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700529 }
530 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700531 }
Shane Farmer280be342017-06-21 15:20:15 -0700532 }
533 group.push_back(result);
534 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700535
Shane Farmer280be342017-06-21 15:20:15 -0700536 return valid;
537};
Shane Farmer74cdea32017-05-12 16:22:36 -0700538
539ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700540 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
541 std::string label = GetLabel(root_element, diag);
542 if (label.empty()) {
543 return false;
544 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700545
Shane Farmer280be342017-06-21 15:20:15 -0700546 auto& group = config->device_feature_groups[label];
547 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700548
Shane Farmer280be342017-06-21 15:20:15 -0700549 for (auto* child : root_element->GetChildElements()) {
550 if (child->name != "supports-feature") {
551 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
552 << child->name);
553 valid = false;
554 } else {
555 for (auto& node : child->children) {
556 xml::Text* t;
557 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
558 group.push_back(TrimWhitespace(t->text).to_string());
559 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700560 }
561 }
Shane Farmer280be342017-06-21 15:20:15 -0700562 }
563 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700564
Shane Farmer280be342017-06-21 15:20:15 -0700565 return valid;
566};
Shane Farmer74cdea32017-05-12 16:22:36 -0700567
568} // namespace aapt