Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: third_party/libaddressinput/chromium/suggestions.cc

Issue 298863012: Use upstream libaddressinput in Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merged in https://codereview.chromium.org/300303002/ Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/libaddressinput/chromium/suggestions.cc
diff --git a/third_party/libaddressinput/chromium/suggestions.cc b/third_party/libaddressinput/chromium/suggestions.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7005e701d9bd06b590623c031f9d1fd77ff09db8
--- /dev/null
+++ b/third_party/libaddressinput/chromium/suggestions.cc
@@ -0,0 +1,435 @@
+// Copyright 2014 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 "third_party/libaddressinput/chromium/suggestions.h"
+
+// Use "base/memory/scoped_ptr.h" instead.
+#define I18N_ADDRESSINPUT_UTIL_SCOPED_PTR_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "third_party/icu/source/common/unicode/errorcode.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/preload_supplier.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/region_data.h"
+
+namespace autofill {
+
+using ::i18n::addressinput::ADMIN_AREA;
+using ::i18n::addressinput::DEPENDENT_LOCALITY;
+using ::i18n::addressinput::RegionData;
+
+typedef std::set<const RegionData*> RegionContainer;
+
+namespace {
+
+// Collects regions based on whether they have a parent in the given list.
+class ParentedRegionCollector {
+ public:
+ // Retains a reference to both of the parameters. Does not make a copy of
+ // |parent_regions|. Does not take ownership of |regions_with_parents|. The
+ // |regions_with_parents| parameter should not be NULL.
+ ParentedRegionCollector(const RegionContainer& parent_regions,
+ RegionContainer* regions_with_parents)
+ : parent_regions_(parent_regions),
+ regions_with_parents_(regions_with_parents) {
+ DCHECK(regions_with_parents_);
+ }
+
+ ~ParentedRegionCollector() {}
+
+ // Adds |region_to_test| to the |regions_with_parents_| collection, if the
+ // given region has a parent in |parent_regions_|. The |region_to_test|
+ // parameter should not be NULL.
+ void operator()(const RegionData* region_to_test) {
+ DCHECK(region_to_test);
+ if (parent_regions_.find(&region_to_test->parent()) !=
+ parent_regions_.end()) {
+ regions_with_parents_->insert(region_to_test);
+ }
+ }
+
+ private:
+ const RegionContainer parent_regions_;
+ RegionContainer* regions_with_parents_;
+};
+
+} // namespace
+
+class Suggestions::CanonicalizerImpl {
+ public:
+ CanonicalizerImpl() {
+ UErrorCode error_code = U_ZERO_ERROR;
+ collator_.reset(
+ icu::Collator::createInstance(icu::Locale::getRoot(), error_code));
+ DCHECK(U_SUCCESS(error_code));
+ collator_->setStrength(icu::Collator::PRIMARY);
+ }
+
+ ~CanonicalizerImpl() {}
+
+ // Returns a canonical version of the string that can be used for comparing
+ // strings regardless of diacritics and capitalization.
+ // CanonicalizeString("Texas") == CanonicalizeString("T\u00E9xas");
+ // CanonicalizeString("Texas") == CanonicalizeString("teXas");
+ // CanonicalizeString("Texas") != CanonicalizeString("California");
+ //
+ // The output is not human-readable.
+ // CanonicalizeString("Texas") != "Texas";
+ std::string CanonicalizeString(const std::string& original) {
+ icu::UnicodeString icu_str(
+ original.c_str(), static_cast<int32_t>(original.length()));
+ int32_t buffer_size = collator_->getSortKey(icu_str, NULL, 0);
+ scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
+ DCHECK(buffer.get());
+ int32_t filled_size =
+ collator_->getSortKey(icu_str, buffer.get(), buffer_size);
+ DCHECK_EQ(buffer_size, filled_size);
+ return std::string(reinterpret_cast<const char*>(buffer.get()));
+ }
+
+ private:
+ scoped_ptr<icu::Collator> collator_;
+
+ DISALLOW_COPY_AND_ASSIGN(CanonicalizerImpl);
+};
+
+Suggestions::Suggestions(const PreloadSupplier* supplier)
+ : supplier_(supplier),
+ canonicalizer_(new CanonicalizerImpl) {}
+
+Suggestions::~Suggestions() {
+ // Delete the maps and trie objects owned by |tries_| field.
+ for (RegionCodeMap::const_iterator region_it = tries_.begin();
+ region_it != tries_.end(); ++region_it) {
+ LanguageTagMap* lang_map = region_it->second;
+ DCHECK(lang_map);
+
+ for (LanguageTagMap::const_iterator lang_it = lang_map->begin();
+ lang_it != lang_map->end(); ++lang_it) {
+ AddressFieldMap* field_map = lang_it->second;
+ DCHECK(field_map);
+
+ for (AddressFieldMap::const_iterator field_it = field_map->begin();
+ field_it != field_map->end(); ++field_it) {
+ RegionIdMap* id_map = field_it->second;
+ DCHECK(id_map);
+
+ for (RegionIdMap::const_iterator id_it = id_map->begin();
+ id_it != id_map->end(); ++id_it) {
+ // The tries do not own the region objects.
+ Trie<const RegionData*>* trie = id_it->second;
+ delete trie;
+ }
+ delete id_map;
+ }
+ delete field_map;
+ }
+ delete lang_map;
+ }
+}
+
+void Suggestions::GetSuggestions(const AddressData& user_input,
+ AddressField focused_field,
+ size_t suggestions_limit,
+ std::vector<AddressData>* suggestions) {
+ /*
+ !!!TODO: IMPL!!!
+ DCHECK(suggestions);
+
+ std::map<std::string, Ruleset*>::const_iterator ruleset_it =
+ rules_.find(user_input.region_code);
+
+ if (ruleset_it == rules_.end()) {
+ return
+ loading_rules_.find(user_input.region_code) != loading_rules_.end()
+ ? RULES_NOT_READY
+ : RULES_UNAVAILABLE;
+ }
+
+ if (suggestions == NULL) {
+ return SUCCESS;
+ }
+ suggestions->clear();
+
+ assert(ruleset_it->second != NULL);
+
+ // Do not suggest anything if the user is typing in the field for which
+ // there's no validation data.
+ if (focused_field != POSTAL_CODE &&
+ (focused_field < ADMIN_AREA || focused_field > DEPENDENT_LOCALITY)) {
+ return SUCCESS;
+ }
+
+ // Do not suggest anything if the user input is empty.
+ if (user_input.GetFieldValue(focused_field).empty()) {
+ return SUCCESS;
+ }
+
+ const Ruleset& country_ruleset = *ruleset_it->second;
+ const Rule& country_rule =
+ country_ruleset.GetLanguageCodeRule(user_input.language_code);
+
+ // Do not suggest anything if the user is typing the postal code that is not
+ // valid for the country.
+ if (!user_input.postal_code.empty() &&
+ focused_field == POSTAL_CODE &&
+ !country_rule.GetPostalCodeFormat().empty() &&
+ !ValueMatchesPrefixRegex(
+ user_input.postal_code, country_rule.GetPostalCodeFormat())) {
+ return SUCCESS;
+ }
+
+ // Initialize the prefix search index lazily.
+ if (!ruleset_it->second->prefix_search_index_ready()) {
+ ruleset_it->second->BuildPrefixSearchIndex();
+ }
+
+ if (focused_field != POSTAL_CODE &&
+ focused_field > country_ruleset.deepest_ruleset_level()) {
+ return SUCCESS;
+ }
+
+ // Determine the most specific address field that can be suggested.
+ AddressField suggestion_field = focused_field != POSTAL_CODE
+ ? focused_field : DEPENDENT_LOCALITY;
+ if (suggestion_field > country_ruleset.deepest_ruleset_level()) {
+ suggestion_field = country_ruleset.deepest_ruleset_level();
+ }
+ if (focused_field != POSTAL_CODE) {
+ while (user_input.GetFieldValue(suggestion_field).empty() &&
+ suggestion_field > ADMIN_AREA) {
+ suggestion_field = static_cast<AddressField>(suggestion_field - 1);
+ }
+ }
+
+ // Find all rulesets that match user input.
+ AddressFieldRulesets rulesets;
+ for (int i = ADMIN_AREA; i <= suggestion_field; ++i) {
+ for (int j = Rule::KEY; j <= Rule::LATIN_NAME; ++j) {
+ AddressField address_field = static_cast<AddressField>(i);
+ Rule::IdentityField rule_field = static_cast<Rule::IdentityField>(j);
+
+ // Find all rulesets at |address_field| level whose |rule_field| starts
+ // with user input value.
+ country_ruleset.FindRulesetsByPrefix(
+ user_input.language_code, address_field, rule_field,
+ user_input.GetFieldValue(address_field),
+ &rulesets[address_field][rule_field]);
+
+ // Filter out the rulesets whose parents do not match the user input.
+ if (address_field > ADMIN_AREA) {
+ AddressField parent_field =
+ static_cast<AddressField>(address_field - 1);
+ Rulesets rulesets_with_parents;
+ std::for_each(
+ rulesets[address_field][rule_field].begin(),
+ rulesets[address_field][rule_field].end(),
+ ParentedRulesetCollector(rulesets[parent_field][rule_field],
+ &rulesets_with_parents));
+ rulesets[address_field][rule_field].swap(rulesets_with_parents);
+ }
+ }
+ }
+
+ // Determine the fields in the rules that match the user input. This
+ // operation converts a map of Rule::IdentityField value -> Ruleset into a
+ // map of Ruleset -> Rule::IdentityField bitset.
+ std::map<const Ruleset*, MatchingRuleFields> suggestion_rulesets;
+ for (IdentityFieldRulesets::const_iterator rule_field_it =
+ rulesets[suggestion_field].begin();
+ rule_field_it != rulesets[suggestion_field].end();
+ ++rule_field_it) {
+ const Rule::IdentityField rule_identity_field = rule_field_it->first;
+ for (Rulesets::const_iterator ruleset_it = rule_field_it->second.begin();
+ ruleset_it != rule_field_it->second.end();
+ ++ruleset_it) {
+ suggestion_rulesets[*ruleset_it].set(rule_identity_field);
+ }
+ }
+
+ // Generate suggestions based on the rulesets. Use a Rule::IdentityField
+ // from the bitset to generate address field values.
+ for (std::map<const Ruleset*, MatchingRuleFields>::const_iterator
+ suggestion_it = suggestion_rulesets.begin();
+ suggestion_it != suggestion_rulesets.end();
+ ++suggestion_it) {
+ const Ruleset& ruleset = *suggestion_it->first;
+ const Rule& rule = ruleset.GetLanguageCodeRule(user_input.language_code);
+ const MatchingRuleFields& matching_rule_fields = suggestion_it->second;
+
+ // Do not suggest this region if the postal code in user input does not
+ // match it.
+ if (!user_input.postal_code.empty() &&
+ !rule.GetPostalCodeFormat().empty() &&
+ !ValueMatchesPrefixRegex(
+ user_input.postal_code, rule.GetPostalCodeFormat())) {
+ continue;
+ }
+
+ // Do not add more suggestions than |suggestions_limit|.
+ if (suggestions->size() >= suggestions_limit) {
+ suggestions->clear();
+ return SUCCESS;
+ }
+
+ // If the user's language is not one of the supported languages of a
+ // country that has latinized names for its regions, then prefer to
+ // suggest the latinized region names. If the user types in local script
+ // instead, then the local script names will be suggested.
+ Rule::IdentityField rule_field = Rule::KEY;
+ if (!country_rule.GetLanguage().empty() &&
+ country_rule.GetLanguage() != user_input.language_code &&
+ !rule.GetLatinName().empty() &&
+ matching_rule_fields.test(Rule::LATIN_NAME)) {
+ rule_field = Rule::LATIN_NAME;
+ } else if (matching_rule_fields.test(Rule::KEY)) {
+ rule_field = Rule::KEY;
+ } else if (matching_rule_fields.test(Rule::NAME)) {
+ rule_field = Rule::NAME;
+ } else if (matching_rule_fields.test(Rule::LATIN_NAME)) {
+ rule_field = Rule::LATIN_NAME;
+ } else {
+ assert(false);
+ }
+
+ AddressData suggestion;
+ suggestion.region_code = user_input.region_code;
+ suggestion.postal_code = user_input.postal_code;
+
+ // Traverse the tree of rulesets from the most specific |ruleset| to the
+ // country-wide "root" of the tree. Use the region names found at each of
+ // the levels of the ruleset tree to build the |suggestion|.
+ for (const Ruleset* suggestion_ruleset = &ruleset;
+ suggestion_ruleset->parent() != NULL;
+ suggestion_ruleset = suggestion_ruleset->parent()) {
+ const Rule& suggestion_rule =
+ suggestion_ruleset->GetLanguageCodeRule(user_input.language_code);
+ suggestion.SetFieldValue(suggestion_ruleset->field(),
+ suggestion_rule.GetIdentityField(rule_field));
+ }
+
+ suggestions->push_back(suggestion);
+ }
+
+ return SUCCESS;
+}
+
+void Ruleset::AddSubRegionRulesetsToTrie(const Ruleset& parent_ruleset) {
+ assert(field_ == COUNTRY);
+ assert(canonicalizer_ != NULL);
+
+ for (std::map<std::string, Ruleset*>::const_iterator sub_region_it =
+ parent_ruleset.sub_regions_.begin();
+ sub_region_it != parent_ruleset.sub_regions_.end();
+ ++sub_region_it) {
+ const Ruleset* ruleset = sub_region_it->second;
+ assert(ruleset != NULL);
+
+ if (deepest_ruleset_level_ < ruleset->field()) {
+ deepest_ruleset_level_ = ruleset->field();
+ }
+
+ for (LanguageCodeTries::const_iterator lang_it = tries_.begin();
+ lang_it != tries_.end(); ++lang_it) {
+ const std::string& language_code = lang_it->first;
+ const Rule& rule = ruleset->GetLanguageCodeRule(language_code);
+
+ AddressFieldTries* address_field_tries = lang_it->second;
+ assert(address_field_tries != NULL);
+
+ AddressFieldTries::const_iterator address_field_it =
+ address_field_tries->find(ruleset->field());
+ assert(address_field_it != address_field_tries->end());
+
+ IdentityFieldTries* identity_field_tries = address_field_it->second;
+ assert(identity_field_tries != NULL);
+
+ IdentityFieldTries::const_iterator identity_field_it =
+ identity_field_tries->find(Rule::KEY);
+ assert(identity_field_it != identity_field_tries->end());
+
+ Trie<const Ruleset*>* key_trie = identity_field_it->second;
+ assert(key_trie != NULL);
+
+ identity_field_it = identity_field_tries->find(Rule::NAME);
+ assert(identity_field_it != identity_field_tries->end());
+
+ Trie<const Ruleset*>* name_trie = identity_field_it->second;
+ assert(name_trie != NULL);
+
+ identity_field_it = identity_field_tries->find(Rule::LATIN_NAME);
+ assert(identity_field_it != identity_field_tries->end());
+
+ Trie<const Ruleset*>* latin_name_trie = identity_field_it->second;
+ assert(latin_name_trie != NULL);
+
+ if (!rule.GetKey().empty()) {
+ key_trie->AddDataForKey(
+ canonicalizer_->CanonicalizeString(rule.GetKey()), ruleset);
+ }
+
+ if (!rule.GetName().empty()) {
+ name_trie->AddDataForKey(
+ canonicalizer_->CanonicalizeString(rule.GetName()), ruleset);
+ }
+
+ if (!rule.GetLatinName().empty()) {
+ latin_name_trie->AddDataForKey(
+ canonicalizer_->CanonicalizeString(rule.GetLatinName()), ruleset);
+ }
+ }
+
+ AddSubRegionRulesetsToTrie(*ruleset);
+ }
+ */
+}
+
+void Suggestions::FindRegionsByPrefix(const std::string& region_code,
+ const std::string& language_tag,
+ AddressField address_field,
+ RegionIdentityField region_identity_field,
+ const std::string& canonicalized_prefix,
+ RegionContainer* result) const {
+ DCHECK_GE(address_field, ADMIN_AREA);
+ DCHECK_LE(address_field, DEPENDENT_LOCALITY);
+ DCHECK(result);
+
+ RegionCodeMap::const_iterator region_it = tries_.find(region_code);
+ if (region_it == tries_.end())
+ return;
+
+ const LanguageTagMap* lang_map = region_it->second;
+ DCHECK(lang_map);
+ LanguageTagMap::const_iterator lang_it = lang_map->find(language_tag);
+ if (lang_it == lang_map->end()) {
+ if (!lang_map->empty())
+ lang_it = lang_map->begin();
+ else
+ return;
+ }
+
+ const AddressFieldMap* field_map = lang_it->second;
+ DCHECK(field_map);
+ AddressFieldMap::const_iterator field_it = field_map->find(address_field);
+ if (field_it == field_map->end())
+ return;
+
+ const RegionIdMap* id_map = field_it->second;
+ DCHECK(id_map);
+ RegionIdMap::const_iterator id_it = id_map->find(region_identity_field);
+ if (id_it == id_map->end())
+ return;
+
+ const Trie<const RegionData*>* trie = id_it->second;
+ DCHECK(trie);
+ trie->FindDataForKeyPrefix(canonicalized_prefix, result);
+}
+
+} // namespace autofill

Powered by Google App Engine
This is Rietveld 408576698