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 "Locale.h"
18 #include "util/Util.h"
19
20 #include <algorithm>
21 #include <ctype.h>
22 #include <string>
23 #include <vector>
24
25 namespace aapt {
26
27 using android::ResTable_config;
28
setLanguage(const char * languageChars)29 void LocaleValue::setLanguage(const char* languageChars) {
30 size_t i = 0;
31 while ((*languageChars) != '\0') {
32 language[i++] = ::tolower(*languageChars);
33 languageChars++;
34 }
35 }
36
setRegion(const char * regionChars)37 void LocaleValue::setRegion(const char* regionChars) {
38 size_t i = 0;
39 while ((*regionChars) != '\0') {
40 region[i++] = ::toupper(*regionChars);
41 regionChars++;
42 }
43 }
44
setScript(const char * scriptChars)45 void LocaleValue::setScript(const char* scriptChars) {
46 size_t i = 0;
47 while ((*scriptChars) != '\0') {
48 if (i == 0) {
49 script[i++] = ::toupper(*scriptChars);
50 } else {
51 script[i++] = ::tolower(*scriptChars);
52 }
53 scriptChars++;
54 }
55 }
56
setVariant(const char * variantChars)57 void LocaleValue::setVariant(const char* variantChars) {
58 size_t i = 0;
59 while ((*variantChars) != '\0') {
60 variant[i++] = *variantChars;
61 variantChars++;
62 }
63 }
64
isAlpha(const std::string & str)65 static inline bool isAlpha(const std::string& str) {
66 return std::all_of(std::begin(str), std::end(str), ::isalpha);
67 }
68
isNumber(const std::string & str)69 static inline bool isNumber(const std::string& str) {
70 return std::all_of(std::begin(str), std::end(str), ::isdigit);
71 }
72
initFromFilterString(const StringPiece & str)73 bool LocaleValue::initFromFilterString(const StringPiece& str) {
74 // 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, '_');
77
78 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 }
87
88 if (!valid || numTags == 1) {
89 return valid;
90 }
91
92 // 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 }
104
105 if (!valid || numTags == 2) {
106 return valid;
107 }
108
109 // 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))) && script[0]) {
113 setRegion(part3.c_str());
114 } else if (part3.length() >= 4 && part3.length() <= 8) {
115 setVariant(part3.c_str());
116 } else {
117 valid = false;
118 }
119
120 if (!valid || numTags == 3) {
121 return valid;
122 }
123
124 const std::string& part4 = parts[3];
125 if (part4.length() >= 4 && part4.length() <= 8) {
126 setVariant(part4.c_str());
127 } else {
128 valid = false;
129 }
130
131 if (!valid || numTags > 4) {
132 return false;
133 }
134
135 return true;
136 }
137
initFromParts(std::vector<std::string>::iterator iter,std::vector<std::string>::iterator end)138 ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
139 std::vector<std::string>::iterator end) {
140 const std::vector<std::string>::iterator startIter = iter;
141
142 std::string& part = *iter;
143 if (part[0] == 'b' && part[1] == '+') {
144 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
145 // except that the separator is "+" and not "-".
146 std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
147 subtags.erase(subtags.begin());
148 if (subtags.size() == 1) {
149 setLanguage(subtags[0].c_str());
150 } else if (subtags.size() == 2) {
151 setLanguage(subtags[0].c_str());
152
153 // The second tag can either be a region, a variant or a script.
154 switch (subtags[1].size()) {
155 case 2:
156 case 3:
157 setRegion(subtags[1].c_str());
158 break;
159 case 4:
160 if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
161 // This is a variant: fall through
162 } else {
163 setScript(subtags[1].c_str());
164 break;
165 }
166 case 5:
167 case 6:
168 case 7:
169 case 8:
170 setVariant(subtags[1].c_str());
171 break;
172 default:
173 return -1;
174 }
175 } else if (subtags.size() == 3) {
176 // The language is always the first subtag.
177 setLanguage(subtags[0].c_str());
178
179 // The second subtag can either be a script or a region code.
180 // If its size is 4, it's a script code, else it's a region code.
181 if (subtags[1].size() == 4) {
182 setScript(subtags[1].c_str());
183 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
184 setRegion(subtags[1].c_str());
185 } else {
186 return -1;
187 }
188
189 // The third tag can either be a region code (if the second tag was
190 // a script), else a variant code.
191 if (subtags[2].size() >= 4) {
192 setVariant(subtags[2].c_str());
193 } else {
194 setRegion(subtags[2].c_str());
195 }
196 } else if (subtags.size() == 4) {
197 setLanguage(subtags[0].c_str());
198 setScript(subtags[1].c_str());
199 setRegion(subtags[2].c_str());
200 setVariant(subtags[3].c_str());
201 } else {
202 return -1;
203 }
204
205 ++iter;
206
207 } else {
208 if ((part.length() == 2 || part.length() == 3)
209 && isAlpha(part) && part != "car") {
210 setLanguage(part.c_str());
211 ++iter;
212
213 if (iter != end) {
214 const std::string& regionPart = *iter;
215 if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
216 setRegion(regionPart.c_str() + 1);
217 ++iter;
218 }
219 }
220 }
221 }
222
223 return static_cast<ssize_t>(iter - startIter);
224 }
225
226
toDirName() const227 std::string LocaleValue::toDirName() const {
228 std::string dirName;
229 if (language[0]) {
230 dirName += language;
231 } else {
232 return dirName;
233 }
234
235 if (script[0]) {
236 dirName += "-s";
237 dirName += script;
238 }
239
240 if (region[0]) {
241 dirName += "-r";
242 dirName += region;
243 }
244
245 if (variant[0]) {
246 dirName += "-v";
247 dirName += variant;
248 }
249
250 return dirName;
251 }
252
initFromResTable(const ResTable_config & config)253 void LocaleValue::initFromResTable(const ResTable_config& config) {
254 config.unpackLanguage(language);
255 config.unpackRegion(region);
256 if (config.localeScript[0] && !config.localeScriptWasComputed) {
257 memcpy(script, config.localeScript, sizeof(config.localeScript));
258 }
259
260 if (config.localeVariant[0]) {
261 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
262 }
263 }
264
writeTo(ResTable_config * out) const265 void LocaleValue::writeTo(ResTable_config* out) const {
266 out->packLanguage(language);
267 out->packRegion(region);
268
269 if (script[0]) {
270 memcpy(out->localeScript, script, sizeof(out->localeScript));
271 }
272
273 if (variant[0]) {
274 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
275 }
276 }
277
278 } // namespace aapt
279