• 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 
19 #include <ctype.h>
20 
21 #include <algorithm>
22 #include <string>
23 #include <vector>
24 
25 #include "util/Util.h"
26 
27 namespace aapt {
28 
29 using android::ResTable_config;
30 
set_language(const char * language_chars)31 void LocaleValue::set_language(const char* language_chars) {
32   size_t i = 0;
33   while ((*language_chars) != '\0') {
34     language[i++] = ::tolower(*language_chars);
35     language_chars++;
36   }
37 }
38 
set_region(const char * region_chars)39 void LocaleValue::set_region(const char* region_chars) {
40   size_t i = 0;
41   while ((*region_chars) != '\0') {
42     region[i++] = ::toupper(*region_chars);
43     region_chars++;
44   }
45 }
46 
set_script(const char * script_chars)47 void LocaleValue::set_script(const char* script_chars) {
48   size_t i = 0;
49   while ((*script_chars) != '\0') {
50     if (i == 0) {
51       script[i++] = ::toupper(*script_chars);
52     } else {
53       script[i++] = ::tolower(*script_chars);
54     }
55     script_chars++;
56   }
57 }
58 
set_variant(const char * variant_chars)59 void LocaleValue::set_variant(const char* variant_chars) {
60   size_t i = 0;
61   while ((*variant_chars) != '\0') {
62     variant[i++] = *variant_chars;
63     variant_chars++;
64   }
65 }
66 
is_alpha(const std::string & str)67 static inline bool is_alpha(const std::string& str) {
68   return std::all_of(std::begin(str), std::end(str), ::isalpha);
69 }
70 
is_number(const std::string & str)71 static inline bool is_number(const std::string& str) {
72   return std::all_of(std::begin(str), std::end(str), ::isdigit);
73 }
74 
InitFromFilterString(const android::StringPiece & str)75 bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
76   // A locale (as specified in the filter) is an underscore separated name such
77   // as "en_US", "en_Latn_US", or "en_US_POSIX".
78   std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
79 
80   const int num_tags = parts.size();
81   bool valid = false;
82   if (num_tags >= 1) {
83     const std::string& lang = parts[0];
84     if (is_alpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
85       set_language(lang.c_str());
86       valid = true;
87     }
88   }
89 
90   if (!valid || num_tags == 1) {
91     return valid;
92   }
93 
94   // At this point, valid == true && numTags > 1.
95   const std::string& part2 = parts[1];
96   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());
101   } else if (part2.length() >= 4 && part2.length() <= 8) {
102     set_variant(part2.c_str());
103   } else {
104     valid = false;
105   }
106 
107   if (!valid || num_tags == 2) {
108     return valid;
109   }
110 
111   // At this point, valid == true && numTags > 1.
112   const std::string& part3 = parts[2];
113   if (((part3.length() == 2 && is_alpha(part3)) ||
114        (part3.length() == 3 && is_number(part3))) &&
115       script[0]) {
116     set_region(part3.c_str());
117   } else if (part3.length() >= 4 && part3.length() <= 8) {
118     set_variant(part3.c_str());
119   } else {
120     valid = false;
121   }
122 
123   if (!valid || num_tags == 3) {
124     return valid;
125   }
126 
127   const std::string& part4 = parts[3];
128   if (part4.length() >= 4 && part4.length() <= 8) {
129     set_variant(part4.c_str());
130   } else {
131     valid = false;
132   }
133 
134   if (!valid || num_tags > 4) {
135     return false;
136   }
137 
138   return true;
139 }
140 
InitFromParts(std::vector<std::string>::iterator iter,std::vector<std::string>::iterator end)141 ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
142                                    std::vector<std::string>::iterator end) {
143   const std::vector<std::string>::iterator start_iter = iter;
144 
145   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 "-".
149     std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
150     subtags.erase(subtags.begin());
151     if (subtags.size() == 1) {
152       set_language(subtags[0].c_str());
153     } else if (subtags.size() == 2) {
154       set_language(subtags[0].c_str());
155 
156       // The second tag can either be a region, a variant or a script.
157       switch (subtags[1].size()) {
158         case 2:
159         case 3:
160           set_region(subtags[1].c_str());
161           break;
162         case 4:
163           if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
164             // This is a variant: fall through
165           } else {
166             set_script(subtags[1].c_str());
167             break;
168           }
169         case 5:
170         case 6:
171         case 7:
172         case 8:
173           set_variant(subtags[1].c_str());
174           break;
175         default:
176           return -1;
177       }
178     } else if (subtags.size() == 3) {
179       // The language is always the first subtag.
180       set_language(subtags[0].c_str());
181 
182       // 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) {
185         set_script(subtags[1].c_str());
186       } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
187         set_region(subtags[1].c_str());
188       } else {
189         return -1;
190       }
191 
192       // 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) {
195         set_variant(subtags[2].c_str());
196       } else {
197         set_region(subtags[2].c_str());
198       }
199     } else if (subtags.size() == 4) {
200       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());
204     } else {
205       return -1;
206     }
207 
208     ++iter;
209 
210   } else {
211     if ((part.length() == 2 || part.length() == 3) && is_alpha(part) &&
212         part != "car") {
213       set_language(part.c_str());
214       ++iter;
215 
216       if (iter != end) {
217         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);
220           ++iter;
221         }
222       }
223     }
224   }
225 
226   return static_cast<ssize_t>(iter - start_iter);
227 }
228 
InitFromResTable(const ResTable_config & config)229 void LocaleValue::InitFromResTable(const ResTable_config& config) {
230   config.unpackLanguage(language);
231   config.unpackRegion(region);
232   if (config.localeScript[0] && !config.localeScriptWasComputed) {
233     memcpy(script, config.localeScript, sizeof(config.localeScript));
234   }
235 
236   if (config.localeVariant[0]) {
237     memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
238   }
239 }
240 
WriteTo(ResTable_config * out) const241 void LocaleValue::WriteTo(ResTable_config* out) const {
242   out->packLanguage(language);
243   out->packRegion(region);
244 
245   if (script[0]) {
246     memcpy(out->localeScript, script, sizeof(out->localeScript));
247   }
248 
249   if (variant[0]) {
250     memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
251   }
252 }
253 
254 }  // namespace aapt
255