/* GENERATED SOURCE. DO NOT MODIFY. */ /* * Copyright (C) 2009 The Libphonenumber Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.i18n.phonenumbers; import com.android.i18n.phonenumbers.Phonemetadata.NumberFormat; import com.android.i18n.phonenumbers.Phonemetadata.PhoneMetadata; import com.android.i18n.phonenumbers.internal.RegexCache; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A formatter which formats phone numbers as they are entered. * *

An AsYouTypeFormatter can be created by invoking * {@link PhoneNumberUtil#getAsYouTypeFormatter}. After that, digits can be added by invoking * {@link #inputDigit} on the formatter instance, and the partially formatted phone number will be * returned each time a digit is added. {@link #clear} can be invoked before formatting a new * number. * *

See the unittests for more details on how the formatter is to be used. * * @author Shaopeng Jia * @hide This class is not part of the Android public SDK API */ public class AsYouTypeFormatter { private String currentOutput = ""; private StringBuilder formattingTemplate = new StringBuilder(); // The pattern from numberFormat that is currently used to create formattingTemplate. private String currentFormattingPattern = ""; private StringBuilder accruedInput = new StringBuilder(); private StringBuilder accruedInputWithoutFormatting = new StringBuilder(); // This indicates whether AsYouTypeFormatter is currently doing the formatting. private boolean ableToFormat = true; // Set to true when users enter their own formatting. AsYouTypeFormatter will do no formatting at // all when this is set to true. private boolean inputHasFormatting = false; // This is set to true when we know the user is entering a full national significant number, since // we have either detected a national prefix or an international dialing prefix. When this is // true, we will no longer use local number formatting patterns. private boolean isCompleteNumber = false; private boolean isExpectingCountryCallingCode = false; private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); private String defaultCountry; // Character used when appropriate to separate a prefix, such as a long NDD or a country calling // code, from the national number. private static final char SEPARATOR_BEFORE_NATIONAL_NUMBER = ' '; private static final PhoneMetadata EMPTY_METADATA = new PhoneMetadata().setInternationalPrefix("NA"); private PhoneMetadata defaultMetadata; private PhoneMetadata currentMetadata; // A pattern that is used to determine if a numberFormat under availableFormats is eligible to be // used by the AYTF. It is eligible when the format element under numberFormat contains groups of // the dollar sign followed by a single digit, separated by valid phone number punctuation. This // prevents invalid punctuation (such as the star sign in Israeli star numbers) getting into the // output of the AYTF. private static final Pattern ELIGIBLE_FORMAT_PATTERN = Pattern.compile("[" + PhoneNumberUtil.VALID_PUNCTUATION + "]*" + "(\\$\\d" + "[" + PhoneNumberUtil.VALID_PUNCTUATION + "]*)+"); // A set of characters that, if found in a national prefix formatting rules, are an indicator to // us that we should separate the national prefix from the number when formatting. private static final Pattern NATIONAL_PREFIX_SEPARATORS_PATTERN = Pattern.compile("[- ]"); // This is the minimum length of national number accrued that is required to trigger the // formatter. The first element of the leadingDigitsPattern of each numberFormat contains a // regular expression that matches up to this number of digits. private static final int MIN_LEADING_DIGITS_LENGTH = 3; // The digits that have not been entered yet will be represented by a \u2008, the punctuation // space. private static final String DIGIT_PLACEHOLDER = "\u2008"; private static final Pattern DIGIT_PATTERN = Pattern.compile(DIGIT_PLACEHOLDER); private int lastMatchPosition = 0; // The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as // found in the original sequence of characters the user entered. private int originalPosition = 0; // The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as // found in accruedInputWithoutFormatting. private int positionToRemember = 0; // This contains anything that has been entered so far preceding the national significant number, // and it is formatted (e.g. with space inserted). For example, this can contain IDD, country // code, and/or NDD, etc. private StringBuilder prefixBeforeNationalNumber = new StringBuilder(); private boolean shouldAddSpaceAfterNationalPrefix = false; // This contains the national prefix that has been extracted. It contains only digits without // formatting. private String extractedNationalPrefix = ""; private StringBuilder nationalNumber = new StringBuilder(); private List possibleFormats = new ArrayList(); // A cache for frequently used country-specific regular expressions. private RegexCache regexCache = new RegexCache(64); /** * Constructs an as-you-type formatter. Should be obtained from {@link * PhoneNumberUtil#getAsYouTypeFormatter}. * * @param regionCode the country/region where the phone number is being entered */ AsYouTypeFormatter(String regionCode) { defaultCountry = regionCode; currentMetadata = getMetadataForRegion(defaultCountry); defaultMetadata = currentMetadata; } // The metadata needed by this class is the same for all regions sharing the same country calling // code. Therefore, we return the metadata for "main" region for this country calling code. private PhoneMetadata getMetadataForRegion(String regionCode) { int countryCallingCode = phoneUtil.getCountryCodeForRegion(regionCode); String mainCountry = phoneUtil.getRegionCodeForCountryCode(countryCallingCode); PhoneMetadata metadata = phoneUtil.getMetadataForRegion(mainCountry); if (metadata != null) { return metadata; } // Set to a default instance of the metadata. This allows us to function with an incorrect // region code, even if formatting only works for numbers specified with "+". return EMPTY_METADATA; } // Returns true if a new template is created as opposed to reusing the existing template. private boolean maybeCreateNewTemplate() { // When there are multiple available formats, the formatter uses the first format where a // formatting template could be created. Iterator it = possibleFormats.iterator(); while (it.hasNext()) { NumberFormat numberFormat = it.next(); String pattern = numberFormat.getPattern(); if (currentFormattingPattern.equals(pattern)) { return false; } if (createFormattingTemplate(numberFormat)) { currentFormattingPattern = pattern; shouldAddSpaceAfterNationalPrefix = NATIONAL_PREFIX_SEPARATORS_PATTERN.matcher( numberFormat.getNationalPrefixFormattingRule()).find(); // With a new formatting template, the matched position using the old template needs to be // reset. lastMatchPosition = 0; return true; } else { // Remove the current number format from possibleFormats. it.remove(); } } ableToFormat = false; return false; } private void getAvailableFormats(String leadingDigits) { // First decide whether we should use international or national number rules. boolean isInternationalNumber = isCompleteNumber && extractedNationalPrefix.length() == 0; List formatList = (isInternationalNumber && currentMetadata.intlNumberFormatSize() > 0) ? currentMetadata.intlNumberFormats() : currentMetadata.numberFormats(); for (NumberFormat format : formatList) { // Discard a few formats that we know are not relevant based on the presence of the national // prefix. if (extractedNationalPrefix.length() > 0 && PhoneNumberUtil.formattingRuleHasFirstGroupOnly( format.getNationalPrefixFormattingRule()) && !format.getNationalPrefixOptionalWhenFormatting() && !format.hasDomesticCarrierCodeFormattingRule()) { // If it is a national number that had a national prefix, any rules that aren't valid with a // national prefix should be excluded. A rule that has a carrier-code formatting rule is // kept since the national prefix might actually be an extracted carrier code - we don't // distinguish between these when extracting it in the AYTF. continue; } else if (extractedNationalPrefix.length() == 0 && !isCompleteNumber && !PhoneNumberUtil.formattingRuleHasFirstGroupOnly( format.getNationalPrefixFormattingRule()) && !format.getNationalPrefixOptionalWhenFormatting()) { // This number was entered without a national prefix, and this formatting rule requires one, // so we discard it. continue; } if (ELIGIBLE_FORMAT_PATTERN.matcher(format.getFormat()).matches()) { possibleFormats.add(format); } } narrowDownPossibleFormats(leadingDigits); } private void narrowDownPossibleFormats(String leadingDigits) { int indexOfLeadingDigitsPattern = leadingDigits.length() - MIN_LEADING_DIGITS_LENGTH; Iterator it = possibleFormats.iterator(); while (it.hasNext()) { NumberFormat format = it.next(); if (format.leadingDigitsPatternSize() == 0) { // Keep everything that isn't restricted by leading digits. continue; } int lastLeadingDigitsPattern = Math.min(indexOfLeadingDigitsPattern, format.leadingDigitsPatternSize() - 1); Pattern leadingDigitsPattern = regexCache.getPatternForRegex( format.getLeadingDigitsPattern(lastLeadingDigitsPattern)); Matcher m = leadingDigitsPattern.matcher(leadingDigits); if (!m.lookingAt()) { it.remove(); } } } private boolean createFormattingTemplate(NumberFormat format) { String numberPattern = format.getPattern(); formattingTemplate.setLength(0); String tempTemplate = getFormattingTemplate(numberPattern, format.getFormat()); if (tempTemplate.length() > 0) { formattingTemplate.append(tempTemplate); return true; } return false; } // Gets a formatting template which can be used to efficiently format a partial number where // digits are added one by one. private String getFormattingTemplate(String numberPattern, String numberFormat) { // Creates a phone number consisting only of the digit 9 that matches the // numberPattern by applying the pattern to the longestPhoneNumber string. String longestPhoneNumber = "999999999999999"; Matcher m = regexCache.getPatternForRegex(numberPattern).matcher(longestPhoneNumber); m.find(); // this will always succeed String aPhoneNumber = m.group(); // No formatting template can be created if the number of digits entered so far is longer than // the maximum the current formatting rule can accommodate. if (aPhoneNumber.length() < nationalNumber.length()) { return ""; } // Formats the number according to numberFormat String template = aPhoneNumber.replaceAll(numberPattern, numberFormat); // Replaces each digit with character DIGIT_PLACEHOLDER template = template.replaceAll("9", DIGIT_PLACEHOLDER); return template; } /** * Clears the internal state of the formatter, so it can be reused. */ @dalvik.annotation.compat.UnsupportedAppUsage public void clear() { currentOutput = ""; accruedInput.setLength(0); accruedInputWithoutFormatting.setLength(0); formattingTemplate.setLength(0); lastMatchPosition = 0; currentFormattingPattern = ""; prefixBeforeNationalNumber.setLength(0); extractedNationalPrefix = ""; nationalNumber.setLength(0); ableToFormat = true; inputHasFormatting = false; positionToRemember = 0; originalPosition = 0; isCompleteNumber = false; isExpectingCountryCallingCode = false; possibleFormats.clear(); shouldAddSpaceAfterNationalPrefix = false; if (!currentMetadata.equals(defaultMetadata)) { currentMetadata = getMetadataForRegion(defaultCountry); } } /** * Formats a phone number on-the-fly as each digit is entered. * * @param nextChar the most recently entered digit of a phone number. Formatting characters are * allowed, but as soon as they are encountered this method formats the number as entered and * not "as you type" anymore. Full width digits and Arabic-indic digits are allowed, and will * be shown as they are. * @return the partially formatted phone number. */ @dalvik.annotation.compat.UnsupportedAppUsage public String inputDigit(char nextChar) { currentOutput = inputDigitWithOptionToRememberPosition(nextChar, false); return currentOutput; } /** * Same as {@link #inputDigit}, but remembers the position where {@code nextChar} is inserted, so * that it can be retrieved later by using {@link #getRememberedPosition}. The remembered * position will be automatically adjusted if additional formatting characters are later * inserted/removed in front of {@code nextChar}. */ @dalvik.annotation.compat.UnsupportedAppUsage public String inputDigitAndRememberPosition(char nextChar) { currentOutput = inputDigitWithOptionToRememberPosition(nextChar, true); return currentOutput; } @SuppressWarnings("fallthrough") private String inputDigitWithOptionToRememberPosition(char nextChar, boolean rememberPosition) { accruedInput.append(nextChar); if (rememberPosition) { originalPosition = accruedInput.length(); } // We do formatting on-the-fly only when each character entered is either a digit, or a plus // sign (accepted at the start of the number only). if (!isDigitOrLeadingPlusSign(nextChar)) { ableToFormat = false; inputHasFormatting = true; } else { nextChar = normalizeAndAccrueDigitsAndPlusSign(nextChar, rememberPosition); } if (!ableToFormat) { // When we are unable to format because of reasons other than that formatting chars have been // entered, it can be due to really long IDDs or NDDs. If that is the case, we might be able // to do formatting again after extracting them. if (inputHasFormatting) { return accruedInput.toString(); } else if (attemptToExtractIdd()) { if (attemptToExtractCountryCallingCode()) { return attemptToChoosePatternWithPrefixExtracted(); } } else if (ableToExtractLongerNdd()) { // Add an additional space to separate long NDD and national significant number for // readability. We don't set shouldAddSpaceAfterNationalPrefix to true, since we don't want // this to change later when we choose formatting templates. prefixBeforeNationalNumber.append(SEPARATOR_BEFORE_NATIONAL_NUMBER); return attemptToChoosePatternWithPrefixExtracted(); } return accruedInput.toString(); } // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits (the plus // sign is counted as a digit as well for this purpose) have been entered. switch (accruedInputWithoutFormatting.length()) { case 0: case 1: case 2: return accruedInput.toString(); case 3: if (attemptToExtractIdd()) { isExpectingCountryCallingCode = true; } else { // No IDD or plus sign is found, might be entering in national format. extractedNationalPrefix = removeNationalPrefixFromNationalNumber(); return attemptToChooseFormattingPattern(); } // fall through default: if (isExpectingCountryCallingCode) { if (attemptToExtractCountryCallingCode()) { isExpectingCountryCallingCode = false; } return prefixBeforeNationalNumber + nationalNumber.toString(); } if (possibleFormats.size() > 0) { // The formatting patterns are already chosen. String tempNationalNumber = inputDigitHelper(nextChar); // See if the accrued digits can be formatted properly already. If not, use the results // from inputDigitHelper, which does formatting based on the formatting pattern chosen. String formattedNumber = attemptToFormatAccruedDigits(); if (formattedNumber.length() > 0) { return formattedNumber; } narrowDownPossibleFormats(nationalNumber.toString()); if (maybeCreateNewTemplate()) { return inputAccruedNationalNumber(); } return ableToFormat ? appendNationalNumber(tempNationalNumber) : accruedInput.toString(); } else { return attemptToChooseFormattingPattern(); } } } private String attemptToChoosePatternWithPrefixExtracted() { ableToFormat = true; isExpectingCountryCallingCode = false; possibleFormats.clear(); lastMatchPosition = 0; formattingTemplate.setLength(0); currentFormattingPattern = ""; return attemptToChooseFormattingPattern(); } // @VisibleForTesting String getExtractedNationalPrefix() { return extractedNationalPrefix; } // Some national prefixes are a substring of others. If extracting the shorter NDD doesn't result // in a number we can format, we try to see if we can extract a longer version here. private boolean ableToExtractLongerNdd() { if (extractedNationalPrefix.length() > 0) { // Put the extracted NDD back to the national number before attempting to extract a new NDD. nationalNumber.insert(0, extractedNationalPrefix); // Remove the previously extracted NDD from prefixBeforeNationalNumber. We cannot simply set // it to empty string because people sometimes incorrectly enter national prefix after the // country code, e.g. +44 (0)20-1234-5678. int indexOfPreviousNdd = prefixBeforeNationalNumber.lastIndexOf(extractedNationalPrefix); prefixBeforeNationalNumber.setLength(indexOfPreviousNdd); } return !extractedNationalPrefix.equals(removeNationalPrefixFromNationalNumber()); } private boolean isDigitOrLeadingPlusSign(char nextChar) { return Character.isDigit(nextChar) || (accruedInput.length() == 1 && PhoneNumberUtil.PLUS_CHARS_PATTERN.matcher(Character.toString(nextChar)).matches()); } /** * Checks to see if there is an exact pattern match for these digits. If so, we should use this * instead of any other formatting template whose leadingDigitsPattern also matches the input. */ String attemptToFormatAccruedDigits() { for (NumberFormat numberFormat : possibleFormats) { Matcher m = regexCache.getPatternForRegex(numberFormat.getPattern()).matcher(nationalNumber); if (m.matches()) { shouldAddSpaceAfterNationalPrefix = NATIONAL_PREFIX_SEPARATORS_PATTERN.matcher( numberFormat.getNationalPrefixFormattingRule()).find(); String formattedNumber = m.replaceAll(numberFormat.getFormat()); // Check that we did not remove nor add any extra digits when we matched // this formatting pattern. This usually happens after we entered the last // digit during AYTF. Eg: In case of MX, we swallow mobile token (1) when // formatted but AYTF should retain all the number entered and not change // in order to match a format (of same leading digits and length) display // in that way. String fullOutput = appendNationalNumber(formattedNumber); String formattedNumberDigitsOnly = PhoneNumberUtil.normalizeDiallableCharsOnly(fullOutput); if (formattedNumberDigitsOnly.contentEquals(accruedInputWithoutFormatting)) { // If it's the same (i.e entered number and format is same), then it's // safe to return this in formatted number as nothing is lost / added. return fullOutput; } } } return ""; } /** * Returns the current position in the partially formatted phone number of the character which was * previously passed in as the parameter of {@link #inputDigitAndRememberPosition}. */ @dalvik.annotation.compat.UnsupportedAppUsage public int getRememberedPosition() { if (!ableToFormat) { return originalPosition; } int accruedInputIndex = 0; int currentOutputIndex = 0; while (accruedInputIndex < positionToRemember && currentOutputIndex < currentOutput.length()) { if (accruedInputWithoutFormatting.charAt(accruedInputIndex) == currentOutput.charAt(currentOutputIndex)) { accruedInputIndex++; } currentOutputIndex++; } return currentOutputIndex; } /** * Combines the national number with any prefix (IDD/+ and country code or national prefix) that * was collected. A space will be inserted between them if the current formatting template * indicates this to be suitable. */ private String appendNationalNumber(String nationalNumber) { int prefixBeforeNationalNumberLength = prefixBeforeNationalNumber.length(); if (shouldAddSpaceAfterNationalPrefix && prefixBeforeNationalNumberLength > 0 && prefixBeforeNationalNumber.charAt(prefixBeforeNationalNumberLength - 1) != SEPARATOR_BEFORE_NATIONAL_NUMBER) { // We want to add a space after the national prefix if the national prefix formatting rule // indicates that this would normally be done, with the exception of the case where we already // appended a space because the NDD was surprisingly long. return new String(prefixBeforeNationalNumber) + SEPARATOR_BEFORE_NATIONAL_NUMBER + nationalNumber; } else { return prefixBeforeNationalNumber + nationalNumber; } } /** * Attempts to set the formatting template and returns a string which contains the formatted * version of the digits entered so far. */ private String attemptToChooseFormattingPattern() { // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits of national // number (excluding national prefix) have been entered. if (nationalNumber.length() >= MIN_LEADING_DIGITS_LENGTH) { getAvailableFormats(nationalNumber.toString()); // See if the accrued digits can be formatted properly already. String formattedNumber = attemptToFormatAccruedDigits(); if (formattedNumber.length() > 0) { return formattedNumber; } return maybeCreateNewTemplate() ? inputAccruedNationalNumber() : accruedInput.toString(); } else { return appendNationalNumber(nationalNumber.toString()); } } /** * Invokes inputDigitHelper on each digit of the national number accrued, and returns a formatted * string in the end. */ private String inputAccruedNationalNumber() { int lengthOfNationalNumber = nationalNumber.length(); if (lengthOfNationalNumber > 0) { String tempNationalNumber = ""; for (int i = 0; i < lengthOfNationalNumber; i++) { tempNationalNumber = inputDigitHelper(nationalNumber.charAt(i)); } return ableToFormat ? appendNationalNumber(tempNationalNumber) : accruedInput.toString(); } else { return prefixBeforeNationalNumber.toString(); } } /** * Returns true if the current country is a NANPA country and the national number begins with * the national prefix. */ private boolean isNanpaNumberWithNationalPrefix() { // For NANPA numbers beginning with 1[2-9], treat the 1 as the national prefix. The reason is // that national significant numbers in NANPA always start with [2-9] after the national prefix. // Numbers beginning with 1[01] can only be short/emergency numbers, which don't need the // national prefix. return (currentMetadata.getCountryCode() == 1) && (nationalNumber.charAt(0) == '1') && (nationalNumber.charAt(1) != '0') && (nationalNumber.charAt(1) != '1'); } // Returns the national prefix extracted, or an empty string if it is not present. private String removeNationalPrefixFromNationalNumber() { int startOfNationalNumber = 0; if (isNanpaNumberWithNationalPrefix()) { startOfNationalNumber = 1; prefixBeforeNationalNumber.append('1').append(SEPARATOR_BEFORE_NATIONAL_NUMBER); isCompleteNumber = true; } else if (currentMetadata.hasNationalPrefixForParsing()) { Pattern nationalPrefixForParsing = regexCache.getPatternForRegex(currentMetadata.getNationalPrefixForParsing()); Matcher m = nationalPrefixForParsing.matcher(nationalNumber); // Since some national prefix patterns are entirely optional, check that a national prefix // could actually be extracted. if (m.lookingAt() && m.end() > 0) { // When the national prefix is detected, we use international formatting rules instead of // national ones, because national formatting rules could contain local formatting rules // for numbers entered without area code. isCompleteNumber = true; startOfNationalNumber = m.end(); prefixBeforeNationalNumber.append(nationalNumber.substring(0, startOfNationalNumber)); } } String nationalPrefix = nationalNumber.substring(0, startOfNationalNumber); nationalNumber.delete(0, startOfNationalNumber); return nationalPrefix; } /** * Extracts IDD and plus sign to prefixBeforeNationalNumber when they are available, and places * the remaining input into nationalNumber. * * @return true when accruedInputWithoutFormatting begins with the plus sign or valid IDD for * defaultCountry. */ private boolean attemptToExtractIdd() { Pattern internationalPrefix = regexCache.getPatternForRegex("\\" + PhoneNumberUtil.PLUS_SIGN + "|" + currentMetadata.getInternationalPrefix()); Matcher iddMatcher = internationalPrefix.matcher(accruedInputWithoutFormatting); if (iddMatcher.lookingAt()) { isCompleteNumber = true; int startOfCountryCallingCode = iddMatcher.end(); nationalNumber.setLength(0); nationalNumber.append(accruedInputWithoutFormatting.substring(startOfCountryCallingCode)); prefixBeforeNationalNumber.setLength(0); prefixBeforeNationalNumber.append( accruedInputWithoutFormatting.substring(0, startOfCountryCallingCode)); if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN) { prefixBeforeNationalNumber.append(SEPARATOR_BEFORE_NATIONAL_NUMBER); } return true; } return false; } /** * Extracts the country calling code from the beginning of nationalNumber to * prefixBeforeNationalNumber when they are available, and places the remaining input into * nationalNumber. * * @return true when a valid country calling code can be found. */ private boolean attemptToExtractCountryCallingCode() { if (nationalNumber.length() == 0) { return false; } StringBuilder numberWithoutCountryCallingCode = new StringBuilder(); int countryCode = phoneUtil.extractCountryCode(nationalNumber, numberWithoutCountryCallingCode); if (countryCode == 0) { return false; } nationalNumber.setLength(0); nationalNumber.append(numberWithoutCountryCallingCode); String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode); if (PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY.equals(newRegionCode)) { currentMetadata = phoneUtil.getMetadataForNonGeographicalRegion(countryCode); } else if (!newRegionCode.equals(defaultCountry)) { currentMetadata = getMetadataForRegion(newRegionCode); } String countryCodeString = Integer.toString(countryCode); prefixBeforeNationalNumber.append(countryCodeString).append(SEPARATOR_BEFORE_NATIONAL_NUMBER); // When we have successfully extracted the IDD, the previously extracted NDD should be cleared // because it is no longer valid. extractedNationalPrefix = ""; return true; } // Accrues digits and the plus sign to accruedInputWithoutFormatting for later use. If nextChar // contains a digit in non-ASCII format (e.g. the full-width version of digits), it is first // normalized to the ASCII version. The return value is nextChar itself, or its normalized // version, if nextChar is a digit in non-ASCII format. This method assumes its input is either a // digit or the plus sign. private char normalizeAndAccrueDigitsAndPlusSign(char nextChar, boolean rememberPosition) { char normalizedChar; if (nextChar == PhoneNumberUtil.PLUS_SIGN) { normalizedChar = nextChar; accruedInputWithoutFormatting.append(nextChar); } else { int radix = 10; normalizedChar = Character.forDigit(Character.digit(nextChar, radix), radix); accruedInputWithoutFormatting.append(normalizedChar); nationalNumber.append(normalizedChar); } if (rememberPosition) { positionToRemember = accruedInputWithoutFormatting.length(); } return normalizedChar; } private String inputDigitHelper(char nextChar) { // Note that formattingTemplate is not guaranteed to have a value, it could be empty, e.g. // when the next digit is entered after extracting an IDD or NDD. Matcher digitMatcher = DIGIT_PATTERN.matcher(formattingTemplate); if (digitMatcher.find(lastMatchPosition)) { String tempTemplate = digitMatcher.replaceFirst(Character.toString(nextChar)); formattingTemplate.replace(0, tempTemplate.length(), tempTemplate); lastMatchPosition = digitMatcher.start(); return formattingTemplate.substring(0, lastMatchPosition + 1); } else { if (possibleFormats.size() == 1) { // More digits are entered than we could handle, and there are no other valid patterns to // try. ableToFormat = false; } // else, we just reset the formatting pattern. currentFormattingPattern = ""; return accruedInput.toString(); } } }