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