1// Copyright (C) 2012 The Libphonenumber Authors
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// Author: Patrick Mezard
16
17#include "phonenumbers/geocoding/mapping_file_provider.h"
18
19#include <algorithm>
20#include <cstddef>
21#include <cstring>
22#include <sstream>
23#include <string>
24
25#include "phonenumbers/geocoding/geocoding_data.h"
26
27namespace i18n {
28namespace phonenumbers {
29
30using std::string;
31
32namespace {
33
34struct NormalizedLocale {
35  const char* locale;
36  const char* normalized_locale;
37};
38
39const NormalizedLocale kNormalizedLocales[] = {
40  {"zh_TW", "zh_Hant"},
41  {"zh_HK", "zh_Hant"},
42  {"zh_MO", "zh_Hant"},
43};
44
45const char* GetNormalizedLocale(const string& full_locale) {
46  const int size = sizeof(kNormalizedLocales) / sizeof(*kNormalizedLocales);
47  for (int i = 0; i != size; ++i) {
48    if (full_locale.compare(kNormalizedLocales[i].locale) == 0) {
49      return kNormalizedLocales[i].normalized_locale;
50    }
51  }
52  return NULL;
53}
54
55void AppendLocalePart(const string& part, string* full_locale) {
56  if (!part.empty()) {
57    full_locale->append("_");
58    full_locale->append(part);
59  }
60}
61
62void ConstructFullLocale(const string& language, const string& script, const
63                         string& region, string* full_locale) {
64  full_locale->assign(language);
65  AppendLocalePart(script, full_locale);
66  AppendLocalePart(region, full_locale);
67}
68
69// Returns true if s1 comes strictly before s2 in lexicographic order.
70bool IsLowerThan(const char* s1, const char* s2) {
71  return strcmp(s1, s2) < 0;
72}
73
74// Returns true if languages contains language.
75bool HasLanguage(const CountryLanguages* languages, const string& language) {
76  const char** const start = languages->available_languages;
77  const char** const end = start + languages->available_languages_size;
78  const char** const it =
79      std::lower_bound(start, end, language.c_str(), IsLowerThan);
80  return it != end && strcmp(language.c_str(), *it) == 0;
81}
82
83}  // namespace
84
85MappingFileProvider::MappingFileProvider(
86    const int* country_calling_codes, int country_calling_codes_size,
87    country_languages_getter get_country_languages)
88  : country_calling_codes_(country_calling_codes),
89    country_calling_codes_size_(country_calling_codes_size),
90    get_country_languages_(get_country_languages) {
91}
92
93const string& MappingFileProvider::GetFileName(int country_calling_code,
94                                               const string& language,
95                                               const string& script,
96                                               const string& region,
97                                               string* filename) const {
98  filename->clear();
99  if (language.empty()) {
100    return *filename;
101  }
102  const int* const country_calling_codes_end = country_calling_codes_ +
103      country_calling_codes_size_;
104  const int* const it =
105      std::lower_bound(country_calling_codes_,
106                       country_calling_codes_end,
107                       country_calling_code);
108  if (it == country_calling_codes_end || *it != country_calling_code) {
109    return *filename;
110  }
111  const CountryLanguages* const langs =
112      get_country_languages_(it - country_calling_codes_);
113  if (langs->available_languages_size > 0) {
114    string language_code;
115    FindBestMatchingLanguageCode(langs, language, script, region,
116                                 &language_code);
117  if (!language_code.empty()) {
118    std::stringstream filename_buf;
119    filename_buf << country_calling_code << "_" << language_code;
120    *filename = filename_buf.str();
121    }
122  }
123  return *filename;
124}
125
126void MappingFileProvider::FindBestMatchingLanguageCode(
127  const CountryLanguages* languages, const string& language,
128  const string& script, const string& region, string* best_match) const {
129  string full_locale;
130  ConstructFullLocale(language, script, region, &full_locale);
131  const char* const normalized_locale = GetNormalizedLocale(full_locale);
132  if (normalized_locale != NULL) {
133    string normalized_locale_str(normalized_locale);
134    if (HasLanguage(languages, normalized_locale_str)) {
135      best_match->swap(normalized_locale_str);
136      return;
137    }
138  }
139
140  if (HasLanguage(languages, full_locale)) {
141    best_match->swap(full_locale);
142    return;
143  }
144
145  if (script.empty() != region.empty()) {
146    if (HasLanguage(languages, language)) {
147      *best_match = language;
148      return;
149    }
150  } else if (!script.empty() && !region.empty()) {
151    string lang_with_script(language);
152    lang_with_script.append("_");
153    lang_with_script.append(script);
154    if (HasLanguage(languages, lang_with_script)) {
155      best_match->swap(lang_with_script);
156      return;
157    }
158  }
159
160  string lang_with_region(language);
161  lang_with_region.append("_");
162  lang_with_region.append(region);
163  if (HasLanguage(languages, lang_with_region)) {
164    best_match->swap(lang_with_region);
165    return;
166  }
167  if (HasLanguage(languages, language)) {
168    *best_match = language;
169    return;
170  }
171  best_match->clear();
172}
173
174}  // namespace phonenumbers
175}  // namespace i18n
176