blob: 0032960ff93e6c852db6799e46751d814303bd08 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ResourceUtils.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070018
Nicholas Lativy79f03962019-01-16 16:19:09 +000019#include <algorithm>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070020#include <sstream>
21
Adam Lesinski2eed52e2018-02-21 15:55:58 -080022#include "android-base/stringprintf.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070023#include "androidfw/ResourceTypes.h"
Adam Lesinski929d6512017-01-16 19:11:19 -080024#include "androidfw/ResourceUtils.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070025
Adam Lesinskicacb28f2016-10-19 12:18:14 -070026#include "NameMangler.h"
Adam Lesinskifb6312f2016-06-28 14:40:32 -070027#include "SdkConstants.h"
Adam Lesinski46708052017-09-29 14:49:15 -070028#include "format/binary/ResourceTypeExtensions.h"
Adam Lesinski2eed52e2018-02-21 15:55:58 -080029#include "text/Unicode.h"
30#include "text/Utf8Iterator.h"
Adam Lesinskia6fe3452015-12-09 15:20:52 -080031#include "util/Files.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070032#include "util/Util.h"
33
Adam Lesinski2eed52e2018-02-21 15:55:58 -080034using ::aapt::text::Utf8Iterator;
MÃ¥rten Kongstad24c9aa62018-06-20 08:46:41 +020035using ::android::ConfigDescription;
Adam Lesinski46708052017-09-29 14:49:15 -070036using ::android::StringPiece;
37using ::android::StringPiece16;
Adam Lesinski2eed52e2018-02-21 15:55:58 -080038using ::android::base::StringPrintf;
Adam Lesinskid5083f62017-01-16 15:07:21 -080039
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040namespace aapt {
41namespace ResourceUtils {
42
Ryan Mitchell0ce89732018-10-03 09:20:57 -070043constexpr int32_t kNonBreakingSpace = 0xa0;
44
Adam Lesinskice5e56e2016-10-21 17:56:45 -070045Maybe<ResourceName> ToResourceName(
46 const android::ResTable::resource_name& name_in) {
47 ResourceName name_out;
48 if (!name_in.package) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070049 return {};
50 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070051
Adam Lesinskice5e56e2016-10-21 17:56:45 -070052 name_out.package =
53 util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
Adam Lesinskid0f116b2016-07-08 15:00:32 -070054
Adam Lesinskicacb28f2016-10-19 12:18:14 -070055 const ResourceType* type;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070056 if (name_in.type) {
57 type = ParseResourceType(
58 util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
59 } else if (name_in.type8) {
60 type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061 } else {
62 return {};
63 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070064
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 if (!type) {
66 return {};
67 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -070068
Adam Lesinskice5e56e2016-10-21 17:56:45 -070069 name_out.type = *type;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070070
Adam Lesinskice5e56e2016-10-21 17:56:45 -070071 if (name_in.name) {
72 name_out.entry =
73 util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
74 } else if (name_in.name8) {
Adam Lesinskid5083f62017-01-16 15:07:21 -080075 name_out.entry.assign(name_in.name8, name_in.nameLen);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070076 } else {
77 return {};
78 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070079 return name_out;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070080}
81
Adam Lesinskice5e56e2016-10-21 17:56:45 -070082bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
83 bool* out_private) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 if (str.empty()) {
85 return false;
86 }
87
88 size_t offset = 0;
89 bool priv = false;
90 if (str.data()[0] == '*') {
91 priv = true;
92 offset = 1;
93 }
94
95 StringPiece package;
96 StringPiece type;
97 StringPiece entry;
Adam Lesinski929d6512017-01-16 19:11:19 -080098 if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type,
99 &entry)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700100 return false;
101 }
102
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103 const ResourceType* parsed_type = ParseResourceType(type);
104 if (!parsed_type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700105 return false;
106 }
107
108 if (entry.empty()) {
109 return false;
110 }
111
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700112 if (out_ref) {
113 out_ref->package = package;
114 out_ref->type = *parsed_type;
115 out_ref->entry = entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700116 }
117
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700118 if (out_private) {
119 *out_private = priv;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700120 }
121 return true;
122}
123
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700124bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
125 bool* out_create, bool* out_private) {
126 StringPiece trimmed_str(util::TrimWhitespace(str));
127 if (trimmed_str.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700128 return false;
129 }
130
131 bool create = false;
132 bool priv = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133 if (trimmed_str.data()[0] == '@') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700134 size_t offset = 1;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700135 if (trimmed_str.data()[1] == '+') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700136 create = true;
137 offset += 1;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800138 }
139
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700140 ResourceNameRef name;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700141 if (!ParseResourceName(
142 trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 &priv)) {
144 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800145 }
146
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700147 if (create && priv) {
148 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800149 }
150
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700151 if (create && name.type != ResourceType::kId) {
152 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800153 }
154
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700155 if (out_ref) {
156 *out_ref = name;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 }
158
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700159 if (out_create) {
160 *out_create = create;
Adam Lesinski467f1712015-11-16 17:35:44 -0800161 }
162
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700163 if (out_private) {
164 *out_private = priv;
Adam Lesinski467f1712015-11-16 17:35:44 -0800165 }
166 return true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700167 }
168 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700169}
170
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700171bool IsReference(const StringPiece& str) {
172 return ParseReference(str, nullptr, nullptr, nullptr);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800173}
174
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700175bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
176 StringPiece trimmed_str(util::TrimWhitespace(str));
177 if (trimmed_str.empty()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700178 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700179 }
180
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700181 if (*trimmed_str.data() == '?') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700182 StringPiece package;
183 StringPiece type;
184 StringPiece entry;
Adam Lesinski929d6512017-01-16 19:11:19 -0800185 if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package,
186 &type, &entry)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700187 return false;
188 }
189
190 if (!type.empty() && type != "attr") {
191 return false;
192 }
193
194 if (entry.empty()) {
195 return false;
196 }
197
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700198 if (out_ref) {
199 out_ref->package = package;
200 out_ref->type = ResourceType::kAttr;
201 out_ref->entry = entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 }
203 return true;
204 }
205 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700206}
207
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700208bool IsAttributeReference(const StringPiece& str) {
209 return ParseAttributeReference(str, nullptr);
Adam Lesinski7298bc9c2015-11-16 12:31:52 -0800210}
211
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700212/*
213 * Style parent's are a bit different. We accept the following formats:
214 *
Adam Lesinski52364f72016-01-11 13:10:24 -0800215 * @[[*]package:][style/]<entry>
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800216 * ?[[*]package:]style/<entry>
217 * <[*]package>:[style/]<entry>
218 * [[*]package:style/]<entry>
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700219 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700220Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
221 std::string* out_error) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700222 if (str.empty()) {
223 return {};
224 }
225
226 StringPiece name = str;
227
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700228 bool has_leading_identifiers = false;
229 bool private_ref = false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700230
231 // Skip over these identifiers. A style's parent is a normal reference.
232 if (name.data()[0] == '@' || name.data()[0] == '?') {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700233 has_leading_identifiers = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 name = name.substr(1, name.size() - 1);
235 }
236
237 if (name.data()[0] == '*') {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700238 private_ref = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700239 name = name.substr(1, name.size() - 1);
240 }
241
242 ResourceNameRef ref;
243 ref.type = ResourceType::kStyle;
244
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700245 StringPiece type_str;
Adam Lesinski929d6512017-01-16 19:11:19 -0800246 android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700247 if (!type_str.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 // If we have a type, make sure it is a Style.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700249 const ResourceType* parsed_type = ParseResourceType(type_str);
250 if (!parsed_type || *parsed_type != ResourceType::kStyle) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700251 std::stringstream err;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700252 err << "invalid resource type '" << type_str << "' for parent of style";
253 *out_error = err.str();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700254 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700255 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700256 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700257
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700258 if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700259 std::stringstream err;
260 err << "invalid parent reference '" << str << "'";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700261 *out_error = err.str();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700262 return {};
263 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700264
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700265 Reference result(ref);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700266 result.private_reference = private_ref;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700267 return result;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700268}
269
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700270Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
271 StringPiece trimmed_str = util::TrimWhitespace(str);
272 const char* start = trimmed_str.data();
273 const char* const end = start + trimmed_str.size();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700274 const char* p = start;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700275
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700276 Reference ref;
277 if (p != end && *p == '*') {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700278 ref.private_reference = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700279 start++;
280 p++;
281 }
282
283 StringPiece package;
284 StringPiece name;
285 while (p != end) {
286 if (*p == ':') {
287 package = StringPiece(start, p - start);
288 name = StringPiece(p + 1, end - (p + 1));
289 break;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700290 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700291 p++;
292 }
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700293
Adam Lesinskid5083f62017-01-16 15:07:21 -0800294 ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700295 return Maybe<Reference>(std::move(ref));
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700296}
297
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700298std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
299 bool* out_create) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700300 ResourceNameRef ref;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700301 bool private_ref = false;
302 if (ParseReference(str, &ref, out_create, &private_ref)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700303 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700304 value->private_reference = private_ref;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700305 return value;
306 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700307
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700308 if (ParseAttributeReference(str, &ref)) {
309 if (out_create) {
310 *out_create = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700312 return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
313 }
314 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700315}
316
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700317std::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) {
318 const StringPiece trimmed_str(util::TrimWhitespace(str));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700319 if (trimmed_str == "@null") {
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700320 return MakeNull();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700321 } else if (trimmed_str == "@empty") {
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700322 return MakeEmpty();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700323 }
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700324 return {};
325}
326
327std::unique_ptr<Reference> MakeNull() {
328 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
329 // Instead we set the data type to TYPE_REFERENCE with a value of 0.
330 return util::make_unique<Reference>();
331}
332
333std::unique_ptr<BinaryPrimitive> MakeEmpty() {
334 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL,
335 android::Res_value::DATA_NULL_EMPTY);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700336}
337
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700338std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700339 const StringPiece& str) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700340 StringPiece trimmed_str(util::TrimWhitespace(str));
341 for (const Attribute::Symbol& symbol : enum_attr->symbols) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700342 // Enum symbols are stored as @package:id/symbol resources,
343 // so we need to match against the 'entry' part of the identifier.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700344 const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
345 if (trimmed_str == enum_symbol_resource_name.entry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700346 android::Res_value value = {};
347 value.dataType = android::Res_value::TYPE_INT_DEC;
348 value.data = symbol.value;
349 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700350 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700351 }
352 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353}
354
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700356 const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700357 android::Res_value flags = {};
358 flags.dataType = android::Res_value::TYPE_INT_HEX;
359 flags.data = 0u;
Adam Lesinski52364f72016-01-11 13:10:24 -0800360
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700361 if (util::TrimWhitespace(str).empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700362 // Empty string is a valid flag (0).
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700363 return util::make_unique<BinaryPrimitive>(flags);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700364 }
365
Chih-Hung Hsieha1b644e2018-12-11 11:09:20 -0800366 for (const StringPiece& part : util::Tokenize(str, '|')) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700367 StringPiece trimmed_part = util::TrimWhitespace(part);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700368
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700369 bool flag_set = false;
370 for (const Attribute::Symbol& symbol : flag_attr->symbols) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700371 // Flag symbols are stored as @package:id/symbol resources,
372 // so we need to match against the 'entry' part of the identifier.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700373 const ResourceName& flag_symbol_resource_name =
374 symbol.symbol.name.value();
375 if (trimmed_part == flag_symbol_resource_name.entry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700376 flags.data |= symbol.value;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700377 flag_set = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700378 break;
379 }
380 }
381
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700382 if (!flag_set) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700383 return {};
384 }
385 }
386 return util::make_unique<BinaryPrimitive>(flags);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700387}
388
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700389static uint32_t ParseHex(char c, bool* out_error) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700390 if (c >= '0' && c <= '9') {
391 return c - '0';
392 } else if (c >= 'a' && c <= 'f') {
393 return c - 'a' + 0xa;
394 } else if (c >= 'A' && c <= 'F') {
395 return c - 'A' + 0xa;
396 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700397 *out_error = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700398 return 0xffffffffu;
399 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700400}
401
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700402std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
403 StringPiece color_str(util::TrimWhitespace(str));
404 const char* start = color_str.data();
405 const size_t len = color_str.size();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700406 if (len == 0 || start[0] != '#') {
407 return {};
408 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700409
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700410 android::Res_value value = {};
411 bool error = false;
412 if (len == 4) {
413 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
414 value.data = 0xff000000u;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700415 value.data |= ParseHex(start[1], &error) << 20;
416 value.data |= ParseHex(start[1], &error) << 16;
417 value.data |= ParseHex(start[2], &error) << 12;
418 value.data |= ParseHex(start[2], &error) << 8;
419 value.data |= ParseHex(start[3], &error) << 4;
420 value.data |= ParseHex(start[3], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700421 } else if (len == 5) {
422 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700423 value.data |= ParseHex(start[1], &error) << 28;
424 value.data |= ParseHex(start[1], &error) << 24;
425 value.data |= ParseHex(start[2], &error) << 20;
426 value.data |= ParseHex(start[2], &error) << 16;
427 value.data |= ParseHex(start[3], &error) << 12;
428 value.data |= ParseHex(start[3], &error) << 8;
429 value.data |= ParseHex(start[4], &error) << 4;
430 value.data |= ParseHex(start[4], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700431 } else if (len == 7) {
432 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
433 value.data = 0xff000000u;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700434 value.data |= ParseHex(start[1], &error) << 20;
435 value.data |= ParseHex(start[2], &error) << 16;
436 value.data |= ParseHex(start[3], &error) << 12;
437 value.data |= ParseHex(start[4], &error) << 8;
438 value.data |= ParseHex(start[5], &error) << 4;
439 value.data |= ParseHex(start[6], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700440 } else if (len == 9) {
441 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700442 value.data |= ParseHex(start[1], &error) << 28;
443 value.data |= ParseHex(start[2], &error) << 24;
444 value.data |= ParseHex(start[3], &error) << 20;
445 value.data |= ParseHex(start[4], &error) << 16;
446 value.data |= ParseHex(start[5], &error) << 12;
447 value.data |= ParseHex(start[6], &error) << 8;
448 value.data |= ParseHex(start[7], &error) << 4;
449 value.data |= ParseHex(start[8], &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700450 } else {
451 return {};
452 }
453 return error ? std::unique_ptr<BinaryPrimitive>()
454 : util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700455}
456
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700457Maybe<bool> ParseBool(const StringPiece& str) {
458 StringPiece trimmed_str(util::TrimWhitespace(str));
459 if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700460 return Maybe<bool>(true);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700461 } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
462 trimmed_str == "False") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700463 return Maybe<bool>(false);
464 }
465 return {};
Adam Lesinskib23f1e02015-11-03 12:24:17 -0800466}
467
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700468Maybe<uint32_t> ParseInt(const StringPiece& str) {
469 std::u16string str16 = util::Utf8ToUtf16(str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700470 android::Res_value value;
471 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
472 return value.data;
473 }
474 return {};
Adam Lesinski36c73a52016-08-11 13:39:24 -0700475}
476
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700477Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
478 StringPiece trimmed_str(util::TrimWhitespace(str));
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700479
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700480 std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700481 android::Res_value value;
482 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
483 if (value.dataType == android::Res_value::TYPE_INT_HEX) {
484 ResourceId id(value.data);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800485 if (id.is_valid_dynamic()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700486 return id;
487 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700488 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700489 }
490 return {};
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700491}
492
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700493Maybe<int> ParseSdkVersion(const StringPiece& str) {
494 StringPiece trimmed_str(util::TrimWhitespace(str));
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700495
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700496 std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700497 android::Res_value value;
498 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
499 return static_cast<int>(value.data);
500 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700501
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700502 // Try parsing the code name.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700503 std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
504 if (entry.first == trimmed_str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700505 return entry.second;
506 }
Nicholas Lativy79f03962019-01-16 16:19:09 +0000507
508 // Try parsing codename from "[codename].[preview_sdk_fingerprint]" value.
509 const StringPiece::const_iterator begin = std::begin(trimmed_str);
510 const StringPiece::const_iterator end = std::end(trimmed_str);
511 const StringPiece::const_iterator codename_end = std::find(begin, end, '.');
512 if (codename_end != end && entry.first == trimmed_str.substr(begin, codename_end)) {
513 return entry.second;
514 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700515 return {};
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700516}
517
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700518std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
519 if (Maybe<bool> maybe_result = ParseBool(str)) {
Adam Lesinski5924d8c2017-05-30 15:15:58 -0700520 const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
521 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700522 }
523 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700524}
525
Adam Lesinski5924d8c2017-05-30 15:15:58 -0700526std::unique_ptr<BinaryPrimitive> MakeBool(bool val) {
527 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN,
528 val ? 0xffffffffu : 0u);
529}
530
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700531std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
Adam Lesinski8a3bffe2017-06-27 12:27:43 -0700532 std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700533 android::Res_value value;
534 if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
535 return {};
536 }
537 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700538}
539
Shane Farmerd05b9132018-02-14 15:40:35 -0800540std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) {
541 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val);
542}
543
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700544std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
Adam Lesinski8a3bffe2017-06-27 12:27:43 -0700545 std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700546 android::Res_value value;
547 if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
548 return {};
549 }
550 return util::make_unique<BinaryPrimitive>(value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700551}
552
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700553uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700554 switch (type) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700555 case android::Res_value::TYPE_NULL:
556 case android::Res_value::TYPE_REFERENCE:
557 case android::Res_value::TYPE_ATTRIBUTE:
558 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800559 case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700560 return android::ResTable_map::TYPE_REFERENCE;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700561
562 case android::Res_value::TYPE_STRING:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700563 return android::ResTable_map::TYPE_STRING;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700564
565 case android::Res_value::TYPE_FLOAT:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700566 return android::ResTable_map::TYPE_FLOAT;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700567
568 case android::Res_value::TYPE_DIMENSION:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700569 return android::ResTable_map::TYPE_DIMENSION;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700570
571 case android::Res_value::TYPE_FRACTION:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700572 return android::ResTable_map::TYPE_FRACTION;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700573
574 case android::Res_value::TYPE_INT_DEC:
575 case android::Res_value::TYPE_INT_HEX:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700576 return android::ResTable_map::TYPE_INTEGER |
577 android::ResTable_map::TYPE_ENUM |
578 android::ResTable_map::TYPE_FLAGS;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700579
580 case android::Res_value::TYPE_INT_BOOLEAN:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700581 return android::ResTable_map::TYPE_BOOLEAN;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700582
583 case android::Res_value::TYPE_INT_COLOR_ARGB8:
584 case android::Res_value::TYPE_INT_COLOR_RGB8:
585 case android::Res_value::TYPE_INT_COLOR_ARGB4:
586 case android::Res_value::TYPE_INT_COLOR_RGB4:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700587 return android::ResTable_map::TYPE_COLOR;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700588
589 default:
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700590 return 0;
591 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700592}
593
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700594std::unique_ptr<Item> TryParseItemForAttribute(
595 const StringPiece& value, uint32_t type_mask,
596 const std::function<void(const ResourceName&)>& on_create_reference) {
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700597 using android::ResTable_map;
598
599 auto null_or_empty = TryParseNullOrEmpty(value);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700600 if (null_or_empty) {
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700601 return null_or_empty;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700602 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700603
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700604 bool create = false;
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700605 auto reference = TryParseReference(value, &create);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700606 if (reference) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700607 if (create && on_create_reference) {
608 on_create_reference(reference->name.value());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700609 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700610 return std::move(reference);
611 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700612
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700613 if (type_mask & ResTable_map::TYPE_COLOR) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700614 // Try parsing this as a color.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700615 auto color = TryParseColor(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700616 if (color) {
617 return std::move(color);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700618 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700619 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700620
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700621 if (type_mask & ResTable_map::TYPE_BOOLEAN) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700622 // Try parsing this as a boolean.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700623 auto boolean = TryParseBool(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700624 if (boolean) {
625 return std::move(boolean);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700626 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700627 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700628
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700629 if (type_mask & ResTable_map::TYPE_INTEGER) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700630 // Try parsing this as an integer.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700631 auto integer = TryParseInt(value);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700632 if (integer) {
633 return std::move(integer);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700634 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700635 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700636
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700637 const uint32_t float_mask =
638 ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700639 if (type_mask & float_mask) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700640 // Try parsing this as a float.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700641 auto floating_point = TryParseFloat(value);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700642 if (floating_point) {
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700643 if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700644 return std::move(floating_point);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700645 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700646 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700647 }
648 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700649}
650
651/**
652 * We successively try to parse the string as a resource type that the Attribute
653 * allows.
654 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700655std::unique_ptr<Item> TryParseItemForAttribute(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700656 const StringPiece& str, const Attribute* attr,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700657 const std::function<void(const ResourceName&)>& on_create_reference) {
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700658 using android::ResTable_map;
659
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700660 const uint32_t type_mask = attr->type_mask;
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700661 auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700662 if (value) {
663 return value;
664 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700665
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700666 if (type_mask & ResTable_map::TYPE_ENUM) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700667 // Try parsing this as an enum.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700668 auto enum_value = TryParseEnumSymbol(attr, str);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700669 if (enum_value) {
670 return std::move(enum_value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700671 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700672 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700673
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700674 if (type_mask & ResTable_map::TYPE_FLAGS) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700675 // Try parsing this as a flag.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700676 auto flag_value = TryParseFlagSymbol(attr, str);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700677 if (flag_value) {
678 return std::move(flag_value);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700679 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700680 }
681 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700682}
683
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700684std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700685 std::stringstream out;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700686 out << "res/" << res_file.name.type;
687 if (res_file.config != ConfigDescription{}) {
688 out << "-" << res_file.config;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700689 }
690 out << "/";
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800691
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700692 if (mangler && mangler->ShouldMangle(res_file.name.package)) {
693 out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700694 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700695 out << res_file.name.entry;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700696 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700697 out << file::GetExtension(res_file.source.path);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700698 return out.str();
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800699}
700
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700701std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config,
702 const android::ResStringPool& src_pool,
703 const android::Res_value& res_value,
704 StringPool* dst_pool) {
705 if (type == ResourceType::kId) {
706 return util::make_unique<Id>();
707 }
708
709 const uint32_t data = util::DeviceToHost32(res_value.data);
710 switch (res_value.dataType) {
711 case android::Res_value::TYPE_STRING: {
712 const std::string str = util::GetString(src_pool, data);
713 const android::ResStringPool_span* spans = src_pool.styleAt(data);
714
715 // Check if the string has a valid style associated with it.
716 if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
717 StyleString style_str = {str};
718 while (spans->name.index != android::ResStringPool_span::END) {
719 style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
720 spans->firstChar, spans->lastChar});
721 spans++;
722 }
723 return util::make_unique<StyledString>(dst_pool->MakeRef(
Adam Lesinski060b53d2017-07-28 17:10:35 -0700724 style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700725 } else {
726 if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
727 // This must be a FileReference.
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700728 std::unique_ptr<FileReference> file_ref =
729 util::make_unique<FileReference>(dst_pool->MakeRef(
Ryan Mitchell90b7a082019-02-15 17:39:58 +0000730 str, StringPool::Context(StringPool::Context::kHighPriority, config)));
Pierre Lecesne70fdf762017-11-27 19:29:42 +0000731 if (type == ResourceType::kRaw) {
732 file_ref->type = ResourceFile::Type::kUnknown;
733 } else if (util::EndsWith(*file_ref->path, ".xml")) {
Adam Lesinskie59f0d82017-10-13 09:36:53 -0700734 file_ref->type = ResourceFile::Type::kBinaryXml;
735 } else if (util::EndsWith(*file_ref->path, ".png")) {
736 file_ref->type = ResourceFile::Type::kPng;
737 }
738 return std::move(file_ref);
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700739 }
740
741 // There are no styles associated with this string, so treat it as a simple string.
Ryan Mitchell90b7a082019-02-15 17:39:58 +0000742 return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700743 }
744 } break;
745
746 case android::Res_value::TYPE_REFERENCE:
747 case android::Res_value::TYPE_ATTRIBUTE:
748 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
749 case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
750 Reference::Type ref_type = Reference::Type::kResource;
751 if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE ||
752 res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
753 ref_type = Reference::Type::kAttribute;
754 }
755
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700756 if (data == 0u) {
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700757 // A reference of 0, must be the magic @null reference.
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700758 return util::make_unique<Reference>();
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700759 }
760
761 // This is a normal reference.
762 return util::make_unique<Reference>(data, ref_type);
763 } break;
764 }
765
766 // Treat this as a raw binary primitive.
767 return util::make_unique<BinaryPrimitive>(res_value);
768}
769
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800770// Converts the codepoint to UTF-8 and appends it to the string.
771static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
772 ssize_t len = utf32_to_utf8_length(&codepoint, 1);
773 if (len < 0) {
774 return false;
775 }
776
777 const size_t start_append_pos = output->size();
778
779 // Make room for the next character.
780 output->resize(output->size() + len);
781
782 char* dst = &*(output->begin() + start_append_pos);
783 utf32_to_utf8(&codepoint, 1, dst, len + 1);
784 return true;
785}
786
787// Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
788// Unicode codepoint represented by the escape sequence to the string.
789static bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
790 char32_t code = 0;
791 for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
792 char32_t codepoint = iter->Next();
793 char32_t a;
794 if (codepoint >= U'0' && codepoint <= U'9') {
795 a = codepoint - U'0';
796 } else if (codepoint >= U'a' && codepoint <= U'f') {
797 a = codepoint - U'a' + 10;
798 } else if (codepoint >= U'A' && codepoint <= U'F') {
799 a = codepoint - U'A' + 10;
800 } else {
801 return {};
802 }
803 code = (code << 4) | a;
804 }
805 return AppendCodepointToUtf8String(code, output);
806}
807
808StringBuilder::StringBuilder(bool preserve_spaces)
809 : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
810}
811
Ryan Mitchellcb76d732018-06-05 10:15:04 -0700812StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) {
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800813 if (!error_.empty()) {
814 return *this;
815 }
816
Ryan Mitchellcb76d732018-06-05 10:15:04 -0700817 // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed
818 // to preserve spaces
819 preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_;
820
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800821 const size_t previous_len = xml_string_.text.size();
822 Utf8Iterator iter(text);
823 while (iter.HasNext()) {
824 char32_t codepoint = iter.Next();
Ryan Mitchell0ce89732018-10-03 09:20:57 -0700825 if (!preserve_spaces && !quote_ && codepoint != kNonBreakingSpace && iswspace(codepoint)) {
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800826 if (!last_codepoint_was_space_) {
827 // Emit a space if it's the first.
828 xml_string_.text += ' ';
829 last_codepoint_was_space_ = true;
830 }
831
832 // Keep eating spaces.
833 continue;
834 }
835
836 // This is not a space.
837 last_codepoint_was_space_ = false;
838
839 if (codepoint == U'\\') {
840 if (iter.HasNext()) {
841 codepoint = iter.Next();
842 switch (codepoint) {
843 case U't':
844 xml_string_.text += '\t';
845 break;
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800846 case U'n':
847 xml_string_.text += '\n';
848 break;
849
850 case U'#':
851 case U'@':
852 case U'?':
853 case U'"':
854 case U'\'':
855 case U'\\':
856 xml_string_.text += static_cast<char>(codepoint);
857 break;
858
859 case U'u':
860 if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
861 error_ =
862 StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
863 return *this;
864 }
865 break;
866
867 default:
868 // Ignore the escape character and just include the codepoint.
869 AppendCodepointToUtf8String(codepoint, &xml_string_.text);
870 break;
871 }
872 }
Ryan Mitchellcb76d732018-06-05 10:15:04 -0700873 } else if (!preserve_spaces && codepoint == U'"') {
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800874 // Only toggle the quote state when we are not preserving spaces.
875 quote_ = !quote_;
876
Ryan Mitchellcb76d732018-06-05 10:15:04 -0700877 } else if (!preserve_spaces && !quote_ && codepoint == U'\'') {
878 // This should be escaped when we are not preserving spaces
Adam Lesinski2eed52e2018-02-21 15:55:58 -0800879 error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
880 return *this;
881
882 } else {
883 AppendCodepointToUtf8String(codepoint, &xml_string_.text);
884 }
885 }
886
887 // Accumulate the added string's UTF-16 length.
888 const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
889 const size_t utf8_length = xml_string_.text.size();
890 ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
891 if (len < 0) {
892 error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
893 return *this;
894 }
895
896 utf16_len_ += static_cast<uint32_t>(len);
897 return *this;
898}
899
900StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
901 if (!error_.empty()) {
902 return 0u;
903 }
904
905 // When we start a span, all state associated with whitespace truncation and quotation is ended.
906 ResetTextState();
907 Span span;
908 span.name = name;
909 span.first_char = span.last_char = utf16_len_;
910 xml_string_.spans.push_back(std::move(span));
911 return xml_string_.spans.size() - 1;
912}
913
914void StringBuilder::EndSpan(SpanHandle handle) {
915 if (!error_.empty()) {
916 return;
917 }
918
919 // When we end a span, all state associated with whitespace truncation and quotation is ended.
920 ResetTextState();
921 xml_string_.spans[handle].last_char = utf16_len_ - 1u;
922}
923
924StringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
925 if (!error_.empty()) {
926 return 0u;
927 }
928
929 UntranslatableSection section;
930 section.start = section.end = xml_string_.text.size();
931 xml_string_.untranslatable_sections.push_back(section);
932 return xml_string_.untranslatable_sections.size() - 1;
933}
934
935void StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
936 if (!error_.empty()) {
937 return;
938 }
939 xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
940}
941
942FlattenedXmlString StringBuilder::GetFlattenedString() const {
943 return xml_string_;
944}
945
946std::string StringBuilder::to_string() const {
947 return xml_string_.text;
948}
949
950StringBuilder::operator bool() const {
951 return error_.empty();
952}
953
954std::string StringBuilder::GetError() const {
955 return error_;
956}
957
958void StringBuilder::ResetTextState() {
959 quote_ = preserve_spaces_;
960 last_codepoint_was_space_ = false;
961}
962
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700963} // namespace ResourceUtils
964} // namespace aapt