blob: a0f764175e24c1ef5f9ea933e6912193597cf28a [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 Lesinski1ab598f2015-08-14 14:26:04 -070018#include "util/Util.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080019
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <ctype.h>
Adam Lesinskicacb28f2016-10-19 12:18:14 -070021#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080022#include <string>
23#include <vector>
24
25namespace aapt {
26
27using android::ResTable_config;
28
29void LocaleValue::setLanguage(const char* languageChars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070030 size_t i = 0;
31 while ((*languageChars) != '\0') {
32 language[i++] = ::tolower(*languageChars);
33 languageChars++;
34 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080035}
36
37void LocaleValue::setRegion(const char* regionChars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070038 size_t i = 0;
39 while ((*regionChars) != '\0') {
40 region[i++] = ::toupper(*regionChars);
41 regionChars++;
42 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043}
44
45void LocaleValue::setScript(const char* scriptChars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070046 size_t i = 0;
47 while ((*scriptChars) != '\0') {
48 if (i == 0) {
49 script[i++] = ::toupper(*scriptChars);
50 } else {
51 script[i++] = ::tolower(*scriptChars);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080052 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070053 scriptChars++;
54 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080055}
56
57void LocaleValue::setVariant(const char* variantChars) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058 size_t i = 0;
59 while ((*variantChars) != '\0') {
60 variant[i++] = *variantChars;
61 variantChars++;
62 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080063}
64
65static inline bool isAlpha(const std::string& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070066 return std::all_of(std::begin(str), std::end(str), ::isalpha);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067}
68
69static inline bool isNumber(const std::string& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070070 return std::all_of(std::begin(str), std::end(str), ::isdigit);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080071}
72
Adam Lesinski6a008172016-02-02 17:02:58 -080073bool LocaleValue::initFromFilterString(const StringPiece& str) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 // A locale (as specified in the filter) is an underscore separated name such
75 // as "en_US", "en_Latn_US", or "en_US_POSIX".
76 std::vector<std::string> parts = util::splitAndLowercase(str, '_');
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080077
Adam Lesinskicacb28f2016-10-19 12:18:14 -070078 const int numTags = parts.size();
79 bool valid = false;
80 if (numTags >= 1) {
81 const std::string& lang = parts[0];
82 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
83 setLanguage(lang.c_str());
84 valid = true;
85 }
86 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080087
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 if (!valid || numTags == 1) {
89 return valid;
90 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080091
Adam Lesinskicacb28f2016-10-19 12:18:14 -070092 // At this point, valid == true && numTags > 1.
93 const std::string& part2 = parts[1];
94 if ((part2.length() == 2 && isAlpha(part2)) ||
95 (part2.length() == 3 && isNumber(part2))) {
96 setRegion(part2.c_str());
97 } else if (part2.length() == 4 && isAlpha(part2)) {
98 setScript(part2.c_str());
99 } else if (part2.length() >= 4 && part2.length() <= 8) {
100 setVariant(part2.c_str());
101 } else {
102 valid = false;
103 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800104
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700105 if (!valid || numTags == 2) {
106 return valid;
107 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800108
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700109 // At this point, valid == true && numTags > 1.
110 const std::string& part3 = parts[2];
111 if (((part3.length() == 2 && isAlpha(part3)) ||
112 (part3.length() == 3 && isNumber(part3))) &&
113 script[0]) {
114 setRegion(part3.c_str());
115 } else if (part3.length() >= 4 && part3.length() <= 8) {
116 setVariant(part3.c_str());
117 } else {
118 valid = false;
119 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800120
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121 if (!valid || numTags == 3) {
122 return valid;
123 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800124
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700125 const std::string& part4 = parts[3];
126 if (part4.length() >= 4 && part4.length() <= 8) {
127 setVariant(part4.c_str());
128 } else {
129 valid = false;
130 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800131
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 if (!valid || numTags > 4) {
133 return false;
134 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800135
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700136 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800137}
138
139ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700140 std::vector<std::string>::iterator end) {
141 const std::vector<std::string>::iterator startIter = iter;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800142
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 std::string& part = *iter;
144 if (part[0] == 'b' && part[1] == '+') {
145 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
146 // except that the separator is "+" and not "-".
147 std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
148 subtags.erase(subtags.begin());
149 if (subtags.size() == 1) {
150 setLanguage(subtags[0].c_str());
151 } else if (subtags.size() == 2) {
152 setLanguage(subtags[0].c_str());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800153
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700154 // The second tag can either be a region, a variant or a script.
155 switch (subtags[1].size()) {
156 case 2:
157 case 3:
158 setRegion(subtags[1].c_str());
159 break;
160 case 4:
161 if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
162 // This is a variant: fall through
163 } else {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800164 setScript(subtags[1].c_str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700165 break;
166 }
167 case 5:
168 case 6:
169 case 7:
170 case 8:
171 setVariant(subtags[1].c_str());
172 break;
173 default:
174 return -1;
175 }
176 } else if (subtags.size() == 3) {
177 // The language is always the first subtag.
178 setLanguage(subtags[0].c_str());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800179
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700180 // The second subtag can either be a script or a region code.
181 // If its size is 4, it's a script code, else it's a region code.
182 if (subtags[1].size() == 4) {
183 setScript(subtags[1].c_str());
184 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
185 setRegion(subtags[1].c_str());
186 } else {
187 return -1;
188 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800189
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700190 // The third tag can either be a region code (if the second tag was
191 // a script), else a variant code.
192 if (subtags[2].size() >= 4) {
193 setVariant(subtags[2].c_str());
194 } else {
195 setRegion(subtags[2].c_str());
196 }
197 } else if (subtags.size() == 4) {
198 setLanguage(subtags[0].c_str());
199 setScript(subtags[1].c_str());
200 setRegion(subtags[2].c_str());
201 setVariant(subtags[3].c_str());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800202 } else {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700203 return -1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800204 }
205
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700206 ++iter;
207
208 } else {
209 if ((part.length() == 2 || part.length() == 3) && isAlpha(part) &&
210 part != "car") {
211 setLanguage(part.c_str());
212 ++iter;
213
214 if (iter != end) {
215 const std::string& regionPart = *iter;
216 if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
217 setRegion(regionPart.c_str() + 1);
218 ++iter;
219 }
220 }
221 }
222 }
223
224 return static_cast<ssize_t>(iter - startIter);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800225}
226
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800227void LocaleValue::initFromResTable(const ResTable_config& config) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700228 config.unpackLanguage(language);
229 config.unpackRegion(region);
230 if (config.localeScript[0] && !config.localeScriptWasComputed) {
231 memcpy(script, config.localeScript, sizeof(config.localeScript));
232 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800233
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 if (config.localeVariant[0]) {
235 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
236 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800237}
238
239void LocaleValue::writeTo(ResTable_config* out) const {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700240 out->packLanguage(language);
241 out->packRegion(region);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800242
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700243 if (script[0]) {
244 memcpy(out->localeScript, script, sizeof(out->localeScript));
245 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800246
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700247 if (variant[0]) {
248 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
249 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800250}
251
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700252} // namespace aapt