diff options
Diffstat (limited to 'geocoding/phonenumber_offline_geocoder.cc')
-rw-r--r-- | geocoding/phonenumber_offline_geocoder.cc | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/geocoding/phonenumber_offline_geocoder.cc b/geocoding/phonenumber_offline_geocoder.cc new file mode 100644 index 0000000..9eac002 --- /dev/null +++ b/geocoding/phonenumber_offline_geocoder.cc @@ -0,0 +1,216 @@ +// 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 <algorithm> +#include <map> +#include <string> + +#include <unicode/unistr.h> // 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::map; +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() { + 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()) { + it = LoadAreaCodeMapFromFile(filename); + if (it == available_maps_.end()) { + return NULL; + } + } + return it->second; +} + +PhoneNumberOfflineGeocoder::AreaCodeMaps::const_iterator +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; + } + return available_maps_.end(); +} + +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 { + if (!phone_util_->IsValidNumber(number)) { + return ""; + } + return GetDescriptionForValidNumber(number, locale); +} + +string PhoneNumberOfflineGeocoder::GetDescriptionForNumber( + const PhoneNumber& number, const Locale& language, + const string& user_region) const { + if (!phone_util_->IsValidNumber(number)) { + return ""; + } + 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 |