1 // Copyright (C) 2014 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "country_rules_aggregator.h"
17 #include <libaddressinput/address_field.h>
18 #include <libaddressinput/callback.h>
19 #include <libaddressinput/util/scoped_ptr.h>
28 #include "retriever.h"
31 #include "util/json.h"
34 namespace addressinput
{
36 CountryRulesAggregator::CountryRulesAggregator(scoped_ptr
<Retriever
> retriever
)
37 : retriever_(retriever
.Pass()),
42 non_default_languages_() {
43 assert(retriever_
!= NULL
);
46 CountryRulesAggregator::~CountryRulesAggregator() {}
48 void CountryRulesAggregator::AggregateRules(const std::string
& country_code
,
49 scoped_ptr
<Callback
> rules_ready
) {
51 country_code_
= country_code
;
52 rules_ready_
= rules_ready
.Pass();
55 // https://code.google.com/p/libaddressinput/wiki/AddressValidationMetadata
56 // Example of a country-level key: "data/CA".
57 key_
= "data/" + country_code_
;
59 key_
, BuildCallback(this, &CountryRulesAggregator::OnDataReady
));
62 bool CountryRulesAggregator::OnDataReady(bool success
,
63 const std::string
& key
,
64 const std::string
& data
) {
66 return true; // An abandoned request.
69 json_
= Json::Build().Pass();
70 if (!success
|| !json_
->ParseObject(data
)) {
71 (*rules_ready_
)(false, country_code_
, scoped_ptr
<Ruleset
>());
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());
84 scoped_ptr
<Ruleset
> CountryRulesAggregator::Build(
85 const std::string
& key
,
87 const std::map
<std::string
, std::string
>& language_specific_keys
) {
88 scoped_ptr
<Rule
> rule
= ParseRule(key
, field
);
90 return scoped_ptr
<Ruleset
>();
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
);
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
;
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
124 scoped_ptr
<Rule
> language_specific_rule
=
125 ParseRule(language_specific_key
+ "--" + *non_default_language_it
,
128 if (language_specific_rule
== NULL
||
129 language_specific_rule
->GetSubKeys().size() !=
130 ruleset
->rule().GetSubKeys().size()) {
131 return scoped_ptr
<Ruleset
>();
134 // Build the language specific subkeys for the next level of
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
;
146 ruleset
->AddLanguageCodeRule(
147 *non_default_language_it
, language_specific_rule
.Pass());
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
=
158 static_cast<AddressField
>(field
+ 1),
159 language_specific_subkeys
[subkey
]);
161 if (sub_ruleset
== NULL
) {
162 return scoped_ptr
<Ruleset
>();
165 ruleset
->AddSubRegionRuleset(*subkey_it
, sub_ruleset
.Pass());
168 return ruleset
.Pass();
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
>();
177 scoped_ptr
<Rule
> rule(new Rule
);
178 if (field
== COUNTRY
) {
179 rule
->CopyFrom(Rule::GetDefault());
181 rule
->ParseJsonRule(*value
);
185 void CountryRulesAggregator::Reset() {
186 country_code_
.clear();
188 rules_ready_
.reset();
190 non_default_languages_
.clear();
193 } // namespace addressinput