Home | History | Annotate | Download | only in src
      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/scoped_ptr.h>
     20 
     21 #include <algorithm>
     22 #include <cassert>
     23 #include <cstddef>
     24 #include <map>
     25 #include <string>
     26 #include <vector>
     27 
     28 #include "retriever.h"
     29 #include "rule.h"
     30 #include "ruleset.h"
     31 #include "util/json.h"
     32 
     33 namespace i18n {
     34 namespace addressinput {
     35 
     36 CountryRulesAggregator::CountryRulesAggregator(scoped_ptr<Retriever> retriever)
     37     : retriever_(retriever.Pass()),
     38       country_code_(),
     39       key_(),
     40       rules_ready_(),
     41       json_(),
     42       non_default_languages_() {
     43   assert(retriever_ != NULL);
     44 }
     45 
     46 CountryRulesAggregator::~CountryRulesAggregator() {}
     47 
     48 void CountryRulesAggregator::AggregateRules(const std::string& country_code,
     49                                             scoped_ptr<Callback> rules_ready) {
     50   Reset();
     51   country_code_ = country_code;
     52   rules_ready_ = rules_ready.Pass();
     53 
     54   // Key construction:
     55   // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata
     56   // Example of a country-level key: "data/CA".
     57   key_ = "data/" + country_code_;
     58   retriever_->Retrieve(
     59       key_, BuildCallback(this, &CountryRulesAggregator::OnDataReady));
     60 }
     61 
     62 bool CountryRulesAggregator::OnDataReady(bool success,
     63                                          const std::string& key,
     64                                          const std::string& data) {
     65   if (key != key_) {
     66     return true;  // An abandoned request.
     67   }
     68 
     69   json_ = Json::Build().Pass();
     70   if (!success || !json_->ParseObject(data)) {
     71     (*rules_ready_)(false, country_code_, scoped_ptr<Ruleset>());
     72     Reset();
     73     return false;
     74   }
     75 
     76   std::map<std::string, std::string> language_keys;
     77   scoped_ptr<Ruleset> ruleset = Build(key_, COUNTRY, language_keys);
     78   const bool parse_success = ruleset != NULL;
     79   (*rules_ready_)(parse_success, country_code_, ruleset.Pass());
     80   Reset();
     81   return parse_success;
     82 }
     83 
     84 scoped_ptr<Ruleset> CountryRulesAggregator::Build(
     85     const std::string& key,
     86     AddressField field,
     87     const std::map<std::string, std::string>& language_specific_keys) {
     88   scoped_ptr<Rule> rule = ParseRule(key, field);
     89   if (rule == NULL) {
     90     return scoped_ptr<Ruleset>();
     91   }
     92 
     93   // Determine the languages that have language-specific rules. For example,
     94   // the default language in Switzerland is "de", but "fr" and "it" have
     95   // language specific rules.
     96   if (field == COUNTRY) {
     97     non_default_languages_ = rule->GetLanguages();
     98     std::vector<std::string>::iterator default_language_it =
     99         std::find(non_default_languages_.begin(),
    100                   non_default_languages_.end(),
    101                   rule->GetLanguage());
    102     if (default_language_it != non_default_languages_.end()) {
    103       non_default_languages_.erase(default_language_it);
    104     }
    105   }
    106 
    107   scoped_ptr<Ruleset> ruleset(new Ruleset(field, rule.Pass()));
    108   std::map<std::string, std::map<std::string, std::string> >
    109       language_specific_subkeys;
    110 
    111   // Parse the language-specific rules. For example, parse the rules for "fr"
    112   // and "it" languages in Switzerland.
    113   for (std::vector<std::string>::const_iterator
    114            non_default_language_it = non_default_languages_.begin();
    115        non_default_language_it != non_default_languages_.end();
    116        ++non_default_language_it) {
    117     std::map<std::string, std::string>::const_iterator
    118         language_specific_key_it =
    119             language_specific_keys.find(*non_default_language_it);
    120     std::string language_specific_key =
    121         language_specific_key_it != language_specific_keys.end()
    122             ? language_specific_key_it->second
    123             : key;
    124     scoped_ptr<Rule> language_specific_rule =
    125         ParseRule(language_specific_key + "--" + *non_default_language_it,
    126                   field);
    127 
    128     if (language_specific_rule == NULL ||
    129         language_specific_rule->GetSubKeys().size() !=
    130             ruleset->rule().GetSubKeys().size()) {
    131       return scoped_ptr<Ruleset>();
    132     }
    133 
    134     // Build the language specific subkeys for the next level of
    135     // rules. Examples:
    136     //    ["data/CA/AB"]["fr"] <- "data/CA/AB"
    137     //    ["data/HK/"]["en"] <- "data/HK/Kowloon"
    138     for (size_t i = 0; i < ruleset->rule().GetSubKeys().size(); ++i) {
    139       const std::string& subkey = key + "/" + ruleset->rule().GetSubKeys()[i];
    140       const std::string& language_specific_subkey =
    141           key + "/" + language_specific_rule->GetSubKeys()[i];
    142       language_specific_subkeys[subkey][*non_default_language_it] =
    143           language_specific_subkey;
    144     }
    145 
    146     ruleset->AddLanguageCodeRule(
    147         *non_default_language_it, language_specific_rule.Pass());
    148   }
    149 
    150   // Parse the sub-keys recursively. For example, parse the rules for all of the
    151   // states in US: "CA", "TX", "NY", etc.
    152   for (std::vector<std::string>::const_iterator
    153            subkey_it = ruleset->rule().GetSubKeys().begin();
    154        subkey_it != ruleset->rule().GetSubKeys().end(); ++subkey_it) {
    155     std::string subkey = key + "/" + *subkey_it;
    156     scoped_ptr<Ruleset> sub_ruleset =
    157         Build(subkey,
    158               static_cast<AddressField>(field + 1),
    159               language_specific_subkeys[subkey]);
    160 
    161     if (sub_ruleset == NULL) {
    162       return scoped_ptr<Ruleset>();
    163     }
    164 
    165     ruleset->AddSubRegionRuleset(*subkey_it, sub_ruleset.Pass());
    166   }
    167 
    168   return ruleset.Pass();
    169 }
    170 
    171 scoped_ptr<Rule> CountryRulesAggregator::ParseRule(const std::string& key,
    172                                                    AddressField field) const {
    173   scoped_ptr<Json> value;
    174   if (!json_->GetJsonValueForKey(key, &value) || value == NULL) {
    175     return scoped_ptr<Rule>();
    176   }
    177   scoped_ptr<Rule> rule(new Rule);
    178   if (field == COUNTRY) {
    179     rule->CopyFrom(Rule::GetDefault());
    180   }
    181   rule->ParseJsonRule(*value);
    182   return rule.Pass();
    183 }
    184 
    185 void CountryRulesAggregator::Reset() {
    186   country_code_.clear();
    187   key_.clear();
    188   rules_ready_.reset();
    189   json_.reset();
    190   non_default_languages_.clear();
    191 }
    192 
    193 }  // namespace addressinput
    194 }  // namespace i18n
    195