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.
BuildRegionTreeRecursively(const std::map<std::string,const Rule * > & rules,std::map<std::string,const Rule * >::const_iterator hint,const LookupKey & parent_key,RegionData * parent_region,const std::vector<std::string> & keys,bool prefer_latin_name,size_t region_max_depth)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.
BuildRegion(const std::map<std::string,const Rule * > & rules,const std::string & region_code,const Language & language)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
RegionDataBuilder(PreloadSupplier * supplier)129 RegionDataBuilder::RegionDataBuilder(PreloadSupplier* supplier)
130 : supplier_(supplier),
131 cache_() {
132 assert(supplier_ != NULL);
133 }
134
~RegionDataBuilder()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
Build(const std::string & region_code,const std::string & ui_language_tag,std::string * best_region_tree_language_tag)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