Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 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 "components/autofill/core/browser/contact_info.h"
      6 
      7 #include <stddef.h>
      8 #include <ostream>
      9 #include <string>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/logging.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "components/autofill/core/browser/autofill_type.h"
     16 
     17 namespace autofill {
     18 
     19 namespace {
     20 
     21 const char* const name_prefixes[] = {
     22     "1lt", "1st", "2lt", "2nd", "3rd", "admiral", "capt", "captain", "col",
     23     "cpt", "dr", "gen", "general", "lcdr", "lt", "ltc", "ltg", "ltjg", "maj",
     24     "major", "mg", "mr", "mrs", "ms", "pastor", "prof", "rep", "reverend",
     25     "rev", "sen", "st" };
     26 
     27 const char* const name_suffixes[] = {
     28     "b.a", "ba", "d.d.s", "dds", "i", "ii", "iii", "iv", "ix", "jr", "m.a",
     29     "m.d", "ma", "md", "ms", "ph.d", "phd", "sr", "v", "vi", "vii", "viii",
     30     "x" };
     31 
     32 const char* const family_name_prefixes[] = {
     33     "d'", "de", "del", "der", "di", "la", "le", "mc", "san", "st", "ter",
     34     "van", "von" };
     35 
     36 // Returns true if |set| contains |element|, modulo a final period.
     37 bool ContainsString(const char* const set[],
     38                     size_t set_size,
     39                     const base::string16& element) {
     40   if (!base::IsStringASCII(element))
     41     return false;
     42 
     43   base::string16 trimmed_element;
     44   base::TrimString(element, base::ASCIIToUTF16("."), &trimmed_element);
     45 
     46   for (size_t i = 0; i < set_size; ++i) {
     47     if (LowerCaseEqualsASCII(trimmed_element, set[i]))
     48       return true;
     49   }
     50 
     51   return false;
     52 }
     53 
     54 // Removes common name prefixes from |name_tokens|.
     55 void StripPrefixes(std::vector<base::string16>* name_tokens) {
     56   std::vector<base::string16>::iterator iter = name_tokens->begin();
     57   while(iter != name_tokens->end()) {
     58     if (!ContainsString(name_prefixes, arraysize(name_prefixes), *iter))
     59       break;
     60     ++iter;
     61   }
     62 
     63   std::vector<base::string16> copy_vector;
     64   copy_vector.assign(iter, name_tokens->end());
     65   *name_tokens = copy_vector;
     66 }
     67 
     68 // Removes common name suffixes from |name_tokens|.
     69 void StripSuffixes(std::vector<base::string16>* name_tokens) {
     70   while(!name_tokens->empty()) {
     71     if (!ContainsString(name_suffixes, arraysize(name_suffixes),
     72                         name_tokens->back())) {
     73       break;
     74     }
     75     name_tokens->pop_back();
     76   }
     77 }
     78 
     79 struct NameParts {
     80   base::string16 given;
     81   base::string16 middle;
     82   base::string16 family;
     83 };
     84 
     85 // TODO(estade): This does Western name splitting. It should do different
     86 // splitting based on the app locale.
     87 NameParts SplitName(const base::string16& name) {
     88   std::vector<base::string16> name_tokens;
     89   Tokenize(name, base::ASCIIToUTF16(" ,"), &name_tokens);
     90 
     91   StripPrefixes(&name_tokens);
     92 
     93   // Don't assume "Ma" is a suffix in John Ma.
     94   if (name_tokens.size() > 2)
     95     StripSuffixes(&name_tokens);
     96 
     97   NameParts parts;
     98 
     99   if (name_tokens.empty()) {
    100     // Bad things have happened; just assume the whole thing is a given name.
    101     parts.given = name;
    102     return parts;
    103   }
    104 
    105   // Only one token, assume given name.
    106   if (name_tokens.size() == 1) {
    107     parts.given = name_tokens[0];
    108     return parts;
    109   }
    110 
    111   // 2 or more tokens. Grab the family, which is the last word plus any
    112   // recognizable family prefixes.
    113   std::vector<base::string16> reverse_family_tokens;
    114   reverse_family_tokens.push_back(name_tokens.back());
    115   name_tokens.pop_back();
    116   while (name_tokens.size() >= 1 &&
    117          ContainsString(family_name_prefixes,
    118                         arraysize(family_name_prefixes),
    119                         name_tokens.back())) {
    120     reverse_family_tokens.push_back(name_tokens.back());
    121     name_tokens.pop_back();
    122   }
    123 
    124   std::vector<base::string16> family_tokens(reverse_family_tokens.rbegin(),
    125                                             reverse_family_tokens.rend());
    126   parts.family = JoinString(family_tokens, base::char16(' '));
    127 
    128   // Take the last remaining token as the middle name (if there are at least 2
    129   // tokens).
    130   if (name_tokens.size() >= 2) {
    131     parts.middle = name_tokens.back();
    132     name_tokens.pop_back();
    133   }
    134 
    135   // Remainder is given name.
    136   parts.given = JoinString(name_tokens, base::char16(' '));
    137 
    138   return parts;
    139 }
    140 
    141 }  // namespace
    142 
    143 NameInfo::NameInfo() {}
    144 
    145 NameInfo::NameInfo(const NameInfo& info) : FormGroup() {
    146   *this = info;
    147 }
    148 
    149 NameInfo::~NameInfo() {}
    150 
    151 NameInfo& NameInfo::operator=(const NameInfo& info) {
    152   if (this == &info)
    153     return *this;
    154 
    155   given_ = info.given_;
    156   middle_ = info.middle_;
    157   family_ = info.family_;
    158   full_ = info.full_;
    159   return *this;
    160 }
    161 
    162 bool NameInfo::EqualsIgnoreCase(const NameInfo& info) {
    163   return (StringToLowerASCII(given_) == StringToLowerASCII(info.given_) &&
    164           StringToLowerASCII(middle_) == StringToLowerASCII(info.middle_) &&
    165           StringToLowerASCII(family_) == StringToLowerASCII(info.family_));
    166 }
    167 
    168 void NameInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const {
    169   supported_types->insert(NAME_FIRST);
    170   supported_types->insert(NAME_MIDDLE);
    171   supported_types->insert(NAME_LAST);
    172   supported_types->insert(NAME_MIDDLE_INITIAL);
    173   supported_types->insert(NAME_FULL);
    174 }
    175 
    176 base::string16 NameInfo::GetRawInfo(ServerFieldType type) const {
    177   DCHECK_EQ(NAME, AutofillType(type).group());
    178   switch (type) {
    179     case NAME_FIRST:
    180       return given_;
    181 
    182     case NAME_MIDDLE:
    183       return middle_;
    184 
    185     case NAME_LAST:
    186       return family_;
    187 
    188     case NAME_MIDDLE_INITIAL:
    189       return MiddleInitial();
    190 
    191     case NAME_FULL:
    192       return FullName();
    193 
    194     default:
    195       return base::string16();
    196   }
    197 }
    198 
    199 void NameInfo::SetRawInfo(ServerFieldType type, const base::string16& value) {
    200   DCHECK_EQ(NAME, AutofillType(type).group());
    201 
    202   // Always clear out the full name if we're making a change.
    203   if (value != GetRawInfo(type))
    204     full_.clear();
    205 
    206   switch (type) {
    207     case NAME_FIRST:
    208       given_ = value;
    209       break;
    210 
    211     case NAME_MIDDLE:
    212     case NAME_MIDDLE_INITIAL:
    213       middle_ = value;
    214       break;
    215 
    216     case NAME_LAST:
    217       family_ = value;
    218       break;
    219 
    220     case NAME_FULL:
    221       // TODO(estade): this should just set |full_|; only SetInfo should attempt
    222       // to be smart. http://crbug.com/384640
    223       SetFullName(value);
    224       break;
    225 
    226     default:
    227       NOTREACHED();
    228   }
    229 }
    230 
    231 base::string16 NameInfo::FullName() const {
    232   if (!full_.empty())
    233     return full_;
    234 
    235   std::vector<base::string16> full_name;
    236   if (!given_.empty())
    237     full_name.push_back(given_);
    238 
    239   if (!middle_.empty())
    240     full_name.push_back(middle_);
    241 
    242   if (!family_.empty())
    243     full_name.push_back(family_);
    244 
    245   return JoinString(full_name, ' ');
    246 }
    247 
    248 base::string16 NameInfo::MiddleInitial() const {
    249   if (middle_.empty())
    250     return base::string16();
    251 
    252   base::string16 middle_name(middle_);
    253   base::string16 initial;
    254   initial.push_back(middle_name[0]);
    255   return initial;
    256 }
    257 
    258 void NameInfo::SetFullName(const base::string16& full) {
    259   // Hack: don't do anything if this wouldn't change the full, concatenated
    260   // name. Otherwise when unpickling data from the database, "First|Middle|"
    261   // will get parsed as "First||Middle".
    262   // TODO(estade): we should be able to remove this when fixing the TODO in
    263   // SetRawInfo. http://crbug.com/384640
    264   if (FullName() == full)
    265     return;
    266 
    267   full_ = full;
    268 
    269   // If |full| is empty, leave the other name parts alone. This might occur
    270   // due to a migrated database with an empty |full_name| value.
    271   if (full.empty())
    272     return;
    273 
    274   NameParts parts = SplitName(full);
    275   given_ = parts.given;
    276   middle_ = parts.middle;
    277   family_ = parts.family;
    278 }
    279 
    280 EmailInfo::EmailInfo() {}
    281 
    282 EmailInfo::EmailInfo(const EmailInfo& info) : FormGroup() {
    283   *this = info;
    284 }
    285 
    286 EmailInfo::~EmailInfo() {}
    287 
    288 EmailInfo& EmailInfo::operator=(const EmailInfo& info) {
    289   if (this == &info)
    290     return *this;
    291 
    292   email_ = info.email_;
    293   return *this;
    294 }
    295 
    296 void EmailInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const {
    297   supported_types->insert(EMAIL_ADDRESS);
    298 }
    299 
    300 base::string16 EmailInfo::GetRawInfo(ServerFieldType type) const {
    301   if (type == EMAIL_ADDRESS)
    302     return email_;
    303 
    304   return base::string16();
    305 }
    306 
    307 void EmailInfo::SetRawInfo(ServerFieldType type, const base::string16& value) {
    308   DCHECK_EQ(EMAIL_ADDRESS, type);
    309   email_ = value;
    310 }
    311 
    312 CompanyInfo::CompanyInfo() {}
    313 
    314 CompanyInfo::CompanyInfo(const CompanyInfo& info) : FormGroup() {
    315   *this = info;
    316 }
    317 
    318 CompanyInfo::~CompanyInfo() {}
    319 
    320 CompanyInfo& CompanyInfo::operator=(const CompanyInfo& info) {
    321   if (this == &info)
    322     return *this;
    323 
    324   company_name_ = info.company_name_;
    325   return *this;
    326 }
    327 
    328 void CompanyInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const {
    329   supported_types->insert(COMPANY_NAME);
    330 }
    331 
    332 base::string16 CompanyInfo::GetRawInfo(ServerFieldType type) const {
    333   if (type == COMPANY_NAME)
    334     return company_name_;
    335 
    336   return base::string16();
    337 }
    338 
    339 void CompanyInfo::SetRawInfo(ServerFieldType type,
    340                              const base::string16& value) {
    341   DCHECK_EQ(COMPANY_NAME, type);
    342   company_name_ = value;
    343 }
    344 
    345 }  // namespace autofill
    346