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