1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
6
7#include <cmath>
8
9#include "base/bind.h"
10#include "base/location.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop.h"
13#include "third_party/libaddressinput/chromium/addressinput_util.h"
14#include "third_party/libaddressinput/chromium/input_suggester.h"
15#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
16#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_normalizer.h"
17#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
18#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
19
20namespace autofill {
21namespace {
22
23using ::i18n::addressinput::AddressData;
24using ::i18n::addressinput::AddressField;
25using ::i18n::addressinput::AddressNormalizer;
26using ::i18n::addressinput::BuildCallback;
27using ::i18n::addressinput::FieldProblemMap;
28using ::i18n::addressinput::PreloadSupplier;
29using ::i18n::addressinput::Source;
30using ::i18n::addressinput::Storage;
31
32using ::i18n::addressinput::ADMIN_AREA;
33using ::i18n::addressinput::DEPENDENT_LOCALITY;
34using ::i18n::addressinput::POSTAL_CODE;
35
36// The maximum number attempts to load rules.
37static const int kMaxAttemptsNumber = 8;
38
39}  // namespace
40
41AddressValidator::AddressValidator(scoped_ptr<Source> source,
42                                   scoped_ptr<Storage> storage,
43                                   LoadRulesListener* load_rules_listener)
44    : supplier_(new PreloadSupplier(source.release(),
45                                    storage.release())),
46      input_suggester_(new InputSuggester(supplier_.get())),
47      normalizer_(new AddressNormalizer(supplier_.get())),
48      validator_(new ::i18n::addressinput::AddressValidator(supplier_.get())),
49      validated_(BuildCallback(this, &AddressValidator::Validated)),
50      rules_loaded_(BuildCallback(this, &AddressValidator::RulesLoaded)),
51      load_rules_listener_(load_rules_listener),
52      weak_factory_(this) {}
53
54AddressValidator::~AddressValidator() {}
55
56void AddressValidator::LoadRules(const std::string& region_code) {
57  attempts_number_[region_code] = 0;
58  supplier_->LoadRules(region_code, *rules_loaded_);
59}
60
61AddressValidator::Status AddressValidator::ValidateAddress(
62    const AddressData& address,
63    const FieldProblemMap* filter,
64    FieldProblemMap* problems) const {
65  if (supplier_->IsPending(address.region_code)) {
66    if (problems)
67      addressinput::ValidateRequiredFields(address, filter, problems);
68    return RULES_NOT_READY;
69  }
70
71  if (!supplier_->IsLoaded(address.region_code)) {
72    if (problems)
73      addressinput::ValidateRequiredFields(address, filter, problems);
74    return RULES_UNAVAILABLE;
75  }
76
77  if (!problems)
78    return SUCCESS;
79
80  validator_->Validate(address,
81                       true,  // Allow postal office boxes.
82                       true,  // Require recipient name.
83                       filter,
84                       problems,
85                       *validated_);
86
87  return SUCCESS;
88}
89
90AddressValidator::Status AddressValidator::GetSuggestions(
91    const AddressData& user_input,
92    AddressField focused_field,
93    size_t suggestion_limit,
94    std::vector<AddressData>* suggestions) const {
95  if (supplier_->IsPending(user_input.region_code))
96    return RULES_NOT_READY;
97
98  if (!supplier_->IsLoaded(user_input.region_code))
99    return RULES_UNAVAILABLE;
100
101  if (!suggestions)
102    return SUCCESS;
103
104  suggestions->clear();
105
106  if (focused_field == POSTAL_CODE ||
107      (focused_field >= ADMIN_AREA && focused_field <= DEPENDENT_LOCALITY)) {
108    input_suggester_->GetSuggestions(
109        user_input, focused_field, suggestion_limit, suggestions);
110  }
111
112  return SUCCESS;
113}
114
115bool AddressValidator::CanonicalizeAdministrativeArea(
116    AddressData* address) const {
117  if (!supplier_->IsLoaded(address->region_code))
118    return false;
119
120  // TODO: It would probably be beneficial to use the full canonicalization.
121  AddressData tmp(*address);
122  normalizer_->Normalize(&tmp);
123  address->administrative_area = tmp.administrative_area;
124
125  return true;
126}
127
128AddressValidator::AddressValidator()
129    : load_rules_listener_(NULL), weak_factory_(this) {}
130
131base::TimeDelta AddressValidator::GetBaseRetryPeriod() const {
132  return base::TimeDelta::FromSeconds(8);
133}
134
135void AddressValidator::Validated(bool success,
136                                 const AddressData&,
137                                 const FieldProblemMap&) {
138  DCHECK(success);
139}
140
141void AddressValidator::RulesLoaded(bool success,
142                                   const std::string& region_code,
143                                   int) {
144  if (load_rules_listener_)
145    load_rules_listener_->OnAddressValidationRulesLoaded(region_code, success);
146
147  // Count the first failed attempt to load rules as well.
148  if (success || attempts_number_[region_code] + 1 >= kMaxAttemptsNumber)
149    return;
150
151  base::MessageLoop::current()->PostDelayedTask(
152      FROM_HERE,
153      base::Bind(&AddressValidator::RetryLoadRules,
154                 weak_factory_.GetWeakPtr(),
155                 region_code),
156      GetBaseRetryPeriod() * pow(2, attempts_number_[region_code]++));
157}
158
159void AddressValidator::RetryLoadRules(const std::string& region_code) {
160  // Do not reset retry count.
161  supplier_->LoadRules(region_code, *rules_loaded_);
162}
163
164}  // namespace autofill
165