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

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: Add comment. 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> 19 #include <libaddressinput/util/basictypes.h>
20 #include <libaddressinput/util/scoped_ptr.h> 20 #include <libaddressinput/util/scoped_ptr.h>
21 21
22 #include <algorithm>
22 #include <cassert> 23 #include <cassert>
23 #include <cstddef> 24 #include <cstddef>
24 #include <map>
25 #include <string> 25 #include <string>
26 #include <utility> 26 #include <vector>
27 27
28 #include "retriever.h" 28 #include "retriever.h"
29 #include "rule.h" 29 #include "rule.h"
30 #include "ruleset.h" 30 #include "ruleset.h"
31 #include "util/stl_util.h" 31 #include "util/json.h"
32 32
33 namespace i18n { 33 namespace i18n {
34 namespace addressinput { 34 namespace addressinput {
35 35
36 // Information about data requests sent to Retriever. This data is not returned 36 namespace {
37 // as part of the ruleset, but is useful in constructing the ruleset 37
38 // asynchronously. 38 // Stores the shared data for parsing multiple rules into a ruleset.
39 struct CountryRulesAggregator::RequestData { 39 class RulesetBuilder {
40 // Does not take ownership of |parent|. 40 public:
41 RequestData(Ruleset* parent, 41 explicit RulesetBuilder(scoped_ptr<Json> json)
42 AddressField level, 42 : json_(json.Pass()),
43 bool is_language_code, 43 languages_() {
44 const std::string& id) 44 assert(json_ != NULL);
45 : parent(parent),
46 level(level),
47 is_language_code(is_language_code),
48 id(id) {
49 assert(parent != NULL || level == COUNTRY);
50 } 45 }
51 46
52 ~RequestData() {} 47 ~RulesetBuilder() {}
53 48
54 // The parent ruleset of the data being downloaded. If NULL, then this is the 49 // Builds and returns the ruleset for |key| at |field| level. Returns NULL on
55 // root ruleset at the COUNTRY level. The language-specific and sub-region 50 // failure, e.g. missing sub-region data in JSON.
56 // rules are added to this ruleset. Owned by |CountryRulesRetriever|. 51 scoped_ptr<Ruleset> Build(const std::string& key, AddressField field) {
57 Ruleset* parent; 52 scoped_ptr<Rule> rule = ParseRule(key, field);
53 if (rule == NULL) {
54 return scoped_ptr<Ruleset>();
55 }
58 56
59 // The level of the data being requested. Ranges from COUNTRY to 57 if (field == COUNTRY) {
60 // DEPENDENT_LOCALITY. If COUNTRY, then the rule should use default rules from 58 languages_ = rule->GetLanguages();
61 // Rule::GetDefault(). 59 std::vector<std::string>::iterator default_language_it =
62 AddressField level; 60 std::find(languages_.begin(), languages_.end(), rule->GetLanguage());
61 if (default_language_it != languages_.end()) {
62 languages_.erase(default_language_it);
63 }
64 }
63 65
64 // If true, then |id| is a language. The data received for this request should 66 scoped_ptr<Ruleset> ruleset(new Ruleset(field, rule.Pass()));
65 // be placed into a language-specific rule.
66 bool is_language_code;
67 67
68 // Can be a region name (e.g. "CA") or a language (e.g. "fr"). Used to add a 68 for (std::vector<std::string>::const_iterator lang_it = languages_.begin();
Evan Stade 2014/01/18 19:14:32 please add short comments to each of these loops
please use gerrit instead 2014/01/21 18:43:54 Done.
69 // sub-region or a language-specific rule to |parent|. 69 lang_it != languages_.end(); ++lang_it) {
70 std::string id; 70 scoped_ptr<Rule> lang_rule = ParseRule(key + "--" + *lang_it, field);
71 if (lang_rule == NULL) {
72 return scoped_ptr<Ruleset>();
73 }
74 ruleset->AddLanguageCodeRule(*lang_it, lang_rule.Pass());
75 }
76
77 if (field == DEPENDENT_LOCALITY) {
Evan Stade 2014/01/18 19:14:32 why is this necessary? Wouldn't the dependent loca
please use gerrit instead 2014/01/21 18:43:54 True. I've changed this statement to an assert(),
78 return ruleset.Pass();
79 }
80
81 for (std::vector<std::string>::const_iterator
82 subkey_it = ruleset->rule().GetSubKeys().begin();
83 subkey_it != ruleset->rule().GetSubKeys().end(); ++subkey_it) {
84 scoped_ptr<Ruleset> sub_ruleset =
85 Build(key + "/" + *subkey_it, static_cast<AddressField>(field + 1));
86 if (sub_ruleset == NULL) {
87 return scoped_ptr<Ruleset>();
88 }
89 ruleset->AddSubRegionRuleset(*subkey_it, sub_ruleset.Pass());
90 }
91
92 return ruleset.Pass();
93 }
94
95 private:
96 // Builds and returns the rule for |key| at |field| level. Returns NULL if
97 // |key| is not in JSON.
98 scoped_ptr<Rule> ParseRule(const std::string& key, AddressField field) {
99 Json* value = NULL;
100 json_->GetJsonValueForKey(key, &value);
Evan Stade 2014/01/18 19:14:32 why did you make GetJsonValueForKey return a bool
please use gerrit instead 2014/01/21 18:43:54 Using the bool now.
101 if (value == NULL) {
102 return scoped_ptr<Rule>();
103 }
104 scoped_ptr<Json> value_scoped_ptr(value);
105 scoped_ptr<Rule> rule(new Rule);
106 if (field == COUNTRY) {
107 rule->CopyFrom(Rule::GetDefault());
108 }
109 rule->ParseJsonRule(*value_scoped_ptr);
110 return rule.Pass();
111 }
112
113 // The collection of rules for a country code.
114 scoped_ptr<Json> json_;
115
116 // The non-default languages that have custom rules.
117 std::vector<std::string> languages_;
Evan Stade 2014/01/18 19:14:32 nit: non_default_languages_? other_languages_?
please use gerrit instead 2014/01/21 18:43:54 non_default_languages_.
118
119 DISALLOW_COPY_AND_ASSIGN(RulesetBuilder);
71 }; 120 };
72 121
122 } // namespace
123
73 CountryRulesAggregator::CountryRulesAggregator(scoped_ptr<Retriever> retriever) 124 CountryRulesAggregator::CountryRulesAggregator(scoped_ptr<Retriever> retriever)
74 : retriever_(retriever.Pass()), 125 : retriever_(retriever.Pass()),
75 requests_(),
76 country_code_(), 126 country_code_(),
77 rules_ready_(), 127 key_(),
78 root_(), 128 rules_ready_() {
79 default_language_(),
80 languages_() {
81 assert(retriever_ != NULL); 129 assert(retriever_ != NULL);
82 } 130 }
83 131
84 CountryRulesAggregator::~CountryRulesAggregator() {} 132 CountryRulesAggregator::~CountryRulesAggregator() {}
85 133
86 void CountryRulesAggregator::AggregateRules(const std::string& country_code, 134 void CountryRulesAggregator::AggregateRules(const std::string& country_code,
87 scoped_ptr<Callback> rules_ready) { 135 scoped_ptr<Callback> rules_ready) {
88 Reset(); 136 Reset();
89 country_code_ = country_code; 137 country_code_ = country_code;
90 rules_ready_.reset(rules_ready.release()); 138 rules_ready_.reset(rules_ready.release());
91 139
92 // Key construction: 140 // Key construction:
93 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata 141 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata
94 // Example of a country-level key: "data/CA". 142 // Example of a country-level key: "data/CA".
95 std::string key = "data/" + country_code_; 143 key_ = "data/" + country_code_;
96 requests_.insert(std::make_pair(
97 key, RequestData(NULL, COUNTRY, false, std::string())));
98
99 retriever_->Retrieve( 144 retriever_->Retrieve(
100 key, BuildCallback(this, &CountryRulesAggregator::OnDataReady)); 145 key_, BuildCallback(this, &CountryRulesAggregator::OnDataReady));
101 } 146 }
102 147
103 void CountryRulesAggregator::OnDataReady(bool success, 148 void CountryRulesAggregator::OnDataReady(bool success,
104 const std::string& key, 149 const std::string& key,
105 const std::string& data) { 150 const std::string& data) {
106 std::map<std::string, RequestData>::iterator request_it = 151 if (key != key_) {
107 requests_.find(key);
108 if (request_it == requests_.end()) {
109 return; // An abandoned request. 152 return; // An abandoned request.
110 } 153 }
111 154
112 if (!success) { 155 scoped_ptr<Json> json(Json::Build());
156 if (!success || !json->ParseObject(data)) {
113 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>()); 157 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>());
114 Reset(); 158 Reset();
115 return; 159 return;
116 } 160 }
117 161
118 RequestData request = request_it->second; 162 RulesetBuilder builder(json.Pass());
119 requests_.erase(request_it); 163 scoped_ptr<Ruleset> ruleset = builder.Build(key_, COUNTRY);
120 164 (*rules_ready_)(ruleset != NULL, country_code_, ruleset.Pass());
121 // All country-level rules are based on the default rule. 165 Reset();
122 scoped_ptr<Rule> rule(new Rule);
123 if (request.level == COUNTRY) {
124 rule->CopyFrom(Rule::GetDefault());
125 }
126
127 if (!rule->ParseSerializedRule(data)) {
128 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>());
129 Reset();
130 return;
131 }
132
133 // Place the rule in the correct location in the ruleset.
134 Ruleset* ruleset = NULL;
135 if (request.is_language_code) {
136 assert(request.parent != NULL);
137 request.parent->AddLanguageCodeRule(request.id, rule.Pass());
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 }
192 }
193
194 if (requests_.empty()) {
195 (*rules_ready_)(true, country_code_, root_.Pass());
196 Reset();
197 }
198 } 166 }
199 167
200 void CountryRulesAggregator::Reset() { 168 void CountryRulesAggregator::Reset() {
201 requests_.clear();
202 country_code_.clear(); 169 country_code_.clear();
170 key_.clear();
203 rules_ready_.reset(); 171 rules_ready_.reset();
204 root_.reset();
205 default_language_.clear();
206 languages_.clear();
207 } 172 }
208 173
209 } // namespace addressinput 174 } // namespace addressinput
210 } // namespace i18n 175 } // namespace i18n
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698