Home | History | Annotate | Download | only in aapt2
      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