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 "validation_task.h"
17 #include <libaddressinput/address_data.h>
18 #include <libaddressinput/address_field.h>
19 #include <libaddressinput/address_metadata.h>
20 #include <libaddressinput/address_problem.h>
21 #include <libaddressinput/address_validator.h>
22 #include <libaddressinput/callback.h>
23 #include <libaddressinput/supplier.h>
24 #include <libaddressinput/util/basictypes.h>
35 #include "lookup_key.h"
36 #include "post_box_matchers.h"
38 #include "util/re2ptr.h"
41 namespace addressinput {
43 ValidationTask::ValidationTask(const AddressData& address,
46 const FieldProblemMap* filter,
47 FieldProblemMap* problems,
48 const AddressValidator::Callback& validated)
50 allow_postal_(allow_postal),
51 require_name_(require_name),
54 validated_(validated),
55 supplied_(BuildCallback(this, &ValidationTask::Validate)),
56 lookup_key_(new LookupKey) {
57 assert(problems_ != NULL);
58 assert(supplied_ != NULL);
59 assert(lookup_key_ != NULL);
62 ValidationTask::~ValidationTask() {
65 void ValidationTask::Run(Supplier* supplier) const {
66 assert(supplier != NULL);
68 lookup_key_->FromAddress(address_);
69 supplier->Supply(*lookup_key_, *supplied_);
72 void ValidationTask::Validate(bool success,
73 const LookupKey& lookup_key,
74 const Supplier::RuleHierarchy& hierarchy) {
75 assert(&lookup_key == lookup_key_.get()); // Sanity check.
78 if (address_.IsFieldEmpty(COUNTRY)) {
79 ReportProblemMaybe(COUNTRY, MISSING_REQUIRED_FIELD);
80 } else if (hierarchy.rule[0] == NULL) {
81 ReportProblemMaybe(COUNTRY, UNKNOWN_VALUE);
83 // Checks which use statically linked metadata.
84 const std::string& region_code = address_.region_code;
85 CheckUnexpectedField(region_code);
86 CheckMissingRequiredField(region_code);
88 // Checks which use data from the metadata server. Note that
89 // CheckPostalCodeFormatAndValue assumes CheckUnexpectedField has already
91 CheckUnknownValue(hierarchy);
92 CheckPostalCodeFormatAndValue(hierarchy);
93 CheckUsesPoBox(hierarchy);
97 validated_(success, address_, *problems_);
101 // A field will return an UNEXPECTED_FIELD problem type if the current value of
102 // that field is not empty and the field should not be used by that region.
103 void ValidationTask::CheckUnexpectedField(
104 const std::string& region_code) const {
105 static const AddressField kFields[] = {
106 // COUNTRY is never unexpected.
117 for (size_t i = 0; i < arraysize(kFields); ++i) {
118 AddressField field = kFields[i];
119 if (!address_.IsFieldEmpty(field) && !IsFieldUsed(field, region_code)) {
120 ReportProblemMaybe(field, UNEXPECTED_FIELD);
125 // A field will return an MISSING_REQUIRED_FIELD problem type if the current
126 // value of that field is empty and the field is required by that region.
127 void ValidationTask::CheckMissingRequiredField(
128 const std::string& region_code) const {
129 static const AddressField kFields[] = {
130 // COUNTRY is assumed to have already been checked.
137 // ORGANIZATION is never required.
138 // RECIPIENT is handled separately.
141 for (size_t i = 0; i < arraysize(kFields); ++i) {
142 AddressField field = kFields[i];
143 if (address_.IsFieldEmpty(field) && IsFieldRequired(field, region_code)) {
144 ReportProblemMaybe(field, MISSING_REQUIRED_FIELD);
148 if (require_name_ && address_.IsFieldEmpty(RECIPIENT)) {
149 ReportProblemMaybe(RECIPIENT, MISSING_REQUIRED_FIELD);
153 // A field is UNKNOWN_VALUE if the metadata contains a list of possible values
154 // for the field and the address data server could not match the current value
155 // of that field to one of those possible values, therefore returning NULL.
156 void ValidationTask::CheckUnknownValue(
157 const Supplier::RuleHierarchy& hierarchy) const {
158 for (size_t depth = 1; depth < arraysize(LookupKey::kHierarchy); ++depth) {
159 AddressField field = LookupKey::kHierarchy[depth];
160 if (!(address_.IsFieldEmpty(field) ||
161 hierarchy.rule[depth - 1] == NULL ||
162 hierarchy.rule[depth - 1]->GetSubKeys().empty() ||
163 hierarchy.rule[depth] != NULL)) {
164 ReportProblemMaybe(field, UNKNOWN_VALUE);
169 // Note that it is assumed that CheckUnexpectedField has already been called.
170 void ValidationTask::CheckPostalCodeFormatAndValue(
171 const Supplier::RuleHierarchy& hierarchy) const {
172 assert(hierarchy.rule[0] != NULL);
173 const Rule& country_rule = *hierarchy.rule[0];
175 if (!(ShouldReport(POSTAL_CODE, INVALID_FORMAT) ||
176 ShouldReport(POSTAL_CODE, MISMATCHING_VALUE))) {
180 if (address_.IsFieldEmpty(POSTAL_CODE)) {
182 } else if (std::find(problems_->begin(), problems_->end(),
183 FieldProblemMap::value_type(POSTAL_CODE,
185 != problems_->end()) {
186 return; // Problem already reported.
189 // Validate general postal code format. A country-level rule specifies the
190 // regular expression for the whole postal code.
191 const RE2ptr* format_ptr = country_rule.GetPostalCodeMatcher();
192 if (format_ptr != NULL &&
193 !RE2::FullMatch(address_.postal_code, *format_ptr->ptr) &&
194 ShouldReport(POSTAL_CODE, INVALID_FORMAT)) {
195 ReportProblem(POSTAL_CODE, INVALID_FORMAT);
199 if (!ShouldReport(POSTAL_CODE, MISMATCHING_VALUE)) {
203 for (size_t depth = arraysize(LookupKey::kHierarchy) - 1;
204 depth > 0; --depth) {
205 if (hierarchy.rule[depth] != NULL) {
206 // Validate sub-region specific postal code format. A sub-region specifies
207 // the regular expression for a prefix of the postal code.
208 const RE2ptr* prefix_ptr = hierarchy.rule[depth]->GetPostalCodeMatcher();
209 if (prefix_ptr != NULL) {
210 if (!RE2::PartialMatch(address_.postal_code, *prefix_ptr->ptr)) {
211 ReportProblem(POSTAL_CODE, MISMATCHING_VALUE);
219 void ValidationTask::CheckUsesPoBox(
220 const Supplier::RuleHierarchy& hierarchy) const {
221 assert(hierarchy.rule[0] != NULL);
222 const Rule& country_rule = *hierarchy.rule[0];
225 !ShouldReport(STREET_ADDRESS, USES_P_O_BOX) ||
226 address_.IsFieldEmpty(STREET_ADDRESS)) {
230 std::vector<const RE2ptr*> matchers =
231 PostBoxMatchers::GetMatchers(country_rule);
232 for (std::vector<std::string>::const_iterator
233 line = address_.address_line.begin();
234 line != address_.address_line.end(); ++line) {
235 for (std::vector<const RE2ptr*>::const_iterator
236 matcher = matchers.begin();
237 matcher != matchers.end(); ++matcher) {
238 if (RE2::PartialMatch(*line, *(*matcher)->ptr)) {
239 ReportProblem(STREET_ADDRESS, USES_P_O_BOX);
246 void ValidationTask::ReportProblem(AddressField field,
247 AddressProblem problem) const {
248 problems_->insert(std::make_pair(field, problem));
251 void ValidationTask::ReportProblemMaybe(AddressField field,
252 AddressProblem problem) const {
253 if (ShouldReport(field, problem)) {
254 ReportProblem(field, problem);
258 bool ValidationTask::ShouldReport(AddressField field,
259 AddressProblem problem) const {
260 return filter_ == NULL || filter_->empty() ||
261 std::find(filter_->begin(),
263 FieldProblemMap::value_type(field, problem)) !=
267 } // namespace addressinput