blob: 73a194e90ea2970612053e382c8b6b5f2471a249 [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
Adam Lesinskia6fe3452015-12-09 15:20:52 -080017#include "NameMangler.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070018#include "ResourceUtils.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 Lesinskid0f116b2016-07-08 15:00:32 -070030Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& nameIn) {
31 ResourceName nameOut;
32 if (!nameIn.package) {
33 return {};
34 }
35
36 nameOut.package = util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen));
37
38 const ResourceType* type;
39 if (nameIn.type) {
40 type = parseResourceType(util::utf16ToUtf8(StringPiece16(nameIn.type, nameIn.typeLen)));
41 } else if (nameIn.type8) {
42 type = parseResourceType(StringPiece(nameIn.type8, nameIn.typeLen));
43 } else {
44 return {};
45 }
46
47 if (!type) {
48 return {};
49 }
50
51 nameOut.type = *type;
52
53 if (nameIn.name) {
54 nameOut.entry = util::utf16ToUtf8(StringPiece16(nameIn.name, nameIn.nameLen));
55 } else if (nameIn.name8) {
56 nameOut.entry = StringPiece(nameIn.name8, nameIn.nameLen).toString();
57 } else {
58 return {};
59 }
60 return nameOut;
61}
62
63bool extractResourceName(const StringPiece& str, StringPiece* outPackage,
64 StringPiece* outType, StringPiece* outEntry) {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080065 bool hasPackageSeparator = false;
66 bool hasTypeSeparator = false;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070067 const char* start = str.data();
68 const char* end = start + str.size();
69 const char* current = start;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070070 while (current != end) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -070071 if (outType->size() == 0 && *current == '/') {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080072 hasTypeSeparator = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070073 outType->assign(start, current - start);
74 start = current + 1;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070075 } else if (outPackage->size() == 0 && *current == ':') {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080076 hasPackageSeparator = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070077 outPackage->assign(start, current - start);
78 start = current + 1;
79 }
80 current++;
81 }
82 outEntry->assign(start, end - start);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080083
84 return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
Adam Lesinski1ab598f2015-08-14 14:26:04 -070085}
86
Adam Lesinskid0f116b2016-07-08 15:00:32 -070087bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* outPrivate) {
Adam Lesinski59e04c62016-02-04 15:59:23 -080088 if (str.empty()) {
89 return false;
90 }
91
Adam Lesinski467f1712015-11-16 17:35:44 -080092 size_t offset = 0;
93 bool priv = false;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070094 if (str.data()[0] == '*') {
Adam Lesinski467f1712015-11-16 17:35:44 -080095 priv = true;
96 offset = 1;
97 }
98
Adam Lesinskid0f116b2016-07-08 15:00:32 -070099 StringPiece package;
100 StringPiece type;
101 StringPiece entry;
Adam Lesinski467f1712015-11-16 17:35:44 -0800102 if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
103 return false;
104 }
105
106 const ResourceType* parsedType = parseResourceType(type);
107 if (!parsedType) {
108 return false;
109 }
110
111 if (entry.empty()) {
112 return false;
113 }
114
115 if (outRef) {
116 outRef->package = package;
117 outRef->type = *parsedType;
118 outRef->entry = entry;
119 }
120
121 if (outPrivate) {
122 *outPrivate = priv;
123 }
124 return true;
125}
126
Adam Lesinski36c73a52016-08-11 13:39:24 -0700127bool parseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128 bool* outPrivate) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700129 StringPiece trimmedStr(util::trimWhitespace(str));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700130 if (trimmedStr.empty()) {
131 return false;
132 }
133
134 bool create = false;
135 bool priv = false;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700136 if (trimmedStr.data()[0] == '@') {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 size_t offset = 1;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700138 if (trimmedStr.data()[1] == '+') {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700139 create = true;
140 offset += 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700141 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800142
143 ResourceNameRef name;
144 if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
145 &name, &priv)) {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800146 return false;
147 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700148
Adam Lesinski467f1712015-11-16 17:35:44 -0800149 if (create && priv) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700150 return false;
151 }
152
Adam Lesinski467f1712015-11-16 17:35:44 -0800153 if (create && name.type != ResourceType::kId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700154 return false;
155 }
156
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800157 if (outRef) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800158 *outRef = name;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800159 }
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800160
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700161 if (outCreate) {
162 *outCreate = create;
163 }
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800164
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700165 if (outPrivate) {
166 *outPrivate = priv;
167 }
168 return true;
169 }
170 return false;
171}
172
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700173bool isReference(const StringPiece& str) {
Adam Lesinski36c73a52016-08-11 13:39:24 -0700174 return parseReference(str, nullptr, nullptr, nullptr);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800175}
176
Adam Lesinski36c73a52016-08-11 13:39:24 -0700177bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700178 StringPiece trimmedStr(util::trimWhitespace(str));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700179 if (trimmedStr.empty()) {
180 return false;
181 }
182
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700183 if (*trimmedStr.data() == '?') {
184 StringPiece package;
185 StringPiece type;
186 StringPiece entry;
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800187 if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1),
188 &package, &type, &entry)) {
189 return false;
190 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700191
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700192 if (!type.empty() && type != "attr") {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700193 return false;
194 }
195
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800196 if (entry.empty()) {
197 return false;
198 }
199
200 if (outRef) {
201 outRef->package = package;
202 outRef->type = ResourceType::kAttr;
203 outRef->entry = entry;
204 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700205 return true;
206 }
207 return false;
208}
209
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700210bool isAttributeReference(const StringPiece& str) {
Adam Lesinski36c73a52016-08-11 13:39:24 -0700211 return parseAttributeReference(str, nullptr);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800212}
213
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700214/*
215 * Style parent's are a bit different. We accept the following formats:
216 *
Adam Lesinski52364f72016-01-11 13:10:24 -0800217 * @[[*]package:][style/]<entry>
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800218 * ?[[*]package:]style/<entry>
219 * <[*]package>:[style/]<entry>
220 * [[*]package:style/]<entry>
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 */
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700222Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700223 if (str.empty()) {
224 return {};
225 }
226
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700227 StringPiece name = str;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700228
229 bool hasLeadingIdentifiers = false;
230 bool privateRef = false;
231
232 // Skip over these identifiers. A style's parent is a normal reference.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700233 if (name.data()[0] == '@' || name.data()[0] == '?') {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700234 hasLeadingIdentifiers = true;
235 name = name.substr(1, name.size() - 1);
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800236 }
237
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700238 if (name.data()[0] == '*') {
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800239 privateRef = true;
240 name = name.substr(1, name.size() - 1);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700241 }
242
243 ResourceNameRef ref;
244 ref.type = ResourceType::kStyle;
245
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700246 StringPiece typeStr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700247 extractResourceName(name, &ref.package, &typeStr, &ref.entry);
248 if (!typeStr.empty()) {
249 // If we have a type, make sure it is a Style.
250 const ResourceType* parsedType = parseResourceType(typeStr);
251 if (!parsedType || *parsedType != ResourceType::kStyle) {
252 std::stringstream err;
253 err << "invalid resource type '" << typeStr << "' for parent of style";
254 *outError = err.str();
255 return {};
256 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700257 }
258
259 if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
260 std::stringstream err;
261 err << "invalid parent reference '" << str << "'";
262 *outError = err.str();
263 return {};
264 }
265
266 Reference result(ref);
267 result.privateReference = privateRef;
268 return result;
269}
270
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700271Maybe<Reference> parseXmlAttributeName(const StringPiece& str) {
272 StringPiece trimmedStr = util::trimWhitespace(str);
273 const char* start = trimmedStr.data();
274 const char* const end = start + trimmedStr.size();
275 const char* p = start;
276
277 Reference ref;
278 if (p != end && *p == '*') {
279 ref.privateReference = true;
280 start++;
281 p++;
282 }
283
284 StringPiece package;
285 StringPiece name;
286 while (p != end) {
287 if (*p == ':') {
288 package = StringPiece(start, p - start);
289 name = StringPiece(p + 1, end - (p + 1));
290 break;
291 }
292 p++;
293 }
294
295 ref.name = ResourceName(package.toString(), ResourceType::kAttr,
296 name.empty() ? trimmedStr.toString() : name.toString());
297 return Maybe<Reference>(std::move(ref));
298}
299
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700300std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700301 ResourceNameRef ref;
302 bool privateRef = false;
Adam Lesinski36c73a52016-08-11 13:39:24 -0700303 if (parseReference(str, &ref, outCreate, &privateRef)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700304 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
305 value->privateReference = privateRef;
306 return value;
307 }
308
Adam Lesinski36c73a52016-08-11 13:39:24 -0700309 if (parseAttributeReference(str, &ref)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700310 if (outCreate) {
311 *outCreate = false;
312 }
313 return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
314 }
315 return {};
316}
317
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700318std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) {
319 StringPiece trimmedStr(util::trimWhitespace(str));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700320 android::Res_value value = { };
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700321 if (trimmedStr == "@null") {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700322 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
323 // Instead we set the data type to TYPE_REFERENCE with a value of 0.
324 value.dataType = android::Res_value::TYPE_REFERENCE;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700325 } else if (trimmedStr == "@empty") {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700326 // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
327 value.dataType = android::Res_value::TYPE_NULL;
328 value.data = android::Res_value::DATA_NULL_EMPTY;
329 } else {
330 return {};
331 }
332 return util::make_unique<BinaryPrimitive>(value);
333}
334
335std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700336 const StringPiece& str) {
337 StringPiece trimmedStr(util::trimWhitespace(str));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700338 for (const Attribute::Symbol& symbol : enumAttr->symbols) {
339 // Enum symbols are stored as @package:id/symbol resources,
340 // so we need to match against the 'entry' part of the identifier.
341 const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
342 if (trimmedStr == enumSymbolResourceName.entry) {
343 android::Res_value value = { };
344 value.dataType = android::Res_value::TYPE_INT_DEC;
345 value.data = symbol.value;
346 return util::make_unique<BinaryPrimitive>(value);
347 }
348 }
349 return {};
350}
351
352std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700353 const StringPiece& str) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700354 android::Res_value flags = { };
Adam Lesinski458b8772016-04-25 14:20:21 -0700355 flags.dataType = android::Res_value::TYPE_INT_HEX;
Adam Lesinski52364f72016-01-11 13:10:24 -0800356 flags.data = 0u;
357
358 if (util::trimWhitespace(str).empty()) {
359 // Empty string is a valid flag (0).
360 return util::make_unique<BinaryPrimitive>(flags);
361 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700362
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700363 for (StringPiece part : util::tokenize(str, '|')) {
364 StringPiece trimmedPart = util::trimWhitespace(part);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700365
366 bool flagSet = false;
367 for (const Attribute::Symbol& symbol : flagAttr->symbols) {
368 // Flag symbols are stored as @package:id/symbol resources,
369 // so we need to match against the 'entry' part of the identifier.
370 const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
371 if (trimmedPart == flagSymbolResourceName.entry) {
372 flags.data |= symbol.value;
373 flagSet = true;
374 break;
375 }
376 }
377
378 if (!flagSet) {
379 return {};
380 }
381 }
382 return util::make_unique<BinaryPrimitive>(flags);
383}
384
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700385static uint32_t parseHex(char c, bool* outError) {
386 if (c >= '0' && c <= '9') {
387 return c - '0';
388 } else if (c >= 'a' && c <= 'f') {
389 return c - 'a' + 0xa;
390 } else if (c >= 'A' && c <= 'F') {
391 return c - 'A' + 0xa;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700392 } else {
393 *outError = true;
394 return 0xffffffffu;
395 }
396}
397
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700398std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) {
399 StringPiece colorStr(util::trimWhitespace(str));
400 const char* start = colorStr.data();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700401 const size_t len = colorStr.size();
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700402 if (len == 0 || start[0] != '#') {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700403 return {};
404 }
405
406 android::Res_value value = { };
407 bool error = false;
408 if (len == 4) {
409 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
410 value.data = 0xff000000u;
411 value.data |= parseHex(start[1], &error) << 20;
412 value.data |= parseHex(start[1], &error) << 16;
413 value.data |= parseHex(start[2], &error) << 12;
414 value.data |= parseHex(start[2], &error) << 8;
415 value.data |= parseHex(start[3], &error) << 4;
416 value.data |= parseHex(start[3], &error);
417 } else if (len == 5) {
418 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
419 value.data |= parseHex(start[1], &error) << 28;
420 value.data |= parseHex(start[1], &error) << 24;
421 value.data |= parseHex(start[2], &error) << 20;
422 value.data |= parseHex(start[2], &error) << 16;
423 value.data |= parseHex(start[3], &error) << 12;
424 value.data |= parseHex(start[3], &error) << 8;
425 value.data |= parseHex(start[4], &error) << 4;
426 value.data |= parseHex(start[4], &error);
427 } else if (len == 7) {
428 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
429 value.data = 0xff000000u;
430 value.data |= parseHex(start[1], &error) << 20;
431 value.data |= parseHex(start[2], &error) << 16;
432 value.data |= parseHex(start[3], &error) << 12;
433 value.data |= parseHex(start[4], &error) << 8;
434 value.data |= parseHex(start[5], &error) << 4;
435 value.data |= parseHex(start[6], &error);
436 } else if (len == 9) {
437 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
438 value.data |= parseHex(start[1], &error) << 28;
439 value.data |= parseHex(start[2], &error) << 24;
440 value.data |= parseHex(start[3], &error) << 20;
441 value.data |= parseHex(start[4], &error) << 16;
442 value.data |= parseHex(start[5], &error) << 12;
443 value.data |= parseHex(start[6], &error) << 8;
444 value.data |= parseHex(start[7], &error) << 4;
445 value.data |= parseHex(start[8], &error);
446 } else {
447 return {};
448 }
449 return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
450}
451
Adam Lesinski36c73a52016-08-11 13:39:24 -0700452Maybe<bool> parseBool(const StringPiece& str) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700453 StringPiece trimmedStr(util::trimWhitespace(str));
454 if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") {
Adam Lesinski36c73a52016-08-11 13:39:24 -0700455 return Maybe<bool>(true);
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700456 } else if (trimmedStr == "false" || trimmedStr == "FALSE" || trimmedStr == "False") {
Adam Lesinski36c73a52016-08-11 13:39:24 -0700457 return Maybe<bool>(false);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700458 }
Adam Lesinski36c73a52016-08-11 13:39:24 -0700459 return {};
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800460}
461
Adam Lesinski36c73a52016-08-11 13:39:24 -0700462Maybe<uint32_t> parseInt(const StringPiece& str) {
463 std::u16string str16 = util::utf8ToUtf16(str);
464 android::Res_value value;
465 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
466 return value.data;
467 }
468 return {};
469}
470
471Maybe<ResourceId> parseResourceId(const StringPiece& str) {
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700472 StringPiece trimmedStr(util::trimWhitespace(str));
473
474 std::u16string str16 = util::utf8ToUtf16(trimmedStr);
475 android::Res_value value;
476 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
477 if (value.dataType == android::Res_value::TYPE_INT_HEX) {
478 ResourceId id(value.data);
479 if (id.isValid()) {
480 return id;
481 }
482 }
483 }
484 return {};
485}
486
Adam Lesinski36c73a52016-08-11 13:39:24 -0700487Maybe<int> parseSdkVersion(const StringPiece& str) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700488 StringPiece trimmedStr(util::trimWhitespace(str));
489
490 std::u16string str16 = util::utf8ToUtf16(trimmedStr);
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700491 android::Res_value value;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700492 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700493 return static_cast<int>(value.data);
494 }
495
496 // Try parsing the code name.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700497 std::pair<StringPiece, int> entry = getDevelopmentSdkCodeNameAndVersion();
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700498 if (entry.first == trimmedStr) {
499 return entry.second;
500 }
501 return {};
502}
503
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700504std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) {
Adam Lesinski36c73a52016-08-11 13:39:24 -0700505 if (Maybe<bool> maybeResult = parseBool(str)) {
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800506 android::Res_value value = {};
507 value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
508
Adam Lesinski36c73a52016-08-11 13:39:24 -0700509 if (maybeResult.value()) {
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800510 value.data = 0xffffffffu;
511 } else {
512 value.data = 0;
513 }
514 return util::make_unique<BinaryPrimitive>(value);
515 }
516 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700517}
518
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700519std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str) {
520 std::u16string str16 = util::utf8ToUtf16(str);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700521 android::Res_value value;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700522 if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700523 return {};
524 }
525 return util::make_unique<BinaryPrimitive>(value);
526}
527
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700528std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) {
529 std::u16string str16 = util::utf8ToUtf16(str);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700530 android::Res_value value;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700531 if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700532 return {};
533 }
534 return util::make_unique<BinaryPrimitive>(value);
535}
536
537uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
538 switch (type) {
539 case android::Res_value::TYPE_NULL:
540 case android::Res_value::TYPE_REFERENCE:
541 case android::Res_value::TYPE_ATTRIBUTE:
542 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
543 return android::ResTable_map::TYPE_REFERENCE;
544
545 case android::Res_value::TYPE_STRING:
546 return android::ResTable_map::TYPE_STRING;
547
548 case android::Res_value::TYPE_FLOAT:
549 return android::ResTable_map::TYPE_FLOAT;
550
551 case android::Res_value::TYPE_DIMENSION:
552 return android::ResTable_map::TYPE_DIMENSION;
553
554 case android::Res_value::TYPE_FRACTION:
555 return android::ResTable_map::TYPE_FRACTION;
556
557 case android::Res_value::TYPE_INT_DEC:
558 case android::Res_value::TYPE_INT_HEX:
559 return android::ResTable_map::TYPE_INTEGER | android::ResTable_map::TYPE_ENUM
560 | android::ResTable_map::TYPE_FLAGS;
561
562 case android::Res_value::TYPE_INT_BOOLEAN:
563 return android::ResTable_map::TYPE_BOOLEAN;
564
565 case android::Res_value::TYPE_INT_COLOR_ARGB8:
566 case android::Res_value::TYPE_INT_COLOR_RGB8:
567 case android::Res_value::TYPE_INT_COLOR_ARGB4:
568 case android::Res_value::TYPE_INT_COLOR_RGB4:
569 return android::ResTable_map::TYPE_COLOR;
570
571 default:
572 return 0;
573 };
574}
575
Adam Lesinski36c73a52016-08-11 13:39:24 -0700576std::unique_ptr<Item> tryParseItemForAttribute(
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700577 const StringPiece& value,
578 uint32_t typeMask,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700579 const std::function<void(const ResourceName&)>& onCreateReference) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700580 std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
581 if (nullOrEmpty) {
582 return std::move(nullOrEmpty);
583 }
584
585 bool create = false;
586 std::unique_ptr<Reference> reference = tryParseReference(value, &create);
587 if (reference) {
588 if (create && onCreateReference) {
589 onCreateReference(reference->name.value());
590 }
591 return std::move(reference);
592 }
593
594 if (typeMask & android::ResTable_map::TYPE_COLOR) {
595 // Try parsing this as a color.
596 std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
597 if (color) {
598 return std::move(color);
599 }
600 }
601
602 if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
603 // Try parsing this as a boolean.
604 std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
605 if (boolean) {
606 return std::move(boolean);
607 }
608 }
609
610 if (typeMask & android::ResTable_map::TYPE_INTEGER) {
611 // Try parsing this as an integer.
612 std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
613 if (integer) {
614 return std::move(integer);
615 }
616 }
617
618 const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT
619 | android::ResTable_map::TYPE_DIMENSION | android::ResTable_map::TYPE_FRACTION;
620 if (typeMask & floatMask) {
621 // Try parsing this as a float.
622 std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
623 if (floatingPoint) {
624 if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
625 return std::move(floatingPoint);
626 }
627 }
628 }
629 return {};
630}
631
632/**
633 * We successively try to parse the string as a resource type that the Attribute
634 * allows.
635 */
Adam Lesinski36c73a52016-08-11 13:39:24 -0700636std::unique_ptr<Item> tryParseItemForAttribute(
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700637 const StringPiece& str, const Attribute* attr,
Chih-Hung Hsieh9b8528f2016-08-10 14:15:30 -0700638 const std::function<void(const ResourceName&)>& onCreateReference) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700639 const uint32_t typeMask = attr->typeMask;
Adam Lesinski36c73a52016-08-11 13:39:24 -0700640 std::unique_ptr<Item> value = tryParseItemForAttribute(str, typeMask, onCreateReference);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700641 if (value) {
642 return value;
643 }
644
645 if (typeMask & android::ResTable_map::TYPE_ENUM) {
646 // Try parsing this as an enum.
647 std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
648 if (enumValue) {
649 return std::move(enumValue);
650 }
651 }
652
653 if (typeMask & android::ResTable_map::TYPE_FLAGS) {
654 // Try parsing this as a flag.
655 std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
656 if (flagValue) {
657 return std::move(flagValue);
658 }
659 }
660 return {};
661}
662
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800663std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) {
664 std::stringstream out;
665 out << "res/" << resFile.name.type;
666 if (resFile.config != ConfigDescription{}) {
667 out << "-" << resFile.config;
668 }
669 out << "/";
670
671 if (mangler && mangler->shouldMangle(resFile.name.package)) {
672 out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
673 } else {
674 out << resFile.name.entry;
675 }
676 out << file::getExtension(resFile.source.path);
677 return out.str();
678}
679
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700680} // namespace ResourceUtils
681} // namespace aapt