blob: b41be4bab0b8f50b0191966b14863290e67f7c11 [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 Lesinskicacb28f2016-10-19 12:18:14 -070018#include "NameMangler.h"
Adam Lesinskifb6312f2016-06-28 14:40:32 -070019#include "SdkConstants.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080020#include "flatten/ResourceTypeExtensions.h"
Adam Lesinskia6fe3452015-12-09 15:20:52 -080021#include "util/Files.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070022#include "util/Util.h"
23
24#include <androidfw/ResourceTypes.h>
25#include <sstream>
26
27namespace aapt {
28namespace ResourceUtils {
29
Adam Lesinskicacb28f2016-10-19 12:18:14 -070030Maybe<ResourceName> toResourceName(
31 const android::ResTable::resource_name& nameIn) {
32 ResourceName nameOut;
33 if (!nameIn.package) {
34 return {};
35 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070036
Adam Lesinskicacb28f2016-10-19 12:18:14 -070037 nameOut.package =
38 util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen));
Adam Lesinskid0f116b2016-07-08 15:00:32 -070039
Adam Lesinskicacb28f2016-10-19 12:18:14 -070040 const ResourceType* type;
41 if (nameIn.type) {
42 type = parseResourceType(
43 util::utf16ToUtf8(StringPiece16(nameIn.type, nameIn.typeLen)));
44 } else if (nameIn.type8) {
45 type = parseResourceType(StringPiece(nameIn.type8, nameIn.typeLen));
46 } else {
47 return {};
48 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070049
Adam Lesinskicacb28f2016-10-19 12:18:14 -070050 if (!type) {
51 return {};
52 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070053
Adam Lesinskicacb28f2016-10-19 12:18:14 -070054 nameOut.type = *type;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070055
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 if (nameIn.name) {
57 nameOut.entry =
58 util::utf16ToUtf8(StringPiece16(nameIn.name, nameIn.nameLen));
59 } else if (nameIn.name8) {
60 nameOut.entry = StringPiece(nameIn.name8, nameIn.nameLen).toString();
61 } else {
62 return {};
63 }
64 return nameOut;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070065}
66
67bool extractResourceName(const StringPiece& str, StringPiece* outPackage,
68 StringPiece* outType, StringPiece* outEntry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070069 bool hasPackageSeparator = false;
70 bool hasTypeSeparator = false;
71 const char* start = str.data();
72 const char* end = start + str.size();
73 const char* current = start;
74 while (current != end) {
75 if (outType->size() == 0 && *current == '/') {
76 hasTypeSeparator = true;
77 outType->assign(start, current - start);
78 start = current + 1;
79 } else if (outPackage->size() == 0 && *current == ':') {
80 hasPackageSeparator = true;
81 outPackage->assign(start, current - start);
82 start = current + 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070083 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 current++;
85 }
86 outEntry->assign(start, end - start);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080087
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 return !(hasPackageSeparator && outPackage->empty()) &&
89 !(hasTypeSeparator && outType->empty());
Adam Lesinski1ab598f2015-08-14 14:26:04 -070090}
91
Adam Lesinskicacb28f2016-10-19 12:18:14 -070092bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef,
93 bool* outPrivate) {
94 if (str.empty()) {
95 return false;
96 }
97
98 size_t offset = 0;
99 bool priv = false;
100 if (str.data()[0] == '*') {
101 priv = true;
102 offset = 1;
103 }
104
105 StringPiece package;
106 StringPiece type;
107 StringPiece entry;
108 if (!extractResourceName(str.substr(offset, str.size() - offset), &package,
109 &type, &entry)) {
110 return false;
111 }
112
113 const ResourceType* parsedType = parseResourceType(type);
114 if (!parsedType) {
115 return false;
116 }
117
118 if (entry.empty()) {
119 return false;
120 }
121
122 if (outRef) {
123 outRef->package = package;
124 outRef->type = *parsedType;
125 outRef->entry = entry;
126 }
127
128 if (outPrivate) {
129 *outPrivate = priv;
130 }
131 return true;
132}
133
134bool parseReference(const StringPiece& str, ResourceNameRef* outRef,
135 bool* outCreate, bool* outPrivate) {
136 StringPiece trimmedStr(util::trimWhitespace(str));
137 if (trimmedStr.empty()) {
138 return false;
139 }
140
141 bool create = false;
142 bool priv = false;
143 if (trimmedStr.data()[0] == '@') {
144 size_t offset = 1;
145 if (trimmedStr.data()[1] == '+') {
146 create = true;
147 offset += 1;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800148 }
149
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 ResourceNameRef name;
151 if (!parseResourceName(
152 trimmedStr.substr(offset, trimmedStr.size() - offset), &name,
153 &priv)) {
154 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800155 }
156
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 if (create && priv) {
158 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800159 }
160
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 if (create && name.type != ResourceType::kId) {
162 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800163 }
164
165 if (outRef) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 *outRef = name;
167 }
168
169 if (outCreate) {
170 *outCreate = create;
Adam Lesinski467f1712015-11-16 17:35:44 -0800171 }
172
173 if (outPrivate) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700174 *outPrivate = priv;
Adam Lesinski467f1712015-11-16 17:35:44 -0800175 }
176 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700177 }
178 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700179}
180
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700181bool isReference(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700182 return parseReference(str, nullptr, nullptr, nullptr);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800183}
184
Adam Lesinski36c73a52016-08-11 13:39:24 -0700185bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700186 StringPiece trimmedStr(util::trimWhitespace(str));
187 if (trimmedStr.empty()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700189 }
190
191 if (*trimmedStr.data() == '?') {
192 StringPiece package;
193 StringPiece type;
194 StringPiece entry;
195 if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1),
196 &package, &type, &entry)) {
197 return false;
198 }
199
200 if (!type.empty() && type != "attr") {
201 return false;
202 }
203
204 if (entry.empty()) {
205 return false;
206 }
207
208 if (outRef) {
209 outRef->package = package;
210 outRef->type = ResourceType::kAttr;
211 outRef->entry = entry;
212 }
213 return true;
214 }
215 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700216}
217
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700218bool isAttributeReference(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700219 return parseAttributeReference(str, nullptr);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800220}
221
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700222/*
223 * Style parent's are a bit different. We accept the following formats:
224 *
Adam Lesinski52364f72016-01-11 13:10:24 -0800225 * @[[*]package:][style/]<entry>
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800226 * ?[[*]package:]style/<entry>
227 * <[*]package>:[style/]<entry>
228 * [[*]package:style/]<entry>
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700229 */
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700230Maybe<Reference> parseStyleParentReference(const StringPiece& str,
231 std::string* outError) {
232 if (str.empty()) {
233 return {};
234 }
235
236 StringPiece name = str;
237
238 bool hasLeadingIdentifiers = false;
239 bool privateRef = false;
240
241 // Skip over these identifiers. A style's parent is a normal reference.
242 if (name.data()[0] == '@' || name.data()[0] == '?') {
243 hasLeadingIdentifiers = true;
244 name = name.substr(1, name.size() - 1);
245 }
246
247 if (name.data()[0] == '*') {
248 privateRef = true;
249 name = name.substr(1, name.size() - 1);
250 }
251
252 ResourceNameRef ref;
253 ref.type = ResourceType::kStyle;
254
255 StringPiece typeStr;
256 extractResourceName(name, &ref.package, &typeStr, &ref.entry);
257 if (!typeStr.empty()) {
258 // If we have a type, make sure it is a Style.
259 const ResourceType* parsedType = parseResourceType(typeStr);
260 if (!parsedType || *parsedType != ResourceType::kStyle) {
261 std::stringstream err;
262 err << "invalid resource type '" << typeStr << "' for parent of style";
263 *outError = err.str();
264 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700265 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700266 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700267
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700268 if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
269 std::stringstream err;
270 err << "invalid parent reference '" << str << "'";
271 *outError = err.str();
272 return {};
273 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700274
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700275 Reference result(ref);
276 result.privateReference = privateRef;
277 return result;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700278}
279
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700280Maybe<Reference> parseXmlAttributeName(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700281 StringPiece trimmedStr = util::trimWhitespace(str);
282 const char* start = trimmedStr.data();
283 const char* const end = start + trimmedStr.size();
284 const char* p = start;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700285
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700286 Reference ref;
287 if (p != end && *p == '*') {
288 ref.privateReference = true;
289 start++;
290 p++;
291 }
292
293 StringPiece package;
294 StringPiece name;
295 while (p != end) {
296 if (*p == ':') {
297 package = StringPiece(start, p - start);
298 name = StringPiece(p + 1, end - (p + 1));
299 break;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700300 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700301 p++;
302 }
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700303
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700304 ref.name =
305 ResourceName(package.toString(), ResourceType::kAttr,
306 name.empty() ? trimmedStr.toString() : name.toString());
307 return Maybe<Reference>(std::move(ref));
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700308}
309
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700310std::unique_ptr<Reference> tryParseReference(const StringPiece& str,
311 bool* outCreate) {
312 ResourceNameRef ref;
313 bool privateRef = false;
314 if (parseReference(str, &ref, outCreate, &privateRef)) {
315 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
316 value->privateReference = privateRef;
317 return value;
318 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700319
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700320 if (parseAttributeReference(str, &ref)) {
321 if (outCreate) {
322 *outCreate = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700323 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700324 return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
325 }
326 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327}
328
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700329std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700330 StringPiece trimmedStr(util::trimWhitespace(str));
331 android::Res_value value = {};
332 if (trimmedStr == "@null") {
333 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
334 // Instead we set the data type to TYPE_REFERENCE with a value of 0.
335 value.dataType = android::Res_value::TYPE_REFERENCE;
336 } else if (trimmedStr == "@empty") {
337 // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
338 value.dataType = android::Res_value::TYPE_NULL;
339 value.data = android::Res_value::DATA_NULL_EMPTY;
340 } else {
341 return {};
342 }
343 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700344}
345
346std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700347 const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700348 StringPiece trimmedStr(util::trimWhitespace(str));
349 for (const Attribute::Symbol& symbol : enumAttr->symbols) {
350 // Enum symbols are stored as @package:id/symbol resources,
351 // so we need to match against the 'entry' part of the identifier.
352 const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
353 if (trimmedStr == enumSymbolResourceName.entry) {
354 android::Res_value value = {};
355 value.dataType = android::Res_value::TYPE_INT_DEC;
356 value.data = symbol.value;
357 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700358 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700359 }
360 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700361}
362
363std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700364 const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700365 android::Res_value flags = {};
366 flags.dataType = android::Res_value::TYPE_INT_HEX;
367 flags.data = 0u;
Adam Lesinski52364f72016-01-11 13:10:24 -0800368
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700369 if (util::trimWhitespace(str).empty()) {
370 // Empty string is a valid flag (0).
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700371 return util::make_unique<BinaryPrimitive>(flags);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700372 }
373
374 for (StringPiece part : util::tokenize(str, '|')) {
375 StringPiece trimmedPart = util::trimWhitespace(part);
376
377 bool flagSet = false;
378 for (const Attribute::Symbol& symbol : flagAttr->symbols) {
379 // Flag symbols are stored as @package:id/symbol resources,
380 // so we need to match against the 'entry' part of the identifier.
381 const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
382 if (trimmedPart == flagSymbolResourceName.entry) {
383 flags.data |= symbol.value;
384 flagSet = true;
385 break;
386 }
387 }
388
389 if (!flagSet) {
390 return {};
391 }
392 }
393 return util::make_unique<BinaryPrimitive>(flags);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700394}
395
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700396static uint32_t parseHex(char c, bool* outError) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700397 if (c >= '0' && c <= '9') {
398 return c - '0';
399 } else if (c >= 'a' && c <= 'f') {
400 return c - 'a' + 0xa;
401 } else if (c >= 'A' && c <= 'F') {
402 return c - 'A' + 0xa;
403 } else {
404 *outError = true;
405 return 0xffffffffu;
406 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700407}
408
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700409std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700410 StringPiece colorStr(util::trimWhitespace(str));
411 const char* start = colorStr.data();
412 const size_t len = colorStr.size();
413 if (len == 0 || start[0] != '#') {
414 return {};
415 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700416
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700417 android::Res_value value = {};
418 bool error = false;
419 if (len == 4) {
420 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
421 value.data = 0xff000000u;
422 value.data |= parseHex(start[1], &error) << 20;
423 value.data |= parseHex(start[1], &error) << 16;
424 value.data |= parseHex(start[2], &error) << 12;
425 value.data |= parseHex(start[2], &error) << 8;
426 value.data |= parseHex(start[3], &error) << 4;
427 value.data |= parseHex(start[3], &error);
428 } else if (len == 5) {
429 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
430 value.data |= parseHex(start[1], &error) << 28;
431 value.data |= parseHex(start[1], &error) << 24;
432 value.data |= parseHex(start[2], &error) << 20;
433 value.data |= parseHex(start[2], &error) << 16;
434 value.data |= parseHex(start[3], &error) << 12;
435 value.data |= parseHex(start[3], &error) << 8;
436 value.data |= parseHex(start[4], &error) << 4;
437 value.data |= parseHex(start[4], &error);
438 } else if (len == 7) {
439 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
440 value.data = 0xff000000u;
441 value.data |= parseHex(start[1], &error) << 20;
442 value.data |= parseHex(start[2], &error) << 16;
443 value.data |= parseHex(start[3], &error) << 12;
444 value.data |= parseHex(start[4], &error) << 8;
445 value.data |= parseHex(start[5], &error) << 4;
446 value.data |= parseHex(start[6], &error);
447 } else if (len == 9) {
448 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
449 value.data |= parseHex(start[1], &error) << 28;
450 value.data |= parseHex(start[2], &error) << 24;
451 value.data |= parseHex(start[3], &error) << 20;
452 value.data |= parseHex(start[4], &error) << 16;
453 value.data |= parseHex(start[5], &error) << 12;
454 value.data |= parseHex(start[6], &error) << 8;
455 value.data |= parseHex(start[7], &error) << 4;
456 value.data |= parseHex(start[8], &error);
457 } else {
458 return {};
459 }
460 return error ? std::unique_ptr<BinaryPrimitive>()
461 : util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700462}
463
Adam Lesinski36c73a52016-08-11 13:39:24 -0700464Maybe<bool> parseBool(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700465 StringPiece trimmedStr(util::trimWhitespace(str));
466 if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") {
467 return Maybe<bool>(true);
468 } else if (trimmedStr == "false" || trimmedStr == "FALSE" ||
469 trimmedStr == "False") {
470 return Maybe<bool>(false);
471 }
472 return {};
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800473}
474
Adam Lesinski36c73a52016-08-11 13:39:24 -0700475Maybe<uint32_t> parseInt(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700476 std::u16string str16 = util::utf8ToUtf16(str);
477 android::Res_value value;
478 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
479 return value.data;
480 }
481 return {};
Adam Lesinski36c73a52016-08-11 13:39:24 -0700482}
483
484Maybe<ResourceId> parseResourceId(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700485 StringPiece trimmedStr(util::trimWhitespace(str));
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700486
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700487 std::u16string str16 = util::utf8ToUtf16(trimmedStr);
488 android::Res_value value;
489 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
490 if (value.dataType == android::Res_value::TYPE_INT_HEX) {
491 ResourceId id(value.data);
492 if (id.isValid()) {
493 return id;
494 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700495 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700496 }
497 return {};
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700498}
499
Adam Lesinski36c73a52016-08-11 13:39:24 -0700500Maybe<int> parseSdkVersion(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700501 StringPiece trimmedStr(util::trimWhitespace(str));
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700502
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700503 std::u16string str16 = util::utf8ToUtf16(trimmedStr);
504 android::Res_value value;
505 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
506 return static_cast<int>(value.data);
507 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700508
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700509 // Try parsing the code name.
510 std::pair<StringPiece, int> entry = getDevelopmentSdkCodeNameAndVersion();
511 if (entry.first == trimmedStr) {
512 return entry.second;
513 }
514 return {};
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700515}
516
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700517std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700518 if (Maybe<bool> maybeResult = parseBool(str)) {
519 android::Res_value value = {};
520 value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800521
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700522 if (maybeResult.value()) {
523 value.data = 0xffffffffu;
524 } else {
525 value.data = 0;
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800526 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700527 return util::make_unique<BinaryPrimitive>(value);
528 }
529 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700530}
531
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700532std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700533 std::u16string str16 = util::utf8ToUtf16(str);
534 android::Res_value value;
535 if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
536 return {};
537 }
538 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700539}
540
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700541std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700542 std::u16string str16 = util::utf8ToUtf16(str);
543 android::Res_value value;
544 if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
545 return {};
546 }
547 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700548}
549
550uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700551 switch (type) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700552 case android::Res_value::TYPE_NULL:
553 case android::Res_value::TYPE_REFERENCE:
554 case android::Res_value::TYPE_ATTRIBUTE:
555 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700556 return android::ResTable_map::TYPE_REFERENCE;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700557
558 case android::Res_value::TYPE_STRING:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700559 return android::ResTable_map::TYPE_STRING;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700560
561 case android::Res_value::TYPE_FLOAT:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700562 return android::ResTable_map::TYPE_FLOAT;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700563
564 case android::Res_value::TYPE_DIMENSION:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700565 return android::ResTable_map::TYPE_DIMENSION;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700566
567 case android::Res_value::TYPE_FRACTION:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700568 return android::ResTable_map::TYPE_FRACTION;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700569
570 case android::Res_value::TYPE_INT_DEC:
571 case android::Res_value::TYPE_INT_HEX:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700572 return android::ResTable_map::TYPE_INTEGER |
573 android::ResTable_map::TYPE_ENUM |
574 android::ResTable_map::TYPE_FLAGS;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700575
576 case android::Res_value::TYPE_INT_BOOLEAN:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700577 return android::ResTable_map::TYPE_BOOLEAN;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700578
579 case android::Res_value::TYPE_INT_COLOR_ARGB8:
580 case android::Res_value::TYPE_INT_COLOR_RGB8:
581 case android::Res_value::TYPE_INT_COLOR_ARGB4:
582 case android::Res_value::TYPE_INT_COLOR_RGB4:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700583 return android::ResTable_map::TYPE_COLOR;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700584
585 default:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700586 return 0;
587 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700588}
589
Adam Lesinski36c73a52016-08-11 13:39:24 -0700590std::unique_ptr<Item> tryParseItemForAttribute(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700591 const StringPiece& value, uint32_t typeMask,
592 const std::function<void(const ResourceName&)>& onCreateReference) {
593 std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
594 if (nullOrEmpty) {
595 return std::move(nullOrEmpty);
596 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700597
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700598 bool create = false;
599 std::unique_ptr<Reference> reference = tryParseReference(value, &create);
600 if (reference) {
601 if (create && onCreateReference) {
602 onCreateReference(reference->name.value());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700603 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700604 return std::move(reference);
605 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700606
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700607 if (typeMask & android::ResTable_map::TYPE_COLOR) {
608 // Try parsing this as a color.
609 std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
610 if (color) {
611 return std::move(color);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700612 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700613 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700614
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700615 if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
616 // Try parsing this as a boolean.
617 std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
618 if (boolean) {
619 return std::move(boolean);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700620 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700621 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700622
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700623 if (typeMask & android::ResTable_map::TYPE_INTEGER) {
624 // Try parsing this as an integer.
625 std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
626 if (integer) {
627 return std::move(integer);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700628 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700629 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700630
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700631 const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT |
632 android::ResTable_map::TYPE_DIMENSION |
633 android::ResTable_map::TYPE_FRACTION;
634 if (typeMask & floatMask) {
635 // Try parsing this as a float.
636 std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
637 if (floatingPoint) {
638 if (typeMask &
639 androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
640 return std::move(floatingPoint);
641 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700642 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700643 }
644 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700645}
646
647/**
648 * We successively try to parse the string as a resource type that the Attribute
649 * allows.
650 */
Adam Lesinski36c73a52016-08-11 13:39:24 -0700651std::unique_ptr<Item> tryParseItemForAttribute(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700652 const StringPiece& str, const Attribute* attr,
653 const std::function<void(const ResourceName&)>& onCreateReference) {
654 const uint32_t typeMask = attr->typeMask;
655 std::unique_ptr<Item> value =
656 tryParseItemForAttribute(str, typeMask, onCreateReference);
657 if (value) {
658 return value;
659 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700660
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700661 if (typeMask & android::ResTable_map::TYPE_ENUM) {
662 // Try parsing this as an enum.
663 std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
664 if (enumValue) {
665 return std::move(enumValue);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700666 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700667 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700668
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700669 if (typeMask & android::ResTable_map::TYPE_FLAGS) {
670 // Try parsing this as a flag.
671 std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
672 if (flagValue) {
673 return std::move(flagValue);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700674 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700675 }
676 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700677}
678
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700679std::string buildResourceFileName(const ResourceFile& resFile,
680 const NameMangler* mangler) {
681 std::stringstream out;
682 out << "res/" << resFile.name.type;
683 if (resFile.config != ConfigDescription{}) {
684 out << "-" << resFile.config;
685 }
686 out << "/";
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800687
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700688 if (mangler && mangler->shouldMangle(resFile.name.package)) {
689 out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
690 } else {
691 out << resFile.name.entry;
692 }
693 out << file::getExtension(resFile.source.path);
694 return out.str();
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800695}
696
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700697} // namespace ResourceUtils
698} // namespace aapt