// Copyright (C) 2012 The Libphonenumber Authors // // 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. // Author: Patrick Mezard #include "phonenumbers/geocoding/phonenumber_offline_geocoder.h" #include #include #include // NOLINT(build/include_order) #include "phonenumbers/geocoding/area_code_map.h" #include "phonenumbers/geocoding/geocoding_data.h" #include "phonenumbers/geocoding/mapping_file_provider.h" #include "phonenumbers/phonenumberutil.h" #include "phonenumbers/stl_util.h" namespace i18n { namespace phonenumbers { using icu::UnicodeString; using std::string; namespace { // Returns true if s1 comes strictly before s2 in lexicographic order. bool IsLowerThan(const char* s1, const char* s2) { return strcmp(s1, s2) < 0; } } // namespace PhoneNumberOfflineGeocoder::PhoneNumberOfflineGeocoder() { Init(get_country_calling_codes(), get_country_calling_codes_size(), get_country_languages, get_prefix_language_code_pairs(), get_prefix_language_code_pairs_size(), get_prefix_descriptions); } PhoneNumberOfflineGeocoder::PhoneNumberOfflineGeocoder( const int* country_calling_codes, int country_calling_codes_size, country_languages_getter get_country_languages, const char** prefix_language_code_pairs, int prefix_language_code_pairs_size, prefix_descriptions_getter get_prefix_descriptions) { Init(country_calling_codes, country_calling_codes_size, get_country_languages, prefix_language_code_pairs, prefix_language_code_pairs_size, get_prefix_descriptions); } void PhoneNumberOfflineGeocoder::Init( const int* country_calling_codes, int country_calling_codes_size, country_languages_getter get_country_languages, const char** prefix_language_code_pairs, int prefix_language_code_pairs_size, prefix_descriptions_getter get_prefix_descriptions) { phone_util_ = PhoneNumberUtil::GetInstance(); provider_.reset(new MappingFileProvider(country_calling_codes, country_calling_codes_size, get_country_languages)); prefix_language_code_pairs_ = prefix_language_code_pairs; prefix_language_code_pairs_size_ = prefix_language_code_pairs_size; get_prefix_descriptions_ = get_prefix_descriptions; } PhoneNumberOfflineGeocoder::~PhoneNumberOfflineGeocoder() { gtl::STLDeleteContainerPairSecondPointers( available_maps_.begin(), available_maps_.end()); } const AreaCodeMap* PhoneNumberOfflineGeocoder::GetPhonePrefixDescriptions( int prefix, const string& language, const string& script, const string& region) const { string filename; provider_->GetFileName(prefix, language, script, region, &filename); if (filename.empty()) { return NULL; } AreaCodeMaps::const_iterator it = available_maps_.find(filename); if (it == available_maps_.end()) { return LoadAreaCodeMapFromFile(filename); } return it->second; } const AreaCodeMap* PhoneNumberOfflineGeocoder::LoadAreaCodeMapFromFile( const string& filename) const { const char** const prefix_language_code_pairs_end = prefix_language_code_pairs_ + prefix_language_code_pairs_size_; const char** const prefix_language_code_pair = std::lower_bound(prefix_language_code_pairs_, prefix_language_code_pairs_end, filename.c_str(), IsLowerThan); if (prefix_language_code_pair != prefix_language_code_pairs_end && filename.compare(*prefix_language_code_pair) == 0) { AreaCodeMap* const m = new AreaCodeMap(); m->ReadAreaCodeMap(get_prefix_descriptions_( prefix_language_code_pair - prefix_language_code_pairs_)); return available_maps_.insert(AreaCodeMaps::value_type(filename, m)) .first->second; } return NULL; } string PhoneNumberOfflineGeocoder::GetCountryNameForNumber( const PhoneNumber& number, const Locale& language) const { string region_code; phone_util_->GetRegionCodeForNumber(number, ®ion_code); return GetRegionDisplayName(®ion_code, language); } string PhoneNumberOfflineGeocoder::GetRegionDisplayName( const string* region_code, const Locale& language) const { if (region_code == NULL || region_code->compare("ZZ") == 0 || region_code->compare( PhoneNumberUtil::kRegionCodeForNonGeoEntity) == 0) { return ""; } UnicodeString udisplay_country; icu::Locale("", region_code->c_str()).getDisplayCountry( language, udisplay_country); string display_country; udisplay_country.toUTF8String(display_country); return display_country; } string PhoneNumberOfflineGeocoder::GetDescriptionForValidNumber( const PhoneNumber& number, const Locale& language) const { const char* const description = GetAreaDescription( number, language.getLanguage(), "", language.getCountry()); return *description != '\0' ? description : GetCountryNameForNumber(number, language); } string PhoneNumberOfflineGeocoder::GetDescriptionForValidNumber( const PhoneNumber& number, const Locale& language, const string& user_region) const { // If the user region matches the number's region, then we just show the // lower-level description, if one exists - if no description exists, we will // show the region(country) name for the number. string region_code; phone_util_->GetRegionCodeForNumber(number, ®ion_code); if (user_region.compare(region_code) == 0) { return GetDescriptionForValidNumber(number, language); } // Otherwise, we just show the region(country) name for now. return GetRegionDisplayName(®ion_code, language); } string PhoneNumberOfflineGeocoder::GetDescriptionForNumber( const PhoneNumber& number, const Locale& locale) const { PhoneNumberUtil::PhoneNumberType number_type = phone_util_->GetNumberType(number); if (number_type == PhoneNumberUtil::UNKNOWN) { return ""; } else if (!phone_util_->IsNumberGeographical(number_type, number.country_code())) { return GetCountryNameForNumber(number, locale); } return GetDescriptionForValidNumber(number, locale); } string PhoneNumberOfflineGeocoder::GetDescriptionForNumber( const PhoneNumber& number, const Locale& language, const string& user_region) const { PhoneNumberUtil::PhoneNumberType number_type = phone_util_->GetNumberType(number); if (number_type == PhoneNumberUtil::UNKNOWN) { return ""; } else if (!phone_util_->IsNumberGeographical(number_type, number.country_code())) { return GetCountryNameForNumber(number, language); } return GetDescriptionForValidNumber(number, language, user_region); } const char* PhoneNumberOfflineGeocoder::GetAreaDescription( const PhoneNumber& number, const string& lang, const string& script, const string& region) const { const int country_calling_code = number.country_code(); // NANPA area is not split in C++ code. const int phone_prefix = country_calling_code; const AreaCodeMap* const descriptions = GetPhonePrefixDescriptions( phone_prefix, lang, script, region); const char* description = descriptions ? descriptions->Lookup(number) : NULL; // When a location is not available in the requested language, fall back to // English. if ((!description || *description == '\0') && MayFallBackToEnglish(lang)) { const AreaCodeMap* default_descriptions = GetPhonePrefixDescriptions( phone_prefix, "en", "", ""); if (!default_descriptions) { return ""; } description = default_descriptions->Lookup(number); } return description ? description : ""; } // Don't fall back to English if the requested language is among the following: // - Chinese // - Japanese // - Korean bool PhoneNumberOfflineGeocoder::MayFallBackToEnglish( const string& lang) const { return lang.compare("zh") && lang.compare("ja") && lang.compare("ko"); } } // namespace phonenumbers } // namespace i18n