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(Ruleset* parent, | |
42 AddressField level, | |
43 bool is_language_code, | |
44 const std::string& id) | |
45 : parent(parent), | |
46 level(level), | |
47 is_language_code(is_language_code), | |
48 id(id) {} | |
49 | |
50 ~RequestData() {} | |
51 | |
52 // The parent ruleset of the data being downloaded. If NULL, then this is the | |
53 // root ruleset at the COUNTRY level. The language-specific and sub-region | |
Evan Stade
2014/01/08 21:08:59
add an assert for this in the constructor?
please use gerrit instead
2014/01/09 00:38:11
Done.
| |
54 // rules are added to this ruleset. Owned by |CountryRulesRetriever|. | |
55 Ruleset* parent; | |
56 | |
57 // The level of the data being requested. Ranges from COUNTRY to | |
58 // DEPENDENT_LOCALITY. If COUNTRY, then the rule should use default rules from | |
59 // Rule::GetDefault(). | |
60 AddressField level; | |
61 | |
62 // If true, then |id| is a language. The data received for this request should | |
63 // be placed into a language-specific rule. | |
64 bool is_language_code; | |
65 | |
66 // Can be a region name (e.g. "CA") or a language (e.g. "fr"). Used to add a | |
67 // sub-region or a language-specific rule to |parent|. | |
68 std::string id; | |
69 }; | |
70 | |
71 CountryRulesRetriever::CountryRulesRetriever(scoped_ptr<Retriever> retriever) | |
72 : retriever_(retriever.Pass()), | |
73 requests_(), | |
74 country_code_(), | |
75 rules_ready_(), | |
76 root_(), | |
77 success_(true), | |
78 default_language_(), | |
79 languages_() { | |
80 assert(retriever_ != NULL); | |
81 } | |
82 | |
83 CountryRulesRetriever::~CountryRulesRetriever() {} | |
84 | |
85 void CountryRulesRetriever::RetrieveRules(const std::string& country_code, | |
86 scoped_ptr<Callback> rules_ready) { | |
87 Reset(); | |
88 country_code_ = country_code; | |
89 rules_ready_.reset(rules_ready.release()); | |
90 | |
91 // Key construction: | |
92 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata | |
93 // Example of a country-level key: "data/CA". | |
94 std::string key = "data/" + country_code_; | |
95 requests_.insert(std::make_pair( | |
96 key, RequestData(NULL, COUNTRY, false, std::string()))); | |
97 | |
98 retriever_->Retrieve( | |
99 key, BuildCallback(this, &CountryRulesRetriever::OnDataReady)); | |
100 } | |
101 | |
102 void CountryRulesRetriever::OnDataReady(bool success, | |
103 const std::string& key, | |
104 const std::string& data) { | |
105 std::map<std::string, RequestData>::iterator request_it = | |
106 requests_.find(key); | |
107 if (request_it == requests_.end()) { | |
108 return; // An abandoned request. | |
109 } | |
110 | |
111 RequestData request = request_it->second; | |
112 requests_.erase(request_it); | |
113 | |
114 success_ &= success; | |
115 if (!success_) { | |
116 (*rules_ready_)(success_, country_code_, root_.Pass()); | |
Evan Stade
2014/01/08 21:08:59
why do you pass back partial data?
please use gerrit instead
2014/01/09 00:38:11
Removed.
| |
117 Reset(); | |
118 return; | |
119 } | |
120 | |
121 // All country-level rules are based on the default rule. | |
122 scoped_ptr<Rule> rule(new Rule); | |
123 if (request.level == COUNTRY) { | |
124 rule->CopyFrom(Rule::GetDefault()); | |
125 } | |
126 | |
127 success_ &= rule->ParseSerializedRule(data); | |
Evan Stade
2014/01/08 21:08:59
I don't get why you do both this and CopyFrom abov
please use gerrit instead
2014/01/09 00:38:11
If a country-level rule is missing some informatio
| |
128 if (!success_) { | |
129 (*rules_ready_)(success_, country_code_, root_.Pass()); | |
130 Reset(); | |
131 return; | |
132 } | |
133 | |
134 // Place the rule in the correct location in the ruleset. | |
135 Ruleset* ruleset = NULL; | |
136 if (request.is_language_code) { | |
137 assert(request.parent != NULL); | |
138 request.parent->AddLanguageCode(request.id, rule.Pass()); | |
139 ruleset = request.parent; | |
140 } else if (request.level == COUNTRY) { | |
141 // The default language and all supported languages for the country code are | |
142 // in the country-level rule without a language code identifier. For | |
143 // example: "data/CA". | |
144 default_language_ = rule->GetLanguage(); | |
145 languages_ = rule->GetLanguages(); | |
146 | |
147 root_.reset(new Ruleset(rule.Pass())); | |
148 ruleset = root_.get(); | |
149 } else { | |
150 assert(request.parent != NULL); | |
151 ruleset = request.parent->AddSubRegion(request.id, rule.Pass()); | |
152 } | |
153 | |
154 if (!request.is_language_code) { | |
155 // Retrieve the language-specific rules for this region. | |
156 for (std::vector<std::string>::const_iterator | |
157 lang_it = languages_.begin(); | |
Evan Stade
2014/01/08 21:08:59
4 more indent
please use gerrit instead
2014/01/09 00:38:11
Done.
| |
158 lang_it != languages_.end(); | |
159 ++lang_it) { | |
160 if (*lang_it == default_language_) { | |
161 continue; | |
162 } | |
163 // Example of a language-specific key: "data/CA--fr". | |
164 std::string language_code_key = key + "--" + *lang_it; | |
165 requests_.insert(std::make_pair( | |
166 key, RequestData(ruleset, request.level, true, *lang_it))); | |
167 retriever_->Retrieve( | |
168 language_code_key, | |
169 BuildCallback(this, &CountryRulesRetriever::OnDataReady)); | |
170 } | |
171 | |
172 if (request.level < DEPENDENT_LOCALITY) { | |
173 // Retrieve the sub-region rules for this region. | |
174 for (std::vector<std::string>::const_iterator | |
175 subkey_it = ruleset->rule().GetSubKeys().begin(); | |
176 subkey_it != ruleset->rule().GetSubKeys().end(); | |
177 ++subkey_it) { | |
178 // Example of a sub-region key: "data/CA/AB". | |
179 std::string sub_region_key = key + "/" + *subkey_it; | |
180 requests_.insert(std::make_pair( | |
181 key, | |
182 RequestData(ruleset, | |
183 static_cast<AddressField>(request.level + 1), | |
184 false, | |
185 *subkey_it))); | |
186 retriever_->Retrieve( | |
187 sub_region_key, | |
188 BuildCallback(this, &CountryRulesRetriever::OnDataReady)); | |
189 } | |
190 } | |
191 } | |
192 | |
193 if (requests_.empty()) { | |
194 (*rules_ready_)(success_, country_code_, root_.Pass()); | |
195 Reset(); | |
196 } | |
197 } | |
198 | |
199 void CountryRulesRetriever::Reset() { | |
200 requests_.clear(); | |
201 country_code_.clear(); | |
202 rules_ready_.reset(); | |
203 root_.reset(); | |
204 success_ = true; | |
205 default_language_.clear(); | |
206 languages_.clear(); | |
207 } | |
208 | |
209 } // namespace addressinput | |
210 } // namespace i18n | |
OLD | NEW |