blob: 7664fac44be19f3966438a0fc606d163c5e2efc1 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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 "Locale.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080019#include <ctype.h>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070020
Adam Lesinskicacb28f2016-10-19 12:18:14 -070021#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080022#include <string>
23#include <vector>
24
Adam Lesinskice5e56e2016-10-21 17:56:45 -070025#include "util/Util.h"
26
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080027namespace aapt {
28
29using android::ResTable_config;
30
Adam Lesinskice5e56e2016-10-21 17:56:45 -070031void LocaleValue::set_language(const char* language_chars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070032 size_t i = 0;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070033 while ((*language_chars) != '\0') {
34 language[i++] = ::tolower(*language_chars);
35 language_chars++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070036 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080037}
38
Adam Lesinskice5e56e2016-10-21 17:56:45 -070039void LocaleValue::set_region(const char* region_chars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070040 size_t i = 0;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070041 while ((*region_chars) != '\0') {
42 region[i++] = ::toupper(*region_chars);
43 region_chars++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070044 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080045}
46
Adam Lesinskice5e56e2016-10-21 17:56:45 -070047void LocaleValue::set_script(const char* script_chars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070048 size_t i = 0;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070049 while ((*script_chars) != '\0') {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070050 if (i == 0) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070051 script[i++] = ::toupper(*script_chars);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070053 script[i++] = ::tolower(*script_chars);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070055 script_chars++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080057}
58
Adam Lesinskice5e56e2016-10-21 17:56:45 -070059void LocaleValue::set_variant(const char* variant_chars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070060 size_t i = 0;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070061 while ((*variant_chars) != '\0') {
62 variant[i++] = *variant_chars;
63 variant_chars++;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070064 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080065}
66
Adam Lesinskice5e56e2016-10-21 17:56:45 -070067static inline bool is_alpha(const std::string& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070068 return std::all_of(std::begin(str), std::end(str), ::isalpha);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080069}
70
Adam Lesinskice5e56e2016-10-21 17:56:45 -070071static inline bool is_number(const std::string& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070072 return std::all_of(std::begin(str), std::end(str), ::isdigit);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080073}
74
Adam Lesinskid5083f62017-01-16 15:07:21 -080075bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070076 // A locale (as specified in the filter) is an underscore separated name such
77 // as "en_US", "en_Latn_US", or "en_US_POSIX".
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078 std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080079
Adam Lesinskice5e56e2016-10-21 17:56:45 -070080 const int num_tags = parts.size();
Adam Lesinskicacb28f2016-10-19 12:18:14 -070081 bool valid = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070082 if (num_tags >= 1) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 const std::string& lang = parts[0];
Adam Lesinskice5e56e2016-10-21 17:56:45 -070084 if (is_alpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
85 set_language(lang.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -070086 valid = true;
87 }
88 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080089
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 if (!valid || num_tags == 1) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070091 return valid;
92 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080093
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 // At this point, valid == true && numTags > 1.
95 const std::string& part2 = parts[1];
Adam Lesinskice5e56e2016-10-21 17:56:45 -070096 if ((part2.length() == 2 && is_alpha(part2)) ||
97 (part2.length() == 3 && is_number(part2))) {
98 set_region(part2.c_str());
99 } else if (part2.length() == 4 && is_alpha(part2)) {
100 set_script(part2.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700101 } else if (part2.length() >= 4 && part2.length() <= 8) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700102 set_variant(part2.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700103 } else {
104 valid = false;
105 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800106
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700107 if (!valid || num_tags == 2) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700108 return valid;
109 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800110
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700111 // At this point, valid == true && numTags > 1.
112 const std::string& part3 = parts[2];
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700113 if (((part3.length() == 2 && is_alpha(part3)) ||
114 (part3.length() == 3 && is_number(part3))) &&
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700115 script[0]) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700116 set_region(part3.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700117 } else if (part3.length() >= 4 && part3.length() <= 8) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700118 set_variant(part3.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700119 } else {
120 valid = false;
121 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800122
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700123 if (!valid || num_tags == 3) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700124 return valid;
125 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800126
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700127 const std::string& part4 = parts[3];
128 if (part4.length() >= 4 && part4.length() <= 8) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 set_variant(part4.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700130 } else {
131 valid = false;
132 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800133
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700134 if (!valid || num_tags > 4) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700135 return false;
136 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800137
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700138 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800139}
140
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700141ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700142 std::vector<std::string>::iterator end) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700143 const std::vector<std::string>::iterator start_iter = iter;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800144
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700145 std::string& part = *iter;
146 if (part[0] == 'b' && part[1] == '+') {
147 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
148 // except that the separator is "+" and not "-".
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700149 std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 subtags.erase(subtags.begin());
151 if (subtags.size() == 1) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700152 set_language(subtags[0].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700153 } else if (subtags.size() == 2) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700154 set_language(subtags[0].c_str());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800155
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700156 // The second tag can either be a region, a variant or a script.
157 switch (subtags[1].size()) {
158 case 2:
159 case 3:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700160 set_region(subtags[1].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 break;
162 case 4:
163 if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
164 // This is a variant: fall through
165 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700166 set_script(subtags[1].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700167 break;
168 }
169 case 5:
170 case 6:
171 case 7:
172 case 8:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700173 set_variant(subtags[1].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700174 break;
175 default:
176 return -1;
177 }
178 } else if (subtags.size() == 3) {
179 // The language is always the first subtag.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700180 set_language(subtags[0].c_str());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800181
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700182 // The second subtag can either be a script or a region code.
183 // If its size is 4, it's a script code, else it's a region code.
184 if (subtags[1].size() == 4) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700185 set_script(subtags[1].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700186 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700187 set_region(subtags[1].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700188 } else {
189 return -1;
190 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800191
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700192 // The third tag can either be a region code (if the second tag was
193 // a script), else a variant code.
194 if (subtags[2].size() >= 4) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700195 set_variant(subtags[2].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700196 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700197 set_region(subtags[2].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700198 }
199 } else if (subtags.size() == 4) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700200 set_language(subtags[0].c_str());
201 set_script(subtags[1].c_str());
202 set_region(subtags[2].c_str());
203 set_variant(subtags[3].c_str());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800204 } else {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700205 return -1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800206 }
207
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700208 ++iter;
209
210 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700211 if ((part.length() == 2 || part.length() == 3) && is_alpha(part) &&
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 part != "car") {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700213 set_language(part.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700214 ++iter;
215
216 if (iter != end) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700217 const std::string& region_part = *iter;
218 if (region_part.c_str()[0] == 'r' && region_part.length() == 3) {
219 set_region(region_part.c_str() + 1);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220 ++iter;
221 }
222 }
223 }
224 }
225
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700226 return static_cast<ssize_t>(iter - start_iter);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800227}
228
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700229void LocaleValue::InitFromResTable(const ResTable_config& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700230 config.unpackLanguage(language);
231 config.unpackRegion(region);
232 if (config.localeScript[0] && !config.localeScriptWasComputed) {
233 memcpy(script, config.localeScript, sizeof(config.localeScript));
234 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800235
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700236 if (config.localeVariant[0]) {
237 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
238 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800239}
240
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700241void LocaleValue::WriteTo(ResTable_config* out) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700242 out->packLanguage(language);
243 out->packRegion(region);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800244
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700245 if (script[0]) {
246 memcpy(out->localeScript, script, sizeof(out->localeScript));
247 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800248
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700249 if (variant[0]) {
250 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
251 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800252}
253
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700254} // namespace aapt