aboutsummaryrefslogtreecommitdiff
path: root/cpp/src/lookup_key.cc
blob: c0c7b86a2c7e32c6ada69f7353a96f3ec13cdfa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (C) 2014 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "lookup_key.h"

#include <libaddressinput/address_data.h>
#include <libaddressinput/address_field.h>
#include <libaddressinput/util/basictypes.h>

#include <cassert>
#include <cstddef>
#include <functional>
#include <map>
#include <string>
#include <utility>
#include <vector>

#include "language.h"
#include "region_data_constants.h"
#include "rule.h"
#include "util/cctype_tolower_equal.h"

namespace i18n {
namespace addressinput {

namespace {

const char kSlashDelim[] = "/";
const char kDashDelim[] = "--";
const char kData[] = "data";
const char kUnknown[] = "ZZ";

// Assume the language_tag has had "Latn" script removed when this is called.
bool ShouldSetLanguageForKey(const std::string& language_tag,
                             const std::string& region_code) {
  // We only need a language in the key if there is subregion data at all.
  if (RegionDataConstants::GetMaxLookupKeyDepth(region_code) == 0) {
    return false;
  }
  Rule rule;
  rule.CopyFrom(Rule::GetDefault());
  // TODO: Pre-parse the rules and have a map from region code to rule.
  if (!rule.ParseSerializedRule(
          RegionDataConstants::GetRegionData(region_code))) {
    return false;
  }
  const std::vector<std::string>& languages = rule.GetLanguages();
  // Do not add the default language (we want "data/US", not "data/US--en").
  // (empty should not happen here because we have some sub-region data).
  if (languages.empty() || languages[0] == language_tag) {
    return false;
  }
  // Finally, only return true if the language is one of the remaining ones.
  return std::find_if(languages.begin() + 1, languages.end(),
                      std::bind2nd(EqualToTolowerString(), language_tag)) !=
         languages.end();
}

}  // namespace

const AddressField LookupKey::kHierarchy[] = {
  COUNTRY,
  ADMIN_AREA,
  LOCALITY,
  DEPENDENT_LOCALITY
};

LookupKey::LookupKey() {
}

LookupKey::~LookupKey() {
}

void LookupKey::FromAddress(const AddressData& address) {
  nodes_.clear();
  if (address.region_code.empty()) {
    nodes_.insert(std::make_pair(COUNTRY, kUnknown));
  } else {
    for (size_t i = 0; i < arraysize(kHierarchy); ++i) {
      AddressField field = kHierarchy[i];
      const std::string& value = address.GetFieldValue(field);
      if (value.empty()) {
        break;
      }
      nodes_.insert(std::make_pair(field, value));
    }
  }
  Language address_language(address.language_code);
  std::string language_tag_no_latn = address_language.has_latin_script ?
      address_language.base : address_language.tag;
  if (ShouldSetLanguageForKey(language_tag_no_latn, address.region_code)) {
    language_ = language_tag_no_latn;
  }
}

void LookupKey::FromLookupKey(const LookupKey& parent,
                              const std::string& child_node) {
  assert(parent.nodes_.size() < arraysize(kHierarchy));
  assert(!child_node.empty());

  // Copy its nodes if this isn't the parent object.
  if (this != &parent) nodes_ = parent.nodes_;
  AddressField child_field = kHierarchy[nodes_.size()];
  nodes_.insert(std::make_pair(child_field, child_node));
}

std::string LookupKey::ToKeyString(size_t max_depth) const {
  assert(max_depth < arraysize(kHierarchy));
  std::string key_string(kData);

  for (size_t i = 0; i <= max_depth; ++i) {
    AddressField field = kHierarchy[i];
    std::map<AddressField, std::string>::const_iterator it = nodes_.find(field);
    if (it == nodes_.end()) {
      break;
    }
    key_string.append(kSlashDelim);
    key_string.append(it->second);
  }
  if (!language_.empty()) {
    key_string.append(kDashDelim);
    key_string.append(language_);
  }
  return key_string;
}

const std::string& LookupKey::GetRegionCode() const {
  std::map<AddressField, std::string>::const_iterator it = nodes_.find(COUNTRY);
  assert(it != nodes_.end());
  return it->second;
}

size_t LookupKey::GetDepth() const {
  size_t depth = nodes_.size() - 1;
  assert(depth < arraysize(kHierarchy));
  return depth;
}

}  // namespace addressinput
}  // namespace i18n