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 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 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 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 227 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 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 265 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