1 // Copyright (C) 2014 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <libaddressinput/region_data_builder.h>
17 #include <libaddressinput/address_data.h>
18 #include <libaddressinput/preload_supplier.h>
19 #include <libaddressinput/region_data.h>
20 #include <libaddressinput/util/basictypes.h>
29 #include "lookup_key.h"
30 #include "region_data_constants.h"
34 namespace addressinput {
38 // The maximum depth of lookup keys.
39 static const size_t kLookupKeysMaxDepth = arraysize(LookupKey::kHierarchy) - 1;
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);
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);
60 if (hint == rules.end() || hint->first != lookup_key_string) {
61 hint = rules.find(lookup_key_string);
62 if (hint == rules.end()) {
67 const Rule* rule = hint->second;
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);
77 if (!rule->GetSubKeys().empty() &&
78 region_max_depth > parent_key.GetDepth()) {
79 BuildRegionTreeRecursively(rules,
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) {
95 address.region_code = region_code;
98 lookup_key.FromAddress(address);
100 std::map<std::string, const Rule*>::const_iterator hint =
101 rules.find(lookup_key.ToKeyString(kLookupKeysMaxDepth));
102 assert(hint != rules.end());
104 const Rule* rule = hint->second;
105 assert(rule != NULL);
107 RegionData* region = new RegionData(region_code);
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,
120 language.has_latin_script,
129 RegionDataBuilder::RegionDataBuilder(PreloadSupplier* supplier)
130 : supplier_(supplier),
132 assert(supplier_ != NULL);
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;
143 delete region_it->second;
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);
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()) {
158 cache_.insert(std::make_pair(region_code, new LanguageRegionMap)).first;
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.
164 rule.ParseSerializedRule(RegionDataConstants::GetRegionData(region_code));
165 static const Language kUndefinedLanguage("und");
166 const Language& best_language =
167 rule.GetLanguages().empty()
169 : ChooseBestAddressLanguage(rule, Language(ui_language_tag));
170 *best_region_tree_language_tag = best_language.tag;
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);
178 region_it->second->insert(std::make_pair(best_language.tag,
185 return *language_it->second;
188 } // namespace addressinput