11767c5feSopenharmony_ci// Copyright (C) 2012 The Libphonenumber Authors
21767c5feSopenharmony_ci//
31767c5feSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
41767c5feSopenharmony_ci// you may not use this file except in compliance with the License.
51767c5feSopenharmony_ci// You may obtain a copy of the License at
61767c5feSopenharmony_ci//
71767c5feSopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0
81767c5feSopenharmony_ci//
91767c5feSopenharmony_ci// Unless required by applicable law or agreed to in writing, software
101767c5feSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
111767c5feSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121767c5feSopenharmony_ci// See the License for the specific language governing permissions and
131767c5feSopenharmony_ci// limitations under the License.
141767c5feSopenharmony_ci
151767c5feSopenharmony_ci// Author: Patrick Mezard
161767c5feSopenharmony_ci
171767c5feSopenharmony_ci#include "phonenumbers/geocoding/phonenumber_offline_geocoder.h"
181767c5feSopenharmony_ci
191767c5feSopenharmony_ci#include <algorithm>
201767c5feSopenharmony_ci#include <string>
211767c5feSopenharmony_ci
221767c5feSopenharmony_ci#include <unicode/unistr.h>  // NOLINT(build/include_order)
231767c5feSopenharmony_ci
241767c5feSopenharmony_ci#include "phonenumbers/geocoding/area_code_map.h"
251767c5feSopenharmony_ci#include "phonenumbers/geocoding/geocoding_data.h"
261767c5feSopenharmony_ci#include "phonenumbers/geocoding/mapping_file_provider.h"
271767c5feSopenharmony_ci#include "phonenumbers/phonenumberutil.h"
281767c5feSopenharmony_ci#include "phonenumbers/stl_util.h"
291767c5feSopenharmony_ci
301767c5feSopenharmony_ci
311767c5feSopenharmony_cinamespace i18n {
321767c5feSopenharmony_cinamespace phonenumbers {
331767c5feSopenharmony_ci
341767c5feSopenharmony_ciusing icu::UnicodeString;
351767c5feSopenharmony_ciusing std::string;
361767c5feSopenharmony_ci
371767c5feSopenharmony_cinamespace {
381767c5feSopenharmony_ci
391767c5feSopenharmony_ci// Returns true if s1 comes strictly before s2 in lexicographic order.
401767c5feSopenharmony_cibool IsLowerThan(const char* s1, const char* s2) {
411767c5feSopenharmony_ci  return strcmp(s1, s2) < 0;
421767c5feSopenharmony_ci}
431767c5feSopenharmony_ci
441767c5feSopenharmony_ci}  // namespace
451767c5feSopenharmony_ci
461767c5feSopenharmony_ciPhoneNumberOfflineGeocoder::PhoneNumberOfflineGeocoder() {
471767c5feSopenharmony_ci  Init(get_country_calling_codes(), get_country_calling_codes_size(),
481767c5feSopenharmony_ci       get_country_languages, get_prefix_language_code_pairs(),
491767c5feSopenharmony_ci       get_prefix_language_code_pairs_size(), get_prefix_descriptions);
501767c5feSopenharmony_ci}
511767c5feSopenharmony_ci
521767c5feSopenharmony_ciPhoneNumberOfflineGeocoder::PhoneNumberOfflineGeocoder(
531767c5feSopenharmony_ci    const int* country_calling_codes, int country_calling_codes_size,
541767c5feSopenharmony_ci    country_languages_getter get_country_languages,
551767c5feSopenharmony_ci    const char** prefix_language_code_pairs,
561767c5feSopenharmony_ci    int prefix_language_code_pairs_size,
571767c5feSopenharmony_ci    prefix_descriptions_getter get_prefix_descriptions) {
581767c5feSopenharmony_ci  Init(country_calling_codes, country_calling_codes_size,
591767c5feSopenharmony_ci       get_country_languages, prefix_language_code_pairs,
601767c5feSopenharmony_ci       prefix_language_code_pairs_size, get_prefix_descriptions);
611767c5feSopenharmony_ci}
621767c5feSopenharmony_ci
631767c5feSopenharmony_civoid PhoneNumberOfflineGeocoder::Init(
641767c5feSopenharmony_ci    const int* country_calling_codes, int country_calling_codes_size,
651767c5feSopenharmony_ci    country_languages_getter get_country_languages,
661767c5feSopenharmony_ci    const char** prefix_language_code_pairs,
671767c5feSopenharmony_ci    int prefix_language_code_pairs_size,
681767c5feSopenharmony_ci    prefix_descriptions_getter get_prefix_descriptions) {
691767c5feSopenharmony_ci  phone_util_ = PhoneNumberUtil::GetInstance();
701767c5feSopenharmony_ci  provider_.reset(new MappingFileProvider(country_calling_codes,
711767c5feSopenharmony_ci                                          country_calling_codes_size,
721767c5feSopenharmony_ci                                          get_country_languages));
731767c5feSopenharmony_ci  prefix_language_code_pairs_ = prefix_language_code_pairs;
741767c5feSopenharmony_ci  prefix_language_code_pairs_size_ = prefix_language_code_pairs_size;
751767c5feSopenharmony_ci  get_prefix_descriptions_ = get_prefix_descriptions;
761767c5feSopenharmony_ci}
771767c5feSopenharmony_ci
781767c5feSopenharmony_ciPhoneNumberOfflineGeocoder::~PhoneNumberOfflineGeocoder() {
791767c5feSopenharmony_ci  gtl::STLDeleteContainerPairSecondPointers(
801767c5feSopenharmony_ci      available_maps_.begin(), available_maps_.end());
811767c5feSopenharmony_ci}
821767c5feSopenharmony_ci
831767c5feSopenharmony_ciconst AreaCodeMap* PhoneNumberOfflineGeocoder::GetPhonePrefixDescriptions(
841767c5feSopenharmony_ci    int prefix, const string& language, const string& script,
851767c5feSopenharmony_ci    const string& region) const {
861767c5feSopenharmony_ci  string filename;
871767c5feSopenharmony_ci  provider_->GetFileName(prefix, language, script, region, &filename);
881767c5feSopenharmony_ci  if (filename.empty()) {
891767c5feSopenharmony_ci    return NULL;
901767c5feSopenharmony_ci  }
911767c5feSopenharmony_ci  AreaCodeMaps::const_iterator it = available_maps_.find(filename);
921767c5feSopenharmony_ci  if (it == available_maps_.end()) {
931767c5feSopenharmony_ci    return LoadAreaCodeMapFromFile(filename);
941767c5feSopenharmony_ci  }
951767c5feSopenharmony_ci  return it->second;
961767c5feSopenharmony_ci}
971767c5feSopenharmony_ci
981767c5feSopenharmony_ciconst AreaCodeMap* PhoneNumberOfflineGeocoder::LoadAreaCodeMapFromFile(
991767c5feSopenharmony_ci    const string& filename) const {
1001767c5feSopenharmony_ci  const char** const prefix_language_code_pairs_end =
1011767c5feSopenharmony_ci      prefix_language_code_pairs_ + prefix_language_code_pairs_size_;
1021767c5feSopenharmony_ci  const char** const prefix_language_code_pair =
1031767c5feSopenharmony_ci      std::lower_bound(prefix_language_code_pairs_,
1041767c5feSopenharmony_ci                       prefix_language_code_pairs_end,
1051767c5feSopenharmony_ci                       filename.c_str(), IsLowerThan);
1061767c5feSopenharmony_ci  if (prefix_language_code_pair != prefix_language_code_pairs_end &&
1071767c5feSopenharmony_ci      filename.compare(*prefix_language_code_pair) == 0) {
1081767c5feSopenharmony_ci    AreaCodeMap* const m = new AreaCodeMap();
1091767c5feSopenharmony_ci    m->ReadAreaCodeMap(get_prefix_descriptions_(
1101767c5feSopenharmony_ci            prefix_language_code_pair - prefix_language_code_pairs_));
1111767c5feSopenharmony_ci    return available_maps_.insert(AreaCodeMaps::value_type(filename, m))
1121767c5feSopenharmony_ci        .first->second;
1131767c5feSopenharmony_ci  }
1141767c5feSopenharmony_ci  return NULL;
1151767c5feSopenharmony_ci}
1161767c5feSopenharmony_ci
1171767c5feSopenharmony_cistring PhoneNumberOfflineGeocoder::GetCountryNameForNumber(
1181767c5feSopenharmony_ci    const PhoneNumber& number, const Locale& language) const {
1191767c5feSopenharmony_ci  string region_code;
1201767c5feSopenharmony_ci  phone_util_->GetRegionCodeForNumber(number, &region_code);
1211767c5feSopenharmony_ci  return GetRegionDisplayName(&region_code, language);
1221767c5feSopenharmony_ci}
1231767c5feSopenharmony_ci
1241767c5feSopenharmony_cistring PhoneNumberOfflineGeocoder::GetRegionDisplayName(
1251767c5feSopenharmony_ci    const string* region_code, const Locale& language) const {
1261767c5feSopenharmony_ci  if (region_code == NULL || region_code->compare("ZZ") == 0 ||
1271767c5feSopenharmony_ci      region_code->compare(
1281767c5feSopenharmony_ci         PhoneNumberUtil::kRegionCodeForNonGeoEntity) == 0) {
1291767c5feSopenharmony_ci    return "";
1301767c5feSopenharmony_ci  }
1311767c5feSopenharmony_ci  UnicodeString udisplay_country;
1321767c5feSopenharmony_ci  icu::Locale("", region_code->c_str()).getDisplayCountry(
1331767c5feSopenharmony_ci      language, udisplay_country);
1341767c5feSopenharmony_ci  string display_country;
1351767c5feSopenharmony_ci  udisplay_country.toUTF8String(display_country);
1361767c5feSopenharmony_ci  return display_country;
1371767c5feSopenharmony_ci}
1381767c5feSopenharmony_ci
1391767c5feSopenharmony_cistring PhoneNumberOfflineGeocoder::GetDescriptionForValidNumber(
1401767c5feSopenharmony_ci    const PhoneNumber& number, const Locale& language) const {
1411767c5feSopenharmony_ci  const char* const description = GetAreaDescription(
1421767c5feSopenharmony_ci      number, language.getLanguage(), "", language.getCountry());
1431767c5feSopenharmony_ci  return *description != '\0'
1441767c5feSopenharmony_ci        ? description
1451767c5feSopenharmony_ci        : GetCountryNameForNumber(number, language);
1461767c5feSopenharmony_ci}
1471767c5feSopenharmony_ci
1481767c5feSopenharmony_cistring PhoneNumberOfflineGeocoder::GetDescriptionForValidNumber(
1491767c5feSopenharmony_ci    const PhoneNumber& number, const Locale& language,
1501767c5feSopenharmony_ci    const string& user_region) const {
1511767c5feSopenharmony_ci  // If the user region matches the number's region, then we just show the
1521767c5feSopenharmony_ci  // lower-level description, if one exists - if no description exists, we will
1531767c5feSopenharmony_ci  // show the region(country) name for the number.
1541767c5feSopenharmony_ci  string region_code;
1551767c5feSopenharmony_ci  phone_util_->GetRegionCodeForNumber(number, &region_code);
1561767c5feSopenharmony_ci  if (user_region.compare(region_code) == 0) {
1571767c5feSopenharmony_ci    return GetDescriptionForValidNumber(number, language);
1581767c5feSopenharmony_ci  }
1591767c5feSopenharmony_ci  // Otherwise, we just show the region(country) name for now.
1601767c5feSopenharmony_ci  return GetRegionDisplayName(&region_code, language);
1611767c5feSopenharmony_ci}
1621767c5feSopenharmony_ci
1631767c5feSopenharmony_cistring PhoneNumberOfflineGeocoder::GetDescriptionForNumber(
1641767c5feSopenharmony_ci    const PhoneNumber& number, const Locale& locale) const {
1651767c5feSopenharmony_ci  PhoneNumberUtil::PhoneNumberType number_type =
1661767c5feSopenharmony_ci      phone_util_->GetNumberType(number);
1671767c5feSopenharmony_ci  if (number_type == PhoneNumberUtil::UNKNOWN) {
1681767c5feSopenharmony_ci    return "";
1691767c5feSopenharmony_ci  } else if (!phone_util_->IsNumberGeographical(number_type,
1701767c5feSopenharmony_ci                                                number.country_code())) {
1711767c5feSopenharmony_ci    return GetCountryNameForNumber(number, locale);
1721767c5feSopenharmony_ci  }
1731767c5feSopenharmony_ci  return GetDescriptionForValidNumber(number, locale);
1741767c5feSopenharmony_ci}
1751767c5feSopenharmony_ci
1761767c5feSopenharmony_cistring PhoneNumberOfflineGeocoder::GetDescriptionForNumber(
1771767c5feSopenharmony_ci    const PhoneNumber& number, const Locale& language,
1781767c5feSopenharmony_ci    const string& user_region) const {
1791767c5feSopenharmony_ci  PhoneNumberUtil::PhoneNumberType number_type =
1801767c5feSopenharmony_ci      phone_util_->GetNumberType(number);
1811767c5feSopenharmony_ci  if (number_type == PhoneNumberUtil::UNKNOWN) {
1821767c5feSopenharmony_ci    return "";
1831767c5feSopenharmony_ci  } else if (!phone_util_->IsNumberGeographical(number_type,
1841767c5feSopenharmony_ci                                                number.country_code())) {
1851767c5feSopenharmony_ci    return GetCountryNameForNumber(number, language);
1861767c5feSopenharmony_ci  }
1871767c5feSopenharmony_ci  return GetDescriptionForValidNumber(number, language, user_region);
1881767c5feSopenharmony_ci}
1891767c5feSopenharmony_ci
1901767c5feSopenharmony_ciconst char* PhoneNumberOfflineGeocoder::GetAreaDescription(
1911767c5feSopenharmony_ci    const PhoneNumber& number, const string& lang, const string& script,
1921767c5feSopenharmony_ci    const string& region) const {
1931767c5feSopenharmony_ci  const int country_calling_code = number.country_code();
1941767c5feSopenharmony_ci  // NANPA area is not split in C++ code.
1951767c5feSopenharmony_ci  const int phone_prefix = country_calling_code;
1961767c5feSopenharmony_ci  const AreaCodeMap* const descriptions = GetPhonePrefixDescriptions(
1971767c5feSopenharmony_ci      phone_prefix, lang, script, region);
1981767c5feSopenharmony_ci  const char* description = descriptions ? descriptions->Lookup(number) : NULL;
1991767c5feSopenharmony_ci  // When a location is not available in the requested language, fall back to
2001767c5feSopenharmony_ci  // English.
2011767c5feSopenharmony_ci  if ((!description || *description == '\0') && MayFallBackToEnglish(lang)) {
2021767c5feSopenharmony_ci    const AreaCodeMap* default_descriptions = GetPhonePrefixDescriptions(
2031767c5feSopenharmony_ci        phone_prefix, "en", "", "");
2041767c5feSopenharmony_ci    if (!default_descriptions) {
2051767c5feSopenharmony_ci      return "";
2061767c5feSopenharmony_ci    }
2071767c5feSopenharmony_ci    description = default_descriptions->Lookup(number);
2081767c5feSopenharmony_ci  }
2091767c5feSopenharmony_ci  return description ? description : "";
2101767c5feSopenharmony_ci}
2111767c5feSopenharmony_ci
2121767c5feSopenharmony_ci// Don't fall back to English if the requested language is among the following:
2131767c5feSopenharmony_ci// - Chinese
2141767c5feSopenharmony_ci// - Japanese
2151767c5feSopenharmony_ci// - Korean
2161767c5feSopenharmony_cibool PhoneNumberOfflineGeocoder::MayFallBackToEnglish(
2171767c5feSopenharmony_ci    const string& lang) const {
2181767c5feSopenharmony_ci  return lang.compare("zh") && lang.compare("ja") && lang.compare("ko");
2191767c5feSopenharmony_ci}
2201767c5feSopenharmony_ci
2211767c5feSopenharmony_ci}  // namespace phonenumbers
2221767c5feSopenharmony_ci}  // namespace i18n
223