2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "util/Util.h"
27 using android::ResTable_config;
29 void LocaleValue::setLanguage(const char* languageChars) {
31 while ((*languageChars) != '\0') {
32 language[i++] = ::tolower(*languageChars);
37 void LocaleValue::setRegion(const char* regionChars) {
39 while ((*regionChars) != '\0') {
40 region[i++] = ::toupper(*regionChars);
45 void LocaleValue::setScript(const char* scriptChars) {
47 while ((*scriptChars) != '\0') {
49 script[i++] = ::toupper(*scriptChars);
51 script[i++] = ::tolower(*scriptChars);
57 void LocaleValue::setVariant(const char* variantChars) {
59 while ((*variantChars) != '\0') {
60 variant[i++] = *variantChars;
65 static inline bool isAlpha(const std::string& str) {
66 return std::all_of(std::begin(str), std::end(str), ::isalpha);
69 static inline bool isNumber(const std::string& str) {
70 return std::all_of(std::begin(str), std::end(str), ::isdigit);
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, '_');
78 const int numTags = parts.size();
81 const std::string& lang = parts[0];
82 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
83 setLanguage(lang.c_str());
88 if (!valid || numTags == 1) {
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());
105 if (!valid || numTags == 2) {
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());
120 if (!valid || numTags == 3) {
124 const std::string& part4 = parts[3];
125 if (part4.length() >= 4 && part4.length() <= 8) {
126 setVariant(part4.c_str());
131 if (!valid || numTags > 4) {
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;
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());
153 // The second tag can either be a region, a variant or a script.
154 switch (subtags[1].size()) {
157 setRegion(subtags[1].c_str());
160 if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
161 // This is a variant: fall through
163 setScript(subtags[1].c_str());
170 setVariant(subtags[1].c_str());
175 } else if (subtags.size() == 3) {
176 // The language is always the first subtag.
177 setLanguage(subtags[0].c_str());
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());
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());
194 setRegion(subtags[2].c_str());
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());
208 if ((part.length() == 2 || part.length() == 3)
209 && isAlpha(part) && part != "car") {
210 setLanguage(part.c_str());
214 const std::string& regionPart = *iter;
215 if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
216 setRegion(regionPart.c_str() + 1);
223 return static_cast<ssize_t>(iter - startIter);
227 std::string LocaleValue::toDirName() const {
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));
260 if (config.localeVariant[0]) {
261 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
265 void LocaleValue::writeTo(ResTable_config* out) const {
266 out->packLanguage(language);
267 out->packRegion(region);
270 memcpy(out->localeScript, script, sizeof(out->localeScript));
274 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));