Home | History | Annotate | Download | only in phonenumbers
      1 /*
      2  * Copyright (C) 2013 The Libphonenumber Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.i18n.phonenumbers;
     18 
     19 import com.google.i18n.phonenumbers.internal.MatcherApi;
     20 import com.google.i18n.phonenumbers.internal.RegexBasedMatcher;
     21 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
     22 import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
     23 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
     24 
     25 import java.util.ArrayList;
     26 import java.util.Arrays;
     27 import java.util.Collections;
     28 import java.util.HashSet;
     29 import java.util.List;
     30 import java.util.Map;
     31 import java.util.Set;
     32 import java.util.logging.Level;
     33 import java.util.logging.Logger;
     34 
     35 /**
     36  * Methods for getting information about short phone numbers, such as short codes and emergency
     37  * numbers. Note that most commercial short numbers are not handled here, but by the
     38  * {@link PhoneNumberUtil}.
     39  *
     40  * @author Shaopeng Jia
     41  * @author David Yonge-Mallo
     42  */
     43 public class ShortNumberInfo {
     44   private static final Logger logger = Logger.getLogger(ShortNumberInfo.class.getName());
     45 
     46   private static final ShortNumberInfo INSTANCE =
     47       new ShortNumberInfo(RegexBasedMatcher.create());
     48 
     49   // In these countries, if extra digits are added to an emergency number, it no longer connects
     50   // to the emergency service.
     51   private static final Set<String> REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT =
     52       new HashSet<String>();
     53   static {
     54     REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.add("BR");
     55     REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.add("CL");
     56     REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.add("NI");
     57   }
     58 
     59   /** Cost categories of short numbers. */
     60   public enum ShortNumberCost {
     61     TOLL_FREE,
     62     STANDARD_RATE,
     63     PREMIUM_RATE,
     64     UNKNOWN_COST;
     65   }
     66 
     67   /** Returns the singleton instance of the ShortNumberInfo. */
     68   public static ShortNumberInfo getInstance() {
     69     return INSTANCE;
     70   }
     71 
     72   // MatcherApi supports the basic matching method for checking if a given national number matches
     73   // a national number pattern defined in the given {@code PhoneNumberDesc}.
     74   private final MatcherApi matcherApi;
     75 
     76   // A mapping from a country calling code to the region codes which denote the region represented
     77   // by that country calling code. In the case of multiple regions sharing a calling code, such as
     78   // the NANPA regions, the one indicated with "isMainCountryForCode" in the metadata should be
     79   // first.
     80   private final Map<Integer, List<String>> countryCallingCodeToRegionCodeMap;
     81 
     82   // @VisibleForTesting
     83   ShortNumberInfo(MatcherApi matcherApi) {
     84     this.matcherApi = matcherApi;
     85     // TODO: Create ShortNumberInfo for a given map
     86     this.countryCallingCodeToRegionCodeMap =
     87         CountryCodeToRegionCodeMap.getCountryCodeToRegionCodeMap();
     88   }
     89 
     90   /**
     91    * Returns a list with the region codes that match the specific country calling code. For
     92    * non-geographical country calling codes, the region code 001 is returned. Also, in the case
     93    * of no region code being found, an empty list is returned.
     94    */
     95   private List<String> getRegionCodesForCountryCode(int countryCallingCode) {
     96     List<String> regionCodes = countryCallingCodeToRegionCodeMap.get(countryCallingCode);
     97     return Collections.unmodifiableList(regionCodes == null ? new ArrayList<String>(0)
     98                                                             : regionCodes);
     99   }
    100 
    101   /**
    102    * Helper method to check that the country calling code of the number matches the region it's
    103    * being dialed from.
    104    */
    105   private boolean regionDialingFromMatchesNumber(PhoneNumber number,
    106       String regionDialingFrom) {
    107     List<String> regionCodes = getRegionCodesForCountryCode(number.getCountryCode());
    108     return regionCodes.contains(regionDialingFrom);
    109   }
    110 
    111   /**
    112    * Check whether a short number is a possible number when dialed from the given region. This
    113    * provides a more lenient check than {@link #isValidShortNumberForRegion}.
    114    *
    115    * @param number the short number to check
    116    * @param regionDialingFrom the region from which the number is dialed
    117    * @return whether the number is a possible short number
    118    */
    119   public boolean isPossibleShortNumberForRegion(PhoneNumber number, String regionDialingFrom) {
    120     if (!regionDialingFromMatchesNumber(number, regionDialingFrom)) {
    121       return false;
    122     }
    123     PhoneMetadata phoneMetadata =
    124         MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);
    125     if (phoneMetadata == null) {
    126       return false;
    127     }
    128     int numberLength = getNationalSignificantNumber(number).length();
    129     return phoneMetadata.getGeneralDesc().getPossibleLengthList().contains(numberLength);
    130   }
    131 
    132   /**
    133    * Check whether a short number is a possible number. If a country calling code is shared by
    134    * multiple regions, this returns true if it's possible in any of them. This provides a more
    135    * lenient check than {@link #isValidShortNumber}. See {@link
    136    * #isPossibleShortNumberForRegion(PhoneNumber, String)} for details.
    137    *
    138    * @param number the short number to check
    139    * @return whether the number is a possible short number
    140    */
    141   public boolean isPossibleShortNumber(PhoneNumber number) {
    142     List<String> regionCodes = getRegionCodesForCountryCode(number.getCountryCode());
    143     int shortNumberLength = getNationalSignificantNumber(number).length();
    144     for (String region : regionCodes) {
    145       PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(region);
    146       if (phoneMetadata == null) {
    147         continue;
    148       }
    149       if (phoneMetadata.getGeneralDesc().getPossibleLengthList().contains(shortNumberLength)) {
    150         return true;
    151       }
    152     }
    153     return false;
    154   }
    155 
    156   /**
    157    * Tests whether a short number matches a valid pattern in a region. Note that this doesn't verify
    158    * the number is actually in use, which is impossible to tell by just looking at the number
    159    * itself.
    160    *
    161    * @param number the short number for which we want to test the validity
    162    * @param regionDialingFrom the region from which the number is dialed
    163    * @return whether the short number matches a valid pattern
    164    */
    165   public boolean isValidShortNumberForRegion(PhoneNumber number, String regionDialingFrom) {
    166     if (!regionDialingFromMatchesNumber(number, regionDialingFrom)) {
    167       return false;
    168     }
    169     PhoneMetadata phoneMetadata =
    170         MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);
    171     if (phoneMetadata == null) {
    172       return false;
    173     }
    174     String shortNumber = getNationalSignificantNumber(number);
    175     PhoneNumberDesc generalDesc = phoneMetadata.getGeneralDesc();
    176     if (!matchesPossibleNumberAndNationalNumber(shortNumber, generalDesc)) {
    177       return false;
    178     }
    179     PhoneNumberDesc shortNumberDesc = phoneMetadata.getShortCode();
    180     return matchesPossibleNumberAndNationalNumber(shortNumber, shortNumberDesc);
    181   }
    182 
    183   /**
    184    * Tests whether a short number matches a valid pattern. If a country calling code is shared by
    185    * multiple regions, this returns true if it's valid in any of them. Note that this doesn't verify
    186    * the number is actually in use, which is impossible to tell by just looking at the number
    187    * itself. See {@link #isValidShortNumberForRegion(PhoneNumber, String)} for details.
    188    *
    189    * @param number the short number for which we want to test the validity
    190    * @return whether the short number matches a valid pattern
    191    */
    192   public boolean isValidShortNumber(PhoneNumber number) {
    193     List<String> regionCodes = getRegionCodesForCountryCode(number.getCountryCode());
    194     String regionCode = getRegionCodeForShortNumberFromRegionList(number, regionCodes);
    195     if (regionCodes.size() > 1 && regionCode != null) {
    196       // If a matching region had been found for the phone number from among two or more regions,
    197       // then we have already implicitly verified its validity for that region.
    198       return true;
    199     }
    200     return isValidShortNumberForRegion(number, regionCode);
    201   }
    202 
    203   /**
    204    * Gets the expected cost category of a short number when dialed from a region (however, nothing
    205    * is implied about its validity). If it is important that the number is valid, then its validity
    206    * must first be checked using {@link #isValidShortNumberForRegion}. Note that emergency numbers
    207    * are always considered toll-free. Example usage:
    208    * <pre>{@code
    209    * // The region for which the number was parsed and the region we subsequently check against
    210    * // need not be the same. Here we parse the number in the US and check it for Canada.
    211    * PhoneNumber number = phoneUtil.parse("110", "US");
    212    * ...
    213    * String regionCode = "CA";
    214    * ShortNumberInfo shortInfo = ShortNumberInfo.getInstance();
    215    * if (shortInfo.isValidShortNumberForRegion(shortNumber, regionCode)) {
    216    *   ShortNumberCost cost = shortInfo.getExpectedCostForRegion(number, regionCode);
    217    *   // Do something with the cost information here.
    218    * }}</pre>
    219    *
    220    * @param number the short number for which we want to know the expected cost category
    221    * @param regionDialingFrom the region from which the number is dialed
    222    * @return the expected cost category for that region of the short number. Returns UNKNOWN_COST if
    223    *     the number does not match a cost category. Note that an invalid number may match any cost
    224    *     category.
    225    */
    226   public ShortNumberCost getExpectedCostForRegion(PhoneNumber number, String regionDialingFrom) {
    227     if (!regionDialingFromMatchesNumber(number, regionDialingFrom)) {
    228       return ShortNumberCost.UNKNOWN_COST;
    229     }
    230     // Note that regionDialingFrom may be null, in which case phoneMetadata will also be null.
    231     PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(
    232         regionDialingFrom);
    233     if (phoneMetadata == null) {
    234       return ShortNumberCost.UNKNOWN_COST;
    235     }
    236 
    237     String shortNumber = getNationalSignificantNumber(number);
    238 
    239     // The possible lengths are not present for a particular sub-type if they match the general
    240     // description; for this reason, we check the possible lengths against the general description
    241     // first to allow an early exit if possible.
    242     if (!phoneMetadata.getGeneralDesc().getPossibleLengthList().contains(shortNumber.length())) {
    243       return ShortNumberCost.UNKNOWN_COST;
    244     }
    245 
    246     // The cost categories are tested in order of decreasing expense, since if for some reason the
    247     // patterns overlap the most expensive matching cost category should be returned.
    248     if (matchesPossibleNumberAndNationalNumber(shortNumber, phoneMetadata.getPremiumRate())) {
    249       return ShortNumberCost.PREMIUM_RATE;
    250     }
    251     if (matchesPossibleNumberAndNationalNumber(shortNumber, phoneMetadata.getStandardRate())) {
    252       return ShortNumberCost.STANDARD_RATE;
    253     }
    254     if (matchesPossibleNumberAndNationalNumber(shortNumber, phoneMetadata.getTollFree())) {
    255       return ShortNumberCost.TOLL_FREE;
    256     }
    257     if (isEmergencyNumber(shortNumber, regionDialingFrom)) {
    258       // Emergency numbers are implicitly toll-free.
    259       return ShortNumberCost.TOLL_FREE;
    260     }
    261     return ShortNumberCost.UNKNOWN_COST;
    262   }
    263 
    264   /**
    265    * Gets the expected cost category of a short number (however, nothing is implied about its
    266    * validity). If the country calling code is unique to a region, this method behaves exactly the
    267    * same as {@link #getExpectedCostForRegion(PhoneNumber, String)}. However, if the country
    268    * calling code is shared by multiple regions, then it returns the highest cost in the sequence
    269    * PREMIUM_RATE, UNKNOWN_COST, STANDARD_RATE, TOLL_FREE. The reason for the position of
    270    * UNKNOWN_COST in this order is that if a number is UNKNOWN_COST in one region but STANDARD_RATE
    271    * or TOLL_FREE in another, its expected cost cannot be estimated as one of the latter since it
    272    * might be a PREMIUM_RATE number.
    273    * <p>
    274    * For example, if a number is STANDARD_RATE in the US, but TOLL_FREE in Canada, the expected
    275    * cost returned by this method will be STANDARD_RATE, since the NANPA countries share the same
    276    * country calling code.
    277    * <p>
    278    * Note: If the region from which the number is dialed is known, it is highly preferable to call
    279    * {@link #getExpectedCostForRegion(PhoneNumber, String)} instead.
    280    *
    281    * @param number the short number for which we want to know the expected cost category
    282    * @return the highest expected cost category of the short number in the region(s) with the given
    283    *     country calling code
    284    */
    285   public ShortNumberCost getExpectedCost(PhoneNumber number) {
    286     List<String> regionCodes = getRegionCodesForCountryCode(number.getCountryCode());
    287     if (regionCodes.size() == 0) {
    288       return ShortNumberCost.UNKNOWN_COST;
    289     }
    290     if (regionCodes.size() == 1) {
    291       return getExpectedCostForRegion(number, regionCodes.get(0));
    292     }
    293     ShortNumberCost cost = ShortNumberCost.TOLL_FREE;
    294     for (String regionCode : regionCodes) {
    295       ShortNumberCost costForRegion = getExpectedCostForRegion(number, regionCode);
    296       switch (costForRegion) {
    297         case PREMIUM_RATE:
    298           return ShortNumberCost.PREMIUM_RATE;
    299         case UNKNOWN_COST:
    300           cost = ShortNumberCost.UNKNOWN_COST;
    301           break;
    302         case STANDARD_RATE:
    303           if (cost != ShortNumberCost.UNKNOWN_COST) {
    304             cost = ShortNumberCost.STANDARD_RATE;
    305           }
    306           break;
    307         case TOLL_FREE:
    308           // Do nothing.
    309           break;
    310         default:
    311           logger.log(Level.SEVERE, "Unrecognised cost for region: " + costForRegion);
    312       }
    313     }
    314     return cost;
    315   }
    316 
    317   // Helper method to get the region code for a given phone number, from a list of possible region
    318   // codes. If the list contains more than one region, the first region for which the number is
    319   // valid is returned.
    320   private String getRegionCodeForShortNumberFromRegionList(PhoneNumber number,
    321                                                            List<String> regionCodes) {
    322     if (regionCodes.size() == 0) {
    323       return null;
    324     } else if (regionCodes.size() == 1) {
    325       return regionCodes.get(0);
    326     }
    327     String nationalNumber = getNationalSignificantNumber(number);
    328     for (String regionCode : regionCodes) {
    329       PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
    330       if (phoneMetadata != null
    331           && matchesPossibleNumberAndNationalNumber(nationalNumber, phoneMetadata.getShortCode())) {
    332         // The number is valid for this region.
    333         return regionCode;
    334       }
    335     }
    336     return null;
    337   }
    338 
    339   /**
    340    * Convenience method to get a list of what regions the library has metadata for.
    341    */
    342   Set<String> getSupportedRegions() {
    343     return MetadataManager.getSupportedShortNumberRegions();
    344   }
    345 
    346   /**
    347    * Gets a valid short number for the specified region.
    348    *
    349    * @param regionCode the region for which an example short number is needed
    350    * @return a valid short number for the specified region. Returns an empty string when the
    351    *     metadata does not contain such information.
    352    */
    353   // @VisibleForTesting
    354   String getExampleShortNumber(String regionCode) {
    355     PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
    356     if (phoneMetadata == null) {
    357       return "";
    358     }
    359     PhoneNumberDesc desc = phoneMetadata.getShortCode();
    360     if (desc.hasExampleNumber()) {
    361       return desc.getExampleNumber();
    362     }
    363     return "";
    364   }
    365 
    366   /**
    367    * Gets a valid short number for the specified cost category.
    368    *
    369    * @param regionCode the region for which an example short number is needed
    370    * @param cost the cost category of number that is needed
    371    * @return a valid short number for the specified region and cost category. Returns an empty
    372    *     string when the metadata does not contain such information, or the cost is UNKNOWN_COST.
    373    */
    374   // @VisibleForTesting
    375   String getExampleShortNumberForCost(String regionCode, ShortNumberCost cost) {
    376     PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
    377     if (phoneMetadata == null) {
    378       return "";
    379     }
    380     PhoneNumberDesc desc = null;
    381     switch (cost) {
    382       case TOLL_FREE:
    383         desc = phoneMetadata.getTollFree();
    384         break;
    385       case STANDARD_RATE:
    386         desc = phoneMetadata.getStandardRate();
    387         break;
    388       case PREMIUM_RATE:
    389         desc = phoneMetadata.getPremiumRate();
    390         break;
    391       default:
    392         // UNKNOWN_COST numbers are computed by the process of elimination from the other cost
    393         // categories.
    394     }
    395     if (desc != null && desc.hasExampleNumber()) {
    396       return desc.getExampleNumber();
    397     }
    398     return "";
    399   }
    400 
    401   /**
    402    * Returns true if the given number, exactly as dialed, might be used to connect to an emergency
    403    * service in the given region.
    404    * <p>
    405    * This method accepts a string, rather than a PhoneNumber, because it needs to distinguish
    406    * cases such as "+1 911" and "911", where the former may not connect to an emergency service in
    407    * all cases but the latter would. This method takes into account cases where the number might
    408    * contain formatting, or might have additional digits appended (when it is okay to do that in
    409    * the specified region).
    410    *
    411    * @param number the phone number to test
    412    * @param regionCode the region where the phone number is being dialed
    413    * @return whether the number might be used to connect to an emergency service in the given region
    414    */
    415   public boolean connectsToEmergencyNumber(String number, String regionCode) {
    416     return matchesEmergencyNumberHelper(number, regionCode, true /* allows prefix match */);
    417   }
    418 
    419   /**
    420    * Returns true if the given number exactly matches an emergency service number in the given
    421    * region.
    422    * <p>
    423    * This method takes into account cases where the number might contain formatting, but doesn't
    424    * allow additional digits to be appended. Note that {@code isEmergencyNumber(number, region)}
    425    * implies {@code connectsToEmergencyNumber(number, region)}.
    426    *
    427    * @param number the phone number to test
    428    * @param regionCode the region where the phone number is being dialed
    429    * @return whether the number exactly matches an emergency services number in the given region
    430    */
    431   public boolean isEmergencyNumber(CharSequence number, String regionCode) {
    432     return matchesEmergencyNumberHelper(number, regionCode, false /* doesn't allow prefix match */);
    433   }
    434 
    435   private boolean matchesEmergencyNumberHelper(CharSequence number, String regionCode,
    436       boolean allowPrefixMatch) {
    437     CharSequence possibleNumber = PhoneNumberUtil.extractPossibleNumber(number);
    438     if (PhoneNumberUtil.PLUS_CHARS_PATTERN.matcher(possibleNumber).lookingAt()) {
    439       // Returns false if the number starts with a plus sign. We don't believe dialing the country
    440       // code before emergency numbers (e.g. +1911) works, but later, if that proves to work, we can
    441       // add additional logic here to handle it.
    442       return false;
    443     }
    444     PhoneMetadata metadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
    445     if (metadata == null || !metadata.hasEmergency()) {
    446       return false;
    447     }
    448 
    449     String normalizedNumber = PhoneNumberUtil.normalizeDigitsOnly(possibleNumber);
    450     boolean allowPrefixMatchForRegion =
    451         allowPrefixMatch && !REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.contains(regionCode);
    452     return matcherApi.matchNationalNumber(normalizedNumber, metadata.getEmergency(),
    453         allowPrefixMatchForRegion);
    454   }
    455 
    456   /**
    457    * Given a valid short number, determines whether it is carrier-specific (however, nothing is
    458    * implied about its validity). Carrier-specific numbers may connect to a different end-point, or
    459    * not connect at all, depending on the user's carrier. If it is important that the number is
    460    * valid, then its validity must first be checked using {@link #isValidShortNumber} or
    461    * {@link #isValidShortNumberForRegion}.
    462    *
    463    * @param number  the valid short number to check
    464    * @return whether the short number is carrier-specific, assuming the input was a valid short
    465    *     number
    466    */
    467   public boolean isCarrierSpecific(PhoneNumber number) {
    468     List<String> regionCodes = getRegionCodesForCountryCode(number.getCountryCode());
    469     String regionCode = getRegionCodeForShortNumberFromRegionList(number, regionCodes);
    470     String nationalNumber = getNationalSignificantNumber(number);
    471     PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
    472     return (phoneMetadata != null)
    473         && (matchesPossibleNumberAndNationalNumber(nationalNumber,
    474                 phoneMetadata.getCarrierSpecific()));
    475   }
    476 
    477   /**
    478    * Given a valid short number, determines whether it is carrier-specific when dialed from the
    479    * given region (however, nothing is implied about its validity). Carrier-specific numbers may
    480    * connect to a different end-point, or not connect at all, depending on the user's carrier. If
    481    * it is important that the number is valid, then its validity must first be checked using
    482    * {@link #isValidShortNumber} or {@link #isValidShortNumberForRegion}. Returns false if the
    483    * number doesn't match the region provided.
    484    *
    485    * @param number  the valid short number to check
    486    * @param regionDialingFrom  the region from which the number is dialed
    487    * @return  whether the short number is carrier-specific in the provided region, assuming the
    488    *     input was a valid short number
    489    */
    490   public boolean isCarrierSpecificForRegion(PhoneNumber number, String regionDialingFrom) {
    491     if (!regionDialingFromMatchesNumber(number, regionDialingFrom)) {
    492       return false;
    493     }
    494     String nationalNumber = getNationalSignificantNumber(number);
    495     PhoneMetadata phoneMetadata =
    496         MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);
    497     return (phoneMetadata != null)
    498         && (matchesPossibleNumberAndNationalNumber(nationalNumber,
    499                 phoneMetadata.getCarrierSpecific()));
    500   }
    501 
    502   /**
    503    * Given a valid short number, determines whether it is an SMS service (however, nothing is
    504    * implied about its validity). An SMS service is where the primary or only intended usage is to
    505    * receive and/or send text messages (SMSs). This includes MMS as MMS numbers downgrade to SMS if
    506    * the other party isn't MMS-capable. If it is important that the number is valid, then its
    507    * validity must first be checked using {@link #isValidShortNumber} or {@link
    508    * #isValidShortNumberForRegion}. Returns false if the number doesn't match the region provided.
    509    *
    510    * @param number  the valid short number to check
    511    * @param regionDialingFrom  the region from which the number is dialed
    512    * @return  whether the short number is an SMS service in the provided region, assuming the input
    513    *     was a valid short number
    514    */
    515   public boolean isSmsServiceForRegion(PhoneNumber number, String regionDialingFrom) {
    516     if (!regionDialingFromMatchesNumber(number, regionDialingFrom)) {
    517       return false;
    518     }
    519     PhoneMetadata phoneMetadata =
    520         MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);
    521     return phoneMetadata != null
    522         && matchesPossibleNumberAndNationalNumber(getNationalSignificantNumber(number),
    523             phoneMetadata.getSmsServices());
    524   }
    525 
    526   /**
    527    * Gets the national significant number of the a phone number. Note a national significant number
    528    * doesn't contain a national prefix or any formatting.
    529    * <p>
    530    * This is a temporary duplicate of the {@code getNationalSignificantNumber} method from
    531    * {@code PhoneNumberUtil}. Ultimately a canonical static version should exist in a separate
    532    * utility class (to prevent {@code ShortNumberInfo} needing to depend on PhoneNumberUtil).
    533    *
    534    * @param number  the phone number for which the national significant number is needed
    535    * @return  the national significant number of the PhoneNumber object passed in
    536    */
    537   private static String getNationalSignificantNumber(PhoneNumber number) {
    538     // If leading zero(s) have been set, we prefix this now. Note this is not a national prefix.
    539     StringBuilder nationalNumber = new StringBuilder();
    540     if (number.isItalianLeadingZero()) {
    541       char[] zeros = new char[number.getNumberOfLeadingZeros()];
    542       Arrays.fill(zeros, '0');
    543       nationalNumber.append(new String(zeros));
    544     }
    545     nationalNumber.append(number.getNationalNumber());
    546     return nationalNumber.toString();
    547   }
    548 
    549   // TODO: Once we have benchmarked ShortNumberInfo, consider if it is worth keeping
    550   // this performance optimization.
    551   private boolean matchesPossibleNumberAndNationalNumber(String number,
    552       PhoneNumberDesc numberDesc) {
    553     if (numberDesc.getPossibleLengthCount() > 0
    554         && !numberDesc.getPossibleLengthList().contains(number.length())) {
    555       return false;
    556     }
    557     return matcherApi.matchNationalNumber(number, numberDesc, false);
    558   }
    559 }
    560