// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/autofill/address.h"
#include <stddef.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome/browser/autofill/autofill_country.h"
#include "chrome/browser/autofill/autofill_type.h"
#include "chrome/browser/autofill/field_types.h"
namespace {
const char16 kAddressSplitChars[] = {'-', ',', '#', '.', ' ', 0};
const AutofillType::FieldTypeSubGroup kAutofillAddressTypes[] = {
AutofillType::ADDRESS_LINE1,
AutofillType::ADDRESS_LINE2,
AutofillType::ADDRESS_CITY,
AutofillType::ADDRESS_STATE,
AutofillType::ADDRESS_ZIP,
AutofillType::ADDRESS_COUNTRY,
};
const int kAutofillAddressLength = arraysize(kAutofillAddressTypes);
} // namespace
Address::Address() {}
Address::Address(const Address& address) : FormGroup() {
*this = address;
}
Address::~Address() {}
Address& Address::operator=(const Address& address) {
if (this == &address)
return *this;
line1_tokens_ = address.line1_tokens_;
line2_tokens_= address.line2_tokens_;
line1_ = address.line1_;
line2_ = address.line2_;
city_ = address.city_;
state_ = address.state_;
country_code_ = address.country_code_;
zip_code_ = address.zip_code_;
return *this;
}
void Address::GetPossibleFieldTypes(const string16& text,
FieldTypeSet* possible_types) const {
DCHECK(possible_types);
// If the text to match against the field types is empty, then no results will
// match.
if (text.empty())
return;
if (IsLine1(text))
possible_types->insert(ADDRESS_HOME_LINE1);
if (IsLine2(text))
possible_types->insert(ADDRESS_HOME_LINE2);
if (IsCity(text))
possible_types->insert(ADDRESS_HOME_CITY);
if (IsState(text))
possible_types->insert(ADDRESS_HOME_STATE);
if (IsZipCode(text))
possible_types->insert(ADDRESS_HOME_ZIP);
if (IsCountry(text))
possible_types->insert(ADDRESS_HOME_COUNTRY);
}
void Address::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
DCHECK(available_types);
if (!line1_.empty())
available_types->insert(ADDRESS_HOME_LINE1);
if (!line2_.empty())
available_types->insert(ADDRESS_HOME_LINE2);
if (!city_.empty())
available_types->insert(ADDRESS_HOME_CITY);
if (!state_.empty())
available_types->insert(ADDRESS_HOME_STATE);
if (!zip_code_.empty())
available_types->insert(ADDRESS_HOME_ZIP);
if (!country_code_.empty())
available_types->insert(ADDRESS_HOME_COUNTRY);
}
string16 Address::GetInfo(AutofillFieldType type) const {
if (type == ADDRESS_HOME_LINE1)
return line1_;
if (type == ADDRESS_HOME_LINE2)
return line2_;
if (type == ADDRESS_HOME_CITY)
return city_;
if (type == ADDRESS_HOME_STATE)
return state_;
if (type == ADDRESS_HOME_ZIP)
return zip_code_;
if (type == ADDRESS_HOME_COUNTRY)
return Country();
return string16();
}
void Address::SetInfo(AutofillFieldType type, const string16& value) {
FieldTypeSubGroup subgroup = AutofillType(type).subgroup();
if (subgroup == AutofillType::ADDRESS_LINE1)
set_line1(value);
else if (subgroup == AutofillType::ADDRESS_LINE2)
set_line2(value);
else if (subgroup == AutofillType::ADDRESS_CITY)
city_ = value;
else if (subgroup == AutofillType::ADDRESS_STATE)
state_ = value;
else if (subgroup == AutofillType::ADDRESS_COUNTRY)
SetCountry(value);
else if (subgroup == AutofillType::ADDRESS_ZIP)
zip_code_ = value;
else
NOTREACHED();
}
void Address::Clear() {
line1_tokens_.clear();
line1_.clear();
line2_tokens_.clear();
line2_.clear();
city_.clear();
state_.clear();
country_code_.clear();
zip_code_.clear();
}
string16 Address::Country() const {
if (country_code().empty())
return string16();
std::string app_locale = AutofillCountry::ApplicationLocale();
return AutofillCountry(country_code(), app_locale).name();
}
void Address::set_line1(const string16& line1) {
line1_ = line1;
line1_tokens_.clear();
Tokenize(line1, kAddressSplitChars, &line1_tokens_);
LineTokens::iterator iter;
for (iter = line1_tokens_.begin(); iter != line1_tokens_.end(); ++iter)
*iter = StringToLowerASCII(*iter);
}
void Address::set_line2(const string16& line2) {
line2_ = line2;
line2_tokens_.clear();
Tokenize(line2, kAddressSplitChars, &line2_tokens_);
LineTokens::iterator iter;
for (iter = line2_tokens_.begin(); iter != line2_tokens_.end(); ++iter)
*iter = StringToLowerASCII(*iter);
}
void Address::SetCountry(const string16& country) {
std::string app_locale = AutofillCountry::ApplicationLocale();
country_code_ = AutofillCountry::GetCountryCode(country, app_locale);
}
bool Address::IsLine1(const string16& text) const {
return IsLineMatch(text, line1_tokens_);
}
bool Address::IsLine2(const string16& text) const {
return IsLineMatch(text, line2_tokens_);
}
bool Address::IsCity(const string16& text) const {
return (StringToLowerASCII(city_) == StringToLowerASCII(text));
}
bool Address::IsState(const string16& text) const {
return (StringToLowerASCII(state_) == StringToLowerASCII(text));
}
bool Address::IsCountry(const string16& text) const {
std::string app_locale = AutofillCountry::ApplicationLocale();
std::string country_code = AutofillCountry::GetCountryCode(text, app_locale);
return (!country_code.empty() && country_code_ == country_code);
}
bool Address::IsZipCode(const string16& text) const {
return zip_code_ == text;
}
bool Address::IsLineMatch(const string16& text,
const LineTokens& line_tokens) const {
size_t line_tokens_size = line_tokens.size();
if (line_tokens_size == 0)
return false;
LineTokens text_tokens;
Tokenize(text, kAddressSplitChars, &text_tokens);
size_t text_tokens_size = text_tokens.size();
if (text_tokens_size == 0)
return false;
if (text_tokens_size > line_tokens_size)
return false;
// If each of the 'words' contained in the text are also present in the line,
// then we will consider the text to match the line.
LineTokens::iterator iter;
for (iter = text_tokens.begin(); iter != text_tokens.end(); ++iter) {
if (!IsWordInLine(*iter, line_tokens))
return false;
}
return true;
}
bool Address::IsWordInLine(const string16& word,
const LineTokens& line_tokens) const {
LineTokens::const_iterator iter;
for (iter = line_tokens.begin(); iter != line_tokens.end(); ++iter) {
if (StringToLowerASCII(word) == *iter)
return true;
}
return false;
}