@ -16,13 +16,18 @@
package com.google.i18n.phonenumbers ;
package com.google.i18n.phonenumbers ;
import com.google.i18n.phonenumbers.internal.MatcherApi ;
import com.google.i18n.phonenumbers.internal.RegexBasedMatcher ;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata ;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata ;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc ;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc ;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber ;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.HashSet ;
import java.util.HashSet ;
import java.util.List ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import java.util.Set ;
import java.util.logging.Level ;
import java.util.logging.Level ;
import java.util.logging.Logger ;
import java.util.logging.Logger ;
@ -40,7 +45,7 @@ public class ShortNumberInfo {
private static final Logger logger = Logger . getLogger ( ShortNumberInfo . class . getName ( ) ) ;
private static final Logger logger = Logger . getLogger ( ShortNumberInfo . class . getName ( ) ) ;
private static final ShortNumberInfo INSTANCE =
private static final ShortNumberInfo INSTANCE =
new ShortNumberInfo ( PhoneNumberUtil . getInstanc e( ) ) ;
new ShortNumberInfo ( RegexBasedMatcher . creat e( ) ) ;
/ / In these countries , if extra digits are added to an emergency number , it no longer connects
/ / In these countries , if extra digits are added to an emergency number , it no longer connects
/ / to the emergency service .
/ / to the emergency service .
@ -65,11 +70,34 @@ public class ShortNumberInfo {
return INSTANCE ;
return INSTANCE ;
}
}
private final PhoneNumberUtil phoneUtil ;
/ / MatcherApi supports the basic matching method for checking if a given national number matches
/ / a national number patten or a possible number patten defined in the given
/ / { @code PhoneNumberDesc } .
private final MatcherApi matcherApi ;
/ / A mapping from a country calling code to the region codes which denote the region represented
/ / by that country calling code . In the case of multiple regions sharing a calling code , such as
/ / the NANPA regions , the one indicated with "isMainCountryForCode" in the metadata should be
/ / first .
private final Map < Integer , List < String > > countryCallingCodeToRegionCodeMap ;
/ / @VisibleForTesting
/ / @VisibleForTesting
ShortNumberInfo ( PhoneNumberUtil util ) {
phoneUtil = util ;
ShortNumberInfo ( MatcherApi matcherApi ) {
this . matcherApi = matcherApi ;
/ / TODO : Create ShortNumberInfo for a given map
this . countryCallingCodeToRegionCodeMap =
CountryCodeToRegionCodeMap . getCountryCodeToRegionCodeMap ( ) ;
}
/ * *
* Returns a list with the region codes that match the specific country calling code . For
* non - geographical country calling codes , the region code 001 is returned . Also , in the case
* of no region code being found , an empty list is returned .
* /
private List < String > getRegionCodesForCountryCode ( int countryCallingCode ) {
List < String > regionCodes = countryCallingCodeToRegionCodeMap . get ( countryCallingCode ) ;
return Collections . unmodifiableList ( regionCodes = = null ? new ArrayList < String > ( 0 )
: regionCodes ) ;
}
}
/ * *
/ * *
@ -80,32 +108,55 @@ public class ShortNumberInfo {
* @param shortNumber the short number to check as a string
* @param shortNumber the short number to check as a string
* @param regionDialingFrom the region from which the number is dialed
* @param regionDialingFrom the region from which the number is dialed
* @return whether the number is a possible short number
* @return whether the number is a possible short number
* @deprecated Anyone who was using it and passing in a string with whitespace ( or other
* formatting characters ) would have been getting the wrong result . You should parse
* the string to PhoneNumber and use the method
* { @code # isPossibleShortNumberForRegion ( PhoneNumber , String ) } . This method will be
* removed in the next release .
* /
* /
@Deprecated
public boolean isPossibleShortNumberForRegion ( String shortNumber , String regionDialingFrom ) {
public boolean isPossibleShortNumberForRegion ( String shortNumber , String regionDialingFrom ) {
PhoneMetadata phoneMetadata =
PhoneMetadata phoneMetadata =
MetadataManager . getShortNumberMetadataForRegion ( regionDialingFrom ) ;
MetadataManager . getShortNumberMetadataForRegion ( regionDialingFrom ) ;
if ( phoneMetadata = = null ) {
if ( phoneMetadata = = null ) {
return false ;
return false ;
}
}
PhoneNumberDesc generalDesc = phoneMetadata . getGeneralDesc ( ) ;
return phoneUtil . isNumberPossibleForDesc ( shortNumber , generalDesc ) ;
return matcherApi . matchesPossibleNumber ( shortNumber , phoneMetadata . getGeneralDesc ( ) ) ;
}
/ * *
* Check whether a short number is a possible number when dialed from the given region . This
* provides a more lenient check than { @link # isValidShortNumberForRegion } .
*
* @param number the short number to check
* @param regionDialingFrom the region from which the number is dialed
* @return whether the number is a possible short number
* /
public boolean isPossibleShortNumberForRegion ( PhoneNumber number , String regionDialingFrom ) {
PhoneMetadata phoneMetadata =
MetadataManager . getShortNumberMetadataForRegion ( regionDialingFrom ) ;
if ( phoneMetadata = = null ) {
return false ;
}
return matcherApi . matchesPossibleNumber ( getNationalSignificantNumber ( number ) ,
phoneMetadata . getGeneralDesc ( ) ) ;
}
}
/ * *
/ * *
* Check whether a short number is a possible number . If a country calling code is shared by
* Check whether a short number is a possible number . If a country calling code is shared by
* multiple regions , this returns true if it ' s possible in any of them . This provides a more
* multiple regions , this returns true if it ' s possible in any of them . This provides a more
* lenient check than { @link # isValidShortNumber } . See { @link
* lenient check than { @link # isValidShortNumber } . See { @link
* # isPossibleShortNumberForRegion ( String , String ) } for details .
* # isPossibleShortNumberForRegion ( PhoneNumber , String ) } for details .
*
*
* @param number the short number to check
* @param number the short number to check
* @return whether the number is a possible short number
* @return whether the number is a possible short number
* /
* /
public boolean isPossibleShortNumber ( PhoneNumber number ) {
public boolean isPossibleShortNumber ( PhoneNumber number ) {
List < String > regionCodes = phoneUtil . getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
String shortNumber = phoneUtil . getNationalSignificantNumber ( number ) ;
List < String > regionCodes = getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
String shortNumber = getNationalSignificantNumber ( number ) ;
for ( String region : regionCodes ) {
for ( String region : regionCodes ) {
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion ( region ) ;
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion ( region ) ;
if ( phoneUtil . isNumberPossibleForDesc ( shortNumber , phoneMetadata . getGeneralDesc ( ) ) ) {
if ( matcherApi . matchesPossibleNumber ( shortNumber , phoneMetadata . getGeneralDesc ( ) ) ) {
return true ;
return true ;
}
}
}
}
@ -120,7 +171,13 @@ public class ShortNumberInfo {
* @param shortNumber the short number to check as a string
* @param shortNumber the short number to check as a string
* @param regionDialingFrom the region from which the number is dialed
* @param regionDialingFrom the region from which the number is dialed
* @return whether the short number matches a valid pattern
* @return whether the short number matches a valid pattern
* @deprecated Anyone who was using it and passing in a string with whitespace ( or other
* formatting characters ) would have been getting the wrong result . You should parse
* the string to PhoneNumber and use the method
* { @code # isValidShortNumberForRegion ( PhoneNumber , String ) } . This method will be
* removed in the next release .
* /
* /
@Deprecated
public boolean isValidShortNumberForRegion ( String shortNumber , String regionDialingFrom ) {
public boolean isValidShortNumberForRegion ( String shortNumber , String regionDialingFrom ) {
PhoneMetadata phoneMetadata =
PhoneMetadata phoneMetadata =
MetadataManager . getShortNumberMetadataForRegion ( regionDialingFrom ) ;
MetadataManager . getShortNumberMetadataForRegion ( regionDialingFrom ) ;
@ -128,38 +185,55 @@ public class ShortNumberInfo {
return false ;
return false ;
}
}
PhoneNumberDesc generalDesc = phoneMetadata . getGeneralDesc ( ) ;
PhoneNumberDesc generalDesc = phoneMetadata . getGeneralDesc ( ) ;
if ( ! generalDesc . hasNationalNumberPattern ( ) | |
! phoneUtil . isNumberMatchingDesc ( shortNumber , generalDesc ) ) {
if ( ! matchesPossibleNumberAndNationalNumber ( shortNumber , generalDesc ) ) {
return false ;
return false ;
}
}
PhoneNumberDesc shortNumberDesc = phoneMetadata . getShortCode ( ) ;
PhoneNumberDesc shortNumberDesc = phoneMetadata . getShortCode ( ) ;
if ( ! shortNumberDesc . hasNationalNumberPattern ( ) ) {
logger . log ( Level . WARNING , "No short code national number pattern found for region: " +
regionDialingFrom ) ;
return matchesPossibleNumberAndNationalNumber ( shortNumber , shortNumberDesc ) ;
}
/ * *
* Tests whether a short number matches a valid pattern in a region . Note that this doesn ' t verify
* the number is actually in use , which is impossible to tell by just looking at the number
* itself .
*
* @param number the short number for which we want to test the validity
* @param regionDialingFrom the region from which the number is dialed
* @return whether the short number matches a valid pattern
* /
public boolean isValidShortNumberForRegion ( PhoneNumber number , String regionDialingFrom ) {
PhoneMetadata phoneMetadata =
MetadataManager . getShortNumberMetadataForRegion ( regionDialingFrom ) ;
if ( phoneMetadata = = null ) {
return false ;
}
String shortNumber = getNationalSignificantNumber ( number ) ;
PhoneNumberDesc generalDesc = phoneMetadata . getGeneralDesc ( ) ;
if ( ! matchesPossibleNumberAndNationalNumber ( shortNumber , generalDesc ) ) {
return false ;
return false ;
}
}
return phoneUtil . isNumberMatchingDesc ( shortNumber , shortNumberDesc ) ;
PhoneNumberDesc shortNumberDesc = phoneMetadata . getShortCode ( ) ;
return matchesPossibleNumberAndNationalNumber ( shortNumber , shortNumberDesc ) ;
}
}
/ * *
/ * *
* Tests whether a short number matches a valid pattern . If a country calling code is shared by
* Tests whether a short number matches a valid pattern . If a country calling code is shared by
* multiple regions , this returns true if it ' s valid in any of them . Note that this doesn ' t verify
* multiple regions , this returns true if it ' s valid in any of them . Note that this doesn ' t verify
* the number is actually in use , which is impossible to tell by just looking at the number
* the number is actually in use , which is impossible to tell by just looking at the number
* itself . See { @link # isValidShortNumberForRegion ( String , String ) } for details .
* itself . See { @link # isValidShortNumberForRegion ( PhoneNumber , String ) } for details .
*
*
* @param number the short number for which we want to test the validity
* @param number the short number for which we want to test the validity
* @return whether the short number matches a valid pattern
* @return whether the short number matches a valid pattern
* /
* /
public boolean isValidShortNumber ( PhoneNumber number ) {
public boolean isValidShortNumber ( PhoneNumber number ) {
List < String > regionCodes = phoneUtil . getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
String shortNumber = phoneUtil . getNationalSignificantNumber ( number ) ;
List < String > regionCodes = getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
String regionCode = getRegionCodeForShortNumberFromRegionList ( number , regionCodes ) ;
String regionCode = getRegionCodeForShortNumberFromRegionList ( number , regionCodes ) ;
if ( regionCodes . size ( ) > 1 & & regionCode ! = null ) {
if ( regionCodes . size ( ) > 1 & & regionCode ! = null ) {
/ / If a matching region had been found for the phone number from among two or more regions ,
/ / If a matching region had been found for the phone number from among two or more regions ,
/ / then we have already implicitly verified its validity for that region .
/ / then we have already implicitly verified its validity for that region .
return true ;
return true ;
}
}
return isValidShortNumberForRegion ( shortN umber, regionCode ) ;
return isValidShortNumberForRegion ( n umber, regionCode ) ;
}
}
/ * *
/ * *
@ -183,7 +257,13 @@ public class ShortNumberInfo {
* @return the expected cost category for that region of the short number . Returns UNKNOWN_COST if
* @return the expected cost category for that region of the short number . Returns UNKNOWN_COST if
* the number does not match a cost category . Note that an invalid number may match any cost
* the number does not match a cost category . Note that an invalid number may match any cost
* category .
* category .
* @deprecated Anyone who was using it and passing in a string with whitespace ( or other
* formatting characters ) would have been getting the wrong result . You should parse
* the string to PhoneNumber and use the method
* { @code # getExpectedCostForRegion ( PhoneNumber , String ) } . This method will be
* removed in the next release .
* /
* /
@Deprecated
public ShortNumberCost getExpectedCostForRegion ( String shortNumber , String regionDialingFrom ) {
public ShortNumberCost getExpectedCostForRegion ( String shortNumber , String regionDialingFrom ) {
/ / Note that regionDialingFrom may be null , in which case phoneMetadata will also be null .
/ / Note that regionDialingFrom may be null , in which case phoneMetadata will also be null .
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion (
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion (
@ -194,13 +274,64 @@ public class ShortNumberInfo {
/ / The cost categories are tested in order of decreasing expense , since if for some reason the
/ / The cost categories are tested in order of decreasing expense , since if for some reason the
/ / patterns overlap the most expensive matching cost category should be returned .
/ / patterns overlap the most expensive matching cost category should be returned .
if ( phoneUtil . isNumberMatchingDesc ( shortNumber , phoneMetadata . getPremiumRate ( ) ) ) {
if ( matchesPossibleNumberAndNationalNumber ( shortNumber , phoneMetadata . getPremiumRate ( ) ) ) {
return ShortNumberCost . PREMIUM_RATE ;
}
if ( matchesPossibleNumberAndNationalNumber ( shortNumber , phoneMetadata . getStandardRate ( ) ) ) {
return ShortNumberCost . STANDARD_RATE ;
}
if ( matchesPossibleNumberAndNationalNumber ( shortNumber , phoneMetadata . getTollFree ( ) ) ) {
return ShortNumberCost . TOLL_FREE ;
}
if ( isEmergencyNumber ( shortNumber , regionDialingFrom ) ) {
/ / Emergency numbers are implicitly toll - free .
return ShortNumberCost . TOLL_FREE ;
}
return ShortNumberCost . UNKNOWN_COST ;
}
/ * *
* Gets the expected cost category of a short number when dialed from a region ( however , nothing
* is implied about its validity ) . If it is important that the number is valid , then its validity
* must first be checked using { @link # isValidShortNumberForRegion } . Note that emergency numbers
* are always considered toll - free . Example usage :
* < pre > { @code
* / / The region for which the number was parsed and the region we subsequently check against
* / / need not be the same . Here we parse the number in the US and check it for Canada .
* PhoneNumber number = phoneUtil . parse ( "110" , "US" ) ;
* . . .
* String regionCode = "CA" ;
* ShortNumberInfo shortInfo = ShortNumberInfo . getInstance ( ) ;
* if ( shortInfo . isValidShortNumberForRegion ( shortNumber , regionCode ) ) {
* ShortNumberCost cost = shortInfo . getExpectedCostForRegion ( number , regionCode ) ;
* / / Do something with the cost information here .
* } } < / pre >
*
* @param number the short number for which we want to know the expected cost category
* @param regionDialingFrom the region from which the number is dialed
* @return the expected cost category for that region of the short number . Returns UNKNOWN_COST if
* the number does not match a cost category . Note that an invalid number may match any cost
* category .
* /
public ShortNumberCost getExpectedCostForRegion ( PhoneNumber number , String regionDialingFrom ) {
/ / Note that regionDialingFrom may be null , in which case phoneMetadata will also be null .
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion (
regionDialingFrom ) ;
if ( phoneMetadata = = null ) {
return ShortNumberCost . UNKNOWN_COST ;
}
String shortNumber = getNationalSignificantNumber ( number ) ;
/ / The cost categories are tested in order of decreasing expense , since if for some reason the
/ / patterns overlap the most expensive matching cost category should be returned .
if ( matchesPossibleNumberAndNationalNumber ( shortNumber , phoneMetadata . getPremiumRate ( ) ) ) {
return ShortNumberCost . PREMIUM_RATE ;
return ShortNumberCost . PREMIUM_RATE ;
}
}
if ( phoneUtil . isNumberMatchingDesc ( shortNumber , phoneMetadata . getStandardRate ( ) ) ) {
if ( matchesPossibleNumberAndNationalNumber ( shortNumber , phoneMetadata . getStandardRate ( ) ) ) {
return ShortNumberCost . STANDARD_RATE ;
return ShortNumberCost . STANDARD_RATE ;
}
}
if ( phoneUtil . isNumberMatchingDesc ( shortNumber , phoneMetadata . getTollFree ( ) ) ) {
if ( matchesPossibleNumberAndNationalNumber ( shortNumber , phoneMetadata . getTollFree ( ) ) ) {
return ShortNumberCost . TOLL_FREE ;
return ShortNumberCost . TOLL_FREE ;
}
}
if ( isEmergencyNumber ( shortNumber , regionDialingFrom ) ) {
if ( isEmergencyNumber ( shortNumber , regionDialingFrom ) ) {
@ -213,36 +344,35 @@ public class ShortNumberInfo {
/ * *
/ * *
* Gets the expected cost category of a short number ( however , nothing is implied about its
* Gets the expected cost category of a short number ( however , nothing is implied about its
* validity ) . If the country calling code is unique to a region , this method behaves exactly the
* validity ) . If the country calling code is unique to a region , this method behaves exactly the
* same as { @link # getExpectedCostForRegion ( String , String ) } . However , if the country calling
* code is shared by multiple regions , then it returns the highest cost in the sequence
* same as { @link # getExpectedCostForRegion ( PhoneNumber , String ) } . However , if the country
* calling c ode is shared by multiple regions , then it returns the highest cost in the sequence
* PREMIUM_RATE , UNKNOWN_COST , STANDARD_RATE , TOLL_FREE . The reason for the position of
* PREMIUM_RATE , UNKNOWN_COST , STANDARD_RATE , TOLL_FREE . The reason for the position of
* UNKNOWN_COST in this order is that if a number is UNKNOWN_COST in one region but STANDARD_RATE
* UNKNOWN_COST in this order is that if a number is UNKNOWN_COST in one region but STANDARD_RATE
* or TOLL_FREE in another , its expected cost cannot be estimated as one of the latter since it
* or TOLL_FREE in another , its expected cost cannot be estimated as one of the latter since it
* might be a PREMIUM_RATE number .
* might be a PREMIUM_RATE number .
*
* For example , if a number is STANDARD_RATE in the US , but TOLL_FREE in Canada , the expected cost
* returned by this method will be STANDARD_RATE , since the NANPA countries share the same country
* calling code .
*
* < p >
* For example , if a number is STANDARD_RATE in the US , but TOLL_FREE in Canada , the expected
* cost returned by this method will be STANDARD_RATE , since the NANPA countries share the same
* country c alling code .
* < p >
* Note : If the region from which the number is dialed is known , it is highly preferable to call
* Note : If the region from which the number is dialed is known , it is highly preferable to call
* { @link # getExpectedCostForRegion ( String , String ) } instead .
* { @link # getExpectedCostForRegion ( PhoneNumber , String ) } instead .
*
*
* @param number the short number for which we want to know the expected cost category
* @param number the short number for which we want to know the expected cost category
* @return the highest expected cost category of the short number in the region ( s ) with the given
* @return the highest expected cost category of the short number in the region ( s ) with the given
* country calling code
* country calling code
* /
* /
public ShortNumberCost getExpectedCost ( PhoneNumber number ) {
public ShortNumberCost getExpectedCost ( PhoneNumber number ) {
List < String > regionCodes = phoneUtil . getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
List < String > regionCodes = getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
if ( regionCodes . size ( ) = = 0 ) {
if ( regionCodes . size ( ) = = 0 ) {
return ShortNumberCost . UNKNOWN_COST ;
return ShortNumberCost . UNKNOWN_COST ;
}
}
String shortNumber = phoneUtil . getNationalSignificantNumber ( number ) ;
if ( regionCodes . size ( ) = = 1 ) {
if ( regionCodes . size ( ) = = 1 ) {
return getExpectedCostForRegion ( shortN umber, regionCodes . get ( 0 ) ) ;
return getExpectedCostForRegion ( n umber, regionCodes . get ( 0 ) ) ;
}
}
ShortNumberCost cost = ShortNumberCost . TOLL_FREE ;
ShortNumberCost cost = ShortNumberCost . TOLL_FREE ;
for ( String regionCode : regionCodes ) {
for ( String regionCode : regionCodes ) {
ShortNumberCost costForRegion = getExpectedCostForRegion ( shortN umber, regionCode ) ;
ShortNumberCost costForRegion = getExpectedCostForRegion ( n umber, regionCode ) ;
switch ( costForRegion ) {
switch ( costForRegion ) {
case PREMIUM_RATE :
case PREMIUM_RATE :
return ShortNumberCost . PREMIUM_RATE ;
return ShortNumberCost . PREMIUM_RATE ;
@ -274,11 +404,11 @@ public class ShortNumberInfo {
} else if ( regionCodes . size ( ) = = 1 ) {
} else if ( regionCodes . size ( ) = = 1 ) {
return regionCodes . get ( 0 ) ;
return regionCodes . get ( 0 ) ;
}
}
String nationalNumber = phoneUtil . getNationalSignificantNumber ( number ) ;
String nationalNumber = getNationalSignificantNumber ( number ) ;
for ( String regionCode : regionCodes ) {
for ( String regionCode : regionCodes ) {
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion ( regionCode ) ;
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion ( regionCode ) ;
if ( phoneMetadata ! = null & &
phoneUtil . isNumberMatchingDesc ( nationalNumber , phoneMetadata . getShortCode ( ) ) ) {
if ( phoneMetadata ! = null
& & matchesPossibleNumberAndNationalNumber ( nationalNumber , phoneMetadata . getShortCode ( ) ) ) {
/ / The number is valid for this region .
/ / The number is valid for this region .
return regionCode ;
return regionCode ;
}
}
@ -349,11 +479,14 @@ public class ShortNumberInfo {
}
}
/ * *
/ * *
* Returns true if the number might be used to connect to an emergency service in the given
* region .
*
* This method takes into account cases where the number might contain formatting , or might have
* additional digits appended ( when it is okay to do that in the region specified ) .
* Returns true if the given number , exactly as dialed , might be used to connect to an emergency
* service in the given region .
* < p >
* This method accepts a string , rather than a PhoneNumber , because it needs to distinguish
* cases such as "+1 911" and "911" , where the former may not connect to an emergency service in
* all cases but the latter would . This method takes into account cases where the number might
* contain formatting , or might have additional digits appended ( when it is okay to do that in
* the specified region ) .
*
*
* @param number the phone number to test
* @param number the phone number to test
* @param regionCode the region where the phone number is being dialed
* @param regionCode the region where the phone number is being dialed
@ -364,10 +497,12 @@ public class ShortNumberInfo {
}
}
/ * *
/ * *
* Returns true if the number exactly matches an emergency service number in the given region .
*
* Returns true if the given number exactly matches an emergency service number in the given
* region .
* < p >
* This method takes into account cases where the number might contain formatting , but doesn ' t
* This method takes into account cases where the number might contain formatting , but doesn ' t
* allow additional digits to be appended .
* allow additional digits to be appended . Note that { @code isEmergencyNumber ( number , region ) }
* implies { @code connectsToEmergencyNumber ( number , region ) } .
*
*
* @param number the phone number to test
* @param number the phone number to test
* @param regionCode the region where the phone number is being dialed
* @param regionCode the region where the phone number is being dialed
@ -390,12 +525,13 @@ public class ShortNumberInfo {
if ( metadata = = null | | ! metadata . hasEmergency ( ) ) {
if ( metadata = = null | | ! metadata . hasEmergency ( ) ) {
return false ;
return false ;
}
}
Pattern emergencyNumberPattern =
Pattern . compile ( metadata . getEmergency ( ) . getNationalNumberPattern ( ) ) ;
String normalizedNumber = PhoneNumberUtil . normalizeDigitsOnly ( number ) ;
String normalizedNumber = PhoneNumberUtil . normalizeDigitsOnly ( number ) ;
return ( ! allowPrefixMatch | | REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT . contains ( regionCode ) )
? emergencyNumberPattern . matcher ( normalizedNumber ) . matches ( )
: emergencyNumberPattern . matcher ( normalizedNumber ) . lookingAt ( ) ;
PhoneNumberDesc emergencyDesc = metadata . getEmergency ( ) ;
boolean allowPrefixMatchForRegion =
allowPrefixMatch & & ! REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT . contains ( regionCode ) ;
return matcherApi . matchesNationalNumber ( normalizedNumber , emergencyDesc ,
allowPrefixMatchForRegion ) ;
}
}
/ * *
/ * *
@ -409,11 +545,43 @@ public class ShortNumberInfo {
* number ) .
* number ) .
* /
* /
public boolean isCarrierSpecific ( PhoneNumber number ) {
public boolean isCarrierSpecific ( PhoneNumber number ) {
List < String > regionCodes = phoneUtil . getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
List < String > regionCodes = getRegionCodesForCountryCode ( number . getCountryCode ( ) ) ;
String regionCode = getRegionCodeForShortNumberFromRegionList ( number , regionCodes ) ;
String regionCode = getRegionCodeForShortNumberFromRegionList ( number , regionCodes ) ;
String nationalNumber = phoneUtil . getNationalSignificantNumber ( number ) ;
String nationalNumber = getNationalSignificantNumber ( number ) ;
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion ( regionCode ) ;
PhoneMetadata phoneMetadata = MetadataManager . getShortNumberMetadataForRegion ( regionCode ) ;
return ( phoneMetadata ! = null ) & &
( phoneUtil . isNumberMatchingDesc ( nationalNumber , phoneMetadata . getCarrierSpecific ( ) ) ) ;
return ( phoneMetadata ! = null )
& & ( matchesPossibleNumberAndNationalNumber ( nationalNumber ,
phoneMetadata . getCarrierSpecific ( ) ) ) ;
}
/ * *
* Gets the national significant number of the a phone number . Note a national significant number
* doesn ' t contain a national prefix or any formatting .
* < p >
* This is a temporary duplicate of the { @code getNationalSignificantNumber } method from
* { @code PhoneNumberUtil } . Ultimately a canonical static version should exist in a separate
* utility class ( to prevent { @code ShortNumberInfo } needing to depend on PhoneNumberUtil ) .
*
* @param number the phone number for which the national significant number is needed
* @return the national significant number of the PhoneNumber object passed in
* /
private static String getNationalSignificantNumber ( PhoneNumber number ) {
/ / If leading zero ( s ) have been set , we prefix this now . Note this is not a national prefix .
StringBuilder nationalNumber = new StringBuilder ( ) ;
if ( number . isItalianLeadingZero ( ) ) {
char [ ] zeros = new char [ number . getNumberOfLeadingZeros ( ) ] ;
Arrays . fill ( zeros , '0' ) ;
nationalNumber . append ( new String ( zeros ) ) ;
}
nationalNumber . append ( number . getNationalNumber ( ) ) ;
return nationalNumber . toString ( ) ;
}
/ / TODO : Once we have benchmarked ShortNumberInfo , consider if it is worth keeping
/ / this performance optimization , and if so move this into the matcher implementation .
private boolean matchesPossibleNumberAndNationalNumber ( String number ,
PhoneNumberDesc numberDesc ) {
return matcherApi . matchesPossibleNumber ( number , numberDesc )
& & matcherApi . matchesNationalNumber ( number , numberDesc , false ) ;
}
}
}
}