OLD | NEW |
---|---|
(Empty) | |
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 "country_rules_retriever.h" | |
16 | |
17 #include <libaddressinput/address_field.h> | |
18 #include <libaddressinput/callback.h> | |
19 #include <libaddressinput/util/basictypes.h> | |
20 #include <libaddressinput/util/scoped_ptr.h> | |
21 | |
22 #include <cassert> | |
23 #include <cstddef> | |
24 #include <map> | |
25 #include <string> | |
26 #include <utility> | |
27 | |
28 #include "retriever.h" | |
29 #include "rule.h" | |
30 #include "ruleset.h" | |
31 #include "util/stl_util.h" | |
32 | |
33 namespace i18n { | |
34 namespace addressinput { | |
35 | |
36 // Information about data requests sent to Retriever. This data is not returned | |
37 // as part of the ruleset, but is useful in constructing the ruleset | |
38 // asynchronously. | |
39 struct CountryRulesRetriever::RequestData { | |
40 // Does not take ownership of |parent|. | |
41 RequestData(RulesetData* ruleset_data, | |
42 Ruleset* parent, | |
43 AddressField level, | |
44 bool is_language_code, | |
45 const std::string& id) | |
46 : ruleset_data(ruleset_data), | |
47 parent(parent), | |
48 level(level), | |
49 is_language_code(is_language_code), | |
50 id(id) {} | |
51 | |
52 ~RequestData() {} | |
53 | |
54 // Information about the ruleset being downloaded. | |
55 RulesetData* ruleset_data; | |
56 | |
57 // The parent ruleset of the data being downloaded. If NULL, then this is the | |
58 // root ruleset at the COUNTRY level. The language-specific and sub-region | |
59 // rules are added to this ruleset. Owned by |RulesetData|. | |
60 Ruleset* parent; | |
61 | |
62 // The level of the data being requested. Ranges from COUNTRY to | |
63 // DEPENDENT_LOCALITY. If COUNTRY, then the rule should use default rules from | |
64 // Rule::GetDefault(). | |
65 AddressField level; | |
66 | |
67 // If true, then |id| is a language. The data received for this request should | |
68 // be placed into a language-specific rule. | |
69 bool is_language_code; | |
70 | |
71 // Can be a region name (e.g. "CA") or a language (e.g. "fr"). Used to add a | |
72 // sub-region or a language-specific rule to |parent|. | |
73 std::string id; | |
74 }; | |
75 | |
76 // Stores information about a ruleset that is being retrieved for a country | |
77 // code. | |
78 class CountryRulesRetriever::RulesetData { | |
79 public: | |
80 RulesetData(const std::string& country_code, | |
81 scoped_ptr<CountryRulesRetriever::Callback> rules_ready) | |
82 : country_code_(country_code), | |
83 rules_ready_(rules_ready.Pass()), | |
84 root_(), | |
85 num_requested_(0), | |
86 num_received_(0), | |
87 success_(true), | |
88 default_language_(), | |
89 languages_() {} | |
90 | |
91 ~RulesetData() {} | |
92 | |
93 // Sends the |root_| ruleset to |rules_ready_| callback. | |
94 void InvokeRulesReadyCallback() { | |
95 (*rules_ready_)(success_, country_code_, root_.Pass()); | |
96 } | |
97 | |
98 // The country code for which to retrieve the ruleset. Passed to the callback | |
99 // method to identify the ruleset. Examples: "US", "CA", "CH", etc. | |
100 const std::string country_code_; | |
101 | |
102 // The callback to invoke when the ruleset has been retrieved. | |
103 scoped_ptr<CountryRulesRetriever::Callback> rules_ready_; | |
104 | |
105 // The top-level ruleset for the country code. Passed to the callback method | |
106 // as the result of the query. | |
107 scoped_ptr<Ruleset> root_; | |
108 | |
109 // The number of requests sent to Retriever. Used to keep track of pending | |
110 // requests. | |
111 int num_requested_; | |
112 | |
113 // The number of responses received from Retriever. Used to determine when the | |
114 // ruleset construction is finished, by comparing this number to | |
115 // |num_requested_|. | |
116 int num_received_; | |
117 | |
118 // True if all requests to Retriever succeeded. Passed to the callback method | |
119 // as an indication of whether ruleset retrieval succeeded. | |
120 bool success_; | |
121 | |
122 // The default language for the country code. This value is parsed from the | |
123 // country-level rule for the country code and is used to filter out the | |
124 // default language from the list of all supported languages for a country. | |
125 // For example, the list of supported languages for Canada is ["en", "fr"], | |
126 // but the default language is "en". Data requests for "data/CA/AB--fr" will | |
127 // succeed, but "data/CA/AB--en" will not return data. | |
128 std::string default_language_; | |
129 | |
130 // The list of all supported languages for the country code. This value is | |
131 // parsed from the country-level rule for the country and is used to download | |
132 // language-specific rules. | |
133 std::vector<std::string> languages_; | |
134 | |
135 private: | |
136 DISALLOW_COPY_AND_ASSIGN(RulesetData); | |
137 }; | |
138 | |
139 CountryRulesRetriever::CountryRulesRetriever(scoped_ptr<Retriever> retriever) | |
140 : retriever_(retriever.Pass()) { | |
141 assert(retriever_ != NULL); | |
142 } | |
143 | |
144 CountryRulesRetriever::~CountryRulesRetriever() { | |
145 STLDeleteContainerPointers(rulesets_.begin(), rulesets_.end()); | |
146 } | |
147 | |
148 void CountryRulesRetriever::RetrieveRules( | |
149 const std::string& country_code, | |
150 scoped_ptr<Callback> rules_ready) { | |
151 RulesetData* ruleset_data = new RulesetData(country_code, rules_ready.Pass()); | |
152 rulesets_.push_back(ruleset_data); | |
Evan Stade
2014/01/07 20:09:58
why would you need to retrieve rules for more than
please use gerrit instead
2014/01/07 22:11:08
The user might rapidly switch the country combobox
Evan Stade
2014/01/07 22:13:13
in which case you could/should probably abandon th
please use gerrit instead
2014/01/07 22:57:09
Done.
| |
153 | |
154 // Key construction: | |
155 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata | |
156 // Example of a country-level key: "data/CA". | |
157 std::string key = "data/" + ruleset_data->country_code_; | |
158 requests_.insert(std::make_pair( | |
159 key, RequestData(ruleset_data, NULL, COUNTRY, false, std::string()))); | |
160 | |
161 ++ruleset_data->num_requested_; | |
162 retriever_->Retrieve( | |
163 key, BuildCallback(this, &CountryRulesRetriever::OnDataReady)); | |
164 } | |
165 | |
166 void CountryRulesRetriever::OnDataReady(bool success, | |
167 const std::string& key, | |
168 const std::string& data) { | |
169 std::map<std::string, RequestData>::iterator request_it = | |
170 requests_.find(key); | |
Evan Stade
2014/01/07 20:09:58
remove request_it now that you're done with it aft
please use gerrit instead
2014/01/07 22:11:08
Done.
| |
171 assert(request_it != requests_.end()); | |
172 RequestData& request = request_it->second; | |
173 | |
174 RulesetData* ruleset_data = request.ruleset_data; | |
175 assert(ruleset_data != NULL); | |
176 | |
177 ruleset_data->success_ &= success; | |
178 ++ruleset_data->num_received_; | |
179 | |
180 // All country-level rules are based on the default rule. | |
181 scoped_ptr<Rule> rule(new Rule); | |
182 if (request.level == COUNTRY) { | |
183 rule->CopyFrom(Rule::GetDefault()); | |
184 } | |
185 | |
186 ruleset_data->success_ &= rule->ParseSerializedRule(data); | |
187 | |
188 // Place the rule in the correct location in the ruleset. | |
189 Ruleset* ruleset = NULL; | |
190 if (request.is_language_code) { | |
191 assert(request.parent != NULL); | |
192 request.parent->AddLanguageCode(request.id, rule.Pass()); | |
193 ruleset = request.parent; | |
194 } else if (request.level == COUNTRY) { | |
195 // The default language and all supported languages for the country code are | |
196 // in the country-level rule without a language code identifier. For | |
197 // example: "data/CA". | |
198 ruleset_data->default_language_ = rule->GetLanguage(); | |
199 ruleset_data->languages_ = rule->GetLanguages(); | |
200 | |
201 ruleset_data->root_.reset(new Ruleset(rule.Pass())); | |
202 ruleset = ruleset_data->root_.get(); | |
203 } else { | |
204 assert(request.parent != NULL); | |
205 ruleset = request.parent->AddSubRegion(request.id, rule.Pass()); | |
206 } | |
207 | |
208 if (!request.is_language_code) { | |
209 // Retrieve the language-specific rules for this region. | |
210 for (std::vector<std::string>::const_iterator | |
211 lang_it = ruleset_data->languages_.begin(); | |
212 lang_it != ruleset_data->languages_.end(); | |
213 ++lang_it) { | |
214 if (*lang_it == ruleset_data->default_language_) { | |
215 continue; | |
216 } | |
217 // Example of a language-specific key: "data/CA--fr". | |
218 std::string language_code_key = key + "--" + *lang_it; | |
219 requests_.insert(std::make_pair( | |
220 key, | |
221 RequestData(ruleset_data, ruleset, request.level, true, *lang_it))); | |
222 ++ruleset_data->num_requested_; | |
223 retriever_->Retrieve( | |
224 language_code_key, | |
225 BuildCallback(this, &CountryRulesRetriever::OnDataReady)); | |
226 } | |
227 | |
228 if (request.level < DEPENDENT_LOCALITY) { | |
229 // Retrieve the sub-region rules for this region. | |
230 for (std::vector<std::string>::const_iterator | |
231 subkey_it = ruleset->rule().GetSubKeys().begin(); | |
232 subkey_it != ruleset->rule().GetSubKeys().end(); | |
233 ++subkey_it) { | |
234 // Example of a sub-region key: "data/CA/AB". | |
235 std::string sub_region_key = key + "/" + *subkey_it; | |
236 requests_.insert(std::make_pair( | |
237 key, | |
238 RequestData(ruleset_data, | |
239 ruleset, | |
240 static_cast<AddressField>(request.level + 1), | |
241 false, | |
242 *subkey_it))); | |
243 ++ruleset_data->num_requested_; | |
244 retriever_->Retrieve( | |
245 sub_region_key, | |
246 BuildCallback(this, &CountryRulesRetriever::OnDataReady)); | |
247 } | |
248 } | |
249 } | |
250 | |
251 if (ruleset_data->num_received_ == ruleset_data->num_requested_) { | |
252 ruleset_data->InvokeRulesReadyCallback(); | |
253 std::vector<RulesetData*>::iterator ruleset_it = | |
254 std::find(rulesets_.begin(), rulesets_.end(), ruleset_data); | |
255 assert(ruleset_it != rulesets_.end()); | |
256 rulesets_.erase(ruleset_it); | |
257 delete ruleset_data; | |
258 } | |
259 } | |
260 | |
261 } // namespace addressinput | |
262 } // namespace i18n | |
OLD | NEW |