Chromium Code Reviews| 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 |