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.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 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 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 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 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 65 static inline bool isAlpha(const std::string& str) { 66 return std::all_of(std::begin(str), std::end(str), ::isalpha); 67 } 68 69 static inline bool isNumber(const std::string& str) { 70 return std::all_of(std::begin(str), std::end(str), ::isdigit); 71 } 72 73 bool LocaleValue::initFromFilterString(const std::string& 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() >= 5 && 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() >= 5 && 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() >= 5 && 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 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 setScript(subtags[1].c_str()); 161 break; 162 case 5: 163 case 6: 164 case 7: 165 case 8: 166 setVariant(subtags[1].c_str()); 167 break; 168 default: 169 return -1; 170 } 171 } else if (subtags.size() == 3) { 172 // The language is always the first subtag. 173 setLanguage(subtags[0].c_str()); 174 175 // The second subtag can either be a script or a region code. 176 // If its size is 4, it's a script code, else it's a region code. 177 if (subtags[1].size() == 4) { 178 setScript(subtags[1].c_str()); 179 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) { 180 setRegion(subtags[1].c_str()); 181 } else { 182 return -1; 183 } 184 185 // The third tag can either be a region code (if the second tag was 186 // a script), else a variant code. 187 if (subtags[2].size() > 4) { 188 setVariant(subtags[2].c_str()); 189 } else { 190 setRegion(subtags[2].c_str()); 191 } 192 } else if (subtags.size() == 4) { 193 setLanguage(subtags[0].c_str()); 194 setScript(subtags[1].c_str()); 195 setRegion(subtags[2].c_str()); 196 setVariant(subtags[3].c_str()); 197 } else { 198 return -1; 199 } 200 201 ++iter; 202 203 } else { 204 if ((part.length() == 2 || part.length() == 3) 205 && isAlpha(part) && part != "car") { 206 setLanguage(part.c_str()); 207 ++iter; 208 209 if (iter != end) { 210 const std::string& regionPart = *iter; 211 if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) { 212 setRegion(regionPart.c_str() + 1); 213 ++iter; 214 } 215 } 216 } 217 } 218 219 return static_cast<ssize_t>(iter - startIter); 220 } 221 222 223 std::string LocaleValue::toDirName() const { 224 std::string dirName; 225 if (language[0]) { 226 dirName += language; 227 } else { 228 return dirName; 229 } 230 231 if (script[0]) { 232 dirName += "-s"; 233 dirName += script; 234 } 235 236 if (region[0]) { 237 dirName += "-r"; 238 dirName += region; 239 } 240 241 if (variant[0]) { 242 dirName += "-v"; 243 dirName += variant; 244 } 245 246 return dirName; 247 } 248 249 void LocaleValue::initFromResTable(const ResTable_config& config) { 250 config.unpackLanguage(language); 251 config.unpackRegion(region); 252 if (config.localeScript[0]) { 253 memcpy(script, config.localeScript, sizeof(config.localeScript)); 254 } 255 256 if (config.localeVariant[0]) { 257 memcpy(variant, config.localeVariant, sizeof(config.localeVariant)); 258 } 259 } 260 261 void LocaleValue::writeTo(ResTable_config* out) const { 262 out->packLanguage(language); 263 out->packRegion(region); 264 265 if (script[0]) { 266 memcpy(out->localeScript, script, sizeof(out->localeScript)); 267 } 268 269 if (variant[0]) { 270 memcpy(out->localeVariant, variant, sizeof(out->localeVariant)); 271 } 272 } 273 274 } // namespace aapt 275