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
27 namespace i18n {
28 namespace phonenumbers {
29
30 using std::string;
31
32 namespace {
33
34 struct NormalizedLocale {
35 const char* locale;
36 const char* normalized_locale;
37 };
38
39 const NormalizedLocale kNormalizedLocales[] = {
40 {"zh_TW", "zh_Hant"},
41 {"zh_HK", "zh_Hant"},
42 {"zh_MO", "zh_Hant"},
43 };
44
GetNormalizedLocale(const string & full_locale)45 const 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
AppendLocalePart(const string & part,string * full_locale)55 void AppendLocalePart(const string& part, string* full_locale) {
56 if (!part.empty()) {
57 full_locale->append("_");
58 full_locale->append(part);
59 }
60 }
61
ConstructFullLocale(const string & language,const string & script,const string & region,string * full_locale)62 void 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.
IsLowerThan(const char * s1,const char * s2)70 bool IsLowerThan(const char* s1, const char* s2) {
71 return strcmp(s1, s2) < 0;
72 }
73
74 // Returns true if languages contains language.
HasLanguage(const CountryLanguages * languages,const string & language)75 bool 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
MappingFileProvider(const int * country_calling_codes,int country_calling_codes_size,country_languages_getter get_country_languages)85 MappingFileProvider::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
GetFileName(int country_calling_code,const string & language,const string & script,const string & region,string * filename) const93 const 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
FindBestMatchingLanguageCode(const CountryLanguages * languages,const string & language,const string & script,const string & region,string * best_match) const126 void 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