| OLD | NEW |
| 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 // Parse the sub-keys. For example, parse the rules for all of the states in |
| 116 // US: "CA", "TX", "NY", etc. |
| 117 for (std::vector<std::string>::const_iterator |
| 118 subkey_it = ruleset->rule().GetSubKeys().begin(); |
| 119 subkey_it != ruleset->rule().GetSubKeys().end(); ++subkey_it) { |
| 120 scoped_ptr<Ruleset> sub_ruleset = |
| 121 Build(key + "/" + *subkey_it, static_cast<AddressField>(field + 1)); |
| 122 if (sub_ruleset == NULL) { |
| 123 return scoped_ptr<Ruleset>(); |
| 124 } |
| 125 ruleset->AddSubRegionRuleset(*subkey_it, sub_ruleset.Pass()); |
| 126 } |
| 127 |
| 128 return ruleset.Pass(); |
| 129 } |
| 130 |
| 131 scoped_ptr<Rule> CountryRulesAggregator::ParseRule(const std::string& key, |
| 132 AddressField field) const { |
| 133 scoped_ptr<Json> value; |
| 134 if (!json_->GetJsonValueForKey(key, &value) || value == NULL) { |
| 135 return scoped_ptr<Rule>(); |
| 136 } |
| 137 scoped_ptr<Rule> rule(new Rule); |
| 138 if (field == COUNTRY) { |
| 139 rule->CopyFrom(Rule::GetDefault()); |
| 140 } |
| 141 rule->ParseJsonRule(*value); |
| 142 return rule.Pass(); |
| 198 } | 143 } |
| 199 | 144 |
| 200 void CountryRulesAggregator::Reset() { | 145 void CountryRulesAggregator::Reset() { |
| 201 requests_.clear(); | |
| 202 country_code_.clear(); | 146 country_code_.clear(); |
| 147 key_.clear(); |
| 203 rules_ready_.reset(); | 148 rules_ready_.reset(); |
| 204 root_.reset(); | 149 json_.reset(); |
| 205 default_language_.clear(); | 150 non_default_languages_.clear(); |
| 206 languages_.clear(); | |
| 207 } | 151 } |
| 208 | 152 |
| 209 } // namespace addressinput | 153 } // namespace addressinput |
| 210 } // namespace i18n | 154 } // namespace i18n |
| OLD | NEW |