blob: 2870066ccbbaeb4bda83a2ddcffcb2fddee70c20 [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
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020017#include "androidfw/Locale.h"
18#include "androidfw/Util.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080019
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <ctype.h>
Adam Lesinskice5e56e2016-10-21 17:56:45 -070021
Adam Lesinskicacb28f2016-10-19 12:18:14 -070022#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080023#include <string>
24#include <vector>
25
Adam Lesinskib58c3ef2017-09-12 17:39:52 -070026using ::android::ResTable_config;
27using ::android::StringPiece;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080028
Mårten Kongstad24c9aa62018-06-20 08:46:41 +020029namespace android {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080030
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 Lesinskib58c3ef2017-09-12 17:39:52 -070075bool LocaleValue::InitFromFilterString(const 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 Lesinskib58c3ef2017-09-12 17:39:52 -0700141bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) {
142 return InitFromBcp47TagImpl(bcp47tag, '-');
143}
144
145bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) {
146 std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator);
147 if (subtags.size() == 1) {
148 set_language(subtags[0].c_str());
149 } else if (subtags.size() == 2) {
150 set_language(subtags[0].c_str());
151
152 // The second tag can either be a region, a variant or a script.
153 switch (subtags[1].size()) {
154 case 2:
155 case 3:
156 set_region(subtags[1].c_str());
157 break;
158 case 4:
159 if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
160 // This is a variant: fall through
161 } else {
162 set_script(subtags[1].c_str());
163 break;
164 }
165 case 5:
166 case 6:
167 case 7:
168 case 8:
169 set_variant(subtags[1].c_str());
170 break;
171 default:
172 return false;
173 }
174 } else if (subtags.size() == 3) {
175 // The language is always the first subtag.
176 set_language(subtags[0].c_str());
177
178 // The second subtag can either be a script or a region code.
179 // If its size is 4, it's a script code, else it's a region code.
180 if (subtags[1].size() == 4) {
181 set_script(subtags[1].c_str());
182 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
183 set_region(subtags[1].c_str());
184 } else {
185 return false;
186 }
187
188 // The third tag can either be a region code (if the second tag was
189 // a script), else a variant code.
190 if (subtags[2].size() >= 4) {
191 set_variant(subtags[2].c_str());
192 } else {
193 set_region(subtags[2].c_str());
194 }
195 } else if (subtags.size() == 4) {
196 set_language(subtags[0].c_str());
197 set_script(subtags[1].c_str());
198 set_region(subtags[2].c_str());
199 set_variant(subtags[3].c_str());
200 } else {
201 return false;
202 }
203 return true;
204}
205
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700206ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207 std::vector<std::string>::iterator end) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700208 const std::vector<std::string>::iterator start_iter = iter;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800209
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700210 std::string& part = *iter;
211 if (part[0] == 'b' && part[1] == '+') {
212 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
Adam Lesinskib58c3ef2017-09-12 17:39:52 -0700213 // except that the separator is "+" and not "-". Skip the prefix 'b+'.
214 if (!InitFromBcp47TagImpl(StringPiece(part).substr(2), '+')) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700215 return -1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800216 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700217 ++iter;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700218 } else {
Adam Lesinskib58c3ef2017-09-12 17:39:52 -0700219 if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && part != "car") {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700220 set_language(part.c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700221 ++iter;
222
223 if (iter != end) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700224 const std::string& region_part = *iter;
225 if (region_part.c_str()[0] == 'r' && region_part.length() == 3) {
226 set_region(region_part.c_str() + 1);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700227 ++iter;
228 }
229 }
230 }
231 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700232 return static_cast<ssize_t>(iter - start_iter);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800233}
234
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700235void LocaleValue::InitFromResTable(const ResTable_config& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700236 config.unpackLanguage(language);
237 config.unpackRegion(region);
238 if (config.localeScript[0] && !config.localeScriptWasComputed) {
239 memcpy(script, config.localeScript, sizeof(config.localeScript));
240 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800241
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700242 if (config.localeVariant[0]) {
243 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
244 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800245}
246
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700247void LocaleValue::WriteTo(ResTable_config* out) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 out->packLanguage(language);
249 out->packRegion(region);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800250
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700251 if (script[0]) {
252 memcpy(out->localeScript, script, sizeof(out->localeScript));
253 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800254
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700255 if (variant[0]) {
256 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
257 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800258}
259
Mårten Kongstad24c9aa62018-06-20 08:46:41 +0200260} // namespace android