blob: a9d6da0258cbf9f356e7672674b7a2d4faa6317d [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;
56using ::aapt::xml::FindRootElement;
57using ::aapt::xml::NodeCast;
58using ::aapt::xml::XmlActionExecutor;
59using ::aapt::xml::XmlActionExecutorPolicy;
60using ::aapt::xml::XmlNodeAction;
Shane Farmerb1027272017-06-14 09:10:28 -070061using ::android::base::ReadFileToString;
Shane Farmer0a5b2012017-06-22 12:24:12 -070062using ::android::StringPiece;
Shane Farmer74cdea32017-05-12 16:22:36 -070063
Shane Farmer57669432017-06-19 12:52:04 -070064const std::unordered_map<std::string, Abi> kStringToAbiMap = {
65 {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
66 {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
67 {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
68};
69const std::map<Abi, std::string> kAbiToStringMap = {
70 {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"},
71 {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"},
72 {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"},
Shane Farmer74cdea32017-05-12 16:22:36 -070073};
74
75constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
76
77/** A default noop diagnostics context. */
78class NoopDiagnostics : public IDiagnostics {
79 public:
80 void Log(Level level, DiagMessageActual& actualMsg) override {}
81};
82NoopDiagnostics noop_;
83
84std::string GetLabel(const Element* element, IDiagnostics* diag) {
85 std::string label;
86 for (const auto& attr : element->attributes) {
87 if (attr.name == "label") {
88 label = attr.value;
89 break;
90 }
91 }
92
93 if (label.empty()) {
94 diag->Error(DiagMessage() << "No label found for element " << element->name);
95 }
96 return label;
97}
98
99/** XML node visitor that removes all of the namespace URIs from the node and all children. */
100class NamespaceVisitor : public xml::Visitor {
101 public:
102 void Visit(xml::Element* node) override {
103 node->namespace_uri.clear();
104 VisitChildren(node);
105 }
106};
107
108} // namespace
109
Shane Farmer57669432017-06-19 12:52:04 -0700110namespace configuration {
Shane Farmerb1027272017-06-14 09:10:28 -0700111
Shane Farmer57669432017-06-19 12:52:04 -0700112const std::string& AbiToString(Abi abi) {
113 return kAbiToStringMap.find(abi)->second;
114}
115
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700116/**
117 * Attempts to replace the placeholder in the name string with the provided value. Returns true on
118 * success, or false if the either the placeholder is not found in the name, or the value is not
119 * present and the placeholder was.
120 */
Shane Farmer0a5b2012017-06-22 12:24:12 -0700121static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700122 std::string* name, IDiagnostics* diag) {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700123 size_t offset = name->find(placeholder.data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700124 bool found = (offset != std::string::npos);
125
126 // Make sure the placeholder was present if the desired value is present.
127 if (!found) {
128 if (value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700129 diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
130 return false;
131 }
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700132 return true;
133 }
134
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700135 DCHECK(found) << "Missing return path for placeholder not found";
136
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700137 // Make sure the placeholder was not present if the desired value was not present.
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700138 if (!value) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700139 diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700140 return false;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700141 }
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700142
Shane Farmer0a5b2012017-06-22 12:24:12 -0700143 name->replace(offset, placeholder.length(), value.value().data());
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700144
145 // Make sure there was only one instance of the placeholder.
Shane Farmer0a5b2012017-06-22 12:24:12 -0700146 if (name->find(placeholder.data()) != std::string::npos) {
Shane Farmer1a21b8c2017-07-21 09:42:42 -0700147 diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder);
148 return false;
149 }
150 return true;
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700151}
152
Shane Farmer0a5b2012017-06-22 12:24:12 -0700153Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, IDiagnostics* diag,
154 const StringPiece& base_name,
155 const StringPiece& ext) const {
156 std::string result = format.to_string();
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700157
Shane Farmer0a5b2012017-06-22 12:24:12 -0700158 Maybe<StringPiece> maybe_base_name =
159 base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
160 if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700161 return {};
162 }
163
Shane Farmer0a5b2012017-06-22 12:24:12 -0700164 // Extension is optional.
165 if (result.find("${ext}") != std::string::npos) {
166 if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
167 return {};
168 }
169 }
170
171 if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700172 return {};
173 }
174
Shane Farmer0a5b2012017-06-22 12:24:12 -0700175 if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700176 return {};
177 }
178
Shane Farmer0a5b2012017-06-22 12:24:12 -0700179 if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700180 return {};
181 }
182
Shane Farmer0a5b2012017-06-22 12:24:12 -0700183 if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700184 return {};
185 }
186
Shane Farmer0a5b2012017-06-22 12:24:12 -0700187 if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) {
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700188 return {};
189 }
190
Shane Farmer0a5b2012017-06-22 12:24:12 -0700191 if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) {
192 return {};
193 }
194
195 return result;
196}
197
198Maybe<std::string> Artifact::Name(const StringPiece& base_name, const StringPiece& ext,
199 IDiagnostics* diag) const {
200 if (!name) {
201 return {};
202 }
203
204 std::string result = name.value();
205
206 // Base name is optional.
207 if (result.find("${basename}") != std::string::npos) {
208 if (!ReplacePlaceholder("${basename}", {base_name}, &result, diag)) {
209 return {};
210 }
211 }
212
213 // Extension is optional.
214 if (result.find("${ext}") != std::string::npos) {
215 if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
216 return {};
217 }
218 }
219
Shane Farmer9f0e7f12017-06-22 12:26:44 -0700220 return result;
221}
222
Shane Farmer57669432017-06-19 12:52:04 -0700223} // namespace configuration
Shane Farmerb1027272017-06-14 09:10:28 -0700224
225/** Returns a ConfigurationParser for the file located at the provided path. */
226Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
227 std::string contents;
228 if (!ReadFileToString(path, &contents, true)) {
229 return {};
230 }
231 return ConfigurationParser(contents);
232}
233
Shane Farmer74cdea32017-05-12 16:22:36 -0700234ConfigurationParser::ConfigurationParser(std::string contents)
235 : contents_(std::move(contents)),
236 diag_(&noop_) {
237}
238
Shane Farmer280be342017-06-21 15:20:15 -0700239Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
Adam Lesinskiefeb7af2017-08-02 14:57:43 -0700240 StringInputStream in(contents_);
Shane Farmer74cdea32017-05-12 16:22:36 -0700241 auto doc = xml::Inflate(&in, diag_, Source("config.xml"));
242 if (!doc) {
243 return {};
244 }
245
246 // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
247 auto* root = FindRootElement(doc.get());
248 if (root == nullptr) {
249 diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
250 return {};
251 }
252
253 std::string& xml_ns = root->namespace_uri;
254 if (!xml_ns.empty()) {
255 if (xml_ns != kAaptXmlNs) {
256 diag_->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
257 return {};
258 }
259
260 xml_ns.clear();
261 NamespaceVisitor visitor;
262 root->Accept(&visitor);
263 }
264
265 XmlActionExecutor executor;
266 XmlNodeAction& root_action = executor["post-process"];
267 XmlNodeAction& artifacts_action = root_action["artifacts"];
268 XmlNodeAction& groups_action = root_action["groups"];
269
Shane Farmer280be342017-06-21 15:20:15 -0700270 PostProcessingConfiguration config;
Shane Farmer74cdea32017-05-12 16:22:36 -0700271
272 // Helper to bind a static method to an action handler in the DOM executor.
Shane Farmer280be342017-06-21 15:20:15 -0700273 auto bind_handler =
274 [&config](std::function<bool(PostProcessingConfiguration*, Element*, IDiagnostics*)> h)
Shane Farmer74cdea32017-05-12 16:22:36 -0700275 -> XmlNodeAction::ActionFuncWithDiag {
276 return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
277 };
278
279 // Parse the artifact elements.
280 artifacts_action["artifact"].Action(bind_handler(artifact_handler_));
281 artifacts_action["artifact-format"].Action(bind_handler(artifact_format_handler_));
282
283 // Parse the different configuration groups.
284 groups_action["abi-group"].Action(bind_handler(abi_group_handler_));
285 groups_action["screen-density-group"].Action(bind_handler(screen_density_group_handler_));
286 groups_action["locale-group"].Action(bind_handler(locale_group_handler_));
287 groups_action["android-sdk-group"].Action(bind_handler(android_sdk_group_handler_));
288 groups_action["gl-texture-group"].Action(bind_handler(gl_texture_group_handler_));
289 groups_action["device-feature-group"].Action(bind_handler(device_feature_group_handler_));
290
291 if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag_, doc.get())) {
292 diag_->Error(DiagMessage() << "Could not process XML document");
293 return {};
294 }
295
Shane Farmer57669432017-06-19 12:52:04 -0700296 // TODO: Validate all references in the configuration are valid. It should be safe to assume from
297 // this point on that any references from one section to another will be present.
298
Shane Farmer74cdea32017-05-12 16:22:36 -0700299 return {config};
300}
301
302ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700303 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
304 Artifact artifact{};
305 for (const auto& attr : root_element->attributes) {
306 if (attr.name == "name") {
307 artifact.name = attr.value;
308 } else if (attr.name == "abi-group") {
309 artifact.abi_group = {attr.value};
310 } else if (attr.name == "screen-density-group") {
311 artifact.screen_density_group = {attr.value};
312 } else if (attr.name == "locale-group") {
313 artifact.locale_group = {attr.value};
314 } else if (attr.name == "android-sdk-group") {
315 artifact.android_sdk_group = {attr.value};
316 } else if (attr.name == "gl-texture-group") {
317 artifact.gl_texture_group = {attr.value};
318 } else if (attr.name == "device-feature-group") {
319 artifact.device_feature_group = {attr.value};
320 } else {
321 diag->Note(DiagMessage() << "Unknown artifact attribute: " << attr.name << " = "
322 << attr.value);
323 }
324 }
325 config->artifacts.push_back(artifact);
326 return true;
327};
Shane Farmer74cdea32017-05-12 16:22:36 -0700328
329ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700330 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
331 for (auto& node : root_element->children) {
332 xml::Text* t;
333 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
334 config->artifact_format = TrimWhitespace(t->text).to_string();
335 break;
336 }
337 }
338 return true;
339};
340
341ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
342 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
343 std::string label = GetLabel(root_element, diag);
344 if (label.empty()) {
345 return false;
346 }
347
348 auto& group = config->abi_groups[label];
349 bool valid = true;
350
351 for (auto* child : root_element->GetChildElements()) {
352 if (child->name != "abi") {
353 diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
354 valid = false;
355 } else {
356 for (auto& node : child->children) {
Shane Farmer74cdea32017-05-12 16:22:36 -0700357 xml::Text* t;
358 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
Shane Farmer280be342017-06-21 15:20:15 -0700359 group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
Shane Farmer74cdea32017-05-12 16:22:36 -0700360 break;
361 }
362 }
Shane Farmer280be342017-06-21 15:20:15 -0700363 }
364 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700365
Shane Farmer280be342017-06-21 15:20:15 -0700366 return valid;
367};
Shane Farmer74cdea32017-05-12 16:22:36 -0700368
369ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700370 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
371 std::string label = GetLabel(root_element, diag);
372 if (label.empty()) {
373 return false;
374 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700375
Shane Farmer280be342017-06-21 15:20:15 -0700376 auto& group = config->screen_density_groups[label];
377 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700378
Shane Farmer280be342017-06-21 15:20:15 -0700379 for (auto* child : root_element->GetChildElements()) {
380 if (child->name != "screen-density") {
381 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
382 << child->name);
383 valid = false;
384 } else {
385 for (auto& node : child->children) {
386 xml::Text* t;
387 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
388 ConfigDescription config_descriptor;
389 const android::StringPiece& text = TrimWhitespace(t->text);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700390 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
391 if (parsed &&
392 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
393 android::ResTable_config::CONFIG_DENSITY)) {
Shane Farmer280be342017-06-21 15:20:15 -0700394 // Copy the density with the minimum SDK version stripped out.
395 group.push_back(config_descriptor.CopyWithoutSdkVersion());
396 } else {
397 diag->Error(DiagMessage()
398 << "Could not parse config descriptor for screen-density: " << text);
399 valid = false;
Shane Farmer74cdea32017-05-12 16:22:36 -0700400 }
Shane Farmer280be342017-06-21 15:20:15 -0700401 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700402 }
403 }
Shane Farmer280be342017-06-21 15:20:15 -0700404 }
405 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700406
Shane Farmer280be342017-06-21 15:20:15 -0700407 return valid;
408};
Shane Farmer74cdea32017-05-12 16:22:36 -0700409
410ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700411 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
412 std::string label = GetLabel(root_element, diag);
413 if (label.empty()) {
414 return false;
415 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700416
Shane Farmer280be342017-06-21 15:20:15 -0700417 auto& group = config->locale_groups[label];
418 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700419
Shane Farmer280be342017-06-21 15:20:15 -0700420 for (auto* child : root_element->GetChildElements()) {
421 if (child->name != "locale") {
422 diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
423 << child->name);
424 valid = false;
425 } else {
Shane Farmer0a5b2012017-06-22 12:24:12 -0700426 for (auto& node : child->children) {
427 xml::Text* t;
428 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
429 ConfigDescription config_descriptor;
430 const android::StringPiece& text = TrimWhitespace(t->text);
431 bool parsed = ConfigDescription::Parse(text, &config_descriptor);
432 if (parsed &&
433 (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
434 android::ResTable_config::CONFIG_LOCALE)) {
435 // Copy the locale with the minimum SDK version stripped out.
436 group.push_back(config_descriptor.CopyWithoutSdkVersion());
437 } else {
438 diag->Error(DiagMessage()
439 << "Could not parse config descriptor for screen-density: " << text);
440 valid = false;
441 }
442 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700443 }
444 }
Shane Farmer280be342017-06-21 15:20:15 -0700445 }
446 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700447
Shane Farmer280be342017-06-21 15:20:15 -0700448 return valid;
449};
Shane Farmer74cdea32017-05-12 16:22:36 -0700450
451ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700452 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
453 std::string label = GetLabel(root_element, diag);
454 if (label.empty()) {
455 return false;
456 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700457
Shane Farmer280be342017-06-21 15:20:15 -0700458 auto& group = config->android_sdk_groups[label];
459 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700460
Shane Farmer280be342017-06-21 15:20:15 -0700461 for (auto* child : root_element->GetChildElements()) {
462 if (child->name != "android-sdk") {
463 diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
464 valid = false;
465 } else {
466 AndroidSdk entry;
467 for (const auto& attr : child->attributes) {
468 if (attr.name == "minSdkVersion") {
469 entry.min_sdk_version = {attr.value};
470 } else if (attr.name == "targetSdkVersion") {
471 entry.target_sdk_version = {attr.value};
472 } else if (attr.name == "maxSdkVersion") {
473 entry.max_sdk_version = {attr.value};
Shane Farmer74cdea32017-05-12 16:22:36 -0700474 } else {
Shane Farmer280be342017-06-21 15:20:15 -0700475 diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
Shane Farmer74cdea32017-05-12 16:22:36 -0700476 }
477 }
478
Shane Farmer280be342017-06-21 15:20:15 -0700479 // TODO: Fill in the manifest details when they are finalised.
480 for (auto node : child->GetChildElements()) {
481 if (node->name == "manifest") {
482 if (entry.manifest) {
483 diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
484 continue;
485 }
486 entry.manifest = {AndroidManifest()};
487 }
488 }
489
490 group.push_back(entry);
491 }
492 }
493
494 return valid;
495};
Shane Farmer74cdea32017-05-12 16:22:36 -0700496
497ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700498 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
499 std::string label = GetLabel(root_element, diag);
500 if (label.empty()) {
501 return false;
502 }
503
504 auto& group = config->gl_texture_groups[label];
505 bool valid = true;
506
507 GlTexture result;
508 for (auto* child : root_element->GetChildElements()) {
509 if (child->name != "gl-texture") {
510 diag->Error(DiagMessage() << "Unexpected element in GL texture group: " << child->name);
511 valid = false;
512 } else {
513 for (const auto& attr : child->attributes) {
514 if (attr.name == "name") {
515 result.name = attr.value;
516 break;
517 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700518 }
519
Shane Farmer280be342017-06-21 15:20:15 -0700520 for (auto* element : child->GetChildElements()) {
521 if (element->name != "texture-path") {
522 diag->Error(DiagMessage() << "Unexpected element in gl-texture element: " << child->name);
Shane Farmer74cdea32017-05-12 16:22:36 -0700523 valid = false;
Shane Farmer280be342017-06-21 15:20:15 -0700524 continue;
525 }
526 for (auto& node : element->children) {
527 xml::Text* t;
528 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
529 result.texture_paths.push_back(TrimWhitespace(t->text).to_string());
Shane Farmer74cdea32017-05-12 16:22:36 -0700530 }
531 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700532 }
Shane Farmer280be342017-06-21 15:20:15 -0700533 }
534 group.push_back(result);
535 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700536
Shane Farmer280be342017-06-21 15:20:15 -0700537 return valid;
538};
Shane Farmer74cdea32017-05-12 16:22:36 -0700539
540ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
Shane Farmer280be342017-06-21 15:20:15 -0700541 [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
542 std::string label = GetLabel(root_element, diag);
543 if (label.empty()) {
544 return false;
545 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700546
Shane Farmer280be342017-06-21 15:20:15 -0700547 auto& group = config->device_feature_groups[label];
548 bool valid = true;
Shane Farmer74cdea32017-05-12 16:22:36 -0700549
Shane Farmer280be342017-06-21 15:20:15 -0700550 for (auto* child : root_element->GetChildElements()) {
551 if (child->name != "supports-feature") {
552 diag->Error(DiagMessage() << "Unexpected root_element in device feature group: "
553 << child->name);
554 valid = false;
555 } else {
556 for (auto& node : child->children) {
557 xml::Text* t;
558 if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
559 group.push_back(TrimWhitespace(t->text).to_string());
560 break;
Shane Farmer74cdea32017-05-12 16:22:36 -0700561 }
562 }
Shane Farmer280be342017-06-21 15:20:15 -0700563 }
564 }
Shane Farmer74cdea32017-05-12 16:22:36 -0700565
Shane Farmer280be342017-06-21 15:20:15 -0700566 return valid;
567};
Shane Farmer74cdea32017-05-12 16:22:36 -0700568
569} // namespace aapt