Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: third_party/libaddressinput/chromium/cpp/src/country_rules_aggregator.cc

Issue 140823005: [rac] Download country code data in a single HTTP request. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (C) 2014 Google Inc. 1 // Copyright (C) 2014 Google Inc.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with 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 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "country_rules_aggregator.h" 15 #include "country_rules_aggregator.h"
16 16
17 #include <libaddressinput/address_field.h> 17 #include <libaddressinput/address_field.h>
18 #include <libaddressinput/callback.h> 18 #include <libaddressinput/callback.h>
19 #include <libaddressinput/util/basictypes.h>
20 #include <libaddressinput/util/scoped_ptr.h> 19 #include <libaddressinput/util/scoped_ptr.h>
21 20
21 #include <algorithm>
22 #include <cassert> 22 #include <cassert>
23 #include <cstddef> 23 #include <cstddef>
24 #include <map>
25 #include <string> 24 #include <string>
26 #include <utility> 25 #include <vector>
27 26
28 #include "retriever.h" 27 #include "retriever.h"
29 #include "rule.h" 28 #include "rule.h"
30 #include "ruleset.h" 29 #include "ruleset.h"
31 #include "util/stl_util.h" 30 #include "util/json.h"
32 31
33 namespace i18n { 32 namespace i18n {
34 namespace addressinput { 33 namespace addressinput {
35 34
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 CountryRulesAggregator::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 assert(parent != NULL || level == COUNTRY);
50 }
51
52 ~RequestData() {}
53
54 // The parent ruleset of the data being downloaded. If NULL, then this is the
55 // root ruleset at the COUNTRY level. The language-specific and sub-region
56 // rules are added to this ruleset. Owned by |CountryRulesRetriever|.
57 Ruleset* parent;
58
59 // The level of the data being requested. Ranges from COUNTRY to
60 // DEPENDENT_LOCALITY. If COUNTRY, then the rule should use default rules from
61 // Rule::GetDefault().
62 AddressField level;
63
64 // If true, then |id| is a language. The data received for this request should
65 // be placed into a language-specific rule.
66 bool is_language_code;
67
68 // Can be a region name (e.g. "CA") or a language (e.g. "fr"). Used to add a
69 // sub-region or a language-specific rule to |parent|.
70 std::string id;
71 };
72
73 CountryRulesAggregator::CountryRulesAggregator(scoped_ptr<Retriever> retriever) 35 CountryRulesAggregator::CountryRulesAggregator(scoped_ptr<Retriever> retriever)
74 : retriever_(retriever.Pass()), 36 : retriever_(retriever.Pass()),
75 requests_(),
76 country_code_(), 37 country_code_(),
38 key_(),
77 rules_ready_(), 39 rules_ready_(),
78 root_(), 40 json_(),
79 default_language_(), 41 non_default_languages_() {
80 languages_() {
81 assert(retriever_ != NULL); 42 assert(retriever_ != NULL);
82 } 43 }
83 44
84 CountryRulesAggregator::~CountryRulesAggregator() {} 45 CountryRulesAggregator::~CountryRulesAggregator() {}
85 46
86 void CountryRulesAggregator::AggregateRules(const std::string& country_code, 47 void CountryRulesAggregator::AggregateRules(const std::string& country_code,
87 scoped_ptr<Callback> rules_ready) { 48 scoped_ptr<Callback> rules_ready) {
88 Reset(); 49 Reset();
89 country_code_ = country_code; 50 country_code_ = country_code;
90 rules_ready_.reset(rules_ready.release()); 51 rules_ready_.reset(rules_ready.release());
91 52
92 // Key construction: 53 // Key construction:
93 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata 54 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata
94 // Example of a country-level key: "data/CA". 55 // Example of a country-level key: "data/CA".
95 std::string key = "data/" + country_code_; 56 key_ = "data/" + country_code_;
96 requests_.insert(std::make_pair(
97 key, RequestData(NULL, COUNTRY, false, std::string())));
98
99 retriever_->Retrieve( 57 retriever_->Retrieve(
100 key, BuildCallback(this, &CountryRulesAggregator::OnDataReady)); 58 key_, BuildCallback(this, &CountryRulesAggregator::OnDataReady));
101 } 59 }
102 60
103 void CountryRulesAggregator::OnDataReady(bool success, 61 void CountryRulesAggregator::OnDataReady(bool success,
104 const std::string& key, 62 const std::string& key,
105 const std::string& data) { 63 const std::string& data) {
106 std::map<std::string, RequestData>::iterator request_it = 64 if (key != key_) {
107 requests_.find(key);
108 if (request_it == requests_.end()) {
109 return; // An abandoned request. 65 return; // An abandoned request.
110 } 66 }
111 67
112 if (!success) { 68 json_ = Json::Build().Pass();
69 if (!success || !json_->ParseObject(data)) {
113 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>()); 70 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>());
114 Reset(); 71 Reset();
115 return; 72 return;
116 } 73 }
117 74
118 RequestData request = request_it->second; 75 scoped_ptr<Ruleset> ruleset = Build(key_, COUNTRY);
119 requests_.erase(request_it); 76 (*rules_ready_)(ruleset != NULL, country_code_, ruleset.Pass());
77 Reset();
78 }
120 79
121 // All country-level rules are based on the default rule. 80 scoped_ptr<Ruleset> CountryRulesAggregator::Build(const std::string& key,
122 scoped_ptr<Rule> rule(new Rule); 81 AddressField field) {
123 if (request.level == COUNTRY) { 82 scoped_ptr<Rule> rule = ParseRule(key, field);
124 rule->CopyFrom(Rule::GetDefault()); 83 if (rule == NULL) {
84 return scoped_ptr<Ruleset>();
125 } 85 }
126 86
127 if (!rule->ParseSerializedRule(data)) { 87 // Determine the languages that have language-specific rules. For example,
128 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>()); 88 // the default language in Switzerland is "de", but "fr" and "it" have
129 Reset(); 89 // language specific rules.
130 return; 90 if (field == COUNTRY) {
131 } 91 non_default_languages_ = rule->GetLanguages();
132 92 std::vector<std::string>::iterator default_language_it =
133 // Place the rule in the correct location in the ruleset. 93 std::find(non_default_languages_.begin(),
134 Ruleset* ruleset = NULL; 94 non_default_languages_.end(),
135 if (request.is_language_code) { 95 rule->GetLanguage());
136 assert(request.parent != NULL); 96 if (default_language_it != non_default_languages_.end()) {
137 request.parent->AddLanguageCodeRule(request.id, rule.Pass()); 97 non_default_languages_.erase(default_language_it);
138 ruleset = request.parent;
139 } else if (request.level == COUNTRY) {
140 // The default language and all supported languages for the country code are
141 // in the country-level rule without a language code identifier. For
142 // example: "data/CA".
143 default_language_ = rule->GetLanguage();
144 languages_ = rule->GetLanguages();
145
146 root_.reset(new Ruleset(request.level, rule.Pass()));
147 ruleset = root_.get();
148 } else {
149 assert(request.parent != NULL);
150 ruleset = new Ruleset(request.level, rule.Pass());
151 request.parent->AddSubRegionRuleset(
152 request.id, scoped_ptr<Ruleset>(ruleset));
153 }
154
155 if (!request.is_language_code) {
156 // Retrieve the language-specific rules for this region.
157 for (std::vector<std::string>::const_iterator
158 lang_it = languages_.begin();
159 lang_it != languages_.end();
160 ++lang_it) {
161 if (*lang_it == default_language_) {
162 continue;
163 }
164 // Example of a language-specific key: "data/CA--fr".
165 std::string language_code_key = key + "--" + *lang_it;
166 requests_.insert(std::make_pair(
167 key, RequestData(ruleset, request.level, true, *lang_it)));
168 retriever_->Retrieve(
169 language_code_key,
170 BuildCallback(this, &CountryRulesAggregator::OnDataReady));
171 }
172
173 if (request.level < DEPENDENT_LOCALITY) {
174 // Retrieve the sub-region rules for this region.
175 for (std::vector<std::string>::const_iterator
176 subkey_it = ruleset->rule().GetSubKeys().begin();
177 subkey_it != ruleset->rule().GetSubKeys().end();
178 ++subkey_it) {
179 // Example of a sub-region key: "data/CA/AB".
180 std::string sub_region_key = key + "/" + *subkey_it;
181 requests_.insert(std::make_pair(
182 key,
183 RequestData(ruleset,
184 static_cast<AddressField>(request.level + 1),
185 false,
186 *subkey_it)));
187 retriever_->Retrieve(
188 sub_region_key,
189 BuildCallback(this, &CountryRulesAggregator::OnDataReady));
190 }
191 } 98 }
192 } 99 }
193 100
194 if (requests_.empty()) { 101 scoped_ptr<Ruleset> ruleset(new Ruleset(field, rule.Pass()));
195 (*rules_ready_)(true, country_code_, root_.Pass()); 102
196 Reset(); 103 // Parse the language-specific rules. For example, parse the rules for "fr"
104 // and "it" languages in Switzerland.
105 for (std::vector<std::string>::const_iterator
106 lang_it = non_default_languages_.begin();
107 lang_it != non_default_languages_.end(); ++lang_it) {
108 scoped_ptr<Rule> lang_rule = ParseRule(key + "--" + *lang_it, field);
109 if (lang_rule == NULL) {
110 return scoped_ptr<Ruleset>();
111 }
112 ruleset->AddLanguageCodeRule(*lang_it, lang_rule.Pass());
197 } 113 }
114
115 // A dependent locality rule should not have sub-keys.
116 assert(field != DEPENDENT_LOCALITY || ruleset->rule().GetSubKeys().empty());
Evan Stade 2014/01/22 01:22:03 even this seems out of place to me; I might expect
please use gerrit instead 2014/01/22 01:46:21 Done.
117
118 // Parse the sub-keys. For example, parse the rules for all of the states in
119 // US: "CA", "TX", "NY", etc.
120 for (std::vector<std::string>::const_iterator
121 subkey_it = ruleset->rule().GetSubKeys().begin();
122 subkey_it != ruleset->rule().GetSubKeys().end(); ++subkey_it) {
Evan Stade 2014/01/22 01:22:03 indent is off by one
please use gerrit instead 2014/01/22 01:46:21 Done.
123 scoped_ptr<Ruleset> sub_ruleset =
124 Build(key + "/" + *subkey_it, static_cast<AddressField>(field + 1));
125 if (sub_ruleset == NULL) {
126 return scoped_ptr<Ruleset>();
127 }
128 ruleset->AddSubRegionRuleset(*subkey_it, sub_ruleset.Pass());
129 }
130
131 return ruleset.Pass();
132 }
133
134 scoped_ptr<Rule> CountryRulesAggregator::ParseRule(const std::string& key,
135 AddressField field) const {
136 scoped_ptr<Json> value;
137 if (!json_->GetJsonValueForKey(key, &value) || value == NULL) {
138 return scoped_ptr<Rule>();
139 }
140 scoped_ptr<Rule> rule(new Rule);
141 if (field == COUNTRY) {
142 rule->CopyFrom(Rule::GetDefault());
143 }
144 rule->ParseJsonRule(*value);
145 return rule.Pass();
198 } 146 }
199 147
200 void CountryRulesAggregator::Reset() { 148 void CountryRulesAggregator::Reset() {
201 requests_.clear();
202 country_code_.clear(); 149 country_code_.clear();
150 key_.clear();
203 rules_ready_.reset(); 151 rules_ready_.reset();
204 root_.reset(); 152 json_.reset();
205 default_language_.clear(); 153 non_default_languages_.clear();
206 languages_.clear();
207 } 154 }
208 155
209 } // namespace addressinput 156 } // namespace addressinput
210 } // namespace i18n 157 } // namespace i18n
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698