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