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 
     21 #include <cassert>
     22 #include <cstddef>
     23 #include <string>
     24 #include <utility>
     25 #include <vector>
     26 
     27 #include "language.h"
     28 #include "lookup_key.h"
     29 #include "region_data_constants.h"
     30 #include "rule.h"
     31 
     32 namespace i18n {
     33 namespace addressinput {
     34 
     35 namespace {
     36 
     37 // Does not take ownership of |supplier| or |parent_region|, neither of which is
     38 // allowed to be NULL.
     39 void BuildRegionTreeRecursively(PreloadSupplier* supplier,
     40                                 const LookupKey& parent_key,
     41                                 RegionData* parent_region,
     42                                 const std::vector<std::string>& keys,
     43                                 bool prefer_latin_name) {
     44   assert(supplier != NULL);
     45   assert(parent_region != NULL);
     46 
     47   LookupKey lookup_key;
     48   for (std::vector<std::string>::const_iterator key_it = keys.begin();
     49        key_it != keys.end(); ++key_it) {
     50     lookup_key.FromLookupKey(parent_key, *key_it);
     51     const Rule* rule = supplier->GetRule(lookup_key);
     52     if (rule == NULL) {
     53       return;
     54     }
     55     const std::string& local_name = rule->GetName().empty()
     56         ? *key_it : rule->GetName();
     57     const std::string& name =
     58         prefer_latin_name && !rule->GetLatinName().empty()
     59             ? rule->GetLatinName() : local_name;
     60     RegionData* region = parent_region->AddSubRegion(*key_it, name);
     61     if (!rule->GetSubKeys().empty()) {
     62       BuildRegionTreeRecursively(supplier, lookup_key, region,
     63                                  rule->GetSubKeys(), prefer_latin_name);
     64     }
     65   }
     66 }
     67 
     68 // Does not take ownership of |supplier|, which cannot be NULL. The caller owns
     69 // the result.
     70 RegionData* BuildRegion(PreloadSupplier* supplier,
     71                         const std::string& region_code,
     72                         const Language& language) {
     73   assert(supplier != NULL);
     74 
     75   AddressData address;
     76   address.region_code = region_code;
     77 
     78   LookupKey lookup_key;
     79   lookup_key.FromAddress(address);
     80 
     81   const Rule* const rule = supplier->GetRule(lookup_key);
     82   assert(rule != NULL);
     83 
     84   RegionData* region = new RegionData(region_code);
     85   BuildRegionTreeRecursively(supplier, lookup_key, region,
     86                              rule->GetSubKeys(), language.has_latin_script);
     87 
     88   return region;
     89 }
     90 
     91 }  // namespace
     92 
     93 RegionDataBuilder::RegionDataBuilder(PreloadSupplier* supplier)
     94     : supplier_(supplier),
     95       cache_() {
     96   assert(supplier_ != NULL);
     97 }
     98 
     99 RegionDataBuilder::~RegionDataBuilder() {
    100   for (RegionCodeDataMap::const_iterator region_it = cache_.begin();
    101        region_it != cache_.end(); ++region_it) {
    102     for (LanguageRegionMap::const_iterator
    103          language_it = region_it->second->begin();
    104          language_it != region_it->second->end(); ++language_it) {
    105       delete language_it->second;
    106     }
    107     delete region_it->second;
    108   }
    109 }
    110 
    111 const RegionData& RegionDataBuilder::Build(
    112     const std::string& region_code,
    113     const std::string& ui_language_tag,
    114     std::string* best_region_tree_language_tag) {
    115   assert(supplier_->IsLoaded(region_code));
    116   assert(best_region_tree_language_tag != NULL);
    117 
    118   // Look up the region tree in cache first before building it.
    119   RegionCodeDataMap::const_iterator region_it = cache_.find(region_code);
    120   if (region_it == cache_.end()) {
    121     region_it =
    122         cache_.insert(std::make_pair(region_code, new LanguageRegionMap)).first;
    123   }
    124 
    125   // No need to copy from default rule first, because only languages and Latin
    126   // format are going to be used, which do not exist in the default rule.
    127   Rule rule;
    128   rule.ParseSerializedRule(RegionDataConstants::GetRegionData(region_code));
    129   static const Language kUndefinedLanguage("und");
    130   const Language& best_language = rule.GetLanguages().empty()
    131       ? kUndefinedLanguage
    132       : ChooseBestAddressLanguage(rule, Language(ui_language_tag));
    133   *best_region_tree_language_tag = best_language.tag;
    134 
    135   LanguageRegionMap::const_iterator language_it =
    136       region_it->second->find(best_language.tag);
    137   if (language_it == region_it->second->end()) {
    138     language_it = region_it->second->insert(std::make_pair(
    139         best_language.tag,
    140         BuildRegion(supplier_, region_code, best_language))).first;
    141   }
    142 
    143   return *language_it->second;
    144 }
    145 
    146 }  // namespace addressinput
    147 }  // namespace i18n
    148