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