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_aggregator.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 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) |
| 74 : retriever_(retriever.Pass()), |
| 75 requests_(), |
| 76 country_code_(), |
| 77 rules_ready_(), |
| 78 root_(), |
| 79 default_language_(), |
| 80 languages_() { |
| 81 assert(retriever_ != NULL); |
| 82 } |
| 83 |
| 84 CountryRulesAggregator::~CountryRulesAggregator() {} |
| 85 |
| 86 void CountryRulesAggregator::AggregateRules(const std::string& country_code, |
| 87 scoped_ptr<Callback> rules_ready) { |
| 88 Reset(); |
| 89 country_code_ = country_code; |
| 90 rules_ready_.reset(rules_ready.release()); |
| 91 |
| 92 // Key construction: |
| 93 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata |
| 94 // Example of a country-level key: "data/CA". |
| 95 std::string key = "data/" + country_code_; |
| 96 requests_.insert(std::make_pair( |
| 97 key, RequestData(NULL, COUNTRY, false, std::string()))); |
| 98 |
| 99 retriever_->Retrieve( |
| 100 key, BuildCallback(this, &CountryRulesAggregator::OnDataReady)); |
| 101 } |
| 102 |
| 103 void CountryRulesAggregator::OnDataReady(bool success, |
| 104 const std::string& key, |
| 105 const std::string& data) { |
| 106 std::map<std::string, RequestData>::iterator request_it = |
| 107 requests_.find(key); |
| 108 if (request_it == requests_.end()) { |
| 109 return; // An abandoned request. |
| 110 } |
| 111 |
| 112 if (!success) { |
| 113 (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>()); |
| 114 Reset(); |
| 115 return; |
| 116 } |
| 117 |
| 118 RequestData request = request_it->second; |
| 119 requests_.erase(request_it); |
| 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 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(rule.Pass())); |
| 147 ruleset = root_.get(); |
| 148 } else { |
| 149 assert(request.parent != NULL); |
| 150 ruleset = request.parent->AddSubRegionRuleset(request.id, rule.Pass()); |
| 151 } |
| 152 |
| 153 if (!request.is_language_code) { |
| 154 // Retrieve the language-specific rules for this region. |
| 155 for (std::vector<std::string>::const_iterator |
| 156 lang_it = languages_.begin(); |
| 157 lang_it != languages_.end(); |
| 158 ++lang_it) { |
| 159 if (*lang_it == default_language_) { |
| 160 continue; |
| 161 } |
| 162 // Example of a language-specific key: "data/CA--fr". |
| 163 std::string language_code_key = key + "--" + *lang_it; |
| 164 requests_.insert(std::make_pair( |
| 165 key, RequestData(ruleset, request.level, true, *lang_it))); |
| 166 retriever_->Retrieve( |
| 167 language_code_key, |
| 168 BuildCallback(this, &CountryRulesAggregator::OnDataReady)); |
| 169 } |
| 170 |
| 171 if (request.level < DEPENDENT_LOCALITY) { |
| 172 // Retrieve the sub-region rules for this region. |
| 173 for (std::vector<std::string>::const_iterator |
| 174 subkey_it = ruleset->rule().GetSubKeys().begin(); |
| 175 subkey_it != ruleset->rule().GetSubKeys().end(); |
| 176 ++subkey_it) { |
| 177 // Example of a sub-region key: "data/CA/AB". |
| 178 std::string sub_region_key = key + "/" + *subkey_it; |
| 179 requests_.insert(std::make_pair( |
| 180 key, |
| 181 RequestData(ruleset, |
| 182 static_cast<AddressField>(request.level + 1), |
| 183 false, |
| 184 *subkey_it))); |
| 185 retriever_->Retrieve( |
| 186 sub_region_key, |
| 187 BuildCallback(this, &CountryRulesAggregator::OnDataReady)); |
| 188 } |
| 189 } |
| 190 } |
| 191 |
| 192 if (requests_.empty()) { |
| 193 (*rules_ready_)(true, country_code_, root_.Pass()); |
| 194 Reset(); |
| 195 } |
| 196 } |
| 197 |
| 198 void CountryRulesAggregator::Reset() { |
| 199 requests_.clear(); |
| 200 country_code_.clear(); |
| 201 rules_ready_.reset(); |
| 202 root_.reset(); |
| 203 default_language_.clear(); |
| 204 languages_.clear(); |
| 205 } |
| 206 |
| 207 } // namespace addressinput |
| 208 } // namespace i18n |
OLD | NEW |