• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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