blob: fce9b338d726898b6e6c2f63ddc2eed63b1eda80 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
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
17#include "ResourceUtils.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070018
19#include <sstream>
20
21#include "androidfw/ResourceTypes.h"
22
Adam Lesinskicacb28f2016-10-19 12:18:14 -070023#include "NameMangler.h"
Adam Lesinskifb6312f2016-06-28 14:40:32 -070024#include "SdkConstants.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080025#include "flatten/ResourceTypeExtensions.h"
Adam Lesinskia6fe3452015-12-09 15:20:52 -080026#include "util/Files.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027#include "util/Util.h"
28
Adam Lesinski1ab598f2015-08-14 14:26:04 -070029namespace aapt {
30namespace ResourceUtils {
31
Adam Lesinskice5e56e2016-10-21 17:56:45 -070032Maybe<ResourceName> ToResourceName(
33 const android::ResTable::resource_name& name_in) {
34 ResourceName name_out;
35 if (!name_in.package) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070036 return {};
37 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070038
Adam Lesinskice5e56e2016-10-21 17:56:45 -070039 name_out.package =
40 util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
Adam Lesinskid0f116b2016-07-08 15:00:32 -070041
Adam Lesinskicacb28f2016-10-19 12:18:14 -070042 const ResourceType* type;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070043 if (name_in.type) {
44 type = ParseResourceType(
45 util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
46 } else if (name_in.type8) {
47 type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
Adam Lesinskicacb28f2016-10-19 12:18:14 -070048 } else {
49 return {};
50 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070051
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 if (!type) {
53 return {};
54 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070055
Adam Lesinskice5e56e2016-10-21 17:56:45 -070056 name_out.type = *type;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070057
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058 if (name_in.name) {
59 name_out.entry =
60 util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
61 } else if (name_in.name8) {
62 name_out.entry = StringPiece(name_in.name8, name_in.nameLen).ToString();
Adam Lesinskicacb28f2016-10-19 12:18:14 -070063 } else {
64 return {};
65 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070066 return name_out;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070067}
68
Adam Lesinskice5e56e2016-10-21 17:56:45 -070069bool ExtractResourceName(const StringPiece& str, StringPiece* out_package,
70 StringPiece* out_type, StringPiece* out_entry) {
71 bool has_package_separator = false;
72 bool has_type_separator = false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070073 const char* start = str.data();
74 const char* end = start + str.size();
75 const char* current = start;
76 while (current != end) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070077 if (out_type->size() == 0 && *current == '/') {
78 has_type_separator = true;
79 out_type->assign(start, current - start);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070080 start = current + 1;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 } else if (out_package->size() == 0 && *current == ':') {
82 has_package_separator = true;
83 out_package->assign(start, current - start);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 start = current + 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070085 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070086 current++;
87 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070088 out_entry->assign(start, end - start);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080089
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 return !(has_package_separator && out_package->empty()) &&
91 !(has_type_separator && out_type->empty());
Adam Lesinski1ab598f2015-08-14 14:26:04 -070092}
93
Adam Lesinskice5e56e2016-10-21 17:56:45 -070094bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
95 bool* out_private) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070096 if (str.empty()) {
97 return false;
98 }
99
100 size_t offset = 0;
101 bool priv = false;
102 if (str.data()[0] == '*') {
103 priv = true;
104 offset = 1;
105 }
106
107 StringPiece package;
108 StringPiece type;
109 StringPiece entry;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700110 if (!ExtractResourceName(str.substr(offset, str.size() - offset), &package,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700111 &type, &entry)) {
112 return false;
113 }
114
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700115 const ResourceType* parsed_type = ParseResourceType(type);
116 if (!parsed_type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700117 return false;
118 }
119
120 if (entry.empty()) {
121 return false;
122 }
123
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700124 if (out_ref) {
125 out_ref->package = package;
126 out_ref->type = *parsed_type;
127 out_ref->entry = entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700128 }
129
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700130 if (out_private) {
131 *out_private = priv;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 }
133 return true;
134}
135
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700136bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
137 bool* out_create, bool* out_private) {
138 StringPiece trimmed_str(util::TrimWhitespace(str));
139 if (trimmed_str.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700140 return false;
141 }
142
143 bool create = false;
144 bool priv = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700145 if (trimmed_str.data()[0] == '@') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700146 size_t offset = 1;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700147 if (trimmed_str.data()[1] == '+') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 create = true;
149 offset += 1;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800150 }
151
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700152 ResourceNameRef name;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700153 if (!ParseResourceName(
154 trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700155 &priv)) {
156 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800157 }
158
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159 if (create && priv) {
160 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800161 }
162
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700163 if (create && name.type != ResourceType::kId) {
164 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800165 }
166
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700167 if (out_ref) {
168 *out_ref = name;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700169 }
170
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700171 if (out_create) {
172 *out_create = create;
Adam Lesinski467f1712015-11-16 17:35:44 -0800173 }
174
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700175 if (out_private) {
176 *out_private = priv;
Adam Lesinski467f1712015-11-16 17:35:44 -0800177 }
178 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700179 }
180 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700181}
182
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700183bool IsReference(const StringPiece& str) {
184 return ParseReference(str, nullptr, nullptr, nullptr);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800185}
186
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700187bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
188 StringPiece trimmed_str(util::TrimWhitespace(str));
189 if (trimmed_str.empty()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700191 }
192
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700193 if (*trimmed_str.data() == '?') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 StringPiece package;
195 StringPiece type;
196 StringPiece entry;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700197 if (!ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1),
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700198 &package, &type, &entry)) {
199 return false;
200 }
201
202 if (!type.empty() && type != "attr") {
203 return false;
204 }
205
206 if (entry.empty()) {
207 return false;
208 }
209
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700210 if (out_ref) {
211 out_ref->package = package;
212 out_ref->type = ResourceType::kAttr;
213 out_ref->entry = entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700214 }
215 return true;
216 }
217 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700218}
219
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700220bool IsAttributeReference(const StringPiece& str) {
221 return ParseAttributeReference(str, nullptr);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800222}
223
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700224/*
225 * Style parent's are a bit different. We accept the following formats:
226 *
Adam Lesinski52364f72016-01-11 13:10:24 -0800227 * @[[*]package:][style/]<entry>
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800228 * ?[[*]package:]style/<entry>
229 * <[*]package>:[style/]<entry>
230 * [[*]package:style/]<entry>
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700231 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700232Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
233 std::string* out_error) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 if (str.empty()) {
235 return {};
236 }
237
238 StringPiece name = str;
239
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700240 bool has_leading_identifiers = false;
241 bool private_ref = false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700242
243 // Skip over these identifiers. A style's parent is a normal reference.
244 if (name.data()[0] == '@' || name.data()[0] == '?') {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700245 has_leading_identifiers = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700246 name = name.substr(1, name.size() - 1);
247 }
248
249 if (name.data()[0] == '*') {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700250 private_ref = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700251 name = name.substr(1, name.size() - 1);
252 }
253
254 ResourceNameRef ref;
255 ref.type = ResourceType::kStyle;
256
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700257 StringPiece type_str;
258 ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
259 if (!type_str.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700260 // If we have a type, make sure it is a Style.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700261 const ResourceType* parsed_type = ParseResourceType(type_str);
262 if (!parsed_type || *parsed_type != ResourceType::kStyle) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700263 std::stringstream err;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700264 err << "invalid resource type '" << type_str << "' for parent of style";
265 *out_error = err.str();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700266 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700267 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700268 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700269
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700270 if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700271 std::stringstream err;
272 err << "invalid parent reference '" << str << "'";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700273 *out_error = err.str();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700274 return {};
275 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700276
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700277 Reference result(ref);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700278 result.private_reference = private_ref;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700279 return result;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700280}
281
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700282Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
283 StringPiece trimmed_str = util::TrimWhitespace(str);
284 const char* start = trimmed_str.data();
285 const char* const end = start + trimmed_str.size();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700286 const char* p = start;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700287
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700288 Reference ref;
289 if (p != end && *p == '*') {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700290 ref.private_reference = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700291 start++;
292 p++;
293 }
294
295 StringPiece package;
296 StringPiece name;
297 while (p != end) {
298 if (*p == ':') {
299 package = StringPiece(start, p - start);
300 name = StringPiece(p + 1, end - (p + 1));
301 break;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700302 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700303 p++;
304 }
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700305
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700306 ref.name =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700307 ResourceName(package.ToString(), ResourceType::kAttr,
308 name.empty() ? trimmed_str.ToString() : name.ToString());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700309 return Maybe<Reference>(std::move(ref));
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700310}
311
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700312std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
313 bool* out_create) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700314 ResourceNameRef ref;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700315 bool private_ref = false;
316 if (ParseReference(str, &ref, out_create, &private_ref)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700317 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700318 value->private_reference = private_ref;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700319 return value;
320 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700321
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700322 if (ParseAttributeReference(str, &ref)) {
323 if (out_create) {
324 *out_create = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700325 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700326 return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
327 }
328 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700329}
330
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700331std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str) {
332 StringPiece trimmed_str(util::TrimWhitespace(str));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700333 android::Res_value value = {};
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700334 if (trimmed_str == "@null") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700335 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
336 // Instead we set the data type to TYPE_REFERENCE with a value of 0.
337 value.dataType = android::Res_value::TYPE_REFERENCE;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700338 } else if (trimmed_str == "@empty") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700339 // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
340 value.dataType = android::Res_value::TYPE_NULL;
341 value.data = android::Res_value::DATA_NULL_EMPTY;
342 } else {
343 return {};
344 }
345 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700346}
347
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700348std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700349 const StringPiece& str) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700350 StringPiece trimmed_str(util::TrimWhitespace(str));
351 for (const Attribute::Symbol& symbol : enum_attr->symbols) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700352 // Enum symbols are stored as @package:id/symbol resources,
353 // so we need to match against the 'entry' part of the identifier.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700354 const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
355 if (trimmed_str == enum_symbol_resource_name.entry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700356 android::Res_value value = {};
357 value.dataType = android::Res_value::TYPE_INT_DEC;
358 value.data = symbol.value;
359 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700360 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700361 }
362 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700363}
364
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700365std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700366 const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700367 android::Res_value flags = {};
368 flags.dataType = android::Res_value::TYPE_INT_HEX;
369 flags.data = 0u;
Adam Lesinski52364f72016-01-11 13:10:24 -0800370
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700371 if (util::TrimWhitespace(str).empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700372 // Empty string is a valid flag (0).
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700373 return util::make_unique<BinaryPrimitive>(flags);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700374 }
375
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700376 for (StringPiece part : util::Tokenize(str, '|')) {
377 StringPiece trimmed_part = util::TrimWhitespace(part);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700378
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700379 bool flag_set = false;
380 for (const Attribute::Symbol& symbol : flag_attr->symbols) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700381 // Flag symbols are stored as @package:id/symbol resources,
382 // so we need to match against the 'entry' part of the identifier.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700383 const ResourceName& flag_symbol_resource_name =
384 symbol.symbol.name.value();
385 if (trimmed_part == flag_symbol_resource_name.entry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700386 flags.data |= symbol.value;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700387 flag_set = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700388 break;
389 }
390 }
391
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700392 if (!flag_set) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700393 return {};
394 }
395 }
396 return util::make_unique<BinaryPrimitive>(flags);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700397}
398
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700399static uint32_t ParseHex(char c, bool* out_error) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700400 if (c >= '0' && c <= '9') {
401 return c - '0';
402 } else if (c >= 'a' && c <= 'f') {
403 return c - 'a' + 0xa;
404 } else if (c >= 'A' && c <= 'F') {
405 return c - 'A' + 0xa;
406 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700407 *out_error = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700408 return 0xffffffffu;
409 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700410}
411
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700412std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
413 StringPiece color_str(util::TrimWhitespace(str));
414 const char* start = color_str.data();
415 const size_t len = color_str.size();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700416 if (len == 0 || start[0] != '#') {
417 return {};
418 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700419
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700420 android::Res_value value = {};
421 bool error = false;
422 if (len == 4) {
423 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
424 value.data = 0xff000000u;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700425 value.data |= ParseHex(start[1], &error) << 20;
426 value.data |= ParseHex(start[1], &error) << 16;
427 value.data |= ParseHex(start[2], &error) << 12;
428 value.data |= ParseHex(start[2], &error) << 8;
429 value.data |= ParseHex(start[3], &error) << 4;
430 value.data |= ParseHex(start[3], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700431 } else if (len == 5) {
432 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700433 value.data |= ParseHex(start[1], &error) << 28;
434 value.data |= ParseHex(start[1], &error) << 24;
435 value.data |= ParseHex(start[2], &error) << 20;
436 value.data |= ParseHex(start[2], &error) << 16;
437 value.data |= ParseHex(start[3], &error) << 12;
438 value.data |= ParseHex(start[3], &error) << 8;
439 value.data |= ParseHex(start[4], &error) << 4;
440 value.data |= ParseHex(start[4], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700441 } else if (len == 7) {
442 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
443 value.data = 0xff000000u;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700444 value.data |= ParseHex(start[1], &error) << 20;
445 value.data |= ParseHex(start[2], &error) << 16;
446 value.data |= ParseHex(start[3], &error) << 12;
447 value.data |= ParseHex(start[4], &error) << 8;
448 value.data |= ParseHex(start[5], &error) << 4;
449 value.data |= ParseHex(start[6], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700450 } else if (len == 9) {
451 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700452 value.data |= ParseHex(start[1], &error) << 28;
453 value.data |= ParseHex(start[2], &error) << 24;
454 value.data |= ParseHex(start[3], &error) << 20;
455 value.data |= ParseHex(start[4], &error) << 16;
456 value.data |= ParseHex(start[5], &error) << 12;
457 value.data |= ParseHex(start[6], &error) << 8;
458 value.data |= ParseHex(start[7], &error) << 4;
459 value.data |= ParseHex(start[8], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700460 } else {
461 return {};
462 }
463 return error ? std::unique_ptr<BinaryPrimitive>()
464 : util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700465}
466
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700467Maybe<bool> ParseBool(const StringPiece& str) {
468 StringPiece trimmed_str(util::TrimWhitespace(str));
469 if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700470 return Maybe<bool>(true);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700471 } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
472 trimmed_str == "False") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700473 return Maybe<bool>(false);
474 }
475 return {};
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800476}
477
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700478Maybe<uint32_t> ParseInt(const StringPiece& str) {
479 std::u16string str16 = util::Utf8ToUtf16(str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700480 android::Res_value value;
481 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
482 return value.data;
483 }
484 return {};
Adam Lesinski36c73a52016-08-11 13:39:24 -0700485}
486
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700487Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
488 StringPiece trimmed_str(util::TrimWhitespace(str));
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700489
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700490 std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700491 android::Res_value value;
492 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
493 if (value.dataType == android::Res_value::TYPE_INT_HEX) {
494 ResourceId id(value.data);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700495 if (id.is_valid()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700496 return id;
497 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700498 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700499 }
500 return {};
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700501}
502
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700503Maybe<int> ParseSdkVersion(const StringPiece& str) {
504 StringPiece trimmed_str(util::TrimWhitespace(str));
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700505
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700506 std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700507 android::Res_value value;
508 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
509 return static_cast<int>(value.data);
510 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700511
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700512 // Try parsing the code name.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700513 std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
514 if (entry.first == trimmed_str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700515 return entry.second;
516 }
517 return {};
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700518}
519
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700520std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
521 if (Maybe<bool> maybe_result = ParseBool(str)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700522 android::Res_value value = {};
523 value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800524
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700525 if (maybe_result.value()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700526 value.data = 0xffffffffu;
527 } else {
528 value.data = 0;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800529 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700530 return util::make_unique<BinaryPrimitive>(value);
531 }
532 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700533}
534
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700535std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
536 std::u16string str16 = util::Utf8ToUtf16(str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700537 android::Res_value value;
538 if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
539 return {};
540 }
541 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700542}
543
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700544std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
545 std::u16string str16 = util::Utf8ToUtf16(str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700546 android::Res_value value;
547 if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
548 return {};
549 }
550 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700551}
552
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700553uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700554 switch (type) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700555 case android::Res_value::TYPE_NULL:
556 case android::Res_value::TYPE_REFERENCE:
557 case android::Res_value::TYPE_ATTRIBUTE:
558 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700559 return android::ResTable_map::TYPE_REFERENCE;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700560
561 case android::Res_value::TYPE_STRING:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700562 return android::ResTable_map::TYPE_STRING;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700563
564 case android::Res_value::TYPE_FLOAT:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700565 return android::ResTable_map::TYPE_FLOAT;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700566
567 case android::Res_value::TYPE_DIMENSION:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700568 return android::ResTable_map::TYPE_DIMENSION;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700569
570 case android::Res_value::TYPE_FRACTION:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700571 return android::ResTable_map::TYPE_FRACTION;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700572
573 case android::Res_value::TYPE_INT_DEC:
574 case android::Res_value::TYPE_INT_HEX:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700575 return android::ResTable_map::TYPE_INTEGER |
576 android::ResTable_map::TYPE_ENUM |
577 android::ResTable_map::TYPE_FLAGS;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700578
579 case android::Res_value::TYPE_INT_BOOLEAN:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700580 return android::ResTable_map::TYPE_BOOLEAN;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700581
582 case android::Res_value::TYPE_INT_COLOR_ARGB8:
583 case android::Res_value::TYPE_INT_COLOR_RGB8:
584 case android::Res_value::TYPE_INT_COLOR_ARGB4:
585 case android::Res_value::TYPE_INT_COLOR_RGB4:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700586 return android::ResTable_map::TYPE_COLOR;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700587
588 default:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700589 return 0;
590 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700591}
592
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700593std::unique_ptr<Item> TryParseItemForAttribute(
594 const StringPiece& value, uint32_t type_mask,
595 const std::function<void(const ResourceName&)>& on_create_reference) {
596 std::unique_ptr<BinaryPrimitive> null_or_empty = TryParseNullOrEmpty(value);
597 if (null_or_empty) {
598 return std::move(null_or_empty);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700599 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700600
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700601 bool create = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700602 std::unique_ptr<Reference> reference = TryParseReference(value, &create);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700603 if (reference) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700604 if (create && on_create_reference) {
605 on_create_reference(reference->name.value());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700606 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700607 return std::move(reference);
608 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700609
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700610 if (type_mask & android::ResTable_map::TYPE_COLOR) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700611 // Try parsing this as a color.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700612 std::unique_ptr<BinaryPrimitive> color = TryParseColor(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700613 if (color) {
614 return std::move(color);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700615 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700616 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700617
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700618 if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700619 // Try parsing this as a boolean.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700620 std::unique_ptr<BinaryPrimitive> boolean = TryParseBool(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700621 if (boolean) {
622 return std::move(boolean);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700623 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700624 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700625
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700626 if (type_mask & android::ResTable_map::TYPE_INTEGER) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700627 // Try parsing this as an integer.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700628 std::unique_ptr<BinaryPrimitive> integer = TryParseInt(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700629 if (integer) {
630 return std::move(integer);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700631 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700632 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700633
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700634 const uint32_t float_mask = android::ResTable_map::TYPE_FLOAT |
635 android::ResTable_map::TYPE_DIMENSION |
636 android::ResTable_map::TYPE_FRACTION;
637 if (type_mask & float_mask) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700638 // Try parsing this as a float.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700639 std::unique_ptr<BinaryPrimitive> floating_point = TryParseFloat(value);
640 if (floating_point) {
641 if (type_mask &
642 AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
643 return std::move(floating_point);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700644 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700645 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700646 }
647 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700648}
649
650/**
651 * We successively try to parse the string as a resource type that the Attribute
652 * allows.
653 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700654std::unique_ptr<Item> TryParseItemForAttribute(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700655 const StringPiece& str, const Attribute* attr,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700656 const std::function<void(const ResourceName&)>& on_create_reference) {
657 const uint32_t type_mask = attr->type_mask;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700658 std::unique_ptr<Item> value =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700659 TryParseItemForAttribute(str, type_mask, on_create_reference);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700660 if (value) {
661 return value;
662 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700663
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700664 if (type_mask & android::ResTable_map::TYPE_ENUM) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700665 // Try parsing this as an enum.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700666 std::unique_ptr<BinaryPrimitive> enum_value = TryParseEnumSymbol(attr, str);
667 if (enum_value) {
668 return std::move(enum_value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700669 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700670 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700671
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700672 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700673 // Try parsing this as a flag.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700674 std::unique_ptr<BinaryPrimitive> flag_value = TryParseFlagSymbol(attr, str);
675 if (flag_value) {
676 return std::move(flag_value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700677 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700678 }
679 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700680}
681
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700682std::string BuildResourceFileName(const ResourceFile& res_file,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700683 const NameMangler* mangler) {
684 std::stringstream out;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700685 out << "res/" << res_file.name.type;
686 if (res_file.config != ConfigDescription{}) {
687 out << "-" << res_file.config;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700688 }
689 out << "/";
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800690
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700691 if (mangler && mangler->ShouldMangle(res_file.name.package)) {
692 out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700693 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700694 out << res_file.name.entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700695 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700696 out << file::GetExtension(res_file.source.path);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700697 return out.str();
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800698}
699
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700700} // namespace ResourceUtils
701} // namespace aapt