1 // Copyright (C) 2012 The Libphonenumber Authors
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 // Author: David Yonge-Mallo
17 #include "phonenumbers/shortnumberinfo.h"
23 #include "phonenumbers/base/memory/scoped_ptr.h"
24 #include "phonenumbers/default_logger.h"
25 #include "phonenumbers/matcher_api.h"
26 #include "phonenumbers/phonemetadata.pb.h"
27 #include "phonenumbers/phonenumberutil.h"
28 #include "phonenumbers/regex_based_matcher.h"
29 #include "phonenumbers/region_code.h"
30 #include "phonenumbers/short_metadata.h"
33 namespace phonenumbers {
38 bool LoadCompiledInMetadata(PhoneMetadataCollection* metadata) {
39 if (!metadata->ParseFromArray(short_metadata_get(), short_metadata_size())) {
40 LOG(ERROR) << "Could not parse binary data.";
46 ShortNumberInfo::ShortNumberInfo()
47 : phone_util_(*PhoneNumberUtil::GetInstance()),
48 matcher_api_(new RegexBasedMatcher()),
49 region_to_short_metadata_map_(new map<string, PhoneMetadata>()),
50 regions_where_emergency_numbers_must_be_exact_(new set<string>()) {
51 PhoneMetadataCollection metadata_collection;
52 if (!LoadCompiledInMetadata(&metadata_collection)) {
53 LOG(DFATAL) << "Could not parse compiled-in metadata.";
56 for (RepeatedPtrField<PhoneMetadata>::const_iterator it =
57 metadata_collection.metadata().begin();
58 it != metadata_collection.metadata().end();
60 const string& region_code = it->id();
61 region_to_short_metadata_map_->insert(std::make_pair(region_code, *it));
63 regions_where_emergency_numbers_must_be_exact_->insert("BR");
64 regions_where_emergency_numbers_must_be_exact_->insert("CL");
65 regions_where_emergency_numbers_must_be_exact_->insert("NI");
68 ShortNumberInfo::~ShortNumberInfo() {}
70 // Returns a pointer to the phone metadata for the appropriate region or NULL
71 // if the region code is invalid or unknown.
72 const PhoneMetadata* ShortNumberInfo::GetMetadataForRegion(
73 const string& region_code) const {
74 map<string, PhoneMetadata>::const_iterator it =
75 region_to_short_metadata_map_->find(region_code);
76 if (it != region_to_short_metadata_map_->end()) {
83 // Same as the matchesPossibleNumberAndNationalNumber method in
84 // java/libphonenumber/src/com/google/i18n/phonenumbers/ShortNumberInfo.java
85 // TODO: Once we have benchmarked ShortNumberInfo, consider if it is
86 // worth keeping this performance optimization, and if so move this into the
87 // matcher implementation.
88 bool MatchesPossibleNumberAndNationalNumber(
89 const MatcherApi& matcher_api, const string& number,
90 const PhoneNumberDesc& number_desc) {
91 return matcher_api.MatchesPossibleNumber(number, number_desc) &&
92 matcher_api.MatchesNationalNumber(number, number_desc, false);
96 bool ShortNumberInfo::IsPossibleShortNumberForRegion(
97 const string& short_number, const string& region_dialing_from) const {
98 const PhoneMetadata* phone_metadata =
99 GetMetadataForRegion(region_dialing_from);
100 if (!phone_metadata) {
103 const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
104 return matcher_api_->MatchesPossibleNumber(short_number, general_desc);
107 bool ShortNumberInfo::IsPossibleShortNumberForRegion(const PhoneNumber& number,
108 const string& region_dialing_from) const {
109 const PhoneMetadata* phone_metadata =
110 GetMetadataForRegion(region_dialing_from);
111 if (!phone_metadata) {
115 phone_util_.GetNationalSignificantNumber(number, &short_number);
116 const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
117 return matcher_api_->MatchesPossibleNumber(short_number, general_desc);
120 bool ShortNumberInfo::IsPossibleShortNumber(const PhoneNumber& number) const {
121 list<string> region_codes;
122 phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
125 phone_util_.GetNationalSignificantNumber(number, &short_number);
126 for (list<string>::const_iterator it = region_codes.begin();
127 it != region_codes.end(); ++it) {
128 const PhoneMetadata* phone_metadata = GetMetadataForRegion(*it);
129 if (matcher_api_->MatchesPossibleNumber(short_number,
130 phone_metadata->general_desc())) {
137 bool ShortNumberInfo::IsValidShortNumberForRegion(
138 const string& short_number, const string& region_dialing_from) const {
139 const PhoneMetadata* phone_metadata =
140 GetMetadataForRegion(region_dialing_from);
141 if (!phone_metadata) {
144 const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
145 if (!MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
149 const PhoneNumberDesc& short_number_desc = phone_metadata->short_code();
150 return MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
154 bool ShortNumberInfo::IsValidShortNumberForRegion(
155 const PhoneNumber& number, const string& region_dialing_from) const {
156 const PhoneMetadata* phone_metadata =
157 GetMetadataForRegion(region_dialing_from);
158 if (!phone_metadata) {
162 phone_util_.GetNationalSignificantNumber(number, &short_number);
163 const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
164 if (!MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
168 const PhoneNumberDesc& short_number_desc = phone_metadata->short_code();
169 return MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
173 bool ShortNumberInfo::IsValidShortNumber(const PhoneNumber& number) const {
174 list<string> region_codes;
175 phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
178 GetRegionCodeForShortNumberFromRegionList(number, region_codes, ®ion_code);
179 if (region_codes.size() > 1 && region_code != RegionCode::GetUnknown()) {
182 return IsValidShortNumberForRegion(number, region_code);
185 ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCostForRegion(
186 const string& short_number, const string& region_dialing_from) const {
187 const PhoneMetadata* phone_metadata =
188 GetMetadataForRegion(region_dialing_from);
189 if (!phone_metadata) {
190 return ShortNumberInfo::UNKNOWN_COST;
193 // The cost categories are tested in order of decreasing expense, since if
194 // for some reason the patterns overlap the most expensive matching cost
195 // category should be returned.
196 if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
197 phone_metadata->premium_rate())) {
198 return ShortNumberInfo::PREMIUM_RATE;
200 if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
201 phone_metadata->standard_rate())) {
202 return ShortNumberInfo::STANDARD_RATE;
204 if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
205 phone_metadata->toll_free())) {
206 return ShortNumberInfo::TOLL_FREE;
208 if (IsEmergencyNumber(short_number, region_dialing_from)) {
209 // Emergency numbers are implicitly toll-free.
210 return ShortNumberInfo::TOLL_FREE;
212 return ShortNumberInfo::UNKNOWN_COST;
215 ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCostForRegion(
216 const PhoneNumber& number, const string& region_dialing_from) const {
217 const PhoneMetadata* phone_metadata =
218 GetMetadataForRegion(region_dialing_from);
219 if (!phone_metadata) {
220 return ShortNumberInfo::UNKNOWN_COST;
223 phone_util_.GetNationalSignificantNumber(number, &short_number);
225 // The cost categories are tested in order of decreasing expense, since if
226 // for some reason the patterns overlap the most expensive matching cost
227 // category should be returned.
228 if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
229 phone_metadata->premium_rate())) {
230 return ShortNumberInfo::PREMIUM_RATE;
232 if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
233 phone_metadata->standard_rate())) {
234 return ShortNumberInfo::STANDARD_RATE;
236 if (MatchesPossibleNumberAndNationalNumber(*matcher_api_, short_number,
237 phone_metadata->toll_free())) {
238 return ShortNumberInfo::TOLL_FREE;
240 if (IsEmergencyNumber(short_number, region_dialing_from)) {
241 // Emergency numbers are implicitly toll-free.
242 return ShortNumberInfo::TOLL_FREE;
244 return ShortNumberInfo::UNKNOWN_COST;
247 ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCost(
248 const PhoneNumber& number) const {
249 list<string> region_codes;
250 phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
252 if (region_codes.size() == 0) {
253 return ShortNumberInfo::UNKNOWN_COST;
255 if (region_codes.size() == 1) {
256 return GetExpectedCostForRegion(number, region_codes.front());
258 ShortNumberInfo::ShortNumberCost cost = ShortNumberInfo::TOLL_FREE;
259 for (list<string>::const_iterator it = region_codes.begin();
260 it != region_codes.end(); ++it) {
261 ShortNumberInfo::ShortNumberCost cost_for_region =
262 GetExpectedCostForRegion(number, *it);
263 switch (cost_for_region) {
264 case ShortNumberInfo::PREMIUM_RATE:
265 return ShortNumberInfo::PREMIUM_RATE;
266 case ShortNumberInfo::UNKNOWN_COST:
267 return ShortNumberInfo::UNKNOWN_COST;
268 case ShortNumberInfo::STANDARD_RATE:
269 if (cost != ShortNumberInfo::UNKNOWN_COST) {
270 cost = ShortNumberInfo::STANDARD_RATE;
273 case ShortNumberInfo::TOLL_FREE:
277 LOG(ERROR) << "Unrecognised cost for region: "
278 << static_cast<int>(cost_for_region);
285 void ShortNumberInfo::GetRegionCodeForShortNumberFromRegionList(
286 const PhoneNumber& number, const list<string>& region_codes,
287 string* region_code) const {
288 if (region_codes.size() == 0) {
289 region_code->assign(RegionCode::GetUnknown());
291 } else if (region_codes.size() == 1) {
292 region_code->assign(region_codes.front());
295 string national_number;
296 phone_util_.GetNationalSignificantNumber(number, &national_number);
297 for (list<string>::const_iterator it = region_codes.begin();
298 it != region_codes.end(); ++it) {
299 const PhoneMetadata* phone_metadata = GetMetadataForRegion(*it);
300 if (phone_metadata != NULL &&
301 MatchesPossibleNumberAndNationalNumber(*matcher_api_, national_number,
302 phone_metadata->short_code())) {
303 // The number is valid for this region.
304 region_code->assign(*it);
308 region_code->assign(RegionCode::GetUnknown());
311 string ShortNumberInfo::GetExampleShortNumber(const string& region_code) const {
312 const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
313 if (!phone_metadata) {
316 const PhoneNumberDesc& desc = phone_metadata->short_code();
317 if (desc.has_example_number()) {
318 return desc.example_number();
323 string ShortNumberInfo::GetExampleShortNumberForCost(const string& region_code,
324 ShortNumberInfo::ShortNumberCost cost) const {
325 const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
326 if (!phone_metadata) {
329 const PhoneNumberDesc* desc = NULL;
332 desc = &(phone_metadata->toll_free());
335 desc = &(phone_metadata->standard_rate());
338 desc = &(phone_metadata->premium_rate());
341 // UNKNOWN_COST numbers are computed by the process of elimination from
342 // the other cost categories.
345 if (desc != NULL && desc->has_example_number()) {
346 return desc->example_number();
351 bool ShortNumberInfo::ConnectsToEmergencyNumber(const string& number,
352 const string& region_code) const {
353 return MatchesEmergencyNumberHelper(number, region_code,
354 true /* allows prefix match */);
357 bool ShortNumberInfo::IsEmergencyNumber(const string& number,
358 const string& region_code) const {
359 return MatchesEmergencyNumberHelper(number, region_code,
360 false /* doesn't allow prefix match */);
363 bool ShortNumberInfo::MatchesEmergencyNumberHelper(const string& number,
364 const string& region_code, bool allow_prefix_match) const {
365 string extracted_number;
366 phone_util_.ExtractPossibleNumber(number, &extracted_number);
367 if (phone_util_.StartsWithPlusCharsPattern(extracted_number)) {
368 // Returns false if the number starts with a plus sign. We don't believe
369 // dialing the country code before emergency numbers (e.g. +1911) works,
370 // but later, if that proves to work, we can add additional logic here to
374 const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
375 if (!metadata || !metadata->has_emergency()) {
378 phone_util_.NormalizeDigitsOnly(&extracted_number);
379 bool allow_prefix_match_for_region =
380 allow_prefix_match &&
381 regions_where_emergency_numbers_must_be_exact_->find(region_code) ==
382 regions_where_emergency_numbers_must_be_exact_->end();
383 return matcher_api_->MatchesNationalNumber(
384 extracted_number, metadata->emergency(), allow_prefix_match_for_region);
387 bool ShortNumberInfo::IsCarrierSpecific(const PhoneNumber& number) const {
388 list<string> region_codes;
389 phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
392 GetRegionCodeForShortNumberFromRegionList(number, region_codes, ®ion_code);
393 string national_number;
394 phone_util_.GetNationalSignificantNumber(number, &national_number);
395 const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
396 return phone_metadata &&
397 MatchesPossibleNumberAndNationalNumber(*matcher_api_, national_number,
398 phone_metadata->carrier_specific());
401 } // namespace phonenumbers