2 * Copyright (C) 2009 The Libphonenumber Authors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.google.i18n.phonenumbers;
19 import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
20 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
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;
29 * A formatter which formats phone numbers as they are entered.
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
37 * <p>See the unittests for more details on how the formatter is to be used.
39 * @author Shaopeng Jia
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;
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;
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
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(?=[^,}][^,}])");
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("[- ]");
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;
96 // The digits that have not been entered yet will be represented by a \u2008, the punctuation
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
114 private String extractedNationalPrefix = "";
115 private StringBuilder nationalNumber = new StringBuilder();
116 private List<NumberFormat> possibleFormats = new ArrayList<NumberFormat>();
118 // A cache for frequently used country-specific regular expressions.
119 private RegexCache regexCache = new RegexCache(64);
122 * Constructs an as-you-type formatter. Should be obtained from {@link
123 * PhoneNumberUtil#getAsYouTypeFormatter}.
125 * @param regionCode the country/region where the phone number is being entered
127 AsYouTypeFormatter(String regionCode) {
128 defaultCountry = regionCode;
129 currentMetadata = getMetadataForRegion(defaultCountry);
130 defaultMetadata = currentMetadata;
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) {
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;
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)) {
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
165 lastMatchPosition = 0;
167 } else { // Remove the current number format from possibleFormats.
171 ableToFormat = false;
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 || isCompleteNumber ||
183 format.isNationalPrefixOptionalWhenFormatting() ||
184 PhoneNumberUtil.formattingRuleHasFirstGroupOnly(
185 format.getNationalPrefixFormattingRule())) {
186 if (isFormatEligible(format.getFormat())) {
187 possibleFormats.add(format);
191 narrowDownPossibleFormats(leadingDigits);
194 private boolean isFormatEligible(String format) {
195 return ELIGIBLE_FORMAT_PATTERN.matcher(format).matches();
198 private void narrowDownPossibleFormats(String leadingDigits) {
199 int indexOfLeadingDigitsPattern = leadingDigits.length() - MIN_LEADING_DIGITS_LENGTH;
200 Iterator<NumberFormat> it = possibleFormats.iterator();
201 while (it.hasNext()) {
202 NumberFormat format = it.next();
203 if (format.leadingDigitsPatternSize() == 0) {
204 // Keep everything that isn't restricted by leading digits.
207 int lastLeadingDigitsPattern =
208 Math.min(indexOfLeadingDigitsPattern, format.leadingDigitsPatternSize() - 1);
209 Pattern leadingDigitsPattern = regexCache.getPatternForRegex(
210 format.getLeadingDigitsPattern(lastLeadingDigitsPattern));
211 Matcher m = leadingDigitsPattern.matcher(leadingDigits);
212 if (!m.lookingAt()) {
218 private boolean createFormattingTemplate(NumberFormat format) {
219 String numberPattern = format.getPattern();
221 // The formatter doesn't format numbers when numberPattern contains "|", e.g.
222 // (20|3)\d{4}. In those cases we quickly return.
223 if (numberPattern.indexOf('|') != -1) {
227 // Replace anything in the form of [..] with \d
228 numberPattern = CHARACTER_CLASS_PATTERN.matcher(numberPattern).replaceAll("\\\\d");
230 // Replace any standalone digit (not the one in d{}) with \d
231 numberPattern = STANDALONE_DIGIT_PATTERN.matcher(numberPattern).replaceAll("\\\\d");
232 formattingTemplate.setLength(0);
233 String tempTemplate = getFormattingTemplate(numberPattern, format.getFormat());
234 if (tempTemplate.length() > 0) {
235 formattingTemplate.append(tempTemplate);
241 // Gets a formatting template which can be used to efficiently format a partial number where
242 // digits are added one by one.
243 private String getFormattingTemplate(String numberPattern, String numberFormat) {
244 // Creates a phone number consisting only of the digit 9 that matches the
245 // numberPattern by applying the pattern to the longestPhoneNumber string.
246 String longestPhoneNumber = "999999999999999";
247 Matcher m = regexCache.getPatternForRegex(numberPattern).matcher(longestPhoneNumber);
248 m.find(); // this will always succeed
249 String aPhoneNumber = m.group();
250 // No formatting template can be created if the number of digits entered so far is longer than
251 // the maximum the current formatting rule can accommodate.
252 if (aPhoneNumber.length() < nationalNumber.length()) {
255 // Formats the number according to numberFormat
256 String template = aPhoneNumber.replaceAll(numberPattern, numberFormat);
257 // Replaces each digit with character DIGIT_PLACEHOLDER
258 template = template.replaceAll("9", DIGIT_PLACEHOLDER);
263 * Clears the internal state of the formatter, so it can be reused.
265 public void clear() {
267 accruedInput.setLength(0);
268 accruedInputWithoutFormatting.setLength(0);
269 formattingTemplate.setLength(0);
270 lastMatchPosition = 0;
271 currentFormattingPattern = "";
272 prefixBeforeNationalNumber.setLength(0);
273 extractedNationalPrefix = "";
274 nationalNumber.setLength(0);
276 inputHasFormatting = false;
277 positionToRemember = 0;
278 originalPosition = 0;
279 isCompleteNumber = false;
280 isExpectingCountryCallingCode = false;
281 possibleFormats.clear();
282 shouldAddSpaceAfterNationalPrefix = false;
283 if (!currentMetadata.equals(defaultMetadata)) {
284 currentMetadata = getMetadataForRegion(defaultCountry);
289 * Formats a phone number on-the-fly as each digit is entered.
291 * @param nextChar the most recently entered digit of a phone number. Formatting characters are
292 * allowed, but as soon as they are encountered this method formats the number as entered and
293 * not "as you type" anymore. Full width digits and Arabic-indic digits are allowed, and will
294 * be shown as they are.
295 * @return the partially formatted phone number.
297 public String inputDigit(char nextChar) {
298 currentOutput = inputDigitWithOptionToRememberPosition(nextChar, false);
299 return currentOutput;
303 * Same as {@link #inputDigit}, but remembers the position where {@code nextChar} is inserted, so
304 * that it can be retrieved later by using {@link #getRememberedPosition}. The remembered
305 * position will be automatically adjusted if additional formatting characters are later
306 * inserted/removed in front of {@code nextChar}.
308 public String inputDigitAndRememberPosition(char nextChar) {
309 currentOutput = inputDigitWithOptionToRememberPosition(nextChar, true);
310 return currentOutput;
313 @SuppressWarnings("fallthrough")
314 private String inputDigitWithOptionToRememberPosition(char nextChar, boolean rememberPosition) {
315 accruedInput.append(nextChar);
316 if (rememberPosition) {
317 originalPosition = accruedInput.length();
319 // We do formatting on-the-fly only when each character entered is either a digit, or a plus
320 // sign (accepted at the start of the number only).
321 if (!isDigitOrLeadingPlusSign(nextChar)) {
322 ableToFormat = false;
323 inputHasFormatting = true;
325 nextChar = normalizeAndAccrueDigitsAndPlusSign(nextChar, rememberPosition);
328 // When we are unable to format because of reasons other than that formatting chars have been
329 // entered, it can be due to really long IDDs or NDDs. If that is the case, we might be able
330 // to do formatting again after extracting them.
331 if (inputHasFormatting) {
332 return accruedInput.toString();
333 } else if (attemptToExtractIdd()) {
334 if (attemptToExtractCountryCallingCode()) {
335 return attemptToChoosePatternWithPrefixExtracted();
337 } else if (ableToExtractLongerNdd()) {
338 // Add an additional space to separate long NDD and national significant number for
339 // readability. We don't set shouldAddSpaceAfterNationalPrefix to true, since we don't want
340 // this to change later when we choose formatting templates.
341 prefixBeforeNationalNumber.append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
342 return attemptToChoosePatternWithPrefixExtracted();
344 return accruedInput.toString();
347 // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits (the plus
348 // sign is counted as a digit as well for this purpose) have been entered.
349 switch (accruedInputWithoutFormatting.length()) {
353 return accruedInput.toString();
355 if (attemptToExtractIdd()) {
356 isExpectingCountryCallingCode = true;
357 } else { // No IDD or plus sign is found, might be entering in national format.
358 extractedNationalPrefix = removeNationalPrefixFromNationalNumber();
359 return attemptToChooseFormattingPattern();
362 if (isExpectingCountryCallingCode) {
363 if (attemptToExtractCountryCallingCode()) {
364 isExpectingCountryCallingCode = false;
366 return prefixBeforeNationalNumber + nationalNumber.toString();
368 if (possibleFormats.size() > 0) { // The formatting patterns are already chosen.
369 String tempNationalNumber = inputDigitHelper(nextChar);
370 // See if the accrued digits can be formatted properly already. If not, use the results
371 // from inputDigitHelper, which does formatting based on the formatting pattern chosen.
372 String formattedNumber = attemptToFormatAccruedDigits();
373 if (formattedNumber.length() > 0) {
374 return formattedNumber;
376 narrowDownPossibleFormats(nationalNumber.toString());
377 if (maybeCreateNewTemplate()) {
378 return inputAccruedNationalNumber();
381 ? appendNationalNumber(tempNationalNumber)
382 : accruedInput.toString();
384 return attemptToChooseFormattingPattern();
389 private String attemptToChoosePatternWithPrefixExtracted() {
391 isExpectingCountryCallingCode = false;
392 possibleFormats.clear();
393 lastMatchPosition = 0;
394 formattingTemplate.setLength(0);
395 currentFormattingPattern = "";
396 return attemptToChooseFormattingPattern();
399 // @VisibleForTesting
400 String getExtractedNationalPrefix() {
401 return extractedNationalPrefix;
404 // Some national prefixes are a substring of others. If extracting the shorter NDD doesn't result
405 // in a number we can format, we try to see if we can extract a longer version here.
406 private boolean ableToExtractLongerNdd() {
407 if (extractedNationalPrefix.length() > 0) {
408 // Put the extracted NDD back to the national number before attempting to extract a new NDD.
409 nationalNumber.insert(0, extractedNationalPrefix);
410 // Remove the previously extracted NDD from prefixBeforeNationalNumber. We cannot simply set
411 // it to empty string because people sometimes incorrectly enter national prefix after the
412 // country code, e.g. +44 (0)20-1234-5678.
413 int indexOfPreviousNdd = prefixBeforeNationalNumber.lastIndexOf(extractedNationalPrefix);
414 prefixBeforeNationalNumber.setLength(indexOfPreviousNdd);
416 return !extractedNationalPrefix.equals(removeNationalPrefixFromNationalNumber());
419 private boolean isDigitOrLeadingPlusSign(char nextChar) {
420 return Character.isDigit(nextChar) ||
421 (accruedInput.length() == 1 &&
422 PhoneNumberUtil.PLUS_CHARS_PATTERN.matcher(Character.toString(nextChar)).matches());
426 * Check to see if there is an exact pattern match for these digits. If so, we should use this
427 * instead of any other formatting template whose leadingDigitsPattern also matches the input.
429 String attemptToFormatAccruedDigits() {
430 for (NumberFormat numberFormat : possibleFormats) {
431 Matcher m = regexCache.getPatternForRegex(numberFormat.getPattern()).matcher(nationalNumber);
433 shouldAddSpaceAfterNationalPrefix =
434 NATIONAL_PREFIX_SEPARATORS_PATTERN.matcher(
435 numberFormat.getNationalPrefixFormattingRule()).find();
436 String formattedNumber = m.replaceAll(numberFormat.getFormat());
437 return appendNationalNumber(formattedNumber);
444 * Returns the current position in the partially formatted phone number of the character which was
445 * previously passed in as the parameter of {@link #inputDigitAndRememberPosition}.
447 public int getRememberedPosition() {
449 return originalPosition;
451 int accruedInputIndex = 0, currentOutputIndex = 0;
452 while (accruedInputIndex < positionToRemember && currentOutputIndex < currentOutput.length()) {
453 if (accruedInputWithoutFormatting.charAt(accruedInputIndex) ==
454 currentOutput.charAt(currentOutputIndex)) {
457 currentOutputIndex++;
459 return currentOutputIndex;
463 * Combines the national number with any prefix (IDD/+ and country code or national prefix) that
464 * was collected. A space will be inserted between them if the current formatting template
465 * indicates this to be suitable.
467 private String appendNationalNumber(String nationalNumber) {
468 int prefixBeforeNationalNumberLength = prefixBeforeNationalNumber.length();
469 if (shouldAddSpaceAfterNationalPrefix && prefixBeforeNationalNumberLength > 0 &&
470 prefixBeforeNationalNumber.charAt(prefixBeforeNationalNumberLength - 1)
471 != SEPARATOR_BEFORE_NATIONAL_NUMBER) {
472 // We want to add a space after the national prefix if the national prefix formatting rule
473 // indicates that this would normally be done, with the exception of the case where we already
474 // appended a space because the NDD was surprisingly long.
475 return new String(prefixBeforeNationalNumber) + SEPARATOR_BEFORE_NATIONAL_NUMBER
478 return prefixBeforeNationalNumber + nationalNumber;
483 * Attempts to set the formatting template and returns a string which contains the formatted
484 * version of the digits entered so far.
486 private String attemptToChooseFormattingPattern() {
487 // We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits of national
488 // number (excluding national prefix) have been entered.
489 if (nationalNumber.length() >= MIN_LEADING_DIGITS_LENGTH) {
491 getAvailableFormats(nationalNumber.toString());
492 // See if the accrued digits can be formatted properly already.
493 String formattedNumber = attemptToFormatAccruedDigits();
494 if (formattedNumber.length() > 0) {
495 return formattedNumber;
497 return maybeCreateNewTemplate() ? inputAccruedNationalNumber() : accruedInput.toString();
499 return appendNationalNumber(nationalNumber.toString());
504 * Invokes inputDigitHelper on each digit of the national number accrued, and returns a formatted
507 private String inputAccruedNationalNumber() {
508 int lengthOfNationalNumber = nationalNumber.length();
509 if (lengthOfNationalNumber > 0) {
510 String tempNationalNumber = "";
511 for (int i = 0; i < lengthOfNationalNumber; i++) {
512 tempNationalNumber = inputDigitHelper(nationalNumber.charAt(i));
514 return ableToFormat ? appendNationalNumber(tempNationalNumber) : accruedInput.toString();
516 return prefixBeforeNationalNumber.toString();
521 * Returns true if the current country is a NANPA country and the national number begins with
522 * the national prefix.
524 private boolean isNanpaNumberWithNationalPrefix() {
525 // For NANPA numbers beginning with 1[2-9], treat the 1 as the national prefix. The reason is
526 // that national significant numbers in NANPA always start with [2-9] after the national prefix.
527 // Numbers beginning with 1[01] can only be short/emergency numbers, which don't need the
529 return (currentMetadata.getCountryCode() == 1) && (nationalNumber.charAt(0) == '1') &&
530 (nationalNumber.charAt(1) != '0') && (nationalNumber.charAt(1) != '1');
533 // Returns the national prefix extracted, or an empty string if it is not present.
534 private String removeNationalPrefixFromNationalNumber() {
535 int startOfNationalNumber = 0;
536 if (isNanpaNumberWithNationalPrefix()) {
537 startOfNationalNumber = 1;
538 prefixBeforeNationalNumber.append('1').append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
539 isCompleteNumber = true;
540 } else if (currentMetadata.hasNationalPrefixForParsing()) {
541 Pattern nationalPrefixForParsing =
542 regexCache.getPatternForRegex(currentMetadata.getNationalPrefixForParsing());
543 Matcher m = nationalPrefixForParsing.matcher(nationalNumber);
544 // Since some national prefix patterns are entirely optional, check that a national prefix
545 // could actually be extracted.
546 if (m.lookingAt() && m.end() > 0) {
547 // When the national prefix is detected, we use international formatting rules instead of
548 // national ones, because national formatting rules could contain local formatting rules
549 // for numbers entered without area code.
550 isCompleteNumber = true;
551 startOfNationalNumber = m.end();
552 prefixBeforeNationalNumber.append(nationalNumber.substring(0, startOfNationalNumber));
555 String nationalPrefix = nationalNumber.substring(0, startOfNationalNumber);
556 nationalNumber.delete(0, startOfNationalNumber);
557 return nationalPrefix;
561 * Extracts IDD and plus sign to prefixBeforeNationalNumber when they are available, and places
562 * the remaining input into nationalNumber.
564 * @return true when accruedInputWithoutFormatting begins with the plus sign or valid IDD for
567 private boolean attemptToExtractIdd() {
568 Pattern internationalPrefix =
569 regexCache.getPatternForRegex("\\" + PhoneNumberUtil.PLUS_SIGN + "|" +
570 currentMetadata.getInternationalPrefix());
571 Matcher iddMatcher = internationalPrefix.matcher(accruedInputWithoutFormatting);
572 if (iddMatcher.lookingAt()) {
573 isCompleteNumber = true;
574 int startOfCountryCallingCode = iddMatcher.end();
575 nationalNumber.setLength(0);
576 nationalNumber.append(accruedInputWithoutFormatting.substring(startOfCountryCallingCode));
577 prefixBeforeNationalNumber.setLength(0);
578 prefixBeforeNationalNumber.append(
579 accruedInputWithoutFormatting.substring(0, startOfCountryCallingCode));
580 if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN) {
581 prefixBeforeNationalNumber.append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
589 * Extracts the country calling code from the beginning of nationalNumber to
590 * prefixBeforeNationalNumber when they are available, and places the remaining input into
593 * @return true when a valid country calling code can be found.
595 private boolean attemptToExtractCountryCallingCode() {
596 if (nationalNumber.length() == 0) {
599 StringBuilder numberWithoutCountryCallingCode = new StringBuilder();
600 int countryCode = phoneUtil.extractCountryCode(nationalNumber, numberWithoutCountryCallingCode);
601 if (countryCode == 0) {
604 nationalNumber.setLength(0);
605 nationalNumber.append(numberWithoutCountryCallingCode);
606 String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
607 if (PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY.equals(newRegionCode)) {
608 currentMetadata = phoneUtil.getMetadataForNonGeographicalRegion(countryCode);
609 } else if (!newRegionCode.equals(defaultCountry)) {
610 currentMetadata = getMetadataForRegion(newRegionCode);
612 String countryCodeString = Integer.toString(countryCode);
613 prefixBeforeNationalNumber.append(countryCodeString).append(SEPARATOR_BEFORE_NATIONAL_NUMBER);
614 // When we have successfully extracted the IDD, the previously extracted NDD should be cleared
615 // because it is no longer valid.
616 extractedNationalPrefix = "";
620 // Accrues digits and the plus sign to accruedInputWithoutFormatting for later use. If nextChar
621 // contains a digit in non-ASCII format (e.g. the full-width version of digits), it is first
622 // normalized to the ASCII version. The return value is nextChar itself, or its normalized
623 // version, if nextChar is a digit in non-ASCII format. This method assumes its input is either a
624 // digit or the plus sign.
625 private char normalizeAndAccrueDigitsAndPlusSign(char nextChar, boolean rememberPosition) {
627 if (nextChar == PhoneNumberUtil.PLUS_SIGN) {
628 normalizedChar = nextChar;
629 accruedInputWithoutFormatting.append(nextChar);
632 normalizedChar = Character.forDigit(Character.digit(nextChar, radix), radix);
633 accruedInputWithoutFormatting.append(normalizedChar);
634 nationalNumber.append(normalizedChar);
636 if (rememberPosition) {
637 positionToRemember = accruedInputWithoutFormatting.length();
639 return normalizedChar;
642 private String inputDigitHelper(char nextChar) {
643 // Note that formattingTemplate is not guaranteed to have a value, it could be empty, e.g.
644 // when the next digit is entered after extracting an IDD or NDD.
645 Matcher digitMatcher = DIGIT_PATTERN.matcher(formattingTemplate);
646 if (digitMatcher.find(lastMatchPosition)) {
647 String tempTemplate = digitMatcher.replaceFirst(Character.toString(nextChar));
648 formattingTemplate.replace(0, tempTemplate.length(), tempTemplate);
649 lastMatchPosition = digitMatcher.start();
650 return formattingTemplate.substring(0, lastMatchPosition + 1);
652 if (possibleFormats.size() == 1) {
653 // More digits are entered than we could handle, and there are no other valid patterns to
655 ableToFormat = false;
656 } // else, we just reset the formatting pattern.
657 currentFormattingPattern = "";
658 return accruedInput.toString();