Home | History | Annotate | Download | only in phonenumbers
      1 /*
      2  * Copyright (C) 2009 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.Phonemetadata.NumberFormat;
     20 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
     21 
     22 import java.util.ArrayList;
     23 import java.util.Iterator;
     24 import java.util.List;
     25 import java.util.regex.Matcher;
     26 import java.util.regex.Pattern;
     27 
     28 /**
     29  * A formatter which formats phone numbers as they are entered.
     30  *
     31  * <p>An AsYouTypeFormatter can be created by invoking
     32  * {@link PhoneNumberUtil#getAsYouTypeFormatter}. After that, digits can be added by invoking
     33  * {@link #inputDigit} on the formatter instance, and the partially formatted phone number will be
     34  * returned each time a digit is added. {@link #clear} can be invoked before formatting a new
     35  * number.
     36  *
     37  * <p>See the unittests for more details on how the formatter is to be used.
     38  *
     39  * @author Shaopeng Jia
     40  */
     41 public class AsYouTypeFormatter {
     42   private String currentOutput = "";
     43   private StringBuilder formattingTemplate = new StringBuilder();
     44   // The pattern from numberFormat that is currently used to create formattingTemplate.
     45   private String currentFormattingPattern = "";
     46   private StringBuilder accruedInput = new StringBuilder();
     47   private StringBuilder accruedInputWithoutFormatting = new StringBuilder();
     48   // This indicates whether AsYouTypeFormatter is currently doing the formatting.
     49   private boolean ableToFormat = true;
     50   // Set to true when users enter their own formatting. AsYouTypeFormatter will do no formatting at
     51   // all when this is set to true.
     52   private boolean inputHasFormatting = false;
     53   // This is set to true when we know the user is entering a full national significant number, since
     54   // we have either detected a national prefix or an international dialing prefix. When this is
     55   // true, we will no longer use local number formatting patterns.
     56   private boolean isCompleteNumber = false;
     57   private boolean isExpectingCountryCallingCode = false;
     58   private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
     59   private String defaultCountry;
     60 
     61   // Character used when appropriate to separate a prefix, such as a long NDD or a country calling
     62   // code, from the national number.
     63   private static final char SEPARATOR_BEFORE_NATIONAL_NUMBER = ' ';
     64   private static final PhoneMetadata EMPTY_METADATA =
     65       new PhoneMetadata().setInternationalPrefix("NA");
     66   private PhoneMetadata defaultMetadata;
     67   private PhoneMetadata currentMetadata;
     68 
     69   // A pattern that is used to match character classes in regular expressions. An example of a
     70   // character class is [1-4].
     71   private static final Pattern CHARACTER_CLASS_PATTERN = Pattern.compile("\\[([^\\[\\]])*\\]");
     72   // Any digit in a regular expression that actually denotes a digit. For example, in the regular
     73   // expression 80[0-2]\d{6,10}, the first 2 digits (8 and 0) are standalone digits, but the rest
     74   // are not.
     75   // Two look-aheads are needed because the number following \\d could be a two-digit number, since
     76   // the phone number can be as long as 15 digits.
     77   private static final Pattern STANDALONE_DIGIT_PATTERN = Pattern.compile("\\d(?=[^,}][^,}])");
     78 
     79   // A pattern that is used to determine if a numberFormat under availableFormats is eligible to be
     80   // used by the AYTF. It is eligible when the format element under numberFormat contains groups of
     81   // the dollar sign followed by a single digit, separated by valid phone number punctuation. This
     82   // prevents invalid punctuation (such as the star sign in Israeli star numbers) getting into the
     83   // output of the AYTF.
     84   private static final Pattern ELIGIBLE_FORMAT_PATTERN =
     85       Pattern.compile("[" + PhoneNumberUtil.VALID_PUNCTUATION + "]*"
     86           + "(\\$\\d" + "[" + PhoneNumberUtil.VALID_PUNCTUATION + "]*)+");
     87   // A set of characters that, if found in a national prefix formatting rules, are an indicator to
     88   // us that we should separate the national prefix from the number when formatting.
     89   private static final Pattern NATIONAL_PREFIX_SEPARATORS_PATTERN = Pattern.compile("[- ]");
     90 
     91   // This is the minimum length of national number accrued that is required to trigger the
     92   // formatter. The first element of the leadingDigitsPattern of each numberFormat contains a
     93   // regular expression that matches up to this number of digits.
     94   private static final int MIN_LEADING_DIGITS_LENGTH = 3;
     95 
     96   // The digits that have not been entered yet will be represented by a \u2008, the punctuation
     97   // space.
     98   private static final String DIGIT_PLACEHOLDER = "\u2008";
     99   private static final Pattern DIGIT_PATTERN = Pattern.compile(DIGIT_PLACEHOLDER);
    100   private int lastMatchPosition = 0;
    101   // The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
    102   // found in the original sequence of characters the user entered.
    103   private int originalPosition = 0;
    104   // The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
    105   // found in accruedInputWithoutFormatting.
    106   private int positionToRemember = 0;
    107   // This contains anything that has been entered so far preceding the national significant number,
    108   // and it is formatted (e.g. with space inserted). For example, this can contain IDD, country
    109   // code, and/or NDD, etc.
    110   private StringBuilder prefixBeforeNationalNumber = new StringBuilder();
    111   private boolean shouldAddSpaceAfterNationalPrefix = false;
    112   // This contains the national prefix that has been extracted. It contains only digits without
    113   // formatting.
    114   private String extractedNationalPrefix = "";
    115   private StringBuilder nationalNumber = new StringBuilder();
    116   private List<NumberFormat> possibleFormats = new ArrayList<NumberFormat>();
    117 
    118   // A cache for frequently used country-specific regular expressions.
    119   private RegexCache regexCache = new RegexCache(64);
    120 
    121   /**
    122    * Constructs an as-you-type formatter. Should be obtained from {@link
    123    * PhoneNumberUtil#getAsYouTypeFormatter}.
    124    *
    125    * @param regionCode  the country/region where the phone number is being entered
    126    */
    127   AsYouTypeFormatter(String regionCode) {
    128     defaultCountry = regionCode;
    129     currentMetadata = getMetadataForRegion(defaultCountry);
    130     defaultMetadata = currentMetadata;
    131   }
    132 
    133   // The metadata needed by this class is the same for all regions sharing the same country calling
    134   // code. Therefore, we return the metadata for "main" region for this country calling code.
    135   private PhoneMetadata getMetadataForRegion(String regionCode) {
    136     int countryCallingCode = phoneUtil.getCountryCodeForRegion(regionCode);
    137     String mainCountry = phoneUtil.getRegionCodeForCountryCode(countryCallingCode);
    138     PhoneMetadata metadata = phoneUtil.getMetadataForRegion(mainCountry);
    139     if (metadata != null) {
    140       return metadata;
    141     }
    142     // Set to a default instance of the metadata. This allows us to function with an incorrect
    143     // region code, even if formatting only works for numbers specified with "+".
    144     return EMPTY_METADATA;
    145   }
    146 
    147   // Returns true if a new template is created as opposed to reusing the existing template.
    148   private boolean maybeCreateNewTemplate() {
    149     // When there are multiple available formats, the formatter uses the first format where a
    150     // formatting template could be created.
    151     Iterator<NumberFormat> it = possibleFormats.iterator();
    152     while (it.hasNext()) {
    153       NumberFormat numberFormat = it.next();
    154       String pattern = numberFormat.getPattern();
    155       if (currentFormattingPattern.equals(pattern)) {
    156         return false;
    157       }
    158       if (createFormattingTemplate(numberFormat)) {
    159         currentFormattingPattern = pattern;
    160         shouldAddSpaceAfterNationalPrefix =
    161             NATIONAL_PREFIX_SEPARATORS_PATTERN.matcher(
    162                 numberFormat.getNationalPrefixFormattingRule()).find();
    163         // With a new formatting template, the matched position using the old template needs to be
    164         // reset.
    165         lastMatchPosition = 0;
    166         return true;
    167       } else {  // Remove the current number format from possibleFormats.
    168         it.remove();
    169       }
    170     }
    171     ableToFormat = false;
    172     return false;
    173   }
    174 
    175   private void getAvailableFormats(String leadingDigits) {
    176     List<NumberFormat> formatList =
    177         (isCompleteNumber && currentMetadata.intlNumberFormatSize() > 0)
    178         ? currentMetadata.intlNumberFormats()
    179         : currentMetadata.numberFormats();
    180     boolean nationalPrefixIsUsedByCountry = currentMetadata.hasNationalPrefix();
    181     for (NumberFormat format : formatList) {
    182       if (!nationalPrefixIsUsedByCountry
    183           || isCompleteNumber
    184           || format.isNationalPrefixOptionalWhenFormatting()
    185           || PhoneNumberUtil.formattingRuleHasFirstGroupOnly(
    186               format.getNationalPrefixFormattingRule())) {
    187         if (isFormatEligible(format.getFormat())) {
    188           possibleFormats.add(format);
    189         }
    190       }
    191     }
    192     narrowDownPossibleFormats(leadingDigits);
    193   }
    194 
    195   private boolean isFormatEligible(String format) {
    196     return ELIGIBLE_FORMAT_PATTERN.matcher(format).matches();
    197   }
    198 
    199   private void narrowDownPossibleFormats(String leadingDigits) {
    200     int indexOfLeadingDigitsPattern = leadingDigits.length() - MIN_LEADING_DIGITS_LENGTH;
    201     Iterator<NumberFormat> it = possibleFormats.iterator();
    202     while (it.hasNext()) {
    203       NumberFormat format = it.next();
    204       if (format.leadingDigitsPatternSize() == 0) {
    205         // Keep everything that isn't restricted by leading digits.
    206         continue;
    207       }
    208       int lastLeadingDigitsPattern =
    209           Math.min(indexOfLeadingDigitsPattern, format.leadingDigitsPatternSize() - 1);
    210       Pattern leadingDigitsPattern = regexCache.getPatternForRegex(
    211           format.getLeadingDigitsPattern(lastLeadingDigitsPattern));
    212       Matcher m = leadingDigitsPattern.matcher(leadingDigits);
    213       if (!m.lookingAt()) {
    214         it.remove();
    215       }
    216     }
    217   }
    218 
    219   private boolean createFormattingTemplate(NumberFormat format) {
    220     String numberPattern = format.getPattern();
    221 
    222     // The formatter doesn't format numbers when numberPattern contains "|", e.g.
    223     // (20|3)\d{4}. In those cases we quickly return.
    224     if (numberPattern.indexOf('|') != -1) {
    225       return false;
    226     }
    227 
    228     // Replace anything in the form of [..] with \d
    229     numberPattern = CHARACTER_CLASS_PATTERN.matcher(numberPattern).replaceAll("\\\\d");
    230 
    231     // Replace any standalone digit (not the one in d{}) with \d
    232     numberPattern = STANDALONE_DIGIT_PATTERN.matcher(numberPattern).replaceAll("\\\\d");
    233     formattingTemplate.setLength(0);
    234     String tempTemplate = getFormattingTemplate(numberPattern, format.getFormat());
    235     if (tempTemplate.length() > 0) {
    236       formattingTemplate.append(tempTemplate);
    237       return true;
    238     }
    239     return false;
    240   }
    241 
    242   // Gets a formatting template which can be used to efficiently format a partial number where
    243   // digits are added one by one.
    244   private String getFormattingTemplate(String numberPattern, String numberFormat) {
    245     // Creates a phone number consisting only of the digit 9 that matches the
    246     // numberPattern by applying the pattern to the longestPhoneNumber string.
    247     String longestPhoneNumber = "999999999999999";
    248     Matcher m = regexCache.getPatternForRegex(numberPattern).matcher(longestPhoneNumber);
    249     m.find();  // this will always succeed
    250     String aPhoneNumber = m.group();
    251     // No formatting template can be created if the number of digits entered so far is longer than
    252     // the maximum the current formatting rule can accommodate.
    253     if (aPhoneNumber.length() < nationalNumber.length()) {
    254       return "";
    255     }
    256     // Formats the number according to numberFormat
    257     String template = aPhoneNumber.replaceAll(numberPattern, numberFormat);
    258     // Replaces each digit with character DIGIT_PLACEHOLDER
    259     template = template.replaceAll("9", DIGIT_PLACEHOLDER);
    260     return template;
    261   }
    262 
    263   /**
    264    * Clears the internal state of the formatter, so it can be reused.
    265    */
    266   public void clear() {
    267     currentOutput = "";
    268     accruedInput.setLength(0);
    269     accruedInputWithoutFormatting.setLength(0);
    270     formattingTemplate.setLength(0);
    271     lastMatchPosition = 0;
    272     currentFormattingPattern = "";
    273     prefixBeforeNationalNumber.setLength(0);
    274     extractedNationalPrefix = "";
    275     nationalNumber.setLength(0);
    276     ableToFormat = true;
    277     inputHasFormatting = false;
    278     positionToRemember = 0;
    279     originalPosition = 0;
    280     isCompleteNumber = false;
    281     isExpectingCountryCallingCode = false;
    282     possibleFormats.clear();
    283     shouldAddSpaceAfterNationalPrefix = false;
    284     if (!currentMetadata.equals(defaultMetadata)) {
    285       currentMetadata = getMetadataForRegion(defaultCountry);
    286     }
    287   }
    288 
    289   /**
    290    * Formats a phone number on-the-fly as each digit is entered.
    291    *
    292    * @param nextChar  the most recently entered digit of a phone number. Formatting characters are
    293    *     allowed, but as soon as they are encountered this method formats the number as entered and
    294    *     not "as you type" anymore. Full width digits and Arabic-indic digits are allowed, and will
    295    *     be shown as they are.
    296    * @return  the partially formatted phone number.
    297    */
    298   public String inputDigit(char nextChar) {
    299     currentOutput = inputDigitWithOptionToRememberPosition(nextChar, false);
    300     return currentOutput;
    301   }
    302 
    303   /**
    304    * Same as {@link #inputDigit}, but remembers the position where {@code nextChar} is inserted, so
    305    * that it can be retrieved later by using {@link #getRememberedPosition}. The remembered
    306    * position will be automatically adjusted if additional formatting characters are later
    307    * inserted/removed in front of {@code nextChar}.
    308    */
    309   public String inputDigitAndRememberPosition(char nextChar) {
    310     currentOutput = inputDigitWithOptionToRememberPosition(nextChar, true);
    311     return currentOutput;
    312   }
    313 
    314   @SuppressWarnings("fallthrough")
    315   private String inputDigitWithOptionToRememberPosition(char nextChar, boolean rememberPosition) {
    316     accruedInput.append(nextChar);
    317     if (rememberPosition) {
    318       originalPosition = accruedInput.length();
    319     }
    320     // We do formatting on-the-fly only when each character entered is either a digit, or a plus
    321     // sign (accepted at the start of the number only).
    322     if (!isDigitOrLeadingPlusSign(nextChar)) {
    323       ableToFormat = false;
    324       inputHasFormatting = true;
    325     } else {
    326       nextChar = normalizeAndAccrueDigitsAndPlusSign(nextChar, rememberPosition);
    327     }
    328     if (!ableToFormat) {
    329       // When we are unable to format because of reasons other than that formatting chars have been
    330       // entered, it can be due to really long IDDs or NDDs. If that is the case, we might be able
    331       // to do formatting again after extracting them.
    332       if (inputHasFormatting) {
    333         return accruedInput.toString();
    334       } else if (attemptToExtractIdd()) {
    335         if (attemptToExtractCountryCallingCode()) {
    336           return attemptToChoosePatternWithPrefixExtracted();
    337         }
    338       } else if (ableToExtractLongerNdd()) {
    339         // Add an additional space to separate long NDD and national significant number for
    340         // readability. We don't set shouldAddSpaceAfterNationalPrefix to true, since we don't want
    341         // this to change later when we choose formatting templates.
    342         prefixBeforeNationalNumber.append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
    343         return attemptToChoosePatternWithPrefixExtracted();
    344       }
    345       return accruedInput.toString();
    346     }
    347 
    348     // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits (the plus
    349     // sign is counted as a digit as well for this purpose) have been entered.
    350     switch (accruedInputWithoutFormatting.length()) {
    351       case 0:
    352       case 1:
    353       case 2:
    354         return accruedInput.toString();
    355       case 3:
    356         if (attemptToExtractIdd()) {
    357           isExpectingCountryCallingCode = true;
    358         } else {  // No IDD or plus sign is found, might be entering in national format.
    359           extractedNationalPrefix = removeNationalPrefixFromNationalNumber();
    360           return attemptToChooseFormattingPattern();
    361         }
    362         // fall through
    363       default:
    364         if (isExpectingCountryCallingCode) {
    365           if (attemptToExtractCountryCallingCode()) {
    366             isExpectingCountryCallingCode = false;
    367           }
    368           return prefixBeforeNationalNumber + nationalNumber.toString();
    369         }
    370         if (possibleFormats.size() > 0) {  // The formatting patterns are already chosen.
    371           String tempNationalNumber = inputDigitHelper(nextChar);
    372           // See if the accrued digits can be formatted properly already. If not, use the results
    373           // from inputDigitHelper, which does formatting based on the formatting pattern chosen.
    374           String formattedNumber = attemptToFormatAccruedDigits();
    375           if (formattedNumber.length() > 0) {
    376             return formattedNumber;
    377           }
    378           narrowDownPossibleFormats(nationalNumber.toString());
    379           if (maybeCreateNewTemplate()) {
    380             return inputAccruedNationalNumber();
    381           }
    382           return ableToFormat
    383              ? appendNationalNumber(tempNationalNumber)
    384              : accruedInput.toString();
    385         } else {
    386           return attemptToChooseFormattingPattern();
    387         }
    388     }
    389   }
    390 
    391   private String attemptToChoosePatternWithPrefixExtracted() {
    392     ableToFormat = true;
    393     isExpectingCountryCallingCode = false;
    394     possibleFormats.clear();
    395     lastMatchPosition = 0;
    396     formattingTemplate.setLength(0);
    397     currentFormattingPattern = "";
    398     return attemptToChooseFormattingPattern();
    399   }
    400 
    401   // @VisibleForTesting
    402   String getExtractedNationalPrefix() {
    403     return extractedNationalPrefix;
    404   }
    405 
    406   // Some national prefixes are a substring of others. If extracting the shorter NDD doesn't result
    407   // in a number we can format, we try to see if we can extract a longer version here.
    408   private boolean ableToExtractLongerNdd() {
    409     if (extractedNationalPrefix.length() > 0) {
    410       // Put the extracted NDD back to the national number before attempting to extract a new NDD.
    411       nationalNumber.insert(0, extractedNationalPrefix);
    412       // Remove the previously extracted NDD from prefixBeforeNationalNumber. We cannot simply set
    413       // it to empty string because people sometimes incorrectly enter national prefix after the
    414       // country code, e.g. +44 (0)20-1234-5678.
    415       int indexOfPreviousNdd = prefixBeforeNationalNumber.lastIndexOf(extractedNationalPrefix);
    416       prefixBeforeNationalNumber.setLength(indexOfPreviousNdd);
    417     }
    418     return !extractedNationalPrefix.equals(removeNationalPrefixFromNationalNumber());
    419   }
    420 
    421   private boolean isDigitOrLeadingPlusSign(char nextChar) {
    422     return Character.isDigit(nextChar)
    423         || (accruedInput.length() == 1
    424             && PhoneNumberUtil.PLUS_CHARS_PATTERN.matcher(Character.toString(nextChar)).matches());
    425   }
    426 
    427   /**
    428    * Checks to see if there is an exact pattern match for these digits. If so, we should use this
    429    * instead of any other formatting template whose leadingDigitsPattern also matches the input.
    430    */
    431   String attemptToFormatAccruedDigits() {
    432     for (NumberFormat numberFormat : possibleFormats) {
    433       Matcher m = regexCache.getPatternForRegex(numberFormat.getPattern()).matcher(nationalNumber);
    434       if (m.matches()) {
    435         shouldAddSpaceAfterNationalPrefix =
    436             NATIONAL_PREFIX_SEPARATORS_PATTERN.matcher(
    437                 numberFormat.getNationalPrefixFormattingRule()).find();
    438         String formattedNumber = m.replaceAll(numberFormat.getFormat());
    439         return appendNationalNumber(formattedNumber);
    440       }
    441     }
    442     return "";
    443   }
    444 
    445   /**
    446    * Returns the current position in the partially formatted phone number of the character which was
    447    * previously passed in as the parameter of {@link #inputDigitAndRememberPosition}.
    448    */
    449   public int getRememberedPosition() {
    450     if (!ableToFormat) {
    451       return originalPosition;
    452     }
    453     int accruedInputIndex = 0;
    454     int currentOutputIndex = 0;
    455     while (accruedInputIndex < positionToRemember && currentOutputIndex < currentOutput.length()) {
    456       if (accruedInputWithoutFormatting.charAt(accruedInputIndex)
    457           == currentOutput.charAt(currentOutputIndex)) {
    458         accruedInputIndex++;
    459       }
    460       currentOutputIndex++;
    461     }
    462     return currentOutputIndex;
    463   }
    464 
    465   /**
    466    * Combines the national number with any prefix (IDD/+ and country code or national prefix) that
    467    * was collected. A space will be inserted between them if the current formatting template
    468    * indicates this to be suitable.
    469    */
    470   private String appendNationalNumber(String nationalNumber) {
    471     int prefixBeforeNationalNumberLength = prefixBeforeNationalNumber.length();
    472     if (shouldAddSpaceAfterNationalPrefix && prefixBeforeNationalNumberLength > 0
    473         && prefixBeforeNationalNumber.charAt(prefixBeforeNationalNumberLength - 1)
    474             != SEPARATOR_BEFORE_NATIONAL_NUMBER) {
    475       // We want to add a space after the national prefix if the national prefix formatting rule
    476       // indicates that this would normally be done, with the exception of the case where we already
    477       // appended a space because the NDD was surprisingly long.
    478       return new String(prefixBeforeNationalNumber) + SEPARATOR_BEFORE_NATIONAL_NUMBER
    479           + nationalNumber;
    480     } else {
    481       return prefixBeforeNationalNumber + nationalNumber;
    482     }
    483   }
    484 
    485   /**
    486    * Attempts to set the formatting template and returns a string which contains the formatted
    487    * version of the digits entered so far.
    488    */
    489   private String attemptToChooseFormattingPattern() {
    490     // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits of national
    491     // number (excluding national prefix) have been entered.
    492     if (nationalNumber.length() >= MIN_LEADING_DIGITS_LENGTH) {
    493 
    494       getAvailableFormats(nationalNumber.toString());
    495       // See if the accrued digits can be formatted properly already.
    496       String formattedNumber = attemptToFormatAccruedDigits();
    497       if (formattedNumber.length() > 0) {
    498         return formattedNumber;
    499       }
    500       return maybeCreateNewTemplate() ? inputAccruedNationalNumber() : accruedInput.toString();
    501     } else {
    502       return appendNationalNumber(nationalNumber.toString());
    503     }
    504   }
    505 
    506   /**
    507    * Invokes inputDigitHelper on each digit of the national number accrued, and returns a formatted
    508    * string in the end.
    509    */
    510   private String inputAccruedNationalNumber() {
    511     int lengthOfNationalNumber = nationalNumber.length();
    512     if (lengthOfNationalNumber > 0) {
    513       String tempNationalNumber = "";
    514       for (int i = 0; i < lengthOfNationalNumber; i++) {
    515         tempNationalNumber = inputDigitHelper(nationalNumber.charAt(i));
    516       }
    517       return ableToFormat ? appendNationalNumber(tempNationalNumber) : accruedInput.toString();
    518     } else {
    519       return prefixBeforeNationalNumber.toString();
    520     }
    521   }
    522 
    523   /**
    524    * Returns true if the current country is a NANPA country and the national number begins with
    525    * the national prefix.
    526    */
    527   private boolean isNanpaNumberWithNationalPrefix() {
    528     // For NANPA numbers beginning with 1[2-9], treat the 1 as the national prefix. The reason is
    529     // that national significant numbers in NANPA always start with [2-9] after the national prefix.
    530     // Numbers beginning with 1[01] can only be short/emergency numbers, which don't need the
    531     // national prefix.
    532     return (currentMetadata.getCountryCode() == 1) && (nationalNumber.charAt(0) == '1')
    533         && (nationalNumber.charAt(1) != '0') && (nationalNumber.charAt(1) != '1');
    534   }
    535 
    536   // Returns the national prefix extracted, or an empty string if it is not present.
    537   private String removeNationalPrefixFromNationalNumber() {
    538     int startOfNationalNumber = 0;
    539     if (isNanpaNumberWithNationalPrefix()) {
    540       startOfNationalNumber = 1;
    541       prefixBeforeNationalNumber.append('1').append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
    542       isCompleteNumber = true;
    543     } else if (currentMetadata.hasNationalPrefixForParsing()) {
    544       Pattern nationalPrefixForParsing =
    545           regexCache.getPatternForRegex(currentMetadata.getNationalPrefixForParsing());
    546       Matcher m = nationalPrefixForParsing.matcher(nationalNumber);
    547       // Since some national prefix patterns are entirely optional, check that a national prefix
    548       // could actually be extracted.
    549       if (m.lookingAt() && m.end() > 0) {
    550         // When the national prefix is detected, we use international formatting rules instead of
    551         // national ones, because national formatting rules could contain local formatting rules
    552         // for numbers entered without area code.
    553         isCompleteNumber = true;
    554         startOfNationalNumber = m.end();
    555         prefixBeforeNationalNumber.append(nationalNumber.substring(0, startOfNationalNumber));
    556       }
    557     }
    558     String nationalPrefix = nationalNumber.substring(0, startOfNationalNumber);
    559     nationalNumber.delete(0, startOfNationalNumber);
    560     return nationalPrefix;
    561   }
    562 
    563   /**
    564    * Extracts IDD and plus sign to prefixBeforeNationalNumber when they are available, and places
    565    * the remaining input into nationalNumber.
    566    *
    567    * @return  true when accruedInputWithoutFormatting begins with the plus sign or valid IDD for
    568    *     defaultCountry.
    569    */
    570   private boolean attemptToExtractIdd() {
    571     Pattern internationalPrefix =
    572         regexCache.getPatternForRegex("\\" + PhoneNumberUtil.PLUS_SIGN + "|"
    573             + currentMetadata.getInternationalPrefix());
    574     Matcher iddMatcher = internationalPrefix.matcher(accruedInputWithoutFormatting);
    575     if (iddMatcher.lookingAt()) {
    576       isCompleteNumber = true;
    577       int startOfCountryCallingCode = iddMatcher.end();
    578       nationalNumber.setLength(0);
    579       nationalNumber.append(accruedInputWithoutFormatting.substring(startOfCountryCallingCode));
    580       prefixBeforeNationalNumber.setLength(0);
    581       prefixBeforeNationalNumber.append(
    582           accruedInputWithoutFormatting.substring(0, startOfCountryCallingCode));
    583       if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN) {
    584         prefixBeforeNationalNumber.append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
    585       }
    586       return true;
    587     }
    588     return false;
    589   }
    590 
    591   /**
    592    * Extracts the country calling code from the beginning of nationalNumber to
    593    * prefixBeforeNationalNumber when they are available, and places the remaining input into
    594    * nationalNumber.
    595    *
    596    * @return  true when a valid country calling code can be found.
    597    */
    598   private boolean attemptToExtractCountryCallingCode() {
    599     if (nationalNumber.length() == 0) {
    600       return false;
    601     }
    602     StringBuilder numberWithoutCountryCallingCode = new StringBuilder();
    603     int countryCode = phoneUtil.extractCountryCode(nationalNumber, numberWithoutCountryCallingCode);
    604     if (countryCode == 0) {
    605       return false;
    606     }
    607     nationalNumber.setLength(0);
    608     nationalNumber.append(numberWithoutCountryCallingCode);
    609     String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
    610     if (PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY.equals(newRegionCode)) {
    611       currentMetadata = phoneUtil.getMetadataForNonGeographicalRegion(countryCode);
    612     } else if (!newRegionCode.equals(defaultCountry)) {
    613       currentMetadata = getMetadataForRegion(newRegionCode);
    614     }
    615     String countryCodeString = Integer.toString(countryCode);
    616     prefixBeforeNationalNumber.append(countryCodeString).append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
    617     // When we have successfully extracted the IDD, the previously extracted NDD should be cleared
    618     // because it is no longer valid.
    619     extractedNationalPrefix = "";
    620     return true;
    621   }
    622 
    623   // Accrues digits and the plus sign to accruedInputWithoutFormatting for later use. If nextChar
    624   // contains a digit in non-ASCII format (e.g. the full-width version of digits), it is first
    625   // normalized to the ASCII version. The return value is nextChar itself, or its normalized
    626   // version, if nextChar is a digit in non-ASCII format. This method assumes its input is either a
    627   // digit or the plus sign.
    628   private char normalizeAndAccrueDigitsAndPlusSign(char nextChar, boolean rememberPosition) {
    629     char normalizedChar;
    630     if (nextChar == PhoneNumberUtil.PLUS_SIGN) {
    631       normalizedChar = nextChar;
    632       accruedInputWithoutFormatting.append(nextChar);
    633     } else {
    634       int radix = 10;
    635       normalizedChar = Character.forDigit(Character.digit(nextChar, radix), radix);
    636       accruedInputWithoutFormatting.append(normalizedChar);
    637       nationalNumber.append(normalizedChar);
    638     }
    639     if (rememberPosition) {
    640       positionToRemember = accruedInputWithoutFormatting.length();
    641     }
    642     return normalizedChar;
    643   }
    644 
    645   private String inputDigitHelper(char nextChar) {
    646     // Note that formattingTemplate is not guaranteed to have a value, it could be empty, e.g.
    647     // when the next digit is entered after extracting an IDD or NDD.
    648     Matcher digitMatcher = DIGIT_PATTERN.matcher(formattingTemplate);
    649     if (digitMatcher.find(lastMatchPosition)) {
    650       String tempTemplate = digitMatcher.replaceFirst(Character.toString(nextChar));
    651       formattingTemplate.replace(0, tempTemplate.length(), tempTemplate);
    652       lastMatchPosition = digitMatcher.start();
    653       return formattingTemplate.substring(0, lastMatchPosition + 1);
    654     } else {
    655       if (possibleFormats.size() == 1) {
    656         // More digits are entered than we could handle, and there are no other valid patterns to
    657         // try.
    658         ableToFormat = false;
    659       }  // else, we just reset the formatting pattern.
    660       currentFormattingPattern = "";
    661       return accruedInput.toString();
    662     }
    663   }
    664 }
    665