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 using ::android::ResTable_config; 28 using ::android::StringPiece; 29 30 namespace aapt { 31 32 void LocaleValue::set_language(const char* language_chars) { 33 size_t i = 0; 34 while ((*language_chars) != '\0') { 35 language[i++] = ::tolower(*language_chars); 36 language_chars++; 37 } 38 } 39 40 void LocaleValue::set_region(const char* region_chars) { 41 size_t i = 0; 42 while ((*region_chars) != '\0') { 43 region[i++] = ::toupper(*region_chars); 44 region_chars++; 45 } 46 } 47 48 void LocaleValue::set_script(const char* script_chars) { 49 size_t i = 0; 50 while ((*script_chars) != '\0') { 51 if (i == 0) { 52 script[i++] = ::toupper(*script_chars); 53 } else { 54 script[i++] = ::tolower(*script_chars); 55 } 56 script_chars++; 57 } 58 } 59 60 void LocaleValue::set_variant(const char* variant_chars) { 61 size_t i = 0; 62 while ((*variant_chars) != '\0') { 63 variant[i++] = *variant_chars; 64 variant_chars++; 65 } 66 } 67 68 static inline bool is_alpha(const std::string& str) { 69 return std::all_of(std::begin(str), std::end(str), ::isalpha); 70 } 71 72 static inline bool is_number(const std::string& str) { 73 return std::all_of(std::begin(str), std::end(str), ::isdigit); 74 } 75 76 bool LocaleValue::InitFromFilterString(const StringPiece& str) { 77 // A locale (as specified in the filter) is an underscore separated name such 78 // as "en_US", "en_Latn_US", or "en_US_POSIX". 79 std::vector<std::string> parts = util::SplitAndLowercase(str, '_'); 80 81 const int num_tags = parts.size(); 82 bool valid = false; 83 if (num_tags >= 1) { 84 const std::string& lang = parts[0]; 85 if (is_alpha(lang) && (lang.length() == 2 || lang.length() == 3)) { 86 set_language(lang.c_str()); 87 valid = true; 88 } 89 } 90 91 if (!valid || num_tags == 1) { 92 return valid; 93 } 94 95 // At this point, valid == true && numTags > 1. 96 const std::string& part2 = parts[1]; 97 if ((part2.length() == 2 && is_alpha(part2)) || 98 (part2.length() == 3 && is_number(part2))) { 99 set_region(part2.c_str()); 100 } else if (part2.length() == 4 && is_alpha(part2)) { 101 set_script(part2.c_str()); 102 } else if (part2.length() >= 4 && part2.length() <= 8) { 103 set_variant(part2.c_str()); 104 } else { 105 valid = false; 106 } 107 108 if (!valid || num_tags == 2) { 109 return valid; 110 } 111 112 // At this point, valid == true && numTags > 1. 113 const std::string& part3 = parts[2]; 114 if (((part3.length() == 2 && is_alpha(part3)) || 115 (part3.length() == 3 && is_number(part3))) && 116 script[0]) { 117 set_region(part3.c_str()); 118 } else if (part3.length() >= 4 && part3.length() <= 8) { 119 set_variant(part3.c_str()); 120 } else { 121 valid = false; 122 } 123 124 if (!valid || num_tags == 3) { 125 return valid; 126 } 127 128 const std::string& part4 = parts[3]; 129 if (part4.length() >= 4 && part4.length() <= 8) { 130 set_variant(part4.c_str()); 131 } else { 132 valid = false; 133 } 134 135 if (!valid || num_tags > 4) { 136 return false; 137 } 138 139 return true; 140 } 141 142 bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) { 143 return InitFromBcp47TagImpl(bcp47tag, '-'); 144 } 145 146 bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) { 147 std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator); 148 if (subtags.size() == 1) { 149 set_language(subtags[0].c_str()); 150 } else if (subtags.size() == 2) { 151 set_language(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 set_region(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 set_script(subtags[1].c_str()); 164 break; 165 } 166 case 5: 167 case 6: 168 case 7: 169 case 8: 170 set_variant(subtags[1].c_str()); 171 break; 172 default: 173 return false; 174 } 175 } else if (subtags.size() == 3) { 176 // The language is always the first subtag. 177 set_language(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 set_script(subtags[1].c_str()); 183 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) { 184 set_region(subtags[1].c_str()); 185 } else { 186 return false; 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 set_variant(subtags[2].c_str()); 193 } else { 194 set_region(subtags[2].c_str()); 195 } 196 } else if (subtags.size() == 4) { 197 set_language(subtags[0].c_str()); 198 set_script(subtags[1].c_str()); 199 set_region(subtags[2].c_str()); 200 set_variant(subtags[3].c_str()); 201 } else { 202 return false; 203 } 204 return true; 205 } 206 207 ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter, 208 std::vector<std::string>::iterator end) { 209 const std::vector<std::string>::iterator start_iter = iter; 210 211 std::string& part = *iter; 212 if (part[0] == 'b' && part[1] == '+') { 213 // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags, 214 // except that the separator is "+" and not "-". Skip the prefix 'b+'. 215 if (!InitFromBcp47TagImpl(StringPiece(part).substr(2), '+')) { 216 return -1; 217 } 218 ++iter; 219 } else { 220 if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && part != "car") { 221 set_language(part.c_str()); 222 ++iter; 223 224 if (iter != end) { 225 const std::string& region_part = *iter; 226 if (region_part.c_str()[0] == 'r' && region_part.length() == 3) { 227 set_region(region_part.c_str() + 1); 228 ++iter; 229 } 230 } 231 } 232 } 233 return static_cast<ssize_t>(iter - start_iter); 234 } 235 236 void LocaleValue::InitFromResTable(const ResTable_config& config) { 237 config.unpackLanguage(language); 238 config.unpackRegion(region); 239 if (config.localeScript[0] && !config.localeScriptWasComputed) { 240 memcpy(script, config.localeScript, sizeof(config.localeScript)); 241 } 242 243 if (config.localeVariant[0]) { 244 memcpy(variant, config.localeVariant, sizeof(config.localeVariant)); 245 } 246 } 247 248 void LocaleValue::WriteTo(ResTable_config* out) const { 249 out->packLanguage(language); 250 out->packRegion(region); 251 252 if (script[0]) { 253 memcpy(out->localeScript, script, sizeof(out->localeScript)); 254 } 255 256 if (variant[0]) { 257 memcpy(out->localeVariant, variant, sizeof(out->localeVariant)); 258 } 259 } 260 261 } // namespace aapt 262