Chromium Code Reviews| Index: third_party/libaddressinput/chromium/cpp/src/country_rules_retriever.cc |
| diff --git a/third_party/libaddressinput/chromium/cpp/src/country_rules_retriever.cc b/third_party/libaddressinput/chromium/cpp/src/country_rules_retriever.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d0128766bc6b96da4fbef74a78e2fd1f30fc21af |
| --- /dev/null |
| +++ b/third_party/libaddressinput/chromium/cpp/src/country_rules_retriever.cc |
| @@ -0,0 +1,262 @@ |
| +// Copyright (C) 2014 Google Inc. |
| +// |
| +// Licensed under the Apache License, Version 2.0 (the "License"); |
| +// you may not use this file except in compliance with the License. |
| +// You may obtain a copy of the License at |
| +// |
| +// http://www.apache.org/licenses/LICENSE-2.0 |
| +// |
| +// Unless required by applicable law or agreed to in writing, software |
| +// distributed under the License is distributed on an "AS IS" BASIS, |
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| +// See the License for the specific language governing permissions and |
| +// limitations under the License. |
| + |
| +#include "country_rules_retriever.h" |
| + |
| +#include <libaddressinput/address_field.h> |
| +#include <libaddressinput/callback.h> |
| +#include <libaddressinput/util/basictypes.h> |
| +#include <libaddressinput/util/scoped_ptr.h> |
| + |
| +#include <cassert> |
| +#include <cstddef> |
| +#include <map> |
| +#include <string> |
| +#include <utility> |
| + |
| +#include "retriever.h" |
| +#include "rule.h" |
| +#include "ruleset.h" |
| +#include "util/stl_util.h" |
| + |
| +namespace i18n { |
| +namespace addressinput { |
| + |
| +// Information about data requests sent to Retriever. This data is not returned |
| +// as part of the ruleset, but is useful in constructing the ruleset |
| +// asynchronously. |
| +struct CountryRulesRetriever::RequestData { |
| + // Does not take ownership of |parent|. |
| + RequestData(RulesetData* ruleset_data, |
| + Ruleset* parent, |
| + AddressField level, |
| + bool is_language_code, |
| + const std::string& id) |
| + : ruleset_data(ruleset_data), |
| + parent(parent), |
| + level(level), |
| + is_language_code(is_language_code), |
| + id(id) {} |
| + |
| + ~RequestData() {} |
| + |
| + // Information about the ruleset being downloaded. |
| + RulesetData* ruleset_data; |
| + |
| + // The parent ruleset of the data being downloaded. If NULL, then this is the |
| + // root ruleset at the COUNTRY level. The language-specific and sub-region |
| + // rules are added to this ruleset. Owned by |RulesetData|. |
| + Ruleset* parent; |
| + |
| + // The level of the data being requested. Ranges from COUNTRY to |
| + // DEPENDENT_LOCALITY. If COUNTRY, then the rule should use default rules from |
| + // Rule::GetDefault(). |
| + AddressField level; |
| + |
| + // If true, then |id| is a language. The data received for this request should |
| + // be placed into a language-specific rule. |
| + bool is_language_code; |
| + |
| + // Can be a region name (e.g. "CA") or a language (e.g. "fr"). Used to add a |
| + // sub-region or a language-specific rule to |parent|. |
| + std::string id; |
| +}; |
| + |
| +// Stores information about a ruleset that is being retrieved for a country |
| +// code. |
| +class CountryRulesRetriever::RulesetData { |
| + public: |
| + RulesetData(const std::string& country_code, |
| + scoped_ptr<CountryRulesRetriever::Callback> rules_ready) |
| + : country_code_(country_code), |
| + rules_ready_(rules_ready.Pass()), |
| + root_(), |
| + num_requested_(0), |
| + num_received_(0), |
| + success_(true), |
| + default_language_(), |
| + languages_() {} |
| + |
| + ~RulesetData() {} |
| + |
| + // Sends the |root_| ruleset to |rules_ready_| callback. |
| + void InvokeRulesReadyCallback() { |
| + (*rules_ready_)(success_, country_code_, root_.Pass()); |
| + } |
| + |
| + // The country code for which to retrieve the ruleset. Passed to the callback |
| + // method to identify the ruleset. Examples: "US", "CA", "CH", etc. |
| + const std::string country_code_; |
| + |
| + // The callback to invoke when the ruleset has been retrieved. |
| + scoped_ptr<CountryRulesRetriever::Callback> rules_ready_; |
| + |
| + // The top-level ruleset for the country code. Passed to the callback method |
| + // as the result of the query. |
| + scoped_ptr<Ruleset> root_; |
| + |
| + // The number of requests sent to Retriever. Used to keep track of pending |
| + // requests. |
| + int num_requested_; |
| + |
| + // The number of responses received from Retriever. Used to determine when the |
| + // ruleset construction is finished, by comparing this number to |
| + // |num_requested_|. |
| + int num_received_; |
| + |
| + // True if all requests to Retriever succeeded. Passed to the callback method |
| + // as an indication of whether ruleset retrieval succeeded. |
| + bool success_; |
| + |
| + // The default language for the country code. This value is parsed from the |
| + // country-level rule for the country code and is used to filter out the |
| + // default language from the list of all supported languages for a country. |
| + // For example, the list of supported languages for Canada is ["en", "fr"], |
| + // but the default language is "en". Data requests for "data/CA/AB--fr" will |
| + // succeed, but "data/CA/AB--en" will not return data. |
| + std::string default_language_; |
| + |
| + // The list of all supported languages for the country code. This value is |
| + // parsed from the country-level rule for the country and is used to download |
| + // language-specific rules. |
| + std::vector<std::string> languages_; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(RulesetData); |
| +}; |
| + |
| +CountryRulesRetriever::CountryRulesRetriever(scoped_ptr<Retriever> retriever) |
| + : retriever_(retriever.Pass()) { |
| + assert(retriever_ != NULL); |
| +} |
| + |
| +CountryRulesRetriever::~CountryRulesRetriever() { |
| + STLDeleteContainerPointers(rulesets_.begin(), rulesets_.end()); |
| +} |
| + |
| +void CountryRulesRetriever::RetrieveRules( |
| + const std::string& country_code, |
| + scoped_ptr<Callback> rules_ready) { |
| + RulesetData* ruleset_data = new RulesetData(country_code, rules_ready.Pass()); |
| + 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.
|
| + |
| + // Key construction: |
| + // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata |
| + // Example of a country-level key: "data/CA". |
| + std::string key = "data/" + ruleset_data->country_code_; |
| + requests_.insert(std::make_pair( |
| + key, RequestData(ruleset_data, NULL, COUNTRY, false, std::string()))); |
| + |
| + ++ruleset_data->num_requested_; |
| + retriever_->Retrieve( |
| + key, BuildCallback(this, &CountryRulesRetriever::OnDataReady)); |
| +} |
| + |
| +void CountryRulesRetriever::OnDataReady(bool success, |
| + const std::string& key, |
| + const std::string& data) { |
| + std::map<std::string, RequestData>::iterator request_it = |
| + 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.
|
| + assert(request_it != requests_.end()); |
| + RequestData& request = request_it->second; |
| + |
| + RulesetData* ruleset_data = request.ruleset_data; |
| + assert(ruleset_data != NULL); |
| + |
| + ruleset_data->success_ &= success; |
| + ++ruleset_data->num_received_; |
| + |
| + // All country-level rules are based on the default rule. |
| + scoped_ptr<Rule> rule(new Rule); |
| + if (request.level == COUNTRY) { |
| + rule->CopyFrom(Rule::GetDefault()); |
| + } |
| + |
| + ruleset_data->success_ &= rule->ParseSerializedRule(data); |
| + |
| + // Place the rule in the correct location in the ruleset. |
| + Ruleset* ruleset = NULL; |
| + if (request.is_language_code) { |
| + assert(request.parent != NULL); |
| + request.parent->AddLanguageCode(request.id, rule.Pass()); |
| + ruleset = request.parent; |
| + } else if (request.level == COUNTRY) { |
| + // The default language and all supported languages for the country code are |
| + // in the country-level rule without a language code identifier. For |
| + // example: "data/CA". |
| + ruleset_data->default_language_ = rule->GetLanguage(); |
| + ruleset_data->languages_ = rule->GetLanguages(); |
| + |
| + ruleset_data->root_.reset(new Ruleset(rule.Pass())); |
| + ruleset = ruleset_data->root_.get(); |
| + } else { |
| + assert(request.parent != NULL); |
| + ruleset = request.parent->AddSubRegion(request.id, rule.Pass()); |
| + } |
| + |
| + if (!request.is_language_code) { |
| + // Retrieve the language-specific rules for this region. |
| + for (std::vector<std::string>::const_iterator |
| + lang_it = ruleset_data->languages_.begin(); |
| + lang_it != ruleset_data->languages_.end(); |
| + ++lang_it) { |
| + if (*lang_it == ruleset_data->default_language_) { |
| + continue; |
| + } |
| + // Example of a language-specific key: "data/CA--fr". |
| + std::string language_code_key = key + "--" + *lang_it; |
| + requests_.insert(std::make_pair( |
| + key, |
| + RequestData(ruleset_data, ruleset, request.level, true, *lang_it))); |
| + ++ruleset_data->num_requested_; |
| + retriever_->Retrieve( |
| + language_code_key, |
| + BuildCallback(this, &CountryRulesRetriever::OnDataReady)); |
| + } |
| + |
| + if (request.level < DEPENDENT_LOCALITY) { |
| + // Retrieve the sub-region rules for this region. |
| + for (std::vector<std::string>::const_iterator |
| + subkey_it = ruleset->rule().GetSubKeys().begin(); |
| + subkey_it != ruleset->rule().GetSubKeys().end(); |
| + ++subkey_it) { |
| + // Example of a sub-region key: "data/CA/AB". |
| + std::string sub_region_key = key + "/" + *subkey_it; |
| + requests_.insert(std::make_pair( |
| + key, |
| + RequestData(ruleset_data, |
| + ruleset, |
| + static_cast<AddressField>(request.level + 1), |
| + false, |
| + *subkey_it))); |
| + ++ruleset_data->num_requested_; |
| + retriever_->Retrieve( |
| + sub_region_key, |
| + BuildCallback(this, &CountryRulesRetriever::OnDataReady)); |
| + } |
| + } |
| + } |
| + |
| + if (ruleset_data->num_received_ == ruleset_data->num_requested_) { |
| + ruleset_data->InvokeRulesReadyCallback(); |
| + std::vector<RulesetData*>::iterator ruleset_it = |
| + std::find(rulesets_.begin(), rulesets_.end(), ruleset_data); |
| + assert(ruleset_it != rulesets_.end()); |
| + rulesets_.erase(ruleset_it); |
| + delete ruleset_data; |
| + } |
| +} |
| + |
| +} // namespace addressinput |
| +} // namespace i18n |