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 "validation_task.h"
16
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>
25
26#include <algorithm>
27#include <cassert>
28#include <cstddef>
29#include <string>
30#include <utility>
31#include <vector>
32
33#include <re2/re2.h>
34
35#include "lookup_key.h"
36#include "post_box_matchers.h"
37#include "rule.h"
38#include "util/re2ptr.h"
39
40namespace i18n {
41namespace addressinput {
42
43ValidationTask::ValidationTask(const AddressData& address,
44                               bool allow_postal,
45                               bool require_name,
46                               const FieldProblemMap* filter,
47                               FieldProblemMap* problems,
48                               const AddressValidator::Callback& validated)
49    : address_(address),
50      allow_postal_(allow_postal),
51      require_name_(require_name),
52      filter_(filter),
53      problems_(problems),
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);
60}
61
62ValidationTask::~ValidationTask() {
63}
64
65void ValidationTask::Run(Supplier* supplier) const {
66  assert(supplier != NULL);
67  problems_->clear();
68  lookup_key_->FromAddress(address_);
69  supplier->Supply(*lookup_key_, *supplied_);
70}
71
72void ValidationTask::Validate(bool success,
73                              const LookupKey& lookup_key,
74                              const Supplier::RuleHierarchy& hierarchy) {
75  assert(&lookup_key == lookup_key_.get());  // Sanity check.
76
77  if (success) {
78    if (address_.IsFieldEmpty(COUNTRY)) {
79      ReportProblemMaybe(COUNTRY, MISSING_REQUIRED_FIELD);
80    } else if (hierarchy.rule[0] == NULL) {
81      ReportProblemMaybe(COUNTRY, UNKNOWN_VALUE);
82    } else {
83      // Checks which use statically linked metadata.
84      const std::string& region_code = address_.region_code;
85      CheckUnexpectedField(region_code);
86      CheckMissingRequiredField(region_code);
87
88      // Checks which use data from the metadata server. Note that
89      // CheckPostalCodeFormatAndValue assumes CheckUnexpectedField has already
90      // been called.
91      CheckUnknownValue(hierarchy);
92      CheckPostalCodeFormatAndValue(hierarchy);
93      CheckUsesPoBox(hierarchy);
94    }
95  }
96
97  validated_(success, address_, *problems_);
98  delete this;
99}
100
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.
103void ValidationTask::CheckUnexpectedField(
104    const std::string& region_code) const {
105  static const AddressField kFields[] = {
106    // COUNTRY is never unexpected.
107    ADMIN_AREA,
108    LOCALITY,
109    DEPENDENT_LOCALITY,
110    SORTING_CODE,
111    POSTAL_CODE,
112    STREET_ADDRESS,
113    ORGANIZATION,
114    RECIPIENT
115  };
116
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);
121    }
122  }
123}
124
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.
127void ValidationTask::CheckMissingRequiredField(
128    const std::string& region_code) const {
129  static const AddressField kFields[] = {
130    // COUNTRY is assumed to have already been checked.
131    ADMIN_AREA,
132    LOCALITY,
133    DEPENDENT_LOCALITY,
134    SORTING_CODE,
135    POSTAL_CODE,
136    STREET_ADDRESS
137    // ORGANIZATION is never required.
138    // RECIPIENT is handled separately.
139  };
140
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);
145    }
146  }
147
148  if (require_name_ && address_.IsFieldEmpty(RECIPIENT)) {
149    ReportProblemMaybe(RECIPIENT, MISSING_REQUIRED_FIELD);
150  }
151}
152
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.
156void 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);
165    }
166  }
167}
168
169// Note that it is assumed that CheckUnexpectedField has already been called.
170void ValidationTask::CheckPostalCodeFormatAndValue(
171    const Supplier::RuleHierarchy& hierarchy) const {
172  assert(hierarchy.rule[0] != NULL);
173  const Rule& country_rule = *hierarchy.rule[0];
174
175  if (!(ShouldReport(POSTAL_CODE, INVALID_FORMAT) ||
176        ShouldReport(POSTAL_CODE, MISMATCHING_VALUE))) {
177    return;
178  }
179
180  if (address_.IsFieldEmpty(POSTAL_CODE)) {
181    return;
182  } else if (std::find(problems_->begin(), problems_->end(),
183                       FieldProblemMap::value_type(POSTAL_CODE,
184                                                   UNEXPECTED_FIELD))
185             != problems_->end()) {
186    return;  // Problem already reported.
187  }
188
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);
196    return;
197  }
198
199  if (!ShouldReport(POSTAL_CODE, MISMATCHING_VALUE)) {
200    return;
201  }
202
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);
212        }
213        return;
214      }
215    }
216  }
217}
218
219void ValidationTask::CheckUsesPoBox(
220    const Supplier::RuleHierarchy& hierarchy) const {
221  assert(hierarchy.rule[0] != NULL);
222  const Rule& country_rule = *hierarchy.rule[0];
223
224  if (allow_postal_ ||
225      !ShouldReport(STREET_ADDRESS, USES_P_O_BOX) ||
226      address_.IsFieldEmpty(STREET_ADDRESS)) {
227    return;
228  }
229
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);
240        return;
241      }
242    }
243  }
244}
245
246void ValidationTask::ReportProblem(AddressField field,
247                                   AddressProblem problem) const {
248  problems_->insert(std::make_pair(field, problem));
249}
250
251void ValidationTask::ReportProblemMaybe(AddressField field,
252                                        AddressProblem problem) const {
253  if (ShouldReport(field, problem)) {
254    ReportProblem(field, problem);
255  }
256}
257
258bool ValidationTask::ShouldReport(AddressField field,
259                                  AddressProblem problem) const {
260  return filter_ == NULL || filter_->empty() ||
261         std::find(filter_->begin(),
262                   filter_->end(),
263                   FieldProblemMap::value_type(field, problem)) !=
264             filter_->end();
265}
266
267}  // namespace addressinput
268}  // namespace i18n
269