blob: 302c04fc3780d3e95d204a0f6435d5552b20a0b5 [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 Lesinski7298bc9c2015-11-16 12:31:52 -080030bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
Adam Lesinski1ab598f2015-08-14 14:26:04 -070031 StringPiece16* outType, StringPiece16* outEntry) {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080032 bool hasPackageSeparator = false;
33 bool hasTypeSeparator = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070034 const char16_t* start = str.data();
35 const char16_t* end = start + str.size();
36 const char16_t* current = start;
37 while (current != end) {
38 if (outType->size() == 0 && *current == u'/') {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080039 hasTypeSeparator = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040 outType->assign(start, current - start);
41 start = current + 1;
42 } else if (outPackage->size() == 0 && *current == u':') {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080043 hasPackageSeparator = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070044 outPackage->assign(start, current - start);
45 start = current + 1;
46 }
47 current++;
48 }
49 outEntry->assign(start, end - start);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -080050
51 return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
Adam Lesinski1ab598f2015-08-14 14:26:04 -070052}
53
Adam Lesinski467f1712015-11-16 17:35:44 -080054bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) {
Adam Lesinski59e04c62016-02-04 15:59:23 -080055 if (str.empty()) {
56 return false;
57 }
58
Adam Lesinski467f1712015-11-16 17:35:44 -080059 size_t offset = 0;
60 bool priv = false;
61 if (str.data()[0] == u'*') {
62 priv = true;
63 offset = 1;
64 }
65
66 StringPiece16 package;
67 StringPiece16 type;
68 StringPiece16 entry;
69 if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
70 return false;
71 }
72
73 const ResourceType* parsedType = parseResourceType(type);
74 if (!parsedType) {
75 return false;
76 }
77
78 if (entry.empty()) {
79 return false;
80 }
81
82 if (outRef) {
83 outRef->package = package;
84 outRef->type = *parsedType;
85 outRef->entry = entry;
86 }
87
88 if (outPrivate) {
89 *outPrivate = priv;
90 }
91 return true;
92}
93
Adam Lesinski1ab598f2015-08-14 14:26:04 -070094bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate,
95 bool* outPrivate) {
96 StringPiece16 trimmedStr(util::trimWhitespace(str));
97 if (trimmedStr.empty()) {
98 return false;
99 }
100
101 bool create = false;
102 bool priv = false;
103 if (trimmedStr.data()[0] == u'@') {
104 size_t offset = 1;
105 if (trimmedStr.data()[1] == u'+') {
106 create = true;
107 offset += 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700108 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800109
110 ResourceNameRef name;
111 if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
112 &name, &priv)) {
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800113 return false;
114 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700115
Adam Lesinski467f1712015-11-16 17:35:44 -0800116 if (create && priv) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117 return false;
118 }
119
Adam Lesinski467f1712015-11-16 17:35:44 -0800120 if (create && name.type != ResourceType::kId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700121 return false;
122 }
123
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800124 if (outRef) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800125 *outRef = name;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800126 }
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800127
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128 if (outCreate) {
129 *outCreate = create;
130 }
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800131
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700132 if (outPrivate) {
133 *outPrivate = priv;
134 }
135 return true;
136 }
137 return false;
138}
139
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800140bool isReference(const StringPiece16& str) {
141 return tryParseReference(str, nullptr, nullptr, nullptr);
142}
143
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700144bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRef) {
145 StringPiece16 trimmedStr(util::trimWhitespace(str));
146 if (trimmedStr.empty()) {
147 return false;
148 }
149
150 if (*trimmedStr.data() == u'?') {
151 StringPiece16 package;
152 StringPiece16 type;
153 StringPiece16 entry;
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800154 if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1),
155 &package, &type, &entry)) {
156 return false;
157 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700158
159 if (!type.empty() && type != u"attr") {
160 return false;
161 }
162
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800163 if (entry.empty()) {
164 return false;
165 }
166
167 if (outRef) {
168 outRef->package = package;
169 outRef->type = ResourceType::kAttr;
170 outRef->entry = entry;
171 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700172 return true;
173 }
174 return false;
175}
176
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800177bool isAttributeReference(const StringPiece16& str) {
178 return tryParseAttributeReference(str, nullptr);
179}
180
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700181/*
182 * Style parent's are a bit different. We accept the following formats:
183 *
Adam Lesinski52364f72016-01-11 13:10:24 -0800184 * @[[*]package:][style/]<entry>
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800185 * ?[[*]package:]style/<entry>
186 * <[*]package>:[style/]<entry>
187 * [[*]package:style/]<entry>
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 */
189Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError) {
190 if (str.empty()) {
191 return {};
192 }
193
194 StringPiece16 name = str;
195
196 bool hasLeadingIdentifiers = false;
197 bool privateRef = false;
198
199 // Skip over these identifiers. A style's parent is a normal reference.
200 if (name.data()[0] == u'@' || name.data()[0] == u'?') {
201 hasLeadingIdentifiers = true;
202 name = name.substr(1, name.size() - 1);
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800203 }
204
205 if (name.data()[0] == u'*') {
206 privateRef = true;
207 name = name.substr(1, name.size() - 1);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700208 }
209
210 ResourceNameRef ref;
211 ref.type = ResourceType::kStyle;
212
213 StringPiece16 typeStr;
214 extractResourceName(name, &ref.package, &typeStr, &ref.entry);
215 if (!typeStr.empty()) {
216 // If we have a type, make sure it is a Style.
217 const ResourceType* parsedType = parseResourceType(typeStr);
218 if (!parsedType || *parsedType != ResourceType::kStyle) {
219 std::stringstream err;
220 err << "invalid resource type '" << typeStr << "' for parent of style";
221 *outError = err.str();
222 return {};
223 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700224 }
225
226 if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
227 std::stringstream err;
228 err << "invalid parent reference '" << str << "'";
229 *outError = err.str();
230 return {};
231 }
232
233 Reference result(ref);
234 result.privateReference = privateRef;
235 return result;
236}
237
238std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* outCreate) {
239 ResourceNameRef ref;
240 bool privateRef = false;
241 if (tryParseReference(str, &ref, outCreate, &privateRef)) {
242 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
243 value->privateReference = privateRef;
244 return value;
245 }
246
247 if (tryParseAttributeReference(str, &ref)) {
248 if (outCreate) {
249 *outCreate = false;
250 }
251 return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
252 }
253 return {};
254}
255
256std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str) {
257 StringPiece16 trimmedStr(util::trimWhitespace(str));
258 android::Res_value value = { };
259 if (trimmedStr == u"@null") {
260 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
261 // Instead we set the data type to TYPE_REFERENCE with a value of 0.
262 value.dataType = android::Res_value::TYPE_REFERENCE;
263 } else if (trimmedStr == u"@empty") {
264 // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
265 value.dataType = android::Res_value::TYPE_NULL;
266 value.data = android::Res_value::DATA_NULL_EMPTY;
267 } else {
268 return {};
269 }
270 return util::make_unique<BinaryPrimitive>(value);
271}
272
273std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
274 const StringPiece16& str) {
275 StringPiece16 trimmedStr(util::trimWhitespace(str));
276 for (const Attribute::Symbol& symbol : enumAttr->symbols) {
277 // Enum symbols are stored as @package:id/symbol resources,
278 // so we need to match against the 'entry' part of the identifier.
279 const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
280 if (trimmedStr == enumSymbolResourceName.entry) {
281 android::Res_value value = { };
282 value.dataType = android::Res_value::TYPE_INT_DEC;
283 value.data = symbol.value;
284 return util::make_unique<BinaryPrimitive>(value);
285 }
286 }
287 return {};
288}
289
290std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
291 const StringPiece16& str) {
292 android::Res_value flags = { };
Adam Lesinski458b8772016-04-25 14:20:21 -0700293 flags.dataType = android::Res_value::TYPE_INT_HEX;
Adam Lesinski52364f72016-01-11 13:10:24 -0800294 flags.data = 0u;
295
296 if (util::trimWhitespace(str).empty()) {
297 // Empty string is a valid flag (0).
298 return util::make_unique<BinaryPrimitive>(flags);
299 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700300
301 for (StringPiece16 part : util::tokenize(str, u'|')) {
302 StringPiece16 trimmedPart = util::trimWhitespace(part);
303
304 bool flagSet = false;
305 for (const Attribute::Symbol& symbol : flagAttr->symbols) {
306 // Flag symbols are stored as @package:id/symbol resources,
307 // so we need to match against the 'entry' part of the identifier.
308 const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
309 if (trimmedPart == flagSymbolResourceName.entry) {
310 flags.data |= symbol.value;
311 flagSet = true;
312 break;
313 }
314 }
315
316 if (!flagSet) {
317 return {};
318 }
319 }
320 return util::make_unique<BinaryPrimitive>(flags);
321}
322
323static uint32_t parseHex(char16_t c, bool* outError) {
324 if (c >= u'0' && c <= u'9') {
325 return c - u'0';
326 } else if (c >= u'a' && c <= u'f') {
327 return c - u'a' + 0xa;
328 } else if (c >= u'A' && c <= u'F') {
329 return c - u'A' + 0xa;
330 } else {
331 *outError = true;
332 return 0xffffffffu;
333 }
334}
335
336std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str) {
337 StringPiece16 colorStr(util::trimWhitespace(str));
338 const char16_t* start = colorStr.data();
339 const size_t len = colorStr.size();
340 if (len == 0 || start[0] != u'#') {
341 return {};
342 }
343
344 android::Res_value value = { };
345 bool error = false;
346 if (len == 4) {
347 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
348 value.data = 0xff000000u;
349 value.data |= parseHex(start[1], &error) << 20;
350 value.data |= parseHex(start[1], &error) << 16;
351 value.data |= parseHex(start[2], &error) << 12;
352 value.data |= parseHex(start[2], &error) << 8;
353 value.data |= parseHex(start[3], &error) << 4;
354 value.data |= parseHex(start[3], &error);
355 } else if (len == 5) {
356 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
357 value.data |= parseHex(start[1], &error) << 28;
358 value.data |= parseHex(start[1], &error) << 24;
359 value.data |= parseHex(start[2], &error) << 20;
360 value.data |= parseHex(start[2], &error) << 16;
361 value.data |= parseHex(start[3], &error) << 12;
362 value.data |= parseHex(start[3], &error) << 8;
363 value.data |= parseHex(start[4], &error) << 4;
364 value.data |= parseHex(start[4], &error);
365 } else if (len == 7) {
366 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
367 value.data = 0xff000000u;
368 value.data |= parseHex(start[1], &error) << 20;
369 value.data |= parseHex(start[2], &error) << 16;
370 value.data |= parseHex(start[3], &error) << 12;
371 value.data |= parseHex(start[4], &error) << 8;
372 value.data |= parseHex(start[5], &error) << 4;
373 value.data |= parseHex(start[6], &error);
374 } else if (len == 9) {
375 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
376 value.data |= parseHex(start[1], &error) << 28;
377 value.data |= parseHex(start[2], &error) << 24;
378 value.data |= parseHex(start[3], &error) << 20;
379 value.data |= parseHex(start[4], &error) << 16;
380 value.data |= parseHex(start[5], &error) << 12;
381 value.data |= parseHex(start[6], &error) << 8;
382 value.data |= parseHex(start[7], &error) << 4;
383 value.data |= parseHex(start[8], &error);
384 } else {
385 return {};
386 }
387 return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
388}
389
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800390bool tryParseBool(const StringPiece16& str, bool* outValue) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700391 StringPiece16 trimmedStr(util::trimWhitespace(str));
Adam Lesinski52364f72016-01-11 13:10:24 -0800392 if (trimmedStr == u"true" || trimmedStr == u"TRUE" || trimmedStr == u"True") {
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800393 if (outValue) {
394 *outValue = true;
395 }
396 return true;
Adam Lesinski52364f72016-01-11 13:10:24 -0800397 } else if (trimmedStr == u"false" || trimmedStr == u"FALSE" || trimmedStr == u"False") {
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800398 if (outValue) {
399 *outValue = false;
400 }
401 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700402 }
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800403 return false;
404}
405
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700406Maybe<int> tryParseSdkVersion(const StringPiece16& str) {
407 StringPiece16 trimmedStr(util::trimWhitespace(str));
408 android::Res_value value;
409 if (android::ResTable::stringToInt(trimmedStr.data(), trimmedStr.size(), &value)) {
410 return static_cast<int>(value.data);
411 }
412
413 // Try parsing the code name.
414 std::pair<StringPiece16, int> entry = getDevelopmentSdkCodeNameAndVersion();
415 if (entry.first == trimmedStr) {
416 return entry.second;
417 }
418 return {};
419}
420
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800421std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str) {
422 bool result = false;
423 if (tryParseBool(str, &result)) {
424 android::Res_value value = {};
425 value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
426
427 if (result) {
428 value.data = 0xffffffffu;
429 } else {
430 value.data = 0;
431 }
432 return util::make_unique<BinaryPrimitive>(value);
433 }
434 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700435}
436
437std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str) {
438 android::Res_value value;
439 if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) {
440 return {};
441 }
442 return util::make_unique<BinaryPrimitive>(value);
443}
444
445std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str) {
446 android::Res_value value;
447 if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) {
448 return {};
449 }
450 return util::make_unique<BinaryPrimitive>(value);
451}
452
453uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
454 switch (type) {
455 case android::Res_value::TYPE_NULL:
456 case android::Res_value::TYPE_REFERENCE:
457 case android::Res_value::TYPE_ATTRIBUTE:
458 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
459 return android::ResTable_map::TYPE_REFERENCE;
460
461 case android::Res_value::TYPE_STRING:
462 return android::ResTable_map::TYPE_STRING;
463
464 case android::Res_value::TYPE_FLOAT:
465 return android::ResTable_map::TYPE_FLOAT;
466
467 case android::Res_value::TYPE_DIMENSION:
468 return android::ResTable_map::TYPE_DIMENSION;
469
470 case android::Res_value::TYPE_FRACTION:
471 return android::ResTable_map::TYPE_FRACTION;
472
473 case android::Res_value::TYPE_INT_DEC:
474 case android::Res_value::TYPE_INT_HEX:
475 return android::ResTable_map::TYPE_INTEGER | android::ResTable_map::TYPE_ENUM
476 | android::ResTable_map::TYPE_FLAGS;
477
478 case android::Res_value::TYPE_INT_BOOLEAN:
479 return android::ResTable_map::TYPE_BOOLEAN;
480
481 case android::Res_value::TYPE_INT_COLOR_ARGB8:
482 case android::Res_value::TYPE_INT_COLOR_RGB8:
483 case android::Res_value::TYPE_INT_COLOR_ARGB4:
484 case android::Res_value::TYPE_INT_COLOR_RGB4:
485 return android::ResTable_map::TYPE_COLOR;
486
487 default:
488 return 0;
489 };
490}
491
492std::unique_ptr<Item> parseItemForAttribute(
493 const StringPiece16& value, uint32_t typeMask,
494 std::function<void(const ResourceName&)> onCreateReference) {
495 std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
496 if (nullOrEmpty) {
497 return std::move(nullOrEmpty);
498 }
499
500 bool create = false;
501 std::unique_ptr<Reference> reference = tryParseReference(value, &create);
502 if (reference) {
503 if (create && onCreateReference) {
504 onCreateReference(reference->name.value());
505 }
506 return std::move(reference);
507 }
508
509 if (typeMask & android::ResTable_map::TYPE_COLOR) {
510 // Try parsing this as a color.
511 std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
512 if (color) {
513 return std::move(color);
514 }
515 }
516
517 if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
518 // Try parsing this as a boolean.
519 std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
520 if (boolean) {
521 return std::move(boolean);
522 }
523 }
524
525 if (typeMask & android::ResTable_map::TYPE_INTEGER) {
526 // Try parsing this as an integer.
527 std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
528 if (integer) {
529 return std::move(integer);
530 }
531 }
532
533 const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT
534 | android::ResTable_map::TYPE_DIMENSION | android::ResTable_map::TYPE_FRACTION;
535 if (typeMask & floatMask) {
536 // Try parsing this as a float.
537 std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
538 if (floatingPoint) {
539 if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
540 return std::move(floatingPoint);
541 }
542 }
543 }
544 return {};
545}
546
547/**
548 * We successively try to parse the string as a resource type that the Attribute
549 * allows.
550 */
551std::unique_ptr<Item> parseItemForAttribute(
552 const StringPiece16& str, const Attribute* attr,
553 std::function<void(const ResourceName&)> onCreateReference) {
554 const uint32_t typeMask = attr->typeMask;
555 std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
556 if (value) {
557 return value;
558 }
559
560 if (typeMask & android::ResTable_map::TYPE_ENUM) {
561 // Try parsing this as an enum.
562 std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
563 if (enumValue) {
564 return std::move(enumValue);
565 }
566 }
567
568 if (typeMask & android::ResTable_map::TYPE_FLAGS) {
569 // Try parsing this as a flag.
570 std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
571 if (flagValue) {
572 return std::move(flagValue);
573 }
574 }
575 return {};
576}
577
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800578std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) {
579 std::stringstream out;
580 out << "res/" << resFile.name.type;
581 if (resFile.config != ConfigDescription{}) {
582 out << "-" << resFile.config;
583 }
584 out << "/";
585
586 if (mangler && mangler->shouldMangle(resFile.name.package)) {
587 out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
588 } else {
589 out << resFile.name.entry;
590 }
591 out << file::getExtension(resFile.source.path);
592 return out.str();
593}
594
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700595} // namespace ResourceUtils
596} // namespace aapt