Home | History | Annotate | Download | only in src
      1 // Copyright (C) 2014 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 // http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include <libaddressinput/region_data_builder.h>
     16 
     17 #include <libaddressinput/address_data.h>
     18 #include <libaddressinput/preload_supplier.h>
     19 #include <libaddressinput/region_data.h>
     20 #include <libaddressinput/util/basictypes.h>
     21 
     22 #include <cassert>
     23 #include <cstddef>
     24 #include <string>
     25 #include <utility>
     26 #include <vector>
     27 
     28 #include "language.h"
     29 #include "lookup_key.h"
     30 #include "region_data_constants.h"
     31 #include "rule.h"
     32 
     33 namespace i18n {
     34 namespace addressinput {
     35 
     36 namespace {
     37 
     38 // The maximum depth of lookup keys.
     39 static const size_t kLookupKeysMaxDepth = arraysize(LookupKey::kHierarchy) - 1;
     40 
     41 // Does not take ownership of |parent_region|, which is not allowed to be NULL.
     42 void BuildRegionTreeRecursively(
     43     const std::map<std::string, const Rule*>& rules,
     44     std::map<std::string, const Rule*>::const_iterator hint,
     45     const LookupKey& parent_key,
     46     RegionData* parent_region,
     47     const std::vector<std::string>& keys,
     48     bool prefer_latin_name,
     49     size_t region_max_depth) {
     50   assert(parent_region != NULL);
     51 
     52   LookupKey lookup_key;
     53   for (std::vector<std::string>::const_iterator key_it = keys.begin();
     54        key_it != keys.end(); ++key_it) {
     55     lookup_key.FromLookupKey(parent_key, *key_it);
     56     const std::string& lookup_key_string =
     57         lookup_key.ToKeyString(kLookupKeysMaxDepth);
     58 
     59     ++hint;
     60     if (hint == rules.end() || hint->first != lookup_key_string) {
     61       hint = rules.find(lookup_key_string);
     62       if (hint == rules.end()) {
     63         return;
     64       }
     65     }
     66 
     67     const Rule* rule = hint->second;
     68     assert(rule != NULL);
     69 
     70     const std::string& local_name = rule->GetName().empty()
     71         ? *key_it : rule->GetName();
     72     const std::string& name =
     73         prefer_latin_name && !rule->GetLatinName().empty()
     74             ? rule->GetLatinName() : local_name;
     75     RegionData* region = parent_region->AddSubRegion(*key_it, name);
     76 
     77     if (!rule->GetSubKeys().empty() &&
     78         region_max_depth > parent_key.GetDepth()) {
     79       BuildRegionTreeRecursively(rules,
     80                                  hint,
     81                                  lookup_key,
     82                                  region,
     83                                  rule->GetSubKeys(),
     84                                  prefer_latin_name,
     85                                  region_max_depth);
     86     }
     87   }
     88 }
     89 
     90 // The caller owns the result.
     91 RegionData* BuildRegion(const std::map<std::string, const Rule*>& rules,
     92                         const std::string& region_code,
     93                         const Language& language) {
     94   AddressData address;
     95   address.region_code = region_code;
     96 
     97   LookupKey lookup_key;
     98   lookup_key.FromAddress(address);
     99 
    100   std::map<std::string, const Rule*>::const_iterator hint =
    101       rules.find(lookup_key.ToKeyString(kLookupKeysMaxDepth));
    102   assert(hint != rules.end());
    103 
    104   const Rule* rule = hint->second;
    105   assert(rule != NULL);
    106 
    107   RegionData* region = new RegionData(region_code);
    108 
    109   // If there're sub-keys for field X, but field X is not used in this region
    110   // code, then these sub-keys are skipped over. For example, CH has sub-keys
    111   // for field ADMIN_AREA, but CH does not use ADMIN_AREA field.
    112   size_t region_max_depth =
    113       RegionDataConstants::GetMaxLookupKeyDepth(region_code);
    114   if (region_max_depth > 0) {
    115     BuildRegionTreeRecursively(rules,
    116                                hint,
    117                                lookup_key,
    118                                region,
    119                                rule->GetSubKeys(),
    120                                language.has_latin_script,
    121                                region_max_depth);
    122   }
    123 
    124   return region;
    125 }
    126 
    127 }  // namespace
    128 
    129 RegionDataBuilder::RegionDataBuilder(PreloadSupplier* supplier)
    130     : supplier_(supplier),
    131       cache_() {
    132   assert(supplier_ != NULL);
    133 }
    134 
    135 RegionDataBuilder::~RegionDataBuilder() {
    136   for (RegionCodeDataMap::const_iterator region_it = cache_.begin();
    137        region_it != cache_.end(); ++region_it) {
    138     for (LanguageRegionMap::const_iterator
    139          language_it = region_it->second->begin();
    140          language_it != region_it->second->end(); ++language_it) {
    141       delete language_it->second;
    142     }
    143     delete region_it->second;
    144   }
    145 }
    146 
    147 const RegionData& RegionDataBuilder::Build(
    148     const std::string& region_code,
    149     const std::string& ui_language_tag,
    150     std::string* best_region_tree_language_tag) {
    151   assert(supplier_->IsLoaded(region_code));
    152   assert(best_region_tree_language_tag != NULL);
    153 
    154   // Look up the region tree in cache first before building it.
    155   RegionCodeDataMap::const_iterator region_it = cache_.find(region_code);
    156   if (region_it == cache_.end()) {
    157     region_it =
    158         cache_.insert(std::make_pair(region_code, new LanguageRegionMap)).first;
    159   }
    160 
    161   // No need to copy from default rule first, because only languages and Latin
    162   // format are going to be used, which do not exist in the default rule.
    163   Rule rule;
    164   rule.ParseSerializedRule(RegionDataConstants::GetRegionData(region_code));
    165   static const Language kUndefinedLanguage("und");
    166   const Language& best_language =
    167       rule.GetLanguages().empty()
    168           ? kUndefinedLanguage
    169           : ChooseBestAddressLanguage(rule, Language(ui_language_tag));
    170   *best_region_tree_language_tag = best_language.tag;
    171 
    172   LanguageRegionMap::const_iterator language_it =
    173       region_it->second->find(best_language.tag);
    174   if (language_it == region_it->second->end()) {
    175     const std::map<std::string, const Rule*>& rules =
    176         supplier_->GetRulesForRegion(region_code);
    177     language_it =
    178         region_it->second->insert(std::make_pair(best_language.tag,
    179                                                  BuildRegion(rules,
    180                                                              region_code,
    181                                                              best_language)))
    182             .first;
    183   }
    184 
    185   return *language_it->second;
    186 }
    187 
    188 }  // namespace addressinput
    189 }  // namespace i18n
    190