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 "lookup_key.h" 16 17#include <cassert> 18#include <cstddef> 19#include <map> 20#include <string> 21#include <utility> 22#include <vector> 23 24#include <libaddressinput/address_data.h> 25#include <libaddressinput/address_field.h> 26#include <libaddressinput/util/basictypes.h> 27 28#include "language.h" 29#include "region_data_constants.h" 30#include "rule.h" 31 32namespace i18n { 33namespace addressinput { 34 35namespace { 36 37const char kSlashDelim[] = "/"; 38const char kDashDelim[] = "--"; 39const char kData[] = "data"; 40const char kUnknown[] = "ZZ"; 41 42// Case insensitive matcher for language tags. 43struct LanguageMatcher { 44 LanguageMatcher(const std::string& tag) : tag(tag) { } 45 std::string tag; 46 bool operator() (const std::string& s) { 47 return strcasecmp(tag.c_str(), s.c_str()) == 0; 48 } 49}; 50 51// Assume the language_tag has had "Latn" script removed when this is called. 52bool ShouldSetLanguageForKey(const std::string& language_tag, 53 const std::string& region_code) { 54 // We only need a language in the key if there is subregion data at all. 55 if (RegionDataConstants::GetMaxLookupKeyDepth(region_code) == 0) { 56 return false; 57 } 58 Rule rule; 59 rule.CopyFrom(Rule::GetDefault()); 60 // TODO: Pre-parse the rules and have a map from region code to rule. 61 if (!rule.ParseSerializedRule( 62 RegionDataConstants::GetRegionData(region_code))) { 63 return false; 64 } 65 const std::vector<std::string>& languages = rule.GetLanguages(); 66 // Do not add the default language (we want "data/US", not "data/US--en"). 67 // (empty should not happen here because we have some sub-region data). 68 if (languages.empty() || languages[0] == language_tag) { 69 return false; 70 } 71 // Finally, only return true if the language is one of the remaining ones. 72 return std::find_if(languages.begin() + 1, 73 languages.end(), 74 LanguageMatcher(language_tag)) 75 != languages.end(); 76} 77 78} // namespace 79 80const AddressField LookupKey::kHierarchy[] = { 81 COUNTRY, 82 ADMIN_AREA, 83 LOCALITY, 84 DEPENDENT_LOCALITY 85}; 86 87LookupKey::LookupKey() { 88} 89 90LookupKey::~LookupKey() { 91} 92 93void LookupKey::FromAddress(const AddressData& address) { 94 nodes_.clear(); 95 if (address.region_code.empty()) { 96 nodes_.insert(std::make_pair(COUNTRY, kUnknown)); 97 } else { 98 for (size_t i = 0; i < arraysize(kHierarchy); ++i) { 99 AddressField field = kHierarchy[i]; 100 const std::string& value = address.GetFieldValue(field); 101 if (value.empty()) { 102 break; 103 } 104 nodes_.insert(std::make_pair(field, value)); 105 } 106 } 107 Language address_language(address.language_code); 108 std::string language_tag_no_latn = address_language.has_latin_script ? 109 address_language.base : address_language.tag; 110 if (ShouldSetLanguageForKey(language_tag_no_latn, address.region_code)) { 111 language_ = language_tag_no_latn; 112 } 113} 114 115void LookupKey::FromLookupKey(const LookupKey& parent, 116 const std::string& child_node) { 117 assert(parent.nodes_.size() < arraysize(kHierarchy)); 118 assert(!child_node.empty()); 119 120 nodes_ = parent.nodes_; 121 AddressField child_field = kHierarchy[nodes_.size()]; 122 nodes_.insert(std::make_pair(child_field, child_node)); 123} 124 125std::string LookupKey::ToKeyString(size_t max_depth) const { 126 assert(max_depth < arraysize(kHierarchy)); 127 std::string key_string(kData); 128 129 for (size_t i = 0; i <= max_depth; ++i) { 130 AddressField field = kHierarchy[i]; 131 std::map<AddressField, std::string>::const_iterator it = nodes_.find(field); 132 if (it == nodes_.end()) { 133 break; 134 } 135 key_string.append(kSlashDelim); 136 key_string.append(it->second); 137 } 138 if (!language_.empty()) { 139 key_string.append(kDashDelim); 140 key_string.append(language_); 141 } 142 return key_string; 143} 144 145const std::string& LookupKey::GetRegionCode() const { 146 std::map<AddressField, std::string>::const_iterator it = nodes_.find(COUNTRY); 147 assert(it != nodes_.end()); 148 return it->second; 149} 150 151size_t LookupKey::GetDepth() const { 152 size_t depth = nodes_.size() - 1; 153 assert(depth < arraysize(kHierarchy)); 154 return depth; 155} 156 157} // namespace addressinput 158} // namespace i18n 159