Home | History | Annotate | Download | only in aapt2
      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 "ResourceUtils.h"
     18 
     19 #include <sstream>
     20 
     21 #include "androidfw/ResourceTypes.h"
     22 #include "androidfw/ResourceUtils.h"
     23 
     24 #include "NameMangler.h"
     25 #include "SdkConstants.h"
     26 #include "flatten/ResourceTypeExtensions.h"
     27 #include "util/Files.h"
     28 #include "util/Util.h"
     29 
     30 using android::StringPiece;
     31 using android::StringPiece16;
     32 
     33 namespace aapt {
     34 namespace ResourceUtils {
     35 
     36 Maybe<ResourceName> ToResourceName(
     37     const android::ResTable::resource_name& name_in) {
     38   ResourceName name_out;
     39   if (!name_in.package) {
     40     return {};
     41   }
     42 
     43   name_out.package =
     44       util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
     45 
     46   const ResourceType* type;
     47   if (name_in.type) {
     48     type = ParseResourceType(
     49         util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
     50   } else if (name_in.type8) {
     51     type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
     52   } else {
     53     return {};
     54   }
     55 
     56   if (!type) {
     57     return {};
     58   }
     59 
     60   name_out.type = *type;
     61 
     62   if (name_in.name) {
     63     name_out.entry =
     64         util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
     65   } else if (name_in.name8) {
     66     name_out.entry.assign(name_in.name8, name_in.nameLen);
     67   } else {
     68     return {};
     69   }
     70   return name_out;
     71 }
     72 
     73 bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
     74                        bool* out_private) {
     75   if (str.empty()) {
     76     return false;
     77   }
     78 
     79   size_t offset = 0;
     80   bool priv = false;
     81   if (str.data()[0] == '*') {
     82     priv = true;
     83     offset = 1;
     84   }
     85 
     86   StringPiece package;
     87   StringPiece type;
     88   StringPiece entry;
     89   if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type,
     90                                     &entry)) {
     91     return false;
     92   }
     93 
     94   const ResourceType* parsed_type = ParseResourceType(type);
     95   if (!parsed_type) {
     96     return false;
     97   }
     98 
     99   if (entry.empty()) {
    100     return false;
    101   }
    102 
    103   if (out_ref) {
    104     out_ref->package = package;
    105     out_ref->type = *parsed_type;
    106     out_ref->entry = entry;
    107   }
    108 
    109   if (out_private) {
    110     *out_private = priv;
    111   }
    112   return true;
    113 }
    114 
    115 bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
    116                     bool* out_create, bool* out_private) {
    117   StringPiece trimmed_str(util::TrimWhitespace(str));
    118   if (trimmed_str.empty()) {
    119     return false;
    120   }
    121 
    122   bool create = false;
    123   bool priv = false;
    124   if (trimmed_str.data()[0] == '@') {
    125     size_t offset = 1;
    126     if (trimmed_str.data()[1] == '+') {
    127       create = true;
    128       offset += 1;
    129     }
    130 
    131     ResourceNameRef name;
    132     if (!ParseResourceName(
    133             trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
    134             &priv)) {
    135       return false;
    136     }
    137 
    138     if (create && priv) {
    139       return false;
    140     }
    141 
    142     if (create && name.type != ResourceType::kId) {
    143       return false;
    144     }
    145 
    146     if (out_ref) {
    147       *out_ref = name;
    148     }
    149 
    150     if (out_create) {
    151       *out_create = create;
    152     }
    153 
    154     if (out_private) {
    155       *out_private = priv;
    156     }
    157     return true;
    158   }
    159   return false;
    160 }
    161 
    162 bool IsReference(const StringPiece& str) {
    163   return ParseReference(str, nullptr, nullptr, nullptr);
    164 }
    165 
    166 bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
    167   StringPiece trimmed_str(util::TrimWhitespace(str));
    168   if (trimmed_str.empty()) {
    169     return false;
    170   }
    171 
    172   if (*trimmed_str.data() == '?') {
    173     StringPiece package;
    174     StringPiece type;
    175     StringPiece entry;
    176     if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package,
    177                                       &type, &entry)) {
    178       return false;
    179     }
    180 
    181     if (!type.empty() && type != "attr") {
    182       return false;
    183     }
    184 
    185     if (entry.empty()) {
    186       return false;
    187     }
    188 
    189     if (out_ref) {
    190       out_ref->package = package;
    191       out_ref->type = ResourceType::kAttr;
    192       out_ref->entry = entry;
    193     }
    194     return true;
    195   }
    196   return false;
    197 }
    198 
    199 bool IsAttributeReference(const StringPiece& str) {
    200   return ParseAttributeReference(str, nullptr);
    201 }
    202 
    203 /*
    204  * Style parent's are a bit different. We accept the following formats:
    205  *
    206  * @[[*]package:][style/]<entry>
    207  * ?[[*]package:]style/<entry>
    208  * <[*]package>:[style/]<entry>
    209  * [[*]package:style/]<entry>
    210  */
    211 Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
    212                                            std::string* out_error) {
    213   if (str.empty()) {
    214     return {};
    215   }
    216 
    217   StringPiece name = str;
    218 
    219   bool has_leading_identifiers = false;
    220   bool private_ref = false;
    221 
    222   // Skip over these identifiers. A style's parent is a normal reference.
    223   if (name.data()[0] == '@' || name.data()[0] == '?') {
    224     has_leading_identifiers = true;
    225     name = name.substr(1, name.size() - 1);
    226   }
    227 
    228   if (name.data()[0] == '*') {
    229     private_ref = true;
    230     name = name.substr(1, name.size() - 1);
    231   }
    232 
    233   ResourceNameRef ref;
    234   ref.type = ResourceType::kStyle;
    235 
    236   StringPiece type_str;
    237   android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
    238   if (!type_str.empty()) {
    239     // If we have a type, make sure it is a Style.
    240     const ResourceType* parsed_type = ParseResourceType(type_str);
    241     if (!parsed_type || *parsed_type != ResourceType::kStyle) {
    242       std::stringstream err;
    243       err << "invalid resource type '" << type_str << "' for parent of style";
    244       *out_error = err.str();
    245       return {};
    246     }
    247   }
    248 
    249   if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
    250     std::stringstream err;
    251     err << "invalid parent reference '" << str << "'";
    252     *out_error = err.str();
    253     return {};
    254   }
    255 
    256   Reference result(ref);
    257   result.private_reference = private_ref;
    258   return result;
    259 }
    260 
    261 Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
    262   StringPiece trimmed_str = util::TrimWhitespace(str);
    263   const char* start = trimmed_str.data();
    264   const char* const end = start + trimmed_str.size();
    265   const char* p = start;
    266 
    267   Reference ref;
    268   if (p != end && *p == '*') {
    269     ref.private_reference = true;
    270     start++;
    271     p++;
    272   }
    273 
    274   StringPiece package;
    275   StringPiece name;
    276   while (p != end) {
    277     if (*p == ':') {
    278       package = StringPiece(start, p - start);
    279       name = StringPiece(p + 1, end - (p + 1));
    280       break;
    281     }
    282     p++;
    283   }
    284 
    285   ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
    286   return Maybe<Reference>(std::move(ref));
    287 }
    288 
    289 std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
    290                                              bool* out_create) {
    291   ResourceNameRef ref;
    292   bool private_ref = false;
    293   if (ParseReference(str, &ref, out_create, &private_ref)) {
    294     std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
    295     value->private_reference = private_ref;
    296     return value;
    297   }
    298 
    299   if (ParseAttributeReference(str, &ref)) {
    300     if (out_create) {
    301       *out_create = false;
    302     }
    303     return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
    304   }
    305   return {};
    306 }
    307 
    308 std::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) {
    309   const StringPiece trimmed_str(util::TrimWhitespace(str));
    310   if (trimmed_str == "@null") {
    311     return MakeNull();
    312   } else if (trimmed_str == "@empty") {
    313     return MakeEmpty();
    314   }
    315   return {};
    316 }
    317 
    318 std::unique_ptr<Reference> MakeNull() {
    319   // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
    320   // Instead we set the data type to TYPE_REFERENCE with a value of 0.
    321   return util::make_unique<Reference>();
    322 }
    323 
    324 std::unique_ptr<BinaryPrimitive> MakeEmpty() {
    325   return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL,
    326                                             android::Res_value::DATA_NULL_EMPTY);
    327 }
    328 
    329 std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
    330                                                     const StringPiece& str) {
    331   StringPiece trimmed_str(util::TrimWhitespace(str));
    332   for (const Attribute::Symbol& symbol : enum_attr->symbols) {
    333     // Enum symbols are stored as @package:id/symbol resources,
    334     // so we need to match against the 'entry' part of the identifier.
    335     const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
    336     if (trimmed_str == enum_symbol_resource_name.entry) {
    337       android::Res_value value = {};
    338       value.dataType = android::Res_value::TYPE_INT_DEC;
    339       value.data = symbol.value;
    340       return util::make_unique<BinaryPrimitive>(value);
    341     }
    342   }
    343   return {};
    344 }
    345 
    346 std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
    347                                                     const StringPiece& str) {
    348   android::Res_value flags = {};
    349   flags.dataType = android::Res_value::TYPE_INT_HEX;
    350   flags.data = 0u;
    351 
    352   if (util::TrimWhitespace(str).empty()) {
    353     // Empty string is a valid flag (0).
    354     return util::make_unique<BinaryPrimitive>(flags);
    355   }
    356 
    357   for (StringPiece part : util::Tokenize(str, '|')) {
    358     StringPiece trimmed_part = util::TrimWhitespace(part);
    359 
    360     bool flag_set = false;
    361     for (const Attribute::Symbol& symbol : flag_attr->symbols) {
    362       // Flag symbols are stored as @package:id/symbol resources,
    363       // so we need to match against the 'entry' part of the identifier.
    364       const ResourceName& flag_symbol_resource_name =
    365           symbol.symbol.name.value();
    366       if (trimmed_part == flag_symbol_resource_name.entry) {
    367         flags.data |= symbol.value;
    368         flag_set = true;
    369         break;
    370       }
    371     }
    372 
    373     if (!flag_set) {
    374       return {};
    375     }
    376   }
    377   return util::make_unique<BinaryPrimitive>(flags);
    378 }
    379 
    380 static uint32_t ParseHex(char c, bool* out_error) {
    381   if (c >= '0' && c <= '9') {
    382     return c - '0';
    383   } else if (c >= 'a' && c <= 'f') {
    384     return c - 'a' + 0xa;
    385   } else if (c >= 'A' && c <= 'F') {
    386     return c - 'A' + 0xa;
    387   } else {
    388     *out_error = true;
    389     return 0xffffffffu;
    390   }
    391 }
    392 
    393 std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
    394   StringPiece color_str(util::TrimWhitespace(str));
    395   const char* start = color_str.data();
    396   const size_t len = color_str.size();
    397   if (len == 0 || start[0] != '#') {
    398     return {};
    399   }
    400 
    401   android::Res_value value = {};
    402   bool error = false;
    403   if (len == 4) {
    404     value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
    405     value.data = 0xff000000u;
    406     value.data |= ParseHex(start[1], &error) << 20;
    407     value.data |= ParseHex(start[1], &error) << 16;
    408     value.data |= ParseHex(start[2], &error) << 12;
    409     value.data |= ParseHex(start[2], &error) << 8;
    410     value.data |= ParseHex(start[3], &error) << 4;
    411     value.data |= ParseHex(start[3], &error);
    412   } else if (len == 5) {
    413     value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
    414     value.data |= ParseHex(start[1], &error) << 28;
    415     value.data |= ParseHex(start[1], &error) << 24;
    416     value.data |= ParseHex(start[2], &error) << 20;
    417     value.data |= ParseHex(start[2], &error) << 16;
    418     value.data |= ParseHex(start[3], &error) << 12;
    419     value.data |= ParseHex(start[3], &error) << 8;
    420     value.data |= ParseHex(start[4], &error) << 4;
    421     value.data |= ParseHex(start[4], &error);
    422   } else if (len == 7) {
    423     value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
    424     value.data = 0xff000000u;
    425     value.data |= ParseHex(start[1], &error) << 20;
    426     value.data |= ParseHex(start[2], &error) << 16;
    427     value.data |= ParseHex(start[3], &error) << 12;
    428     value.data |= ParseHex(start[4], &error) << 8;
    429     value.data |= ParseHex(start[5], &error) << 4;
    430     value.data |= ParseHex(start[6], &error);
    431   } else if (len == 9) {
    432     value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
    433     value.data |= ParseHex(start[1], &error) << 28;
    434     value.data |= ParseHex(start[2], &error) << 24;
    435     value.data |= ParseHex(start[3], &error) << 20;
    436     value.data |= ParseHex(start[4], &error) << 16;
    437     value.data |= ParseHex(start[5], &error) << 12;
    438     value.data |= ParseHex(start[6], &error) << 8;
    439     value.data |= ParseHex(start[7], &error) << 4;
    440     value.data |= ParseHex(start[8], &error);
    441   } else {
    442     return {};
    443   }
    444   return error ? std::unique_ptr<BinaryPrimitive>()
    445                : util::make_unique<BinaryPrimitive>(value);
    446 }
    447 
    448 Maybe<bool> ParseBool(const StringPiece& str) {
    449   StringPiece trimmed_str(util::TrimWhitespace(str));
    450   if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
    451     return Maybe<bool>(true);
    452   } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
    453              trimmed_str == "False") {
    454     return Maybe<bool>(false);
    455   }
    456   return {};
    457 }
    458 
    459 Maybe<uint32_t> ParseInt(const StringPiece& str) {
    460   std::u16string str16 = util::Utf8ToUtf16(str);
    461   android::Res_value value;
    462   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
    463     return value.data;
    464   }
    465   return {};
    466 }
    467 
    468 Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
    469   StringPiece trimmed_str(util::TrimWhitespace(str));
    470 
    471   std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
    472   android::Res_value value;
    473   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
    474     if (value.dataType == android::Res_value::TYPE_INT_HEX) {
    475       ResourceId id(value.data);
    476       if (id.is_valid_dynamic()) {
    477         return id;
    478       }
    479     }
    480   }
    481   return {};
    482 }
    483 
    484 Maybe<int> ParseSdkVersion(const StringPiece& str) {
    485   StringPiece trimmed_str(util::TrimWhitespace(str));
    486 
    487   std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
    488   android::Res_value value;
    489   if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
    490     return static_cast<int>(value.data);
    491   }
    492 
    493   // Try parsing the code name.
    494   std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
    495   if (entry.first == trimmed_str) {
    496     return entry.second;
    497   }
    498   return {};
    499 }
    500 
    501 std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
    502   if (Maybe<bool> maybe_result = ParseBool(str)) {
    503     const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
    504     return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
    505   }
    506   return {};
    507 }
    508 
    509 std::unique_ptr<BinaryPrimitive> MakeBool(bool val) {
    510   return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN,
    511                                             val ? 0xffffffffu : 0u);
    512 }
    513 
    514 std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
    515   std::u16string str16 = util::Utf8ToUtf16(str);
    516   android::Res_value value;
    517   if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
    518     return {};
    519   }
    520   return util::make_unique<BinaryPrimitive>(value);
    521 }
    522 
    523 std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
    524   std::u16string str16 = util::Utf8ToUtf16(str);
    525   android::Res_value value;
    526   if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
    527     return {};
    528   }
    529   return util::make_unique<BinaryPrimitive>(value);
    530 }
    531 
    532 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
    533   switch (type) {
    534     case android::Res_value::TYPE_NULL:
    535     case android::Res_value::TYPE_REFERENCE:
    536     case android::Res_value::TYPE_ATTRIBUTE:
    537     case android::Res_value::TYPE_DYNAMIC_REFERENCE:
    538     case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
    539       return android::ResTable_map::TYPE_REFERENCE;
    540 
    541     case android::Res_value::TYPE_STRING:
    542       return android::ResTable_map::TYPE_STRING;
    543 
    544     case android::Res_value::TYPE_FLOAT:
    545       return android::ResTable_map::TYPE_FLOAT;
    546 
    547     case android::Res_value::TYPE_DIMENSION:
    548       return android::ResTable_map::TYPE_DIMENSION;
    549 
    550     case android::Res_value::TYPE_FRACTION:
    551       return android::ResTable_map::TYPE_FRACTION;
    552 
    553     case android::Res_value::TYPE_INT_DEC:
    554     case android::Res_value::TYPE_INT_HEX:
    555       return android::ResTable_map::TYPE_INTEGER |
    556              android::ResTable_map::TYPE_ENUM |
    557              android::ResTable_map::TYPE_FLAGS;
    558 
    559     case android::Res_value::TYPE_INT_BOOLEAN:
    560       return android::ResTable_map::TYPE_BOOLEAN;
    561 
    562     case android::Res_value::TYPE_INT_COLOR_ARGB8:
    563     case android::Res_value::TYPE_INT_COLOR_RGB8:
    564     case android::Res_value::TYPE_INT_COLOR_ARGB4:
    565     case android::Res_value::TYPE_INT_COLOR_RGB4:
    566       return android::ResTable_map::TYPE_COLOR;
    567 
    568     default:
    569       return 0;
    570   };
    571 }
    572 
    573 std::unique_ptr<Item> TryParseItemForAttribute(
    574     const StringPiece& value, uint32_t type_mask,
    575     const std::function<void(const ResourceName&)>& on_create_reference) {
    576   using android::ResTable_map;
    577 
    578   auto null_or_empty = TryParseNullOrEmpty(value);
    579   if (null_or_empty) {
    580     return null_or_empty;
    581   }
    582 
    583   bool create = false;
    584   auto reference = TryParseReference(value, &create);
    585   if (reference) {
    586     if (create && on_create_reference) {
    587       on_create_reference(reference->name.value());
    588     }
    589     return std::move(reference);
    590   }
    591 
    592   if (type_mask & ResTable_map::TYPE_COLOR) {
    593     // Try parsing this as a color.
    594     auto color = TryParseColor(value);
    595     if (color) {
    596       return std::move(color);
    597     }
    598   }
    599 
    600   if (type_mask & ResTable_map::TYPE_BOOLEAN) {
    601     // Try parsing this as a boolean.
    602     auto boolean = TryParseBool(value);
    603     if (boolean) {
    604       return std::move(boolean);
    605     }
    606   }
    607 
    608   if (type_mask & ResTable_map::TYPE_INTEGER) {
    609     // Try parsing this as an integer.
    610     auto integer = TryParseInt(value);
    611     if (integer) {
    612       return std::move(integer);
    613     }
    614   }
    615 
    616   const uint32_t float_mask =
    617       ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
    618   if (type_mask & float_mask) {
    619     // Try parsing this as a float.
    620     auto floating_point = TryParseFloat(value);
    621     if (floating_point) {
    622       if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
    623         return std::move(floating_point);
    624       }
    625     }
    626   }
    627   return {};
    628 }
    629 
    630 /**
    631  * We successively try to parse the string as a resource type that the Attribute
    632  * allows.
    633  */
    634 std::unique_ptr<Item> TryParseItemForAttribute(
    635     const StringPiece& str, const Attribute* attr,
    636     const std::function<void(const ResourceName&)>& on_create_reference) {
    637   using android::ResTable_map;
    638 
    639   const uint32_t type_mask = attr->type_mask;
    640   auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
    641   if (value) {
    642     return value;
    643   }
    644 
    645   if (type_mask & ResTable_map::TYPE_ENUM) {
    646     // Try parsing this as an enum.
    647     auto enum_value = TryParseEnumSymbol(attr, str);
    648     if (enum_value) {
    649       return std::move(enum_value);
    650     }
    651   }
    652 
    653   if (type_mask & ResTable_map::TYPE_FLAGS) {
    654     // Try parsing this as a flag.
    655     auto flag_value = TryParseFlagSymbol(attr, str);
    656     if (flag_value) {
    657       return std::move(flag_value);
    658     }
    659   }
    660   return {};
    661 }
    662 
    663 std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
    664   std::stringstream out;
    665   out << "res/" << res_file.name.type;
    666   if (res_file.config != ConfigDescription{}) {
    667     out << "-" << res_file.config;
    668   }
    669   out << "/";
    670 
    671   if (mangler && mangler->ShouldMangle(res_file.name.package)) {
    672     out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
    673   } else {
    674     out << res_file.name.entry;
    675   }
    676   out << file::GetExtension(res_file.source.path);
    677   return out.str();
    678 }
    679 
    680 std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config,
    681                                           const android::ResStringPool& src_pool,
    682                                           const android::Res_value& res_value,
    683                                           StringPool* dst_pool) {
    684   if (type == ResourceType::kId) {
    685     return util::make_unique<Id>();
    686   }
    687 
    688   const uint32_t data = util::DeviceToHost32(res_value.data);
    689   switch (res_value.dataType) {
    690     case android::Res_value::TYPE_STRING: {
    691       const std::string str = util::GetString(src_pool, data);
    692       const android::ResStringPool_span* spans = src_pool.styleAt(data);
    693 
    694       // Check if the string has a valid style associated with it.
    695       if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
    696         StyleString style_str = {str};
    697         while (spans->name.index != android::ResStringPool_span::END) {
    698           style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
    699                                          spans->firstChar, spans->lastChar});
    700           spans++;
    701         }
    702         return util::make_unique<StyledString>(dst_pool->MakeRef(
    703             style_str, StringPool::Context(StringPool::Context::kStylePriority, config)));
    704       } else {
    705         if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
    706           // This must be a FileReference.
    707           return util::make_unique<FileReference>(dst_pool->MakeRef(
    708               str, StringPool::Context(StringPool::Context::kHighPriority, config)));
    709         }
    710 
    711         // There are no styles associated with this string, so treat it as a simple string.
    712         return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
    713       }
    714     } break;
    715 
    716     case android::Res_value::TYPE_REFERENCE:
    717     case android::Res_value::TYPE_ATTRIBUTE:
    718     case android::Res_value::TYPE_DYNAMIC_REFERENCE:
    719     case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
    720       Reference::Type ref_type = Reference::Type::kResource;
    721       if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE ||
    722           res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
    723         ref_type = Reference::Type::kAttribute;
    724       }
    725 
    726       if (data == 0u) {
    727         // A reference of 0, must be the magic @null reference.
    728         return util::make_unique<Reference>();
    729       }
    730 
    731       // This is a normal reference.
    732       return util::make_unique<Reference>(data, ref_type);
    733     } break;
    734   }
    735 
    736   // Treat this as a raw binary primitive.
    737   return util::make_unique<BinaryPrimitive>(res_value);
    738 }
    739 
    740 }  // namespace ResourceUtils
    741 }  // namespace aapt
    742