commit a7c6c36f8421f0934055fa2affd8333a2408df00 Author: Shaopeng Jia Date: Wed Feb 10 17:54:46 2010 +0000 Checking in first version of java libphonenumber diff --git a/java/build.xml b/java/build.xml new file mode 100644 index 000000000..edc5b41f8 --- /dev/null +++ b/java/build.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/lib/google-guava.jar b/java/lib/google-guava.jar new file mode 100644 index 000000000..1515f5652 Binary files /dev/null and b/java/lib/google-guava.jar differ diff --git a/java/lib/junit-4.8.1.jar b/java/lib/junit-4.8.1.jar new file mode 100644 index 000000000..524cd65ce Binary files /dev/null and b/java/lib/junit-4.8.1.jar differ diff --git a/java/lib/protobuf-java-2.3.0.jar b/java/lib/protobuf-java-2.3.0.jar new file mode 100644 index 000000000..d3afd3393 Binary files /dev/null and b/java/lib/protobuf-java-2.3.0.jar differ diff --git a/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java new file mode 100644 index 000000000..4e0935726 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.i18n.phonenumbers; + +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; +import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; + +import java.util.ArrayList; +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 could be created by invoking the getAsYouTypeFormatter method of the + * PhoneNumberUtil. After that digits could be added by invoking the inputDigit method on the + * formatter instance, and the partially formatted phone number will be returned each time a digit + * is added. The clear method could be invoked before a new number needs to be formatted. + * + * See testAsYouTypeFormatterUS(), testAsYouTestFormatterGB() and testAsYouTypeFormatterDE() in + * PhoneNumberUtilTest.java for more details on how the formatter is to be used. + * + * @author Shaopeng Jia + */ +public class AsYouTypeFormatter { + private StringBuffer currentOutput; + private String formattingTemplate; + private StringBuffer accruedInput; + private StringBuffer accruedInputWithoutFormatting; + private boolean ableToFormat; + private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); + private String defaultCountry; + private PhoneMetadata defaultMetaData; + private PhoneMetadata currentMetaData; + + // The digits that have not been entered yet will be represented by a \u2008, the punctuation + // space. + private String digitPlaceholder = "\u2008"; + private Pattern digitPattern = Pattern.compile(digitPlaceholder); + private int lastMatchPosition = 0; + private Pattern nationalPrefixForParsing; + private Pattern internationalPrefix; + private StringBuffer prefixBeforeNationalNumber; + private StringBuffer nationalNumber; + + /** + * Constructs a light-weight formatter which does no formatting, but outputs exactly what is + * fed into the inputDigit method. + * + * @param regionCode the country/region where the phone number is being entered + */ + AsYouTypeFormatter(String regionCode) { + accruedInput = new StringBuffer(); + accruedInputWithoutFormatting = new StringBuffer(); + currentOutput = new StringBuffer(); + prefixBeforeNationalNumber = new StringBuffer(); + ableToFormat = true; + nationalNumber = new StringBuffer(); + defaultCountry = regionCode; + initializeCountrySpecificInfo(defaultCountry); + defaultMetaData = currentMetaData; + } + + private void initializeCountrySpecificInfo(String regionCode) { + currentMetaData = phoneUtil.getMetadataForRegion(regionCode); + nationalPrefixForParsing = + Pattern.compile(currentMetaData.getNationalPrefixForParsing()); + internationalPrefix = + Pattern.compile("\\+|" + currentMetaData.getInternationalPrefix()); + } + + private void chooseFormatAndCreateTemplate(String leadingFourDigitsOfNationalNumber) { + List formatList = getAvailableFormats(leadingFourDigitsOfNationalNumber); + if (formatList.size() < 1) { + ableToFormat = false; + } else { + // When there are multiple available formats, the formatter uses the first format. + NumberFormat format = formatList.get(0); + if (!createFormattingTemplate(format)) { + ableToFormat = false; + } else { + currentOutput = new StringBuffer(formattingTemplate); + } + } + } + + private List getAvailableFormats(String leadingFourDigits) { + List matchedList = new ArrayList(); + List formatList = currentMetaData.getNumberFormatList(); + for (NumberFormat format : formatList) { + if (format.hasLeadingDigits()) { + Pattern leadingDigitsPattern = Pattern.compile(format.getLeadingDigits()); + Matcher m = leadingDigitsPattern.matcher(leadingFourDigits); + if (m.lookingAt()) { + matchedList.add(format); + } + } else { + matchedList.add(format); + } + } + return matchedList; + } + + private boolean createFormattingTemplate(NumberFormat format) { + String numberFormat = format.getFormat(); + String numberPattern = format.getPattern(); + + // The formatter doesn't format numbers when numberPattern contains "|" or ",", e.g. + // (20|3)\d{4,5}. In those cases we quickly return. + Matcher unsupportedSyntax = Pattern.compile("\\||,").matcher(numberPattern); + if (unsupportedSyntax.find()) { + return false; + } + + // Replace anything in the form of [..] with \d + numberPattern = numberPattern.replaceAll("\\[([^\\[\\]])*\\]","\\\\d"); + + // Replace any standalone digit (not the one in d{}) with \d + numberPattern = numberPattern.replaceAll("\\d(?=[^}])", "\\\\d"); + + formattingTemplate = getFormattingTemplate(numberPattern, numberFormat); + return true; + } + + // Gets a formatting template which could 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 = Pattern.compile(numberPattern).matcher(longestPhoneNumber); + m.find(); // this will always succeed + String aPhoneNumber = m.group(); + // Formats the number according to numberFormat + String template = aPhoneNumber.replaceAll(numberPattern, numberFormat); + // Replaces each digit with character digitPlaceholder + template = template.replaceAll("9", digitPlaceholder); + return template; + } + + /** + * Clears the internal state of the formatter, so it could be reused. + */ + public void clear() { + accruedInput = new StringBuffer(); + accruedInputWithoutFormatting = new StringBuffer(); + currentOutput = new StringBuffer(); + lastMatchPosition = 0; + prefixBeforeNationalNumber = new StringBuffer(); + nationalNumber = new StringBuffer(); + ableToFormat = true; + if (!currentMetaData.equals(defaultMetaData)) { + initializeCountrySpecificInfo(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 they are removed from the result. Full width digits and Arabic-indic digits + * are allowed, and will be shown as they are. + * @return the partially formatted phone number, with the remaining digits each denoted by + * \u2008. Clients could display the result as it is, as \u2008 will be displayed as a normal + * white space. + */ + public String inputDigit(char nextChar) { + accruedInput.append(nextChar); + // * and # are normally used in mobile codes, which we do not format. + if (nextChar == '*' || nextChar == '#') { + ableToFormat = false; + } + if (!ableToFormat) { + return accruedInput.toString(); + } + + nextChar = normalizeAndAccrueDigitsAndPlusSign(nextChar); + + // We start to attempt to format only when at least 6 digits (the plus sign is counted as a + // digit as well for this purpose) have been entered. + switch (accruedInputWithoutFormatting.length()) { + case 0: // this is the case where the first few inputs are neither digits nor the plus sign. + case 1: + case 2: + case 3: + case 4: + case 5: + return accruedInput.toString(); + case 6: + if (!extractIddAndValidCountryCode()) { + ableToFormat = false; + return accruedInput.toString(); + } + removeNationalPrefixFromNationalNumber(); + return attemptToChooseFormattingPattern(); + default: + if (nationalNumber.length() > 4) { // The formatting pattern is already chosen. + return prefixBeforeNationalNumber + inputDigitHelper(nextChar); + } else { + return attemptToChooseFormattingPattern(); + } + } + } + + // 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 as least 4 digits of national number (excluding + // national prefix) have been entered. + if (nationalNumber.length() >= 4) { + chooseFormatAndCreateTemplate(nationalNumber.substring(0, 4)); + return inputAccruedNationalNumber(); + } else { + return prefixBeforeNationalNumber + 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) { + for (int i = 0; i < lengthOfNationalNumber - 1; i++) { + inputDigitHelper(nationalNumber.charAt(i)); + } + return prefixBeforeNationalNumber + + inputDigitHelper(nationalNumber.charAt(lengthOfNationalNumber - 1)); + } else { + return prefixBeforeNationalNumber.toString(); + } + } + + private void removeNationalPrefixFromNationalNumber() { + int startOfNationalNumber = 0; + if (currentMetaData.hasNationalPrefix()) { + Matcher m = nationalPrefixForParsing.matcher(nationalNumber); + if (m.lookingAt()) { + startOfNationalNumber = m.end(); + prefixBeforeNationalNumber.append(nationalNumber.substring(0, startOfNationalNumber)); + } + } + nationalNumber.delete(0, startOfNationalNumber); + } + + /** + * Extracts IDD, plus sign and country code to prefixBeforeNationalNumber when they are available, + * and places the remaining input into nationalNumber. + * + * @return false when accruedInputWithoutFormatting begins with the plus sign or valid IDD for + * defaultCountry, but the sequence of digits after that does not form a valid country code. + * It returns true for all other cases. + */ + private boolean extractIddAndValidCountryCode() { + nationalNumber = new StringBuffer(); + Matcher iddMatcher = internationalPrefix.matcher(accruedInputWithoutFormatting); + if (iddMatcher.lookingAt()) { + int startOfCountryCode = iddMatcher.end(); + StringBuffer numberIncludeCountryCode = + new StringBuffer(accruedInputWithoutFormatting.substring(startOfCountryCode)); + int countryCode = phoneUtil.extractCountryCode(numberIncludeCountryCode, nationalNumber); + if (countryCode == 0) { + return false; + } else { + String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode); + if (!newRegionCode.equals(defaultCountry)) { + initializeCountrySpecificInfo(newRegionCode); + } + prefixBeforeNationalNumber.append( + accruedInputWithoutFormatting.substring(0, startOfCountryCode)); + if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN ) { + prefixBeforeNationalNumber.append(" "); + } + prefixBeforeNationalNumber.append(countryCode).append(" "); + } + } else { + nationalNumber = new StringBuffer(accruedInputWithoutFormatting); + } + 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. + private char normalizeAndAccrueDigitsAndPlusSign(char nextChar) { + if (nextChar == PhoneNumberUtil.PLUS_SIGN) { + accruedInputWithoutFormatting.append(nextChar); + } + + if (PhoneNumberUtil.DIGIT_MAPPINGS.containsKey(nextChar)) { + nextChar = PhoneNumberUtil.DIGIT_MAPPINGS.get(nextChar); + accruedInputWithoutFormatting.append(nextChar); + nationalNumber.append(nextChar); + } + return nextChar; + } + + private String inputDigitHelper(char nextChar) { + if (!PhoneNumberUtil.DIGIT_MAPPINGS.containsKey(nextChar)) { + return currentOutput.toString(); + } + + Matcher digitMatcher = digitPattern.matcher(currentOutput); + if (digitMatcher.find(lastMatchPosition)) { + currentOutput = new StringBuffer(digitMatcher.replaceFirst(Character.toString(nextChar))); + lastMatchPosition = digitMatcher.start(); + } else { // More digits are entered than we could handle. + currentOutput.append(nextChar); + ableToFormat = false; + } + return currentOutput.toString(); + } +} diff --git a/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java new file mode 100644 index 000000000..881618ae3 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.i18n.phonenumbers; + +import junit.framework.TestCase; + +import java.io.InputStream; + +/** + * Unit tests for PhoneNumberUtil.java + * + * Note that these tests use the metadata contained in the file specified by TEST_META_DATA_FILE, + * not the normal metadata file, so should not be used for regression test purposes - these tests + * are illustrative only and test functionality. + * + * @author Shaopeng Jia + */ +public class AsYouTypeFormatterTest extends TestCase { + private PhoneNumberUtil phoneUtil; + private static final String TEST_META_DATA_FILE = + "/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting"; + + public AsYouTypeFormatterTest() { + PhoneNumberUtil.resetInstance(); + InputStream in = PhoneNumberUtilTest.class.getResourceAsStream(TEST_META_DATA_FILE); + phoneUtil = PhoneNumberUtil.getInstance(in); + } + + public void testAsYouTypeFormatterUS() { + AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US"); + assertEquals("6", formatter.inputDigit('6')); + assertEquals("65", formatter.inputDigit('5')); + assertEquals("650", formatter.inputDigit('0')); + assertEquals("6502", formatter.inputDigit('2')); + assertEquals("65025", formatter.inputDigit('5')); + assertEquals("650 253 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("650 253 2\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("650 253 22\u2008\u2008", formatter.inputDigit('2')); + assertEquals("650 253 222\u2008", formatter.inputDigit('2')); + assertEquals("650 253 2222", formatter.inputDigit('2')); + + formatter.clear(); + assertEquals("6", formatter.inputDigit('6')); + assertEquals("65", formatter.inputDigit('5')); + assertEquals("650", formatter.inputDigit('0')); + assertEquals("6502", formatter.inputDigit('2')); + assertEquals("65025", formatter.inputDigit('5')); + assertEquals("650 253 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("650 253 2\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("650 253 22\u2008\u2008", formatter.inputDigit('2')); + assertEquals("650 253 222\u2008", formatter.inputDigit('2')); + assertEquals("650 253 2222", formatter.inputDigit('2')); + + formatter.clear(); + assertEquals("6", formatter.inputDigit('6')); + assertEquals("65", formatter.inputDigit('5')); + assertEquals("650", formatter.inputDigit('0')); + assertEquals("650-", formatter.inputDigit('-')); + assertEquals("650-2", formatter.inputDigit('2')); + assertEquals("650-25", formatter.inputDigit('5')); + assertEquals("650 253 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("650 253 \u2008\u2008\u2008\u2008", formatter.inputDigit('-')); + assertEquals("650 253 2\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("650 253 22\u2008\u2008", formatter.inputDigit('2')); + assertEquals("650 253 222\u2008", formatter.inputDigit('2')); + assertEquals("650 253 2222", formatter.inputDigit('2')); + + formatter.clear(); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("01", formatter.inputDigit('1')); + assertEquals("011", formatter.inputDigit('1')); + assertEquals("0114", formatter.inputDigit('4')); + assertEquals("01148", formatter.inputDigit('8')); + assertEquals("011 48 8", formatter.inputDigit('8')); + assertEquals("011 48 88", formatter.inputDigit('8')); + assertEquals("011 48 881", formatter.inputDigit('1')); + assertEquals("011 48 88 12\u2008 \u2008\u2008 \u2008\u2008", formatter.inputDigit('2')); + assertEquals("011 48 88 123 \u2008\u2008 \u2008\u2008", formatter.inputDigit('3')); + assertEquals("011 48 88 123 1\u2008 \u2008\u2008", formatter.inputDigit('1')); + assertEquals("011 48 88 123 12 \u2008\u2008", formatter.inputDigit('2')); + assertEquals("011 48 88 123 12 1\u2008", formatter.inputDigit('1')); + assertEquals("011 48 88 123 12 12", formatter.inputDigit('2')); + + formatter.clear(); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("01", formatter.inputDigit('1')); + assertEquals("011", formatter.inputDigit('1')); + assertEquals("0114", formatter.inputDigit('4')); + assertEquals("01144", formatter.inputDigit('4')); + assertEquals("011 44 6", formatter.inputDigit('6')); + assertEquals("011 44 61", formatter.inputDigit('1')); + assertEquals("011 44 612", formatter.inputDigit('2')); + assertEquals("011 44 6 123 \u2008\u2008\u2008 \u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("011 44 6 123 1\u2008\u2008 \u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("011 44 6 123 12\u2008 \u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("011 44 6 123 123 \u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("011 44 6 123 123 1\u2008\u2008", formatter.inputDigit('1')); + assertEquals("011 44 6 123 123 12\u2008", formatter.inputDigit('2')); + assertEquals("011 44 6 123 123 123", formatter.inputDigit('3')); + + formatter.clear(); + assertEquals("+", formatter.inputDigit('+')); + assertEquals("+1", formatter.inputDigit('1')); + assertEquals("+16", formatter.inputDigit('6')); + assertEquals("+165", formatter.inputDigit('5')); + assertEquals("+1650", formatter.inputDigit('0')); + assertEquals("+1 650 2\u2008\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("+1 650 25\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('5')); + assertEquals("+1 650 253 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("+1 650 253 2\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("+1 650 253 22\u2008\u2008", formatter.inputDigit('2')); + assertEquals("+1 650 253 222\u2008", formatter.inputDigit('2')); + + formatter.clear(); + assertEquals("+", formatter.inputDigit('+')); + assertEquals("+4", formatter.inputDigit('4')); + assertEquals("+48", formatter.inputDigit('8')); + assertEquals("+488", formatter.inputDigit('8')); + assertEquals("+4888", formatter.inputDigit('8')); + assertEquals("+48 881", formatter.inputDigit('1')); + assertEquals("+48 88 12\u2008 \u2008\u2008 \u2008\u2008", formatter.inputDigit('2')); + assertEquals("+48 88 123 \u2008\u2008 \u2008\u2008", formatter.inputDigit('3')); + assertEquals("+48 88 123 1\u2008 \u2008\u2008", formatter.inputDigit('1')); + assertEquals("+48 88 123 12 \u2008\u2008", formatter.inputDigit('2')); + assertEquals("+48 88 123 12 1\u2008", formatter.inputDigit('1')); + assertEquals("+48 88 123 12 12", formatter.inputDigit('2')); + + // Test US number with full-width characters. + formatter.clear(); + assertEquals("\uFF16", formatter.inputDigit('\uFF16')); + assertEquals("\uFF16\uFF15", formatter.inputDigit('\uFF15')); + assertEquals("\uFF16\uFF15\uFF10", formatter.inputDigit('\uFF10')); + assertEquals("\uFF16\uFF15\uFF10\uFF12", formatter.inputDigit('\uFF12')); + assertEquals("\uFF16\uFF15\uFF10\uFF12\uFF15", formatter.inputDigit('\uFF15')); + assertEquals("650 253 \u2008\u2008\u2008\u2008", formatter.inputDigit('\uFF13')); + assertEquals("650 253 2\u2008\u2008\u2008", formatter.inputDigit('\uFF12')); + assertEquals("650 253 22\u2008\u2008", formatter.inputDigit('\uFF12')); + assertEquals("650 253 222\u2008", formatter.inputDigit('\uFF12')); + assertEquals("650 253 2222", formatter.inputDigit('\uFF12')); + + // Mobile short code. + formatter.clear(); + assertEquals("*", formatter.inputDigit('*')); + assertEquals("*1", formatter.inputDigit('1')); + assertEquals("*12", formatter.inputDigit('2')); + assertEquals("*121", formatter.inputDigit('1')); + assertEquals("*121#", formatter.inputDigit('#')); + } + + public void testAsYouTypeFormatterGBFixedLine() { + AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("GB"); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("02", formatter.inputDigit('2')); + assertEquals("020", formatter.inputDigit('0')); + assertEquals("0207", formatter.inputDigit('7')); + assertEquals("02070", formatter.inputDigit('0')); + assertEquals("020 703\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("020 7031 \u2008\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("020 7031 3\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("020 7031 30\u2008\u2008", formatter.inputDigit('0')); + assertEquals("020 7031 300\u2008", formatter.inputDigit('0')); + assertEquals("020 7031 3000", formatter.inputDigit('0')); + } + + public void testAsYouTypeFormatterGBTollFree() { + AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("GB"); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("08", formatter.inputDigit('8')); + assertEquals("080", formatter.inputDigit('0')); + assertEquals("0807", formatter.inputDigit('7')); + assertEquals("08070", formatter.inputDigit('0')); + assertEquals("080 703\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("080 7031 \u2008\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("080 7031 3\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("080 7031 30\u2008\u2008", formatter.inputDigit('0')); + assertEquals("080 7031 300\u2008", formatter.inputDigit('0')); + assertEquals("080 7031 3000", formatter.inputDigit('0')); + } + + public void testAsYouTypeFormatterGBPremiumRate() { + AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("GB"); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("09", formatter.inputDigit('9')); + assertEquals("090", formatter.inputDigit('0')); + assertEquals("0907", formatter.inputDigit('7')); + assertEquals("09070", formatter.inputDigit('0')); + assertEquals("090 703\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("090 7031 \u2008\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("090 7031 3\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("090 7031 30\u2008\u2008", formatter.inputDigit('0')); + assertEquals("090 7031 300\u2008", formatter.inputDigit('0')); + assertEquals("090 7031 3000", formatter.inputDigit('0')); + } + + public void testAsYouTypeFormatterNZMobile() { + AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("NZ"); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("02", formatter.inputDigit('2')); + assertEquals("021", formatter.inputDigit('1')); + assertEquals("0211", formatter.inputDigit('1')); + assertEquals("02112", formatter.inputDigit('2')); + assertEquals("021123", formatter.inputDigit('3')); + assertEquals("0211234", formatter.inputDigit('4')); + assertEquals("02112345", formatter.inputDigit('5')); + assertEquals("021123456", formatter.inputDigit('6')); + } + + public void testAsYouTypeFormatterDE() { + AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("DE"); + assertEquals("0", formatter.inputDigit('0')); + assertEquals("03", formatter.inputDigit('3')); + assertEquals("030", formatter.inputDigit('0')); + assertEquals("0301", formatter.inputDigit('1')); + assertEquals("03012", formatter.inputDigit('2')); + assertEquals("030123", formatter.inputDigit('3')); + assertEquals("0301234", formatter.inputDigit('4')); + } +} diff --git a/java/src/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java b/java/src/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java new file mode 100644 index 000000000..5d7533f9f --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.i18n.phonenumbers; + +import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +/** + * Tool to convert phone number metadata from the XML format to protocol buffer format. It is + * wrapped in the genrule of the BUILD file and run as a preprocessing step when building the + * phone number library. Example command line invocation: + * + * ./BuildMetadataProtoFromXml PhoneNumberMetadata.xml PhoneNumberMetadataProto true + * + * When liteBuild flag is set to true, the outputFile generated omits certain metadata which is not + * needed for clients using liteBuild. At this moment, example numbers information is omitted. + * + * @author Shaopeng Jia + */ +public class BuildMetadataProtoFromXml { + private BuildMetadataProtoFromXml() { + } + private static final Logger LOGGER = Logger.getLogger(BuildMetadataProtoFromXml.class.getName()); + private static Boolean liteBuild; + + public static void main(String[] args) { + String inputFile = args[0]; + String outputFile = args[1]; + liteBuild = args.length > 2 && Boolean.getBoolean(args[2]); + File xmlFile = new File(inputFile); + try { + FileOutputStream output = new FileOutputStream(outputFile); + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + Document document = builder.parse(xmlFile); + document.getDocumentElement().normalize(); + Element rootElement = document.getDocumentElement(); + NodeList territory = rootElement.getElementsByTagName("territory"); + PhoneMetadataCollection.Builder metadataCollection = PhoneMetadataCollection.newBuilder(); + int numOfTerritories = territory.getLength(); + for (int i = 0; i < numOfTerritories; i++) { + Element territoryElement = (Element) territory.item(i); + String regionCode = territoryElement.getAttribute("id"); + PhoneMetadata metadata = loadCountryMetadata(regionCode, territoryElement); + metadataCollection.addMetadata(metadata); + } + metadataCollection.build().writeTo(output); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, e.toString()); + } catch (SAXException e) { + LOGGER.log(Level.SEVERE, e.toString()); + } catch (ParserConfigurationException e) { + LOGGER.log(Level.SEVERE, e.toString()); + } + } + + private static PhoneMetadata loadCountryMetadata(String regionCode, Element element) { + PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); + metadata.setId(regionCode); + metadata.setCountryCode(Integer.parseInt(element.getAttribute("countryCode"))); + metadata.setInternationalPrefix(element.getAttribute("internationalPrefix")); + if (element.hasAttribute("preferredInternationalPrefix")) { + String preferredInternationalPrefix = element.getAttribute("preferredInternationalPrefix"); + metadata.setPreferredInternationalPrefix(preferredInternationalPrefix); + } + String nationalPrefix = ""; + if (element.hasAttribute("nationalPrefix")) { + nationalPrefix = element.getAttribute("nationalPrefix"); + metadata.setNationalPrefix(nationalPrefix); + metadata.setNationalPrefixFormattingRule( + getNationalPrefixFormattingRuleFromElement(element, nationalPrefix)); + + if (element.hasAttribute("nationalPrefixForParsing")) { + metadata.setNationalPrefixForParsing(element.getAttribute("nationalPrefixForParsing")); + if (element.hasAttribute("nationalPrefixTransformRule")) { + metadata.setNationalPrefixTransformRule( + element.getAttribute("nationalPrefixTransformRule")); + } + } else { + metadata.setNationalPrefixForParsing(nationalPrefix); + } + } + if (element.hasAttribute("preferredExtnPrefix")) { + metadata.setPreferredExtnPrefix(element.getAttribute("preferredExtnPrefix")); + } + + // Extract availableFormats + NodeList numberFormatElements = element.getElementsByTagName("numberFormat"); + int numOfFormatElements = numberFormatElements.getLength(); + if (numOfFormatElements > 0) { + for (int i = 0; i < numOfFormatElements; i++) { + Element numberFormatElement = (Element) numberFormatElements.item(i); + NumberFormat.Builder format = NumberFormat.newBuilder(); + if (numberFormatElement.hasAttribute("nationalPrefixFormattingRule")) { + format.setNationalPrefixFormattingRule( + getNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix)); + } else { + format.setNationalPrefixFormattingRule(metadata.getNationalPrefixFormattingRule()); + } + if (numberFormatElement.hasAttribute("leadingDigits")) { + format.setLeadingDigits(numberFormatElement.getAttribute("leadingDigits")); + } + format.setPattern(numberFormatElement.getAttribute("pattern")); + String formatValue = numberFormatElement.getFirstChild().getNodeValue(); + format.setFormat(formatValue); + metadata.addNumberFormat(format.build()); + } + } + + NodeList intlNumberFormatElements = element.getElementsByTagName("intlNumberFormat"); + int numOfIntlFormatElements = intlNumberFormatElements.getLength(); + if (numOfIntlFormatElements > 0) { + for (int i = 0; i < numOfIntlFormatElements; i++) { + Element numberFormatElement = (Element) intlNumberFormatElements.item(i); + NumberFormat.Builder format = NumberFormat.newBuilder(); + if (numberFormatElement.hasAttribute("leadingDigits")) { + format.setLeadingDigits(numberFormatElement.getAttribute("leadingDigits")); + } + format.setPattern(numberFormatElement.getAttribute("pattern")); + format.setFormat(numberFormatElement.getFirstChild().getNodeValue()); + metadata.addIntlNumberFormat(format.build()); + } + } + + PhoneNumberDesc generalDesc = + processPhoneNumberDescElement(PhoneNumberDesc.newBuilder().build(), + element, "generalDesc"); + metadata.setGeneralDesc(generalDesc); + metadata.setFixedLine(processPhoneNumberDescElement(generalDesc, element, "fixedLine")); + metadata.setMobile(processPhoneNumberDescElement(generalDesc, element, "mobile")); + metadata.setTollFree(processPhoneNumberDescElement(generalDesc, element, "tollFree")); + metadata.setPremiumRate(processPhoneNumberDescElement(generalDesc, element, "premiumRate")); + metadata.setSharedCost(processPhoneNumberDescElement(generalDesc, element, "sharedCost")); + metadata.setVoip(processPhoneNumberDescElement(generalDesc, element, "voip")); + metadata.setPersonalNumber(processPhoneNumberDescElement(generalDesc, element, + "personalNumber")); + + if (metadata.getMobile().getNationalNumberPattern().equals( + metadata.getFixedLine().getNationalNumberPattern())) { + metadata.setSameMobileAndFixedLinePattern(true); + } + return metadata.build(); + } + + private static String getNationalPrefixFormattingRuleFromElement(Element element, + String nationalPrefix) { + String nationalPrefixFormattingRule = element.getAttribute("nationalPrefixFormattingRule"); + // Replace $NP with national prefix and $FG with the first group ($1). + nationalPrefixFormattingRule = + nationalPrefixFormattingRule.replaceFirst("\\$NP", nationalPrefix) + .replaceFirst("\\$FG", "\\$1"); + return nationalPrefixFormattingRule; + } + + /** + * Processes a phone number description element from the XML file and returns it as a + * PhoneNumberDesc. If the description element is a fixed line or mobile number, the general + * description will be used to fill in the whole element if necessary, or any components that are + * missing. For all other types, the general description will only be used to fill in missing + * components if the type has a partial definition. For example, if no "tollFree" element exists, + * we assume there are no toll free numbers for that locale, and return a phone number description + * with "NA" for both the national and possible number patterns. + * + * @param generalDesc a generic phone number description that will be used to fill in missing + * parts of the description + * @param countryElement the XML element representing all the country information + * @param numberType the name of the number type, corresponding to the appropriate tag in the XML + * file with information about that type + * @return complete description of that phone number type + */ + private static PhoneNumberDesc processPhoneNumberDescElement(PhoneNumberDesc generalDesc, + Element countryElement, + String numberType) { + NodeList phoneNumberDescList = countryElement.getElementsByTagName(numberType); + PhoneNumberDesc.Builder numberDesc = PhoneNumberDesc.newBuilder(); + if (phoneNumberDescList.getLength() == 0 && + (!numberType.equals("fixedLine") && !numberType.equals("mobile") && + !numberType.equals("generalDesc"))) { + numberDesc.setNationalNumberPattern("NA"); + numberDesc.setPossibleNumberPattern("NA"); + return numberDesc.build(); + } + numberDesc.mergeFrom(generalDesc); + if (phoneNumberDescList.getLength() > 0) { + Element element = (Element) phoneNumberDescList.item(0); + NodeList possiblePattern = element.getElementsByTagName("possibleNumberPattern"); + if (possiblePattern.getLength() > 0) { + numberDesc.setPossibleNumberPattern(possiblePattern. + item(0).getFirstChild().getNodeValue()); + } + + NodeList validPattern = element.getElementsByTagName("nationalNumberPattern"); + if (validPattern.getLength() > 0) { + numberDesc.setNationalNumberPattern(validPattern. + item(0).getFirstChild().getNodeValue()); + } + + if (!liteBuild) { + NodeList exampleNumber = element.getElementsByTagName("exampleNumber"); + if (exampleNumber.getLength() > 0) { + numberDesc.setExampleNumber(exampleNumber.item(0).getFirstChild().getNodeValue()); + } + } + } + return numberDesc.build(); + } +} diff --git a/java/src/com/google/i18n/phonenumbers/NumberParseException.java b/java/src/com/google/i18n/phonenumbers/NumberParseException.java new file mode 100644 index 000000000..19a5f7ad9 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/NumberParseException.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.i18n.phonenumbers; + +/** + * Generic exception class for errors encountered when parsing phone numbers. + * @author Lara Rennie + */ +public class NumberParseException extends Exception { + + public enum ErrorType { + INVALID_COUNTRY_CODE, + // This generally indicates the string passed in had less than 3 digits in it. More + // specifically, the number failed to match the regular expression VALID_PHONE_NUMBER in + // PhoneNumberUtil.java. + NOT_A_NUMBER, + // This indicates the string started with an international dialing prefix, but after this was + // stripped from the number, had less digits than any valid phone number (including country + // code) could have. + TOO_SHORT_AFTER_IDD, + // This indicates the string, after any country code has been stripped, had less digits than any + // valid phone number could have. + TOO_SHORT_NSN, + // This indicates the string had more digits than any valid phone number could have. + TOO_LONG, + }; + + private ErrorType errorType; + private String message; + + public NumberParseException(ErrorType errorType, String message) { + super(message); + this.message = message; + this.errorType = errorType; + } + + /** + * Returns the error type of the exception that has been thrown. + */ + public ErrorType getErrorType() { + return errorType; + } + + public String toString() { + return "Error type: " + errorType + ". " + message; + } +} diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberMetaData.xml b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetaData.xml new file mode 100644 index 000000000..b83058eba --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetaData.xml @@ -0,0 +1,5281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [2-79]\d{7,8}|800\d{2,9} + \d{5,12} + + + (?:[2-4679][2-8]\d|600[25])\d{5} + \d{7,9} + 22345678 + + + 5[056]\d{7} + \d{9} + 501234567 + + + 400\d{6}|800\d{2,9} + \d{5,12} + 800123456 + + + 900[02]\d{5} + \d{9} + 900234567 + + + 700[05]\d{5} + \d{9} + 700012345 + + + + + + + + $1 $2 $3 + + + [2-7]\d{8} + \d{9} + + + (?:[25][0-8]|[34][0-4]|6[0-5])[2-9]\d{6} + 234567890 + + + 7[057-9]\d{7} + 701234567 + + + + + + + + + [289]\d{9} + \d{7,10} + + + 268(?:4(?:6[0-3]|84)|56[0-2])\d{4} + 2684601234 + + + 268(?:464|7(?:2[0-9]|64|7[0-5]|8[358]))\d{4} + \d{10} + 2684641234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + 26848[01]\d{4} + \d{10} + 2684801234 + + + + + + + + + [289]\d{9} + \d{7,10} + + + 2644(?:6[12]|9[78])\d{4} + 2644612345 + + + 264(?:235|476|5(?:3[6-9]|8[1-4])|7(?:29|72))\d{4} + \d{10} + 2642351234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + + + + + + + + + + + + $1-$2-$3 + $1-$2-$3 + $1 15-$2-$3 + 9 $1 $2-$3 + $1 15-$2-$3 + 9 $1 $2-$3 + $1 15-$2-$3 + 9 $1 $2-$3 + $1 $2-$3 + $1 $2-$3 + + $1 $2-$3 + $1 $2-$3 + $1 $2-$3 + $1 $2-$3 + + + [1-9]\d{9,11} + \d{6,12} + + + [1-9]\d{9} + \d{6,10} + 1123456789 + + + 9(?:11[2-9]\d{7}|(?:2(?:2[013]|37|6[14]|9[179])|3(?:4[1235]|5[138]|8[1578]))[2-9]\d{6}|\d{4}[2-9]\d{5}) + \d{6,12} + 91123456789 + + + 80\d{8} + \d{10} + 8012345678 + + + 6(?:0\d|10)\d{7} + \d{10} + 6001234567 + + + + + + + + + [689]\d{9} + \d{7,10} + + + 6846(?:22|33|44|55|77|88|9[19])\d{4} + 6846221234 + + + 684(?:733|258)\d{4} + \d{10} + 6847331234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + $1 $2 + $1 $2 + $1 $2 + + + \d{4,13} + \d{3,13} + + + + 1\d{3,12}|(?:2(?:1[467]|2[134-8]|5[2357]|6[1-46-8]|7[1-8]|8[124-7]|8[1458])|3(?:1[1-8]|3[23568]|4[5-7]|5[1378]|6[1-38]|8[3-68])|4(?:2[1-8]|35|63|7[1368]|8[2457])|5(?:1[27]|2[1-8]|3[357]|4[147]|5[12578]|6[37])|6(?:13|2[1-47]|4[1-35-8]|5[468]|62)|7(?:2[1-8]|3[25]|4[13478]|5[68]|6[16-8]|7[1-6]|9[45]))\d{3,10}|5(?:0[1-9]|[79]\d)\d{2,10}|720\d{6,10} + 1234567890 + + + 6(?:44|5[0-3579]|6[013-9]|[7-9]\d)\d{4,10} + \d{7,13} + 644123456 + + + 80[02]\d{6,10} + \d{9,13} + 800123456 + + + (?:711|9(?:0[01]|3[019]))\d{6,10} + \d{9,13} + 900123456 + + + 8(?:10|2[018])\d{6,10} + \d{9,13} + 810123456 + + + 780\d{6,10} + \d{9,13} + 780123456 + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + $1 $2 + $1 $2 $3 + + + [1-578]\d{5,9} + \d{6,10} + + + [2378]\d{8} + \d{8,9} + 212345678 + + + 4[0-68]\d{7} + \d{9} + 412345678 + + + + 1(?:80(?:0\d{2})?|3(?:00\d{2})?)\d{4} + \d{6,10} + 1800123456 + + + 190[0126]\d{6} + \d{10} + 1900123456 + + + + 500\d{6} + \d{9} + 500123456 + + + 550\d{6} + \d{9} + 550123456 + + + + + + + + $1 $2 + + + [57-9]\d{6} + \d{7} + + + 5(?:2\d{2}|8(?:[2-7]\d|8[0-79]|9[48]))\d{3} + 5212345 + + + (?:5[69]\d|9(?:6\d|9[02-9])|7[34]\d)\d{4} + 5601234 + + + 800\d{4} + 8001234 + + + 900\d{4} + 9001234 + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 + + + [124-8]\d{7,8} + \d{8,9} + + + (?:1(?:(?:2[3-5]|36|8\d|9)\d|02|1[0-589]|3[358]|4[013-79]|5[0-479]|6[0236-9]|7[0-24-8])|2(?:16|2\d|3[0-24]|4[1468]|55|6[56]|79))\d{5} + 123123456 + + + (?:40|5[015]|7[07])\d{7}|60540\d{4} + \d{9} + 401234567 + + + 88\d{7} + \d{9} + 881234567 + + + + + + + + + + + + + [289]\d{9} + \d{7,10} + + + 246[2-9]\d{6} + 2462345678 + + + 246(?:(?:2[346]|45|82)\d|25[0-4])\d{4} + \d{10} + 2462501234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 $4 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 $4 + + + [1-9]\d{7,8} + \d{8,9} + + + + (?:1[0-69]|[23][2-8]|[49][23]|5\d|6[013-57-9]|7[18])\d{6}|8(?:0[1-9]|[1-79]\d)\d{5} + \d{8} + 12345678 + + + 4(?:7\d|8[4-9]|9[1-9])\d{6} + \d{9} + 470123456 + + + 800\d{5} + \d{8} + 80012345 + + + (?:90|7[07])\d{6} + \d{8} + 90123456 + + + + + + + + + + + + $1 $2 $3 + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 + + + [1-9]\d{6,8} + \d{7,9} + + + 2\d{6,7}|(?:[367]\d|4[124-7]|5[1-9]|8[1-6]|9[1-7])\d{5,6}|43[1-6]\d{4,5} + \d{7,8} + 2123456 + + + (?:8[7-9]|98)\d{7}|43[0789]\d{5}|48\d{6} + \d{8,9} + 48123456 + + + 800\d{5} + \d{8} + 80012345 + + + 90\d{6} + \d{8} + 90123456 + + + + + + + + + + + + + + + + + + + + + + + + + [489]\d{9} + \d{7,10} + + + 441(?:2(?:02|23|[3479]\d)|[46]\d{2}|5(?:40|89)|824)\d{4} + 4412345678 + + + 441(?:[37]\d{2}|5(?:[0-3]\d|9[09]))\d{4} + \d{10} + 4413701234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + $1 $2 + + + [2-578]\d{6} + \d{7} + + + [2-5]\d{6} + 2345678 + + + [78]\d{6} + 7123456 + + + + + + + + + + + + + ($1) $2-$3 + $1-$2 + $1 $2 $3 + + + [1-9]\d{7,9} + \d{8,10} + + + 400\d{5}|(?:[14689][1-9]|2[12478]|3[1-578]|5[13-5]|7[13-579])[2-5]\d{7} + 1123456789 + + + (?:[14689][1-9]|2[12478]|3[1-578]|5[13-5]|7[13-579])[6-9]\d{7} + \d{10} + 1161234567 + + + 800\d{6,7} + 800123456 + + + [359]00\d{6,7} + 300123456 + + + + + + + + + [289]\d{9} + \d{7,10} + + + 242(?:3(?:02|[236][1-9]|4[0-24-9]|5[0-68]|7[3-57]|9[2-5])|4(?:2[237]|51|64|77)|502|636|702)\d{4} + 2423456789 + + + 242(?:[3-5]57|359)\d{4} + \d{10} + 2423591234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 + + + (?:17|[2-8])\d{6} + \d{6,8} + + + (?:2[3-6]|[34][5-7]|5[236]|6[2-46]|7[246]|8[2-4])\d{5} + \d{6,7} + 2345678 + + + 17\d{6} + \d{8} + 17123456 + + + + + + + + + $1 $2 $3 + $1 $2 + + + [2-9]\d{6,7} + \d{7,8} + + + (?:2(?:4[0-48]|6[0-24]|9[0578])|3(?:1[0235-9]|55|6\d|7[01]|9[0-57])|4(?:6[03]|7[1267]|9[0-5])|5(?:3[0389]|4[0489]|7[1-47]|88|9[0-49])|6(?:2[1-35]|5[149]|8[067]))\d{4} + \d{7} + 2401234 + + + 7[1-3]\d{6}|74[0-7]\d{5} + \d{8} + 71123456 + + + 8\d{6} + \d{7} + 8123456 + + + 90\d{5} + \d{7} + 9012345 + + + + + + + + + + + + + + + + + [2-9]\d{9}|3\d{6} + \d{7,10} + + + (?:2(?:04|26|50|89)|306|4(03|16|18|38|50|56)|5(?:00|06|14|19|81|87)|6(?:00|04|13|47)|7(?:00|05|09|10|78|80)|8(?:07|19|67))[2-9]\d{6}|310\d{4} + 2042345678 + + + (?:2(?:04|26|50|89)|306|4(03|16|18|38|50|56)|5(?:00|06|14|19|81|87)|6(?:00|04|13|47)|7(?:00|05|09|10|78|80)|8(?:07|19|67)|9(?:02|05))[2-9]\d{6} + 2042345678 + + + 8(?:00|66|77|88)[2-9]\d{6}|310\d{4} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 $4 + $1 $2 $3 + + + [2-9]\d{8} + \d{9} + + + (?:2[12467]|3[1-4]|4[134]|5[12568]|6[12]|[7-9]1)\d{7} + 212345678 + + + 7[46-9]\d{7} + 741234567 + + + 800\d{6} + 800123456 + + + 90[016]\d{6} + 900123456 + + + 84[0248]\d{6} + 840123456 + + + 878\d{6} + 878123456 + + + + + + + + + $1 $2 $3 $4 + + + [02-5]\d{7} + \d{8} + + + (?:2(?:0[023]|1[02357]|[23][045]|4[03-5])|3(?:0[06]|1[069]|[2-4][07]|5[09]|6[08]))\d{5} + 21234567 + + + (?:0[1-9]|4[4-9]|50|6[067])\d{6} + 01234567 + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 + + + (?:[2-9]|600|123)\d{7,8} + \d{6,11} + + + (?:2|32|41)\d{7}|(?:3[3-5]|4[235]|5[1-3578]|6[13-57]|7[1-35])\d{6,7} + + \d{6,9} + 21234567 + + + 9[6-9]\d{7} + \d{8,9} + 961234567 + + + + + 800\d{6}|1230\d{7} + \d{9,11} + 800123456 + + + 600\d{7,8} + \d{10,11} + 6001234567 + + + 44\d{7} + \d{9} + 441234567 + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 + + + [237-9]\d{7} + \d{8} + + + + (?:22|33)\d{6} + 22123456 + + + [79]\d{7} + 71234567 + + + 800\d{5} + 80012345 + + + + 88\d{6} + 88012345 + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-79]\d{7,11}|8[0-357-9]\d{6,9} + \d{4,12} + + + 21\d{8,10}|(?:10|2[02-57-9]|3(?:11|7[159])|4[135]1|5(?:1\d|2[37]|3[12]|7[13-79]|9[15])|7(?:31|5[457]|6[09])|898)\d{8}|(?:3(?:1[02-9]|35|49|5\d|7[02-68]|9[1-68])|4(?:1[02-9]|2[179]|[35][2-9]|6[4789]|7[0-46-9]|8[23])|5(?:3[03-9]|4[36]|5\d|6[1-6]|7[028]|80|9[2-46-9])|6(?:3[1-5]|6[0238]|9[12])|7(?:01|[1579]\d|2[248]|3[04-9]|4[3-6]|6[2368])|8(?:1[236-8]|2[5-7]|[37]\d|5[1-9]|8[3678]|9[1-7])|9(?:0[1-3689]|1[1-79]|[379]\d|4[13]|5[1-5]))\d{7}|80(?:29|6[03578]|7[018]|81)\d{4} + 1012345678 + + + 1(?:3[0-9]|47|5[0135689]|8[05-9])\d{8} + \d{11} + 13123456789 + + + + + 10800\d{7} + \d{12} + 108001234567 + + + 16[08]\d{5} + \d{8} + 16812345 + + + 400\d{7} + \d{10} + 4001234567 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + + + [2-9]\d{8} + \d{9} + + + 2\d{8}|(?:3[1257-9]|4[16-9]|5[13-9])\d{7} + 212345678 + + + 60[1-8]\d{6}|7[2379]\d{7} + 601123456 + + + 800\d{6} + 800123456 + + + 90[0689]\d{6} + 900123456 + + + 8[134]\d{7} + 811234567 + + + 70[01]\d{6} + 700123456 + + + + + + + + $1/$2 + + + $1/$2 + + $1/$2 + $1/$2 + + $1/$2 + + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + + (?:4[0-8]|[1-35-9]\d)\d{4,12}|49(?:4[1-8]|[0-35-7]\d)\d{2,7} + \d{2,14} + + + (?:[246]\d{2}|3[02-9]\d|5(?:0[2-8]|[38][0-8]|[124-6]\d|[79][0-7])|[789](?:[1-9]\d|0[2-9]))\d{3,10} + 30123456 + + + + 1(?:5\d{9}|7(?:[0-57-9]|6\d)\d{7}|6[02]\d{7,8}|63\d{7}) + \d{10,11} + 15123456789 + + + 800\d{7} + \d{10} + 8001234567 + + + 900(?:[135]\d{6}|9\d{7}) + \d{10,11} + 9001234567 + + + 180\d{5,11} + \d{8,14} + 18012345 + + + 700\d{8} + \d{11} + 70012345678 + + + + + + + + + + + + $1 $2 $3 $4 + + + [1-9]\d{7} + \d{8} + + + (?:3[2-9]|4[3-9]|5[4-9]|6[2-9]|7[02-9]|8[26-9]|9[6-9])\d{6} + 32123456 + + + (?:2[0-9]|3[01]|4[0-2]|5[0-3]|6[01])\d{6} + 20123456 + + + 80\d{6} + 80123456 + + + 90\d{6} + 90123456 + + + + + + + + + [7-9]\d{9} + \d{7,10} + + + 767(?:2(?:55|66)|4(?:2[01]|4[0-25-9])|50[0-4])\d{4} + 7674201234 + + + 767(?:2(?:[2346]5|7[5-7])|31[5-7]|61[4-6])\d{4} + \d{10} + 7672251234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + [89]\d{9} + \d{7,10} + + + 8[02]9[2-9]\d{6} + 8092345678 + + + 8[02]9[2-9]\d{6} + 8092345678 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + + + $1 $2 + $1 $2 $3 + $1 $2 + $1 $2 + + + [3-9]\d{6,7}|800\d{6,7} + \d{6,10} + + + (?:3[23589]|4[3-8]|6\d|7[1-9]|88)\d{5} + \d{7} + 3212345 + + + + (?:5\d|8[1-5])\d{6}|5(?:[02]\d{2}|1(?:[0-8]\d|95)|5[0-478]\d|64[0-4]|65[1-589])\d{3} + \d{7,8} + 51234567 + + + 800(?:0\d{3}|1\d|[2-9])\d{3} + \d{7,10} + 80012345 + + + 900\d{4} + \d{7} + 9001234 + + + 70\d{5} + \d{7} + 7012345 + + + + + + + + $1 $2 + $1 $2 + $1 $2 + $1 $2 $3 + + + [1-689]\d{7,9} + \d{7,10} + + + (?:1[35][23]|2[23]\d|3\d|4(?:0[2-4]|[578][23]|64)|5(?:0[234]|[57][23])|6[24-689]3|8(?:[28][2-4]|42|6[23])|9(?:[25]2|3[24]|6[23]|7[2-4]))\d{6} + \d{7,9} + 234567890 + + + 1[0-246-9]\d{7} + \d{9} + 101234567 + + + 800\d{7} + \d{10} + 8001234567 + + + 900\d{7} + \d{10} + 9001234567 + + + + + + + + + + + + $1 $2 $3 $4 + + + [5-9]\d{8} + \d{9} + + + [89][1-8]\d{7} + 812345678 + + + 6\d{8} + 612345678 + + + [89]00\d{6} + 800123456 + + + 80[367]\d{6} + 803123456 + + + 90[12]\d{6} + 901123456 + + + 70\d{7} + 701234567 + + + + + + + + + + + + $1 $2 + $1 $2 + $1 $2 + + + [1-9]\d{4,11} + \d{5,12} + + + + 1(?:0[1-9]\d{3,7}|[35689][1-8]\d{3,9}|[47]\d{5,10})|2(?:0(?:[16-8]\d{3,7}|2[14-9]\d{1,6}|[3-5]\d{2,7}|9[0-7]\d{1,6})|[1-8]\d{3,9}|9\d{4,8})|3(?:0[1-9]\d{3,7}|[1-8]\d{3,9}|9\d{4,8})|[5689][1-8]\d{3,9}|7(?:1\d{7}|3\d{8}|5[03-9]\d{2,7}) + 1312345678 + + + 4\d{5,10}|50\d{4,8} + \d{6,11} + 412345678 + + + 800\d{4,7} + \d{7,10} + 8001234567 + + + [67]00\d{5,6} + \d{8,9} + 600123456 + + + + + + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 $4 $5 + $1 $2 $3 $4 + + + [1-689]\d{8} + \d{9} + + + [1-5]\d{8} + 123456789 + + + 6\d{8}|7[5-9]\d{7} + 612345678 + + + 80\d{7} + 801234567 + + + 8(?:1[01]|2[0156]|84|9[0-37-9])\d{6} + 810123456 + + + 9\d{8} + 912345678 + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + $1 $2 + $1 $2 + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 + + $1 $2 + $1 $2 $3 + $1 $2 $3 + + + \d{7,10} + \d{6,10} + + + + 1(?:1[3-8]|[2-69]1)\d{7}|1(?:2(?:0[024-9]|2[3-9]|3[3-79]|4[1-689]|[58][02-9]|6[0-47-9]|7[013-9]|8[02-9]|9[0-9])|3(?:0\d|[25][02-9]|3[02-579]|4[0-56-9]|[68][0-46-9]|7[1-35-79]|9[24578])|4(?:0[03-9]|2[02-57-9]|[378]\d|4[02-69]|5[0-8]|[69][0-79])|5(?:0[1-35-9]|2[024-9]|3[014-689]|4[02-9]|[57][03-9]|6\d|8[0-68]|9[0-57-9])|6(?:0[034689]|2[0-689]|3[13-9]|4[1-467]|5[0-69]|6[13-9]|7[0-8]|8[013-9]|9[0-24578])|7(?:0[0246-9]|2\d|3[0236-8]|4[03-9]|5[0-46-9]|6[13-9]|7[0-35-9]|8[024-9]|9[02-9])|8(?:0[35-9]|2[1-9]|3[02-578]|4[0-578]|5[124-9]|6[2-69]|7\d|8[2-9]|9[02569])|9(?:0[02-589]|2[02-689]|3[1-57-9]|4[2-9]|5[0-579]|6[2-47-9]|7[0-24578]|8\d|9[2-57]))\d{5,6}|(?:2[03489]|3[0347]|55)\d{8} + 1212345678 + + + 7(?:[1-57-9]\d{8}|624\d{6}) + \d{10} + 7123456789 + + + + 80(?:01111|\d{7,8})|500\d{6} + \d{7,10} + 8012345678 + + + 9[018]\d{8} + \d{10} + 9012345678 + + + + 8(?:4[3-5]|7[01])\d{7} + \d{10} + 8431234567 + + + 70\d{8} + \d{10} + 7012345678 + + + 56\d{8} + \d{10} + 5612345678 + + + + + + + + + [489]\d{9} + \d{7,10} + + + 473(?:2(?:3[0-2]|69)|3(?:2[89]|86)|4(?:08|3[5-9]|4[0-49]|5[5-79]|68|73|90)|63[68]|7(?:58|84)|938)\d{4} + 4732691234 + + + 473(?:4(?:0[3-79]|1[04-9]|20|58)|53[3-8])\d{4} + \d{10} + 4734031234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + $1 $2 $3 $4 + $1 $2 + $1 $2 + $1 $2 + $1 $2 $3 + $1 $2 $3 $4 + + + [1-3579]\d{7}|8\d{8} + \d{3,9} + + + + (?:122|2(?:22|36|5[03])|3(?:1[0-35-8]|[256]\d|3[1-35679]|4[024-79]|7[0-39]|9[1-35-7])|44[2-6])\d{5} + \d{3,8} + 32123456 + + + + (?:5[014578]|62|7[1479]|9[0135-9])\d{6} + \d{8} + 55123456 + + + + 800\d{6} + \d{9} + 800123456 + + + + + + + + + + + + $1 $2 + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [2-9]\d{4,8} + \d{5,9} + + + + (?:251|3[467]2|41|5(?:[36]1|[78]2)|61|882|9(?:1|6[268]))\d{3}|(?:31[24]|567|6(?:1|5[23])|7(?:2|43)|9(?:1|35))\d{4}|(?:2[12]|3(?:45|9[24])|4(?:2|32])|5(?:1|6[58])|64[28]|7(?:1[567]?|46|5[26]|62)|8(?:1|4[268]|7[26])|9(?:31|53))\d{5}|(?:2[12]|42|51)\d{6}|3\d{8} + \d{5,9} + 251234 + + + 2(?:755\d{4}|(?:4|08)\d{6}|[368]\d{7})|54\d{7} + \d{8,9} + 27551234 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 + + + [26-9]\d{9} + \d{10} + + + 2(?:1\d{2}|2(?:3[1-8]|4[1-7]|5[1-4]|6[1-8]|7[1-5]|[289][1-9])|3(?:1\d|2[1-5]|3[1-4]|[45][1-3]|7[1-7]|8[1-6]|9[1-79])|4(?:1\d|2[1-8]|3[1-4]|4[13-5]|6[1-578]|9[1-5])|5(?:1\d|2[1-3]|4[124]|5[1-6]|[39][1-4])|6(?:1\d|3[24]|4[1-7]|5[13-9]|[269][1-6]|7[14]|8[1-35])|7(?:1\d|[23][1-5]|4[1-7]|5[1-57]|6[134]|9[15-7])|8(?:1\d|2[1-5]|[34][1-4]|9[1-7]))\d{6} + 2123456789 + + + 69\d{8} + 6912345678 + + + 800\d{7} + 8001234567 + + + 90[19]\d{7} + 9091234567 + + + + 8(?:0[16]|12|25)\d{7} + 8011234567 + + + 70\d{8} + 7012345678 + + + + + + + + + + + + + [689]\d{9} + \d{7,10} + + + 671(?:3\d{2}|47\d|56\d|6[3-5]\d|7(?:3\d|89)|828)\d{4} + 6713123456 + + + + 671(?:3\d{2}|47\d|56\d|6[3-5]\d|7(?:3\d|89)|828)\d{4} + 6713123456 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + + + $1 $2 + $1 $2 $3 + $1 $2 $3 $4 + + + [235-9]\d{7,11} + \d{8,11} + + + [23]\d{7} + \d{8} + 21234567 + + + [5-79]\d{7} + \d{8} + 51234567 + + + 800\d{6} + \d{9} + 800123456 + + + 900\d{8} + \d{11} + 90012345678 + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 + $1 $2 $3 + $1 $2 $3 + + + [1-7]\d{5,8}|[89]\d{6,11} + \d{6,12} + + + + (?:1|62)\d{7}|(?:2[0-3]|3[1-5]|4[02-47-9]|5[1-3])\d{6} + \d{6,9} + 12345678 + + + 9[12589]\d{6,10} + \d{8,12} + 912345678 + + + + 800\d{4,7} + \d{7,10} + 8001234567 + + + + 6(?:0\d{3}|1)\d{4} + \d{6,9} + 601234567 + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + \d{8,9} + \d{6,9} + + + + (?:1\d|2(?:1\d|[2-9])|3[2-7]|4[24-9]|5[2-79]|6[23689]|7(?:1\d|[2-9])|8[2-57-9]|9[2-69])\d{6} + 12345678 + + + (?:[237]0|31)\d{7} + \d{9} + 201234567 + + + 80\d{6} + \d{8} + 80123456 + + + 9[01]\d{6} + \d{8} + 90123456 + + + 40\d{6} + \d{8} + 40123456 + + + + + + + + + $1 $2 + $1 $2 + $1-$2-$3 + $1 $2 + + $1 $2 + $1 $2 $3 $4 + + + [1-9]\d{6,10} + \d{5,11} + + + + 2[124]\d{7,8}|(?:2(?:[35][1-4]|6[0-8]|7[1-6]|8\d|9[1-8])|3(?:1|2[1-578]|3[1-68]|4[1-3]|5[1-8]|6[1-3568]|7[0-46]|8\d)|4(?:0[1-589]|1[01347-9]|2[0-36-8]|3[0-24-68]|5[1-378]|6[1-5]|7[134]|8[1245])|5(?:1[1-35-9]|2[25-8]|3[1246-9]|4[1-3589]|5[1-46]|6[1-8])|6(?:19?|[25]\d|3[1-469]|4[1-6])|7(?:1[1-46-9]|2[14-9]|[36]\d|4[1-8]|5[1-9]|7[1-36-9])|9(?:0[12]|1[0134-8]|2[0-479]|5[125-8]|6[23679]|7[159]|8[01346]))\d{5,8} + \d{5,10} + 612345678 + + + 8[1-35-9]\d{7,9} + \d{9,11} + 812345678 + + + 177\d{6,8}|800\d{5,7} + \d{8,11} + 8001234567 + + + 809\d{7} + \d{10} + 8091234567 + + + + + + + + + $1 $2 $3 + $1 $2 + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [124-9]\d{6,9} + \d{5,10} + + + + (?:2[24-9]|4(?:0[24]|7)|5(?:0[45]|8)|6[237-9]|9[3-9])\d{5}|(?:45|[569]1|818)\d{6}|(?:1|4[12469]|5[3679]|6[56]|7[14]|9[04])\d{7}|21\d{6,7}|(?:23|4[34]|52|64)\d{5,7}|48\d{8} + \d{5,10} + 2212345 + + + 8[35-9]\d{7} + \d{9} + 850123456 + + + 1800\d{6} + \d{10} + 1800123456 + + + 15(?:1[2-9]|[2-9]0|59)\d{6} + \d{10} + 1520123456 + + + 18[59]0\d{6} + \d{10} + 1850123456 + + + 700\d{6} + \d{9} + 700123456 + + + 76\d{7} + \d{9} + 761234567 + + + + + + + + + + + + $1-$2-$3 + $1-$2-$3 + $1-$2-$3-$4 + + $1-$2 + $1-$2-$3 + $1-$2-$3 + + + [1-57-9]\d{6,9} + \d{7,10} + + + (?:[2-489]|7[2-46-8])\d{7} + \d{7,9} + 21234567 + + + 5[024679]\d{7} + \d{9} + 501234567 + + + 1(?:80[01]\d{3}|255)\d{3} + \d{7,10} + 1800123456 + + + + + 1(?:212|(?:919|200)\d{2})\d{4} + \d{8,10} + 1919123456 + + + 1(?:700|809)\d{6} + \d{10} + 1700123456 + + + 77\d{7} + \d{9} + 771234567 + + + + + + + + + + $1 $2 $3 + + $1 $2 $3 + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-9]\d{9,10} + \d{6,11} + + + + (?:11|2[02]|33|4[04]|79|80)[2-6]\d{7}|(?:1(?:2[0-249]|3[0-25]|4[145]|5[14]|6[014]|7[1257]|8[01346]|9[14])|2(?:1[257]|3[013]|4[01]|5[0137]|6[0158]|78|8[1568]|9[14])|3(?:26|4[1-3]|5[34]|6[01489]|7[02-46]|8[159])|4(?:1[36]|2[1-47]|3[15]|5[12]|6[126-9]|7[0-24-9]|8[013-57]|9[014-7])|5(?:1[25]|22|3[25]|4[28]|5[12]|6[25]|[78]1|9[15])|6(?:12|[2345]1|57|6[13]|7[14]|80)|7(?:12|2[14]|3[134]|4[47]|5[15]|[67]1|88)|8(?:16|2[014]|3[126]|6[136]|7[078]|8[34]|91))[2-6]\d{6}|(?:1(?:2[35-8]|3[346-9]|4[236-9]|5[0235-9]|6[235-9]|7[34689]|8[257-9]|9[0235-9])|2(?:1[134689]|3[24-8]|4[2-8]|5[25689]|6[2-4679]|7[13-79]|8[2-479]|9[235-9])|3(?:01|1[79]|2[1-5]|4[25-8]|5[125689]|6[235-7]|7[157-9]|8[2-467])|4(?:1[14578]|2[5689]|3[2-467]|5[4-7]|6[35]|73|8[2689]|9[2389])|5(?:1[146-9]|2[14-8]|3[1346]|4[14-69]|5[46]|6[146-9]|7[2-4]|8[2-8]|9[246])|6(?:1[1358]|2[2457]|3[2-4]|4[235-7]|5[2-689]|6[24-58]|7[23-689]|8[1-6])|7(?:1[013-9]|2[0235-9]|3[2679]|4[1-35689]|5[2-46-9]|[67][02-9]|8[0-8]|9\d)|8(?:1[1357-9]|2[235-8]|3[03-57-9]|4[0-24-9]|5\d|6[2457-9]|7[1-6]|8[1256]|9[2-4]))\d[2-6]\d{5} + \d{6,10} + 1123456789 + + + + + (?:9\d(?:0(?:0[1-9]|9[0-8]|[1-8]\d)|[1-9]\d{2})|8(?:0[01589]|1[024]|80)\d{2})\d{5} + \d{10} + 9123456789 + + + + 1(?:800\d?|600)\d{6} + \d{10,11} + 1800123456 + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-7]\d{7,9} + \d{6,10} + + + 1\d{7}|(?:2[13-5]|3[02367]|4[023]|5[03]|6[026])\d{6,7} + \d{6,9} + 12345678 + + + 7[5-9]\d{8} + \d{10} + 7912345678 + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + + [1-9]\d{9} + \d{7,10} + + + [1-8]\d{9} + 2123456789 + + + 9(?:1\d|3[1-8])\d{7} + \d{10} + 9123456789 + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 + $1 $2 $3 + $1 $2 + $1 $2 $3 + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 + $1 $2 + + + [01389]\d{5,10} + \d{6,11} + + + 0\d{7,10} + \d{8,11} + 0212345678 + + + 3\d{8,9} + \d{9,10} + 312345678 + + + 80(?:0\d{6}|3\d{3}) + \d{6,9} + 800123456 + + + 89(?:2\d{3}|9\d{6}) + \d{6,9} + 899123456 + + + 84[78]\d{6,7} + \d{9,10} + 8481234567 + + + + 178\d{6,7} + \d{9,10} + 1781234567 + + + + + + + + + [89]\d{9} + \d{7,10} + + + 876(?:(?:5[0-26]|6\d|7[1-6]|9[2-8])\d{5}|(?:7(?:0[2-689]|8[056]|9[45])|9(?:0[1-8]|1[02378]|9[2-468]))\d{4}) + 8765123456 + + + 876(?:(?:21|3[02-9]|[48]\d|5[78]|77)\d|7(?:0[07]|8[1-47-9]|9[0-36-9])|9(?:[01]9|9[0579]))\d{4} + \d{10} + 8762101234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + $1 $2 $3 + $1 $2 $3 $4 $5 + $1 $2 + $1 $2 + + + [235-9]\d{7,8} + \d{7,9} + + + (?:[2356][2-8])\d{6} + \d{7,8} + 62345678 + + + 7(?:4[5-7]|7[569]|8[5-8]|9[05-7])\d{6} + \d{9} + 790123456 + + + 80\d{6} + \d{8} + 80012345 + + + 90\d{6} + \d{8} + 90012345 + + + (?:8[57]\d|810)\d{5} + \d{8} + 85012345 + + + 70\d{7} + \d{9} + 700123456 + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + $1 $2 $3 + + $1 $2 $3 + + $1 $2 $3 + $1 $2 $3 + + + \d{9,10} + \d{9,10} + + + (?:(?:1[1-9]|9[2-9])[1-9]|(?:[36][1-9]|[24578][2-9])\d)\d{6} + \d{9} + 312345678 + + + [7-9]0\d{8} + \d{10} + 7012345678 + + + + 120\d{6} + \d{9} + 120123456 + + + 990\d{6} + \d{9} + 990123456 + + + + 60\d{7} + \d{9} + 601234567 + + + 50\d{8} + \d{10} + 5012345678 + + + + + + + + + $1 $2 + $1 $2 + $1 $2 + $1 $2 + + + \d{6,10} + \d{4,10} + + + (?:20|4[0-6]|5\d|6[0-24-9])\d{4,7} + \d{4,9} + 201234 + + + 7(?:1[0-5]|2\d|3[2-8]|5[0-2]|7[023])\d{6} + \d{9} + 712123456 + + + + 8(?:00|88)\d{6,7} + \d{9,10} + 800123456 + + + 9(?:00|1)\d{6,7} + \d{8,10} + 900123456 + + + + + + + + $1 $2 $3 + $1 $2 + + + [356-8]\d{8} + \d{5,9} + + + (?:3(?:1(?:2\d|3[1-9]|52|6[1-8])|2(?:22|3[0-479]|6[0-7])|4(?:22|5[6-9]|6[0-4])|5(?:22|3[4-7]|59|6[0-5])|6(?:22|5[35-7]|6[0-3])|7(?:22|3[468]|4[1-8]|59|6\d|7[5-7])|9(?:22|4[1-7]|6[0-8]))|6(?:09|12|2[2-4])\d)\d{5} + 312123456 + + + 5[124-7]\d{7}|7(?:00|7\d)\d{6} + \d{9} + 700123456 + + + 800\d{6} + \d{9} + 800123456 + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + + [1-79]\d{7,9} + \d{6,10} + + + (?:2[3-6]|3[2-6]|4[2-4]|[5-7][2-5])[2-47-9]\d{5} + \d{6,8} + 23456789 + + + (?:1[0-25689]|9[1-49])[2-9]\d{5} + \d{8} + 91234567 + + + 1800(?:1\d|2[09])\d{4} + \d{10} + 1800123456 + + + 1900(?:1\d|2[09])\d{4} + \d{10} + 1900123456 + + + + + + + + + + + + + + + + + [89]\d{9} + \d{7,10} + + + 869(?:2(?:29|36)|4(?:6[5-9]|70))\d{4} + 8692361234 + + + 869(?:5(?:5[6-8]|6[5-7])|66[2-9]|76[2-5])\d{4} + \d{10} + 8695561234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + $1-$2-$3 + $1-$2-$3 + $1-$2 + $1-$2-$3 + $1-$2-$3 + $1-$2-$3 + + + [1-79]\d{3,9}|8\d{8} + \d{4,10} + + + + 2(?:1\d{2,3}|[2367]\d{6,7}|[4589]\d{6})|(?:[34][1-3]|5[13-5]|6[124])\d{7}|(?:52|63)\d{7,8} + \d{4,10} + 22123456 + + + 1[0-26-9]\d{5,7} + \d{7,9} + 12345678 + + + 80\d{7} + \d{9} + 801234567 + + + 60\d{7,8} + \d{9,10} + 601234567 + + + 50\d{7,8} + \d{9,10} + 501234567 + + + 70\d{7,8} + \d{9,10} + 701234567 + + + + + + + + + $1 $2 + $1 $2 + $1 $2 + $1 $2 + + + [12569]\d{6,7} + \d{7,8} + + + (?:18|2[2-5]\d)\d{5} + \d{7,8} + 22345678 + + + (?:5[05]|6[05-7]|9[0479])\d{6} + \d{8} + 50012345 + + + + + + + + + + [389]\d{9} + \d{7,10} + + + 345(?:2(?:22|44)|444|6(?:23|38|40)|7(?:6[6-9]|77)|8(?:00|1[45]|25|4[89]|88)|9(?:14|4[035-9]))\d{4} + 3452221234 + + + 345(?:32[3-79]|5(?:1[467]|2[5-7]|4[5-9])|9(?:1[679]|2[4-9]|3[89]))\d{4} + \d{10} + 3453231234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6}|345976\d{4} + \d{10} + 9002345678 + + + + + + + + $1 $2 $3 + + + (?:[67]\d{2}|80[09])\d{7} + \d{10} + + + 7(?:1\d[2-7]|2(?:[0-689][2-7]|75[279]))\d{6} + 7123456789 + + + + 7(?:(?:(?:1[2-578]|2[13-7])9[01]|2758)\d{5}|(?:0[0-257]|6[02-4]|7[57])\d{7})|6\{9} + 7129012345 + + + 800\d{7} + 8001234567 + + + 809\d{7} + 8091234567 + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 + + + [2-57]\d{7,8} + \d{6,9} + + + (?:[2-57]1|54)\d{6} + \d{6,8} + 21212862 + + + 20[2579]\d{6} + \d{9} + 202345678 + + + + + + + + + + + + + + [789]\d{9} + \d{7,10} + + + 758(?:234|4(?:5[0-9]|6[2-9]|8[0-2])|638|758)\d{4} + 7582345678 + + + 758(?:28[4-7]|384|4(?:6[01]|8[4-9])|5(?:1[89]|20|84)|7(?:1[2-9]|2[034]))\d{4} + \d{10} + 7582845678 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + 0$1 $2 $3 $4 + + + (?:66|80|90)\d{7}|[237-9]\d{6} + \d{7,9} + + + + (?:2(?:17|3\d|6[02-58]|96)|3(?:02|7[01357]|8[048]|9[0269])|870)\d{4} + \d{7} + 2345678 + + + 66(?:[0178][0-4]|2[025-9]|[36]\d|4[129]|5[45]|9[019])\d{5}|7(?:4[2-59]|56|[6-9]\d)\d{4} + \d{7,9} + 661234567 + + + 80(?:0(?:07|2[238]|79|\d{4})|9\d{2})\d{2} + \d{7,9} + 8002222 + + + 90(?:0(?:2[278]|79|\d{4})|1(?:23|\d{4})|6(?:66|\d{4}))\d{2} + \d{7,9} + 9002222 + + + 701\d{4} + \d{7} + 7011234 + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 $4 + + $1 $2 $3 + $1 $2 $3 + + + [3-9]\d{7} + \d{8} + + + (?:3[1478]|4[124-6]|52)\d{6} + 31234567 + + + 6\d{7} + 61234567 + + + 800\d{5} + 80012345 + + + 90[0239]\d{5} + 90012345 + + + + + + + + + + + + $1 $2 $3 + + + [2689]\d{7} + \d{8} + + + 6\d{7} + 61234567 + + + 2\d{7} + 21234567 + + + 80\d{6} + 80123456 + + + 90\d{6} + 90123456 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 + + $1 $2 + $1 $2 $3 + + + [124-8]\d{5,7}|9\d{7,8} + \d{5,9} + + + + (?:1\d|2|4[2-6]|5[2-9]|6\d|7[0-5]|8[1-6])\d{5}|1333\d{4} + \d{5,8} + 1234567 + + + 9(?:[25689]\d|444)\d{5} + \d{8,9} + 92123456 + + + + + + + + + $1 $2 $3 + $1 $2 + $1 $2 + $1 $2 + + + [127-9]\d{7} + \d{8} + + + + (?:[12](?:1\d|2[1-37]|3[2-8]|4[2-68]|5[1-4689])|70)\d{6} + 70123456 + + + (?:88|9[1569])\d{6} + 88123456 + + + + + + + + + $1 $2 + + + [268]\d{7} + \d{8} + + + (?:28[2-57-9]|8[2-57-9]\d)\d{5} + 28212345 + + + 66\d{6} + 66123456 + + + + + + + + + + + [689]\d{9} + \d{7,10} + + + 670(?:2(?:3[3-5]|88|56)|32[23]|4[38]3|532|6(?:64|70|8\d))\d{4} + 6702345678 + + + 670(?:2(?:3[3-5]|88|56)|32[23]|4[38]3|532|6(?:64|70|8\d))\d{4} + 6702345678 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + + + + [689]\d{9} + \d{7,10} + + + 664491\d{4} + 6644912345 + + + 664492\d{4} + \d{10} + 6644923456 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002123456 + + + 900[2-9]\d{6} + \d{10} + 9002123456 + + + + + + + + + + + + + + $1 $2 + + + [2-8]\d{6} + \d{7} + + + + (?:2(?:[034789]\d|1[0-8]|2[0-79])|4(?:[013-8]\d|2[4-7])|[56]\d{2}|8(?:14|3[129]))\d{4} + + + (?:25\d|4(?:2[12389]|9\d)|7\d{2}|87[15-7]|9[13-8]\d)\d{4} + + + 80[012]\d{4} + + + + 30\d{5} + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + 045 $1 $2 $3 + $1 $2 $3 $4 + 045 $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 + + + [1-9]\d{9,10} + \d{7,11} + + + + (?:33|55|81)\d{8}|(?:2(?:2[2-9]|3[1-35-8]|4[13-9]|7[1-689]|8[1-58]|9[467])|3(?:1[1-79]|[2458][1-9]|7[1-8]|9[1-5])|4(?:1[1-57-9]|[24-6][1-9]|[37][1-8]|8[1-35-9]|9[2-689])|5(?:88|9[1-79])|6(?:1[2-68]|[234][1-9]|5[1-3689]|6[12457-9]|7[1-7]|8[67]|9[4-8])|7(?:[13467][1-9]|2[1-8]|5[13-9]|8[1-69]|9[17])|8(?:2[13-689]|3[1-6]|4[124-6]|6[1246-9]|7[1-378]|9[12479])|9(?:1[346-9]|2[1-4]|3[2-46-8]|5[1348]|[69][1-9]|7[12]|8[1-8]))\d{7} + \d{7,10} + 2221234567 + + + 1(?:(?:33|55|81)\d{8}|(?:2(?:2[2-9]|3[1-35-8]|4[13-9]|7[1-689]|8[1-58]|9[467])|3(?:1[1-79]|[2458][1-9]|7[1-8]|9[1-5])|4(?:1[1-57-9]|[24-6][1-9]|[37][1-8]|8[1-35-9]|9[2-689])|5(?:88|9[1-79])|6(?:1[2-68]|[2-4][1-9]|5[1-3689]|6[12457-9]|7[1-7]|8[67]|9[4-8])|7(?:[13467][1-9]|2[1-8]|5[13-9]|8[1-69]|9[17])|8(?:2[13-689]|3[1-6]|4[124-6]|6[1246-9]|7[1-378]|9[12479])|9(?:1[346-9]|2[1-4]|3[2-46-8]|5[1348]|[69][1-9]|7[12]|8[1-8]))\d{7}) + \d{11} + 12221234567 + + + 800\d{7} + \d{10} + 8001234567 + + + 900\d{7} + \d{10} + 9001234567 + + + + + + + + + $1-$2 $3 + $1-$2 $3 + $1-$2 $3 + $1-$2 $3 + $1-$2-$3-$4 + $1-$2 $3 + + + [13-9]\d{7,9} + \d{6,10} + + + (?:3\d{2}|[4-79]\d|8[2-9])\d{6} + \d{6,9} + 312345678 + + + 1[0-46-9]\d{7} + \d{9} + 123456789 + + + 1[38]00\d{6} + \d{10} + 1300123456 + + + 1600\d{6} + \d{10} + 1600123456 + + + 1700\d{6} + \d{10} + 1700123456 + + + 154\d{7} + \d{10} + 1541234567 + + + + + + + + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-69]\d{5,8}|[78]\d{5,13} + \d{5,14} + + + [12]\d{6,7}|9\d{7}|(?:4[023568]|5[02368]|6[02-469]|7[569]|8[2-9])\d{6}|(?:4[47]|5[14579]|6[1578]|7[0-357])\d{5,6}|(?:78|41)\d{5} + \d{5,9} + 12345678 + + + (?:70[3-9]|8(?:0[2-9]|12))\d{7}|(?:702[1-9]|819[01])\d{6} + \d{10} + 8021234567 + + + + 800\d{7,11} + \d{10,14} + 80017591759 + + + 700\d{7,11} + \d{10,14} + 7001234567 + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 + $1 $2 + + + [1-9]\d{6,9} + \d{7,10} + + + (?:1[0135-8]|2[02-69]|3[0-68]|4[0135-9]|[57]\d|8[478])\d{7} + \d{9} + 101234567 + + + 6[1-58]\d{7} + \d{9} + 612345678 + + + 800\d{4,7} + \d{7,10} + 8001234 + + + 90[069]\d{4,7} + \d{7,10} + 9001234 + + + 85\d{7} + \d{9} + + + + + + + + $1 $2 $3 + $1 $2 $3 $4 + + + [2-9]\d{7} + \d{8} + + + + (?:2[1-4]|3[1-3578]|5[1-35-7]|6[1-4679]|7\d)\d{6}|81(?:0(?:0[7-9]|1\d)|5\d{2})\d{3} + 21234567 + + + (?:4[015-8]|9\d)\d{6} + 41234567 + + + 80[01]\d{5} + 80012345 + + + 82[09]\d{5} + 82012345 + + + 810(?:0[0-6]|[2-8]\d)\d{3} + 81021234 + + + 880\d{5} + 88012345 + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + $1 $2 $3 + + + [1-8]\d{5,7}|98[45]\d{7} + \d{6,10} + + + (?:1[014-6]|2[13-79]|3[135-8]|4[146-9]|5[135-7]|6[13-9]|7[15-9]|8[1-4679]|9[1-79])\d{6} + \d{6,8} + 14567890 + + + + 98[45]\d{7} + \d{10} + 9841234567 + + + + + + + + + + + + + + + + + + $1-$2 $3 + + + $1 $2 $3 + + $1 $2 $3 + + $1 $2 $3 + + $1 $2 + + + [2-9]\d{7,9} + \d{7,10} + + + (?:3[2-79]|[479][2-689]|6[235-9])\d{6}|24099\d{3} + \d{7,8} + 32345678 + + + + 2(?:[027]\d{7}|9\d{6,7}|1(?:0\d{5,7}|[12]\d{5,6}|[3-9]\d{5})|4[1-9]\d{6}|8\d{7,8}) + \d{8,10} + 211234567 + + + + (?:800|508)\d{6,7} + + \d{9,10} + 800123456 + + + + 900\d{6,7} + \d{9,10} + 900123456 + + + + + + + + $1 $2 + $1 $2 + $1 $2 + $1 $2 + + + (?:2[3-6]|5|9[25-9])\d{6}|800\d{5,6} + \d{7,9} + + + 2[3-6]\d{6} + \d{8} + 23123456 + + + 9[25-9]\d{6} + \d{8} + 92123456 + + + + 8007\d{4,5}|500\d{4} + \d{7,9} + 80071234 + + + + + + + + + + + + + + + + + + + + + + $1 $2 + $1 $2 $3 + + + [1-9]\d{6,7} + \d{7,8} + + + (?:3\d|47|[56]4|73|85|9[78])\d{5} + \d{7} + 3123456 + + + (?:6[357-9]|7[126]\d)\d{5} + \d{7,8} + 6345678 + + + 180\d{4} + \d{7} + 1801234 + + + 275\d{4} + \d{7} + 2751234 + + + + + + + + + $1 $2 $3 + $1 $2 + $1 $2 + + $1 $2 $3 + $1 $2 $3 + + $1 $2 $3 + $1 $2 $3 $4 + + + [2-9]\d{7,9}|1800\d{7,9} + \d{7,13} + + + (?:2|3[2-68]|4[2-9]|5[2-6]|6[2-58]|7[24578]|8[2-8])\d{7} + \d{7,9} + 21234567 + + + 9(?:0[5-9]|1[025-9]|2[0-36-9]|3[235-8]|7[349])\d{7} + \d{10} + 9051234567 + + + + 1800\d{7,9} + \d{11,13} + 180012345678 + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [2-9]\d{7,9} + \d{7,10} + + + (?:21|42)\d{8}|(?:2(?:[25]|3[2358]|4[2-4]|9[78])|4(?:[0146-9]|5[3479])|5(?:[1-35-8]|4[2-467])|6(?:[1-8]|0[468])|7(?:[14]|2[236])|8(?:[16]|2[2-689]|3[23578]|4[3478]|5[2356])|9(?:1|2[2-8]|3[27-9]|4[2-6]|6[3569]|9[25-8]))\d{7} + 2112345678 + + + 3[0-6]\d{8} + \d{10} + 3012345678 + + + 800\d{5} + \d{8} + 80012345 + + + 900\d{5} + \d{8} + 90012345 + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 $4 + $1 $2 $3 + + + [1-9]\d{8} + \d{9} + + + (?:1[2-8]|2[2-59]|3[2-4]|4[1-468]|5[24-689]|6[1-3578]|7[14-7]|8[1-79]|9[145])\d{7} + 123456789 + + + (?:5[01]|6[069]|7[289]|88)\d{7} + 512345678 + + + 800\d{6} + 800123456 + + + 70\d{7} + 701234567 + + + 801\d{6} + 801234567 + + + 39\d{7} + 391234567 + + + + + + + + + + + + + [789]\d{9} + \d{7,10} + + + (?:787|939)[2-9]\d{6} + 7872345678 + + + (?:787|939)[2-9]\d{6} + 7872345678 + + + 8(00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + + + + + + + + + + $1 $2 $3 + + + [2-46-9]\d{8} + \d{9} + + + 2(?:[12]\d|[35][1-689]|4[1-59]|6[1-35689]|7[1-9]|8[1-69]|9[1256])\d{6} + 212345678 + + + 9(?:[136]\d{2}|2[25-79]\d|4(?:80|9\d))\d{5} + 912345678 + + + 4\d{8}|80[02]\d{6} + 800123456 + + + 71\d{7} + 712345678 + + + 30\d{7} + 301234567 + + + + + + + + + + + + + + + + + $1 $2 + + + [3-8]\d{6} + \d{7} + + + 4\d{6} + 4123456 + + + (?:3[0-5]|[5-7]\d)\d{5} + 3123456 + + + 80\d{5} + 8012345 + + + + + + + + + + $1 $2 $3 $4 + + + [268]\d{8} + \d{9} + + + + 262\d{6} + 262161234 + + + 6(?:9[23]|47)\d{6} + \d{9} + 692123456 + + + + 80\d{7} + 801234567 + + + 8(?:1[01]|2[0156]|84|9[0-37-9])\d{6} + 810123456 + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + + [237-9]\d{8} + \d{6,9} + + + [23][3-6]\d{7} + 231234567 + + + 7\d{8} + \d{9} + 712345678 + + + 800\d{6} + \d{9} + 800123456 + + + 9\d{8} + \d{9} + 912345678 + + + + + + + + $1 $2 + $1 $2 + $1 $2 + $1 $2 + $1 $2 + + + [1-46-9]\d{4,11} + \d{5,12} + + + [1-3]\d{6,9} + \d{5,10} + 1012345 + + + 6[0-689]\d{3,10} + \d{5,12} + 6012345 + + + 800\d{3,6} + \d{6,9} + 80012345 + + + (?:9[0-2]|42)\d{4,7} + \d{6,9} + 90012345 + + + + + + + + + + $1 $2-$3-$4 + + + [3489]\d{9} + \d{10} + + + + (?:3(?:0[12]|4[1-35-79]|5[1-3]|8[1-58]|9[0145])|4(?:01|1[1356]|2[13467]|7[1-5]|8[1-7]|9[1-689])|8(?:1[1-8]|2[01]|3[13-6]|4[0-8]|5[15]|6[1-35-7]|7[1-37-9]))\d{7} + 3011234567 + + + 9\d{9} + 9123456789 + + + 800\d{7} + 8001234567 + + + 809\d{7} + 8091234567 + + + + + + + + $1 $2 $3 + $1 $2 $3 + + + [27-9]\d{8} + \d{9} + + + 25\d{7} + + + 7[258]\d{7} + + + 800\d{6} + + + 900\d{6} + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-9]\d{7,10} + \d{7,11} + + + (?:1[24-7]|2[24-8]|3[35-8]|4[34-68]|6[2-5]|7[235-7])\d{6} + \d{7,8} + 12345678 + + + + (?:5[013-69]\d|8111)\d{6} + \d{9,10} + 512345678 + + + 800\d{7} + \d{10} + 8001234567 + + + 9200\d{7} + \d{11} + 92001234567 + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 $4 + + + \d{7,10} + \d{5,10} + + + 1(?:0[1-8]\d{6}|[136]\d{5,7}|(?:2[0-35]|4[0-4]|5[0-25-9]|7[13-6]|[89]\d)\d{5,6})|2(?:[136]\d{5,7}|(?:2[0-7]|4[0136-8]|5[0-38]|7[018]|8[01]|9[0-57])\d{5,6})|3(?:[356]\d{5,7}|(?:0[0-4]|1\d|2[0-25]|4[056]|7[0-2]|8[0-3]|9[023])\d{5,6})|4(?:[0246]\d{5,7}|(?:1[01-8]|3[0135]|5[14-79]|7[0-246-9]|8[0156]|9[0-689])\d{5,6})|5(?:0[0-6]|1[1-5]|2[0-68]|3[0-4]|4\d|5[0-5]|6[03-5]|7[013]|8[0-79]|9[01])\d{5,6}|6(?:[03]\d{5,7}|(?:1[1-3]|2[0-4]|4[02-57]|5[0-37]|6[0-3]|7[0-2]|8[0247]|9[0-356])\d{5,6})|8\d{6,8}|9(?:0\d{5,7}|(?:1[0-68]|2\d|3[02-59]|4[0-4]|5[0-4]|6[01]|7[0135-8]|8[01])\d{5,6}) + \d{5,9} + 8123456 + + + 7[02-46]\d{7} + \d{9} + 701234567 + + + 20\d{4,7} + \d{6,9} + 201234567 + + + 9(?:00|39|44)\d{7} + \d{10} + 9001234567 + + + + + + + + $1 $2 + $1 $2 $3 + $1 $2 $3 + + + [13689]\d{7,10} + \d{8,11} + + + [36]\d{7} + \d{8} + 31234567 + + + [89]\d{7} + \d{8} + 81234567 + + + 1?800\d{7} + \d{10,11} + 18001234567 + + + 1900\d{7} + \d{11} + 19001234567 + + + + + + + + + + + + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 + + + [1-9]\d{5,7} + \d{6,8} + + + [1-57]\d{6} + \d{7,8} + 1123456 + + + (?:[347][01]|51|64)\d{6} + \d{8} + 31234567 + + + 80\d{4,6} + \d{6,8} + 80123456 + + + 90\d{4,6} + \d{6,8} + 90123456 + + + + + + + + $1/$2 $3 $4 + $1/$2 $3 $4 + $1 $2 $3 + + + [2-689]\d{8} + \d{9} + + + [2-5]\d{8} + 212345678 + + + + 9(?:0[1-8]|1[0-24-9]|4[0489])\d{6} + 912123456 + + + 800\d{6} + 800123456 + + + 9(?:[78]\d{7}|00\d{6}) + 900123456 + + + 8[5-9]\d{7} + 850123456 + + + 6(?:5[0-4]|9[0-6])\d{6} + 690123456 + + + + + + + + + + + + + + + + + $1 $2 $3 $4 + + + [37]\d{8} + \d{9} + + + 3(?:010|3(?:8[1-9]|9[2-9]))\d{5} + 301012345 + + + 7(?:0[1256]0|6(?:1[23]|2[89]|3[3489]|4[6-9]|5[1-389]|6[6-9]|7[45]|8[3-8])|7(?:1[014-8]|2[0-7]|3[0-35-8]|4[0-6]|[56]\d|7[0-389]|[89][01]))\d{5} + 701012345 + + + 33301\d{4} + 333011234 + + + + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-59]\d{7,8} + \d{6,9} + + + (?:1(?:|1\d?|4\d|[2356])|2[1-35]|3(?:1\d|[34])|4[13]|5[1-3])\d{6} + 112345678 + + + 9(?:3[23]|4[47]|55|66|88|99)\d{6} + \d{9} + 944567890 + + + + + + + + + + + + + + [689]\d{9} + \d{7,10} + + + 649(?:712|9(?:4\d|50))\d{4} + 6497121234 + + + 649(?:2(?:3[12]|4[1-5])|3(?:3[1-39]|4[1-57])|4[34][12])\d{4} + \d{10} + 6492311234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + 64971[01]\d{4} + \d{10} + 6497101234 + + + + + + + + + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-8]\d{7,9} + \d{8,10} + + + (?:2[1-9]|3[24-9]|4[2-5]|5[3-6]|7[3-7])\d{6} + \d{8} + 21234567 + + + 8[13-9]\d{7} + \d{9} + 812345678 + + + + 1800\d{6} + \d{10} + 1800123456 + + + 1900\d{6} + \d{10} + 1900123456 + + + 60\d{7} + \d{9} + 601234567 + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [349]\d{8} + \d{3,9} + + + (?:3(?:1[3-5]|2[245]|31|4[24-7]|5[25]|72)|4(?:46|74|87))\d{6} + 372123456 + + + 9[1-35-9]\d{7} + \d{9} + 917123456 + + + + + + + + + + + + + + + + + + $1 $2 $3 $4 + + + (?:12\d|243|[3-5]22)\d{5} + \d{8} + 12345678 + + + + + + + + + + + + + + + + + + $1 $2 $3 + + + [2-589]\d{9} + \d{10} + + + [2-4]\d{9}|850\d{7} + 2123456789 + + + 5\d{9} + 5123456789 + + + 800\d{7} + 8001234567 + + + 900\d{7} + 9001234567 + + + + + + + + + [89]\d{9} + \d{7,10} + + + 868(?:22[1-4]|6(?:1[4-6]|[2-6]\d|7[0-79]|9[0-8])|82[12])\d{4} + 8682211234 + + + 868(?:29\d|3(?:0[1-9]|1[02-9]|[2-9]\d)|4([679]\d|8[0-4])|6(?:20|78|8\d)|7(?:1[02-9]|[2-9]\d))\d{4} + \d{10} + 8682911234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + + + [2-9]\d{7,8} + \d{8,9} + + + [2-8]\d{7,8} + \d{8,9} + 21234567 + + + 9\d{8} + \d{9} + 912345678 + + + 800\d{6} + \d{9} + 800123456 + + + 900\d{6} + \d{9} + 900123456 + + + + + + + + $1 $2 $3 + $1 $2 $3 + + $1 $2 $3 + + + \d{9} + \d{7,9} + + + 2[2-8]\d{7} + \d{7,9} + 222345678 + + + (?:6[158]|7[1-9])(\d{7}) + \d{9} + 612345678 + + + 80[08]\d{6} + \d{9} + 800123456 + + + 90\d{7} + \d{9} + 900123456 + + + 8(?:40|6[01])\d{6} + \d{9} + 840123456 + + + 41\d{7} + \d{9} + 412345678 + + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 + + + [3-689]\d{8} + \d{5,9} + + + (?:3[1-8]|4[13-8]|5[1-7]|6[12459])\d{7} + 311234567 + + + (?:39|50|6[36-8]|9[1-9])\d{7} + \d{9} + 391234567 + + + 800\d{6} + \d{9} + 800123456 + + + 900\d{6} + \d{9} + 900123456 + + + + + + + + + $1 $2 + $1 $2 + $1 $2 + + + \d{9} + \d{5,9} + + + 3\d{8}|4(?:[1-6]\d|7[136]|8[1356]|96)\d{6}|20(?:0\d|24)\d{5} + \d{5,9} + 312345678 + + + 7(?:[1578]\d|0[0-4])\d{6} + \d{9} + 712345678 + + + 800[123]\d{5} + \d{9} + 800123456 + + + 90[123]\d{6} + \d{9} + 901123456 + + + + + + + + + + ($1) $2-$3 + $1-$2 + + $1-$2-$3 + $1-$2 + + + [2-9]\d{9} + \d{7,10} + + + (?:2(?:0[1-35-9]|1[02-9]|2[4589]|3[149]|4[08]|5[1-46]|6[0279]|7[06]|8[13])|3(?:0[1-57-9]|1[02-9]|2[0135]|3[014679]|47|5[12]|6[01]|8[056])|4(?:0[124-9]|1[02-579]|2[3-5]|3[0245]|4[0235]|69|7[089]|8[04])|5(?:0[1-57-9]|1[0235-8]|[23]0|4[01]|5[19]|6[1-37]|7[013-5]|8[056])|6(?:0[1-35-9]|1[024-9]|2[036]|3[016]|4[16]|5[017]|6[0-29]|78|8[12])|7(?:0[1-46-8]|1[2-9]|2[047]|3[124]|4[07]|5[47]|6[02359]|7[02-59]|8[156])|8(?:0[1-68]|1[02-8]|28|3[0-25]|4[3578]|5[06-9]|6[02-5]|7[028])|9(?:0[1346-9]|1[02-9]|2[058]|3[167]|4[0179]|5[1246]|7[0-3589]|8[059]))[2-9]\d{6} + 2012345678 + + + (?:2(?:0[1-35-9]|1[02-9]|2[4589]|3[149]|4[08]|5[1-46]|6[0279]|7[06]|8[13])|3(?:0[1-57-9]|1[02-9]|2[0135]|3[014679]|47|5[12]|6[01]|8[056])|4(?:0[124-9]|1[02-579]|2[3-5]|3[0245]|4[0235]|69|7[089]|8[04])|5(?:0[1-57-9]|1[0235-8]|[23]0|4[01]|5[19]|6[1-37]|7[013-5]|8[056])|6(?:0[1-35-9]|1[024-9]|2[036]|3[016]|4[16]|5[017]|6[0-29]|78|8[12])|7(?:0[1-46-8]|1[2-9]|2[047]|3[124]|4[07]|5[47]|6[02359]|7[02-59]|8[156])|8(?:0[1-68]|1[02-8]|28|3[0-25]|4[3578]|5[06-9]|6[02-5]|7[028])|9(?:0[1346-9]|1[02-9]|2[058]|3[167]|4[0179]|5[1246]|7[0-3589]|8[059]))[2-9]\d{6} + 2012345678 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + + + + + + + + + + $1 $2 $3 $4 + + + [679]\d{8} + \d{7,9} + + + (?:6[125679]|7[1-69])\d{7} + 612345678 + + + 9[0-37-9]\d{7} + 912345678 + + + + + + + + + + (?:784|8(?:00|66|77|88)|900)[2-9]\d{6} + \d{7,10} + + + 784(?:266|3(?:6[6-9]|7\d|8[0-24-6])|4(?:38|5[0-36-8]|8\d|9[01])|555|638|784)\d{4} + 7842661234 + + + 784(?:4(?:3[0-24]|5[45]|9[2-5])|5(?:2[6-9]|3[0-3]|93))\d{4} + \d{10} + 7844301234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + + + + + + + + + + + (?:284|8(?:00|66|77|88)|900)[2-9]\d{6} + \d{7,10} + + + 284(?:(?:229|4(?:46|9[45])|8(?:52|6[459]))\d{4}|496[0-5]\d{3}) + 2842291234 + + + 284(?:(?:30[0-3]|4(?:4[0-5]|68|99)|54[0-4])\d{4}|496[6-9]\d{3}) + \d{10} + 2843001234 + + + 8(?:00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + + + + + + + 340(?:6[49]2|7[17]\d)\d{4}|(?:8(?:00|66|77|88)|900)[2-9]\d{6} + \d{7,10} + + + 3406421234 + + + 3406421234 + + + 8(00|66|77|88)[2-9]\d{6} + \d{10} + 8002345678 + + + 900[2-9]\d{6} + \d{10} + 9002345678 + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 + + + [1-9]\d{6,9} + \d{7,10} + + + [2-8]\d{6,9} + \d{7,10} + 2123456 + + + (?:9[0-8]|1(?:2[1-369]|6[46-9]|99))\d{7} + \d{9,10} + 912345678 + + + 1800\d{4,6} + \d{8,10} + 1800123456 + + + 1900\d{4,6} + \d{8,10} + 1900123456 + + + + + + + + + + + + + + + + + $1 $2 + $1 $2 + $1 $2 + + + [2-8]\d{4,6} + \d{5,7} + + + (?:[2-5]\d|6[1-9]|840\d)\d{3} + \d{5,7} + 22123 + + + (?:60|7[25-7]\d)\d{4} + \d{6,7} + 601234 + + + + 800\d{3} + \d{6} + 800123 + + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [1-7]\d{6,8} + \d{6,9} + + + (?:1(?:7\d|[2-68])|2[2-68]|3[2358]|4[2-58]|5[2-6]|6[3-58]|7[24-68])\d{5} + \d{6,8} + 1234567 + + + 7[137]\d{7} + \d{9} + 712345678 + + + + + + + + + + + + + + $1 $2 $3 + + + \d{9} + \d{8,9} + + + (?:1[0-8]|2[1-478]|3[1-69]|4\d|5[1346-8])\d{7} + \d{8,9} + 101234567 + + + (?:7[1-4689]|8[1-5789])\d{7} + \d{9} + 711234567 + + + 80\d{7} + \d{9} + 801234567 + + + 86\d{7} + \d{9} + 861234567 + + + 87\d{7} + \d{9} + 871234567 + + + + + + + + + + + + + $1 $2 $3 + + $1 $2 $3 + + $1 $2 + $1 $2 $3 + + $1 $2 + $1 $2 $3 + + $1 $2 + $1 $2 $3 + + + (?:[19]1|23)\d{3,8}|[1-69]\d{4,8} + \d{3,10} + + + (?:1[346-8]|2(?:0[45]|2[28]|48|58[23]|[69]|7[2-46-8]|8[13-9])|3(?:08?|17?|3[78]|[45]|7[1569]|8[379])|5(?:18|483|[57-9])|6(?:37?|[459]|88)|848)\d{3,6}|(?:2(?:27|5|7[159]|82)|39|5[346]|6[16-8])\d{4,6}|2(?:0|70)\d{5,6}|(?:9[2-8]|4\d)\d{4,7} + \d{3,10} + 1312345 + + + (?:[19]1|73)\d{3,8} + \d{3,10} + 11123456 + + + + + diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberMetaDataForTesting.xml b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetaDataForTesting.xml new file mode 100644 index 000000000..21d2da387 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetaDataForTesting.xml @@ -0,0 +1,381 @@ + + + + + + + + + + + + $1 $2-$3 + $1 $2-$3 + $1 15 $2-$3 + $1 15 $2-$3 + $1-$2-$3 + $1 $2-$3 + $1 $2-$3 + $1 $2 $3 $4 + $1 $2 $3 $4 + $1-$2-$3 + + + [1-3689]\d{9,10} + \d{6,11} + + + [1-3]\d{9} + \d{6,10} + + + 9\d{10}|[1-3]\d{9} + \d{10,11} + + + 80\d{8} + \d{10} + + + 6(0\d|10)\d{7} + \d{10} + + + + + + + $1 $2 $3 + $1 $2 $3 + + + [1-578]\d{4,14} + \d{5,15} + + + [2378]\d{8} + \d{9} + + + 4\d{8} + \d{9} + + + 1800\d{6} + \d{10} + + + 190[0126]\d{6} + \d{10} + + + + + + + $1 $2 $3 + + + (242|8(00|66|77|88)|900)\d{7} + \d{7,10} + + + 242(?:3(?:02|[236][1-9]|4[0-24-9]|5[0-68]|7[3-57]|9[2-5])|4(?:2[237]|51|64|77)|502|636|702)\d{4} + + + 242(357|359|457|557)\d{4} + \d{10} + + + 8(00|66|77|88)\d{7} + \d{10} + + + 900\d{7} + \d{10} + + + + + + + $1 $2 + $1 $2 + $1 $2 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + \d{4,14} + \d{2,14} + + + (?:[24-6]\d{2}|3[03-9]\d|[789](?:[1-9]\d|0[2-9]))\d{3,8} + 30123456 + + + 1(5\d{9}|7\d{8}|6[02]\d{8}|63\d{7}) + \d{10,11} + + + 800\d{7} + \d{10} + + + 900([135]\d{6}|9\d{7}) + \d{10,11} + + + + + + + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 + $1 $2 $3 + + + \d{10} + \d{6,10} + + + [1-6]\d{9} + + + 7[1-57-9]\d{8} + \d{10} + + + 80\d{8} + \d{10} + + + 9[018]\d{8} + \d{10} + + + 8(?:4[3-5]|7[0-2])\d{7} + \d{10} + + + 56\d{8} + \d{10} + + + 70\d{8} + \d{10} + + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 + + + [0389]\d{5,10} + \d{6,11} + + + 0\d{9,10} + \d{10,11} + + + 3\d{8,9} + \d{9,10} + + + 80(?:0\d{6}|3\d{3}) + \d{6,9} + + + 89(?:2\d{3}|9\d{6}) + \d{6,9} + + + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + 045 $1 $2 $3 + 045 $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 $4 + $1 $2 $3 $4 + + + [1-9]\d{9,10} + \d{7,11} + + + [2-9]\d{9} + \d{7,10} + + + 1\d{10} + \d{11} + + + 800\d{7} + \d{10} + + + 900\d{7} + \d{10} + + + + + + + $1-$2 $3 + $1-$2 $3 + $1 $2 $3 + + + [2-9]\d{7,9} + \d{7,10} + + + 24099\d{3}|(?:3[2-79]|[479][2-689]|6[235-9])\d{6} + \d{7,8} + + + 2(?:[027]\d{7}|9\d{6,7}|1(?:0\d{5,7}|[12]\d{5,6}|[3-9]\d{5})|4[1-9]\d{6}|8\d{7,8}) + \d{8,10} + + + 800\d{6,7} + \d{9,10} + + + 900\d{6,7} + \d{9,10} + + + + + + + + $1 $2 $3 $4 + + + [1-9]\d{8} + \d{9} + + + (?:5[01]|6[069]|7[289]|88)\d{7} + + + 800\d{6} + + + 70\d{7} + + + + + + + + $1 $2 + $1 $2 $3 + $1 $2 $3 + + + [13689]\d{7,10} + \d{8,11} + + + [36]\d{7} + \d{8} + + + [89]\d{7} + \d{8} + + + 1?800\d{7} + \d{10,11} + + + 1900\d{7} + \d{11} + + + + + + + + + $1 $2 $3 + $1 $2 + + + [13-9]\d{9}|2[0-35-9]\d{8} + \d{7,10} + 1234567890 + + + 8(00|66|77|88)\d{7} + \d{10} + + + 900\d{7} + \d{10} + + + + diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto new file mode 100644 index 000000000..1c6227f1e Binary files /dev/null and b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto differ diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting new file mode 100644 index 000000000..769c0fded Binary files /dev/null and b/java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting differ diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java b/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java new file mode 100644 index 000000000..81dc464ee --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java @@ -0,0 +1,1654 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.i18n.phonenumbers; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + +import java.io.InputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility for international phone numbers. Functionality includes formatting, parsing and + * validation. + * + * @author Shaopeng Jia + * @author Lara Rennie + */ +public class PhoneNumberUtil { + // The maximum and minimum length of the national significant number. + private static final int MAX_LENGTH_FOR_NSN = 15; + private static final int MIN_LENGTH_FOR_NSN = 3; + private static final String META_DATA_FILE = + "/com/google/i18n/phonenumbers/PhoneNumberMetadataProto"; + private static final Logger LOGGER = Logger.getLogger(PhoneNumberUtil.class.getName()); + + // A mapping from a country code to a region code which denotes the country/region + // represented by that country code. Note countries under NANPA share the country code 1, + // Russia and Kazakhstan share the country code 7, and many French territories in the Indian + // Ocean share the country code 262. Under this map, 1 is mapped to US, 7 is mapped to RU, + // and 262 is mapped to RE. + private final Map countryCodeToRegionCodeMap = new HashMap(); + + // The set of countries that share country code 1. + private final Set nanpaCountries = new HashSet(); + private static final int NANPA_COUNTRY_CODE = 1; + + // The set of countries that share country code 7. + private final Set russiaFederationCountries = new HashSet(2); + private static final int RUSSIAN_FED_COUNTRY_CODE = 7; + + // The set of countries that share country code 262. + private final Set frenchIndianOceanTerritories = new HashSet(6); + + private static final int FRENCH_INDIAN_OCEAN_COUNTRY_CODE = 262; + + // The PLUS_SIGN signifies the international prefix. + static final char PLUS_SIGN = '+'; + + // These mappings map a character (key) to a specific digit that should replace it for + // normalization purposes. Non-European digits that may be used in phone numbers are mapped to a + // European equivalent. + static final Map DIGIT_MAPPINGS = + new ImmutableMap.Builder() + .put('0', '0') + .put('\uFF10', '0') // Fullwidth digit 0 + .put('\u0660', '0') // Arabic-indic digit 0 + .put('1', '1') + .put('\uFF11', '1') // Fullwidth digit 1 + .put('\u0661', '1') // Arabic-indic digit 1 + .put('2', '2') + .put('\uFF12', '2') // Fullwidth digit 2 + .put('\u0662', '2') // Arabic-indic digit 2 + .put('3', '3') + .put('\uFF13', '3') // Fullwidth digit 3 + .put('\u0663', '3') // Arabic-indic digit 3 + .put('4', '4') + .put('\uFF14', '4') // Fullwidth digit 4 + .put('\u0664', '4') // Arabic-indic digit 4 + .put('5', '5') + .put('\uFF15', '5') // Fullwidth digit 5 + .put('\u0665', '5') // Arabic-indic digit 5 + .put('6', '6') + .put('\uFF16', '6') // Fullwidth digit 6 + .put('\u0666', '6') // Arabic-indic digit 6 + .put('7', '7') + .put('\uFF17', '7') // Fullwidth digit 7 + .put('\u0667', '7') // Arabic-indic digit 7 + .put('8', '8') + .put('\uFF18', '8') // Fullwidth digit 8 + .put('\u0668', '8') // Arabic-indic digit 8 + .put('9', '9') + .put('\uFF19', '9') // Fullwidth digit 9 + .put('\u0669', '9') // Arabic-indic digit 9 + .build(); + + // Only upper-case variants of alpha characters are stored. + private static final Map ALPHA_MAPPINGS = + new ImmutableMap.Builder() + .put('A', '2') + .put('B', '2') + .put('C', '2') + .put('D', '3') + .put('E', '3') + .put('F', '3') + .put('G', '4') + .put('H', '4') + .put('I', '4') + .put('J', '5') + .put('K', '5') + .put('L', '5') + .put('M', '6') + .put('N', '6') + .put('O', '6') + .put('P', '7') + .put('Q', '7') + .put('R', '7') + .put('S', '7') + .put('T', '8') + .put('U', '8') + .put('V', '8') + .put('W', '9') + .put('X', '9') + .put('Y', '9') + .put('Z', '9') + .build(); + + // For performance reasons, amalgamate both into one map. + private static final Map ALL_NORMALIZATION_MAPPINGS = + new ImmutableMap.Builder() + .putAll(ALPHA_MAPPINGS) + .putAll(DIGIT_MAPPINGS) + .build(); + + // Pattern that makes it easy to distinguish whether a country has a unique international dialing + // prefix or not. If a country has a unique international prefix (e.g. 011 in USA), it will be + // represented as a string that contains a sequence of ASCII digits. If there are multiple + // available international prefixes in a country, they will be represented as a regex string that + // always contains character(s) other than ASCII digits. + // Note this regex also includes tilde, which signals waiting for the tone. + private static final Pattern UNIQUE_INTERNATIONAL_PREFIX = + Pattern.compile("[\\d]+([~\u2053\u223C\uFF5E][\\d]+)?"); + + // Regular expression of acceptable punctuation found in phone numbers. This excludes punctuation + // found as a leading character only. + // This consists of dash characters, white space characters, full stops, slashes, + // square brackets, parentheses and tildes. It also includes the letter 'x' as that is found as a + // placeholder for carrier information in some phone numbers. + private static final String VALID_PUNCTUATION = "[-x\u2010-\u2015\u2212\uFF0D-\uFF0F " + + "\u00A0\u200B\u2060\u3000()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E]"; + + // Digits accepted in phone numbers + private static final String VALID_DIGITS = + Arrays.toString(DIGIT_MAPPINGS.keySet().toArray()).replaceAll(", ", ""); + // We accept alpha characters in phone numbers, ASCII only, upper and lower case. + private static final String VALID_ALPHA = + Arrays.toString(ALPHA_MAPPINGS.keySet().toArray()).replaceAll(", ", "") + + Arrays.toString(ALPHA_MAPPINGS.keySet().toArray()).toLowerCase().replaceAll(", ", ""); + private static final String PLUS_CHARS = "+\uFF0B"; + private static final Pattern CAPTURING_DIGIT_PATTERN = + Pattern.compile("([" + VALID_DIGITS + "])"); + + // Regular expression of acceptable characters that may start a phone number for the purposes of + // parsing. This allows us to strip away meaningless prefixes to phone numbers that may be + // mistakenly given to us. This consists of digits, the plus symbol and arabic-indic digits. This + // does not contain alpha characters, although they may be used later in the number. It also does + // not include other punctuation, as this will be stripped later during parsing and is of no + // information value when parsing a number. + private static final String VALID_START_CHAR = "[" + PLUS_CHARS + VALID_DIGITS + "]"; + private static final Pattern VALID_START_CHAR_PATTERN = Pattern.compile(VALID_START_CHAR); + + // Regular expression of characters typically used to start a second phone number for the purposes + // of parsing. This allows us to strip off parts of the number that are actually the start of + // another number, such as for: (530) 583-6985 x302/x2303 -> the second extension here makes this + // actually two phone numbers, (530) 583-6985 x302 and (530) 583-6985 x2303. We remove the second + // extension so that the first number is parsed correctly. + private static final String SECOND_NUMBER_START = "[\\\\/] *x"; + private static final Pattern SECOND_NUMBER_START_PATTERN = Pattern.compile(SECOND_NUMBER_START); + + // Regular expression of viable phone numbers. This is location independent. Checks we have at + // least three leading digits, and only valid punctuation, alpha characters and + // digits in the phone number. Does not include extension data - this is read in from the + // PhoneNumberMetaData.xml file at initialisation time. + // The symbol 'x' is allowed here as valid punctuation since it is often used as a placeholder for + // carrier codes, for example in Brazilian phone numbers. + // Corresponds to the following: + // plus_sign?([punctuation]*[digits]){3,}([punctuation]|[digits]|[alpha])* + private static final String VALID_PHONE_NUMBER = + "[" + PLUS_CHARS + "]?(?:" + VALID_PUNCTUATION + "*[" + VALID_DIGITS + "]){3,}[" + + VALID_ALPHA + VALID_PUNCTUATION + VALID_DIGITS + "]*"; + + // Default extension prefix to use when formatting. This will be put in front of any extension + // component of the number, after the main national number is formatted. For example, if you wish + // the default extension formatting to be " extn: 3456", then you should specify " extn: " here + // as the default extension prefix. This can be overridden by country-specific preferences. + private static final String DEFAULT_EXTN_PREFIX = " ext. "; + + // Regexp of all possible ways to write extensions, for use when parsing. This will be run as a + // case-insensitive regexp match. Wide character versions are also provided after each ascii + // version. There are two regular expressions here: the more generic one starts with optional + // white space and ends with an optional full stop (.), followed by zero or more spaces/tabs and + // then the numbers themselves. The other one covers the special case of American numbers where + // the extension is written with a hash at the end, such as "- 503#". + // Note that the only capturing groups should be around the digits that you want to capture as + // part of the extension, or else parsing will fail! + private static final String KNOWN_EXTN_PATTERNS = "[ \\u00A0\\t,]*(?:ext(?:ensio)?n?|" + + "\\uFF45\\uFF58\\uFF54\\uFF4E?|[,x\\uFF58#\\uFF03~\\uFF5E]|int|\\uFF49\\uFF4E\\uFF54)" + + "[:\\.\\uFF0E]?[ \\u00A0\\t,]*([" + VALID_DIGITS + "]{1,7})|[- ]+([" + VALID_DIGITS + + "]{1,5})#"; + + // Regexp of all known extension prefixes used by different countries followed by 1 or more valid + // digits, for use when parsing. + private static final Pattern EXTN_PATTERN = + Pattern.compile("(?:" + KNOWN_EXTN_PATTERNS + ")$", + Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); + + // We append optionally the extension pattern to the end here, as a valid phone number may + // have an extension prefix appended, followed by 1 or more digits. + private static final Pattern VALID_PHONE_NUMBER_PATTERN = + Pattern.compile(VALID_PHONE_NUMBER + "(?:" + EXTN_PATTERN + ")?", + Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); + + private static PhoneNumberUtil instance = null; + + // A mapping from a region code to the PhoneMetadata for that region. + private Map countryToMetadataMap = + Collections.synchronizedMap(new HashMap()); + + /** + * INTERNATIONAL and NATIONAL formats are consistent with the definition in ITU-T Recommendation + * E. 123. For example, the number of the Google Zürich office will be written as + * "+41 44 668 1800" in INTERNATIONAL format, and as "044 668 1800" in NATIONAL format. + * E164 format is as per INTERNATIONAL format but with no formatting applied, e.g. +41446681800. + * + * Note: If you are considering storing the number in a neutral format, you are highly advised to + * use the phonenumber.proto. + */ + public enum PhoneNumberFormat { + E164, + INTERNATIONAL, + NATIONAL + } + + /** + * Type of phone numbers. + */ + public enum PhoneNumberType { + FIXED_LINE, + MOBILE, + // In some countries (e.g. the USA), it is impossible to distinguish between fixed-line and + // mobile numbers by looking at the phone number itself. + FIXED_LINE_OR_MOBILE, + // Freephone lines + TOLL_FREE, + PREMIUM_RATE, + // The cost of this call is shared between the caller and the recipient, and is hence typically + // less than PREMIUM_RATE calls. See // http://en.wikipedia.org/wiki/Shared_Cost_Service for + // more information. + SHARED_COST, + // Voice over IP numbers. This includes TSoIP (Telephony Service over IP). + VOIP, + // A personal number is associated with a particular person, and may be routed to either a + // MOBILE or FIXED_LINE number. Some more information can be found here: + // http://en.wikipedia.org/wiki/Personal_Numbers + PERSONAL_NUMBER, + // A phone number is of type UNKNOWN when it does not fit any of the known patterns for a + // specific country. + UNKNOWN + } + + /** + * Types of phone number matches. See detailed description beside the isNumberMatch() method. + */ + public enum MatchType { + NO_MATCH, + SHORT_NSN_MATCH, + NSN_MATCH, + EXACT_MATCH, + } + + /** + * Possible outcomes when testing if a PhoneNumber is possible. + */ + public enum ValidationResult { + IS_POSSIBLE, + INVALID_COUNTRY_CODE, + TOO_SHORT, + TOO_LONG, + } + + /** + * This class implements a singleton, so the only constructor is private. + */ + private PhoneNumberUtil() { + } + + private void init(InputStream source) { + // Read in metadata for each country. + try { + PhoneMetadataCollection metadataCollection = PhoneMetadataCollection.parseFrom(source); + for (PhoneMetadata metadata : metadataCollection.getMetadataList()) { + String regionCode = metadata.getId(); + countryToMetadataMap.put(regionCode, metadata); + int countryCode = metadata.getCountryCode(); + switch (countryCode) { + case NANPA_COUNTRY_CODE: + nanpaCountries.add(regionCode); + break; + case RUSSIAN_FED_COUNTRY_CODE: + russiaFederationCountries.add(regionCode); + break; + case FRENCH_INDIAN_OCEAN_COUNTRY_CODE: + frenchIndianOceanTerritories.add(regionCode); + break; + default: + countryCodeToRegionCodeMap.put(countryCode, regionCode); + break; + } + } + + // Override the value, so that 1 is always mapped to US, 7 is always mapped to RU, and 262 to + // RE. + countryCodeToRegionCodeMap.put(NANPA_COUNTRY_CODE, "US"); + countryCodeToRegionCodeMap.put(RUSSIAN_FED_COUNTRY_CODE, "RU"); + countryCodeToRegionCodeMap.put(FRENCH_INDIAN_OCEAN_COUNTRY_CODE, "RE"); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e.toString()); + } + } + + /** + * Attempts to extract a possible number from the string passed in. This currently strips all + * leading characters that could not be used to start a phone number. Characters that can be used + * to start a phone number are defined in the VALID_START_CHAR_PATTERN. If none of these + * characters are found in the number passed in, an empty string is returned. This function also + * attempts to strip off any alternative extensions or endings if two or more are present, such as + * in the case of: (530) 583-6985 x302/x2303. The second extension here makes this actually two + * phone numbers, (530) 583-6985 x302 and (530) 583-6985 x2303. We remove the second extension so + * that the first number is parsed correctly. + * + * @param number the string that might contain a phone number + * @return the number, stripped of any non-phone-number prefix (such as "Tel:") or an empty + * string if no character used to start phone numbers (such as + or any digit) is + * found in the number + */ + @VisibleForTesting + static String extractPossibleNumber(String number) { + // Remove leading and trailing whitespace. + number = number.trim(); + Matcher m = VALID_START_CHAR_PATTERN.matcher(number); + if (m.find()) { + number = number.substring(m.start()); + // Check for extra numbers at the end. + Matcher secondNumber = SECOND_NUMBER_START_PATTERN.matcher(number); + if (secondNumber.find()) { + number = number.substring(0, secondNumber.start()); + } + return number; + } else { + return ""; + } + } + + /** + * Checks to see if the string of characters could possibly be a phone number at all. At the + * moment, checks to see that the string begins with at least 3 digits, ignoring any punctuation + * commonly found in phone numbers. + * This method does not require the number to be normalized in advance - but does assume that + * leading non-number symbols have been removed, such as by the method extractPossibleNumber. + * + * @param number string to be checked for viability as a phone number + * @return true if the number could be a phone number of some sort, otherwise false + */ + @VisibleForTesting + static boolean isViablePhoneNumber(String number) { + if (number.length() < MIN_LENGTH_FOR_NSN) { + return false; + } + Matcher m = VALID_PHONE_NUMBER_PATTERN.matcher(number); + return m.matches(); + } + + /** + * Normalizes a string of characters representing a phone number. This performs the following + * conversions: + * Wide-ascii digits are converted to normal ASCII (European) digits. + * Letters are converted to their numeric representation on a telephone keypad. The keypad + * used here is the one defined in ITU Recommendation E.161. This is only done if there are + * 3 or more letters in the number, to lessen the risk that such letters are typos - + * otherwise alpha characters are stripped. + * Punctuation is stripped. + * Arabic-Indic numerals are converted to European numerals. + * + * @param number a string of characters representing a phone number + * @return the normalized string version of the phone number + */ + static String normalize(String number) { + if (number.matches("(?:.*?[A-Za-z]){3}.*")) { + return normalizeHelper(number, ALL_NORMALIZATION_MAPPINGS, true); + } else { + return normalizeHelper(number, DIGIT_MAPPINGS, true); + } + } + + /** + * Normalizes a string of characters representing a phone number. This is a wrapper for + * normalize(String number) but does in-place normalization of the StringBuffer provided. + * + * @param number a StringBuffer of characters representing a phone number that will be normalized + * in place + */ + static void normalize(StringBuffer number) { + String normalizedNumber = normalize(number.toString()); + number.replace(0, number.length(), normalizedNumber); + } + + /** + * Normalizes a string of characters representing a phone number. This converts wide-ascii and + * arabic-indic numerals to European numerals, and strips punctuation and alpha characters, + * + * @param number a string of characters representing a phone number + * @return the normalized string version of the phone number + */ + public static String normalizeDigitsOnly(String number) { + return normalizeHelper(number, DIGIT_MAPPINGS, true); + } + + /** + * Converts all alpha characters in a number to their respective digits on a keypad, but retains + * existing formatting. Also converts wide-ascii digits to normal ascii digits, and converts + * Arabic-Indic numerals to European numerals. + */ + public static String convertAlphaCharactersInNumber(String number) { + return normalizeHelper(number, ALL_NORMALIZATION_MAPPINGS, false); + } + + /** + * Normalizes a string of characters representing a phone number by replacing all characters found + * in the accompanying map with the values therein, and stripping all other characters if + * removeNonMatches is true. + * + * @param number a string of characters representing a phone number + * @param normalizationReplacements a mapping of characters to what they should be replaced by in + * the normalized version of the phone number + * @param removeNonMatches indicates whether characters that are not able to be replaced + * should be stripped from the number. If this is false, they + * will be left unchanged in the number. + * @return the normalized string version of the phone number + */ + private static String normalizeHelper(String number, + Map normalizationReplacements, + boolean removeNonMatches) { + StringBuffer normalizedNumber = new StringBuffer(number.length()); + char[] numberAsCharArray = number.toCharArray(); + for (char character : numberAsCharArray) { + Character newDigit = normalizationReplacements.get(Character.toUpperCase(character)); + if (newDigit != null) { + normalizedNumber.append(newDigit); + } else if (!removeNonMatches) { + normalizedNumber.append(character); + } + // If neither of the above are true, we remove this character. + } + return normalizedNumber.toString(); + } + + @VisibleForTesting + static synchronized PhoneNumberUtil getInstance(InputStream source) { + if (instance == null) { + instance = new PhoneNumberUtil(); + instance.init(source); + } + return instance; + } + + /** + * Used for testing purposes only to reset the PhoneNumberUtil singleton to null. + */ + @VisibleForTesting + static synchronized void resetInstance() { + instance = null; + } + + @VisibleForTesting + PhoneMetadata getPhoneMetadata(String regionCode) { + PhoneMetadata metadata = countryToMetadataMap.get(regionCode); + // makes a defensive copy + PhoneMetadata.Builder metadataCopy = metadata.newBuilder(); + metadataCopy.mergeFrom(metadata); + return metadataCopy.build(); + } + + /** + * Convenience method to enable tests to get a list of what countries the library has metadata + * for. + */ + @VisibleForTesting + Set getSupportedCountries() { + return countryToMetadataMap.keySet(); + } + + /** + * Gets a PhoneNumberUtil instance to carry out international phone number formatting, parsing, + * or validation. The instance is loaded with phone number metadata for a number of most commonly + * used countries/regions. + * + * The PhoneNumberUtil is implemented as a singleton. Therefore, calling getInstance multiple + * times will only result in one instance being created. + * + * @return a PhoneNumberUtil instance + */ + public static synchronized PhoneNumberUtil getInstance() { + if (instance == null) { + instance = new PhoneNumberUtil(); + InputStream in = PhoneNumberUtil.class.getResourceAsStream(META_DATA_FILE); + instance.init(in); + } + return instance; + } + + /** + * Helper function to check region code is not unknown or null. The number supplied is used only + * for the resultant log message. + */ + private boolean isValidRegionCode(String regionCode, int countryCode, String number) { + if (regionCode == null || regionCode.equals("ZZ")) { + LOGGER.log(Level.WARNING, + "Number " + number + "has invalid or missing country code (" + countryCode + ")"); + return false; + } + return true; + } + + /** + * Formats a phone number in the specified format using default rules. Note that this does not + * promise to produce a phone number that the user can dial from where they are - although we do + * format in either 'national' or 'international' format depending on what the client asks for, we + * do not currently support a more abbreviated format, such as for users in the same "area" who + * could potentially dial the number without area code. Note that if the phone number has a + * country code of 0 or an otherwise invalid country code, we cannot work out which formatting + * rules to apply so we return the national significant number with no formatting applied. + * + * @param number the phone number to be formatted + * @param numberFormat the format the phone number should be formatted into + * @return the formatted phone number + */ + public String format(PhoneNumber number, PhoneNumberFormat numberFormat) { + int countryCode = number.getCountryCode(); + String nationalSignificantNumber = getUnformattedNationalNumber(number); + if (numberFormat == PhoneNumberFormat.E164) { + // Early exit for E164 case since no formatting of the national number needs to be applied. + // Extensions are not formatted. + return formatNumberByFormat(countryCode, PhoneNumberFormat.E164, + nationalSignificantNumber, ""); + } + // Note here that all NANPA formatting rules are contained by US, so we use that to format NANPA + // numbers. The same applies to Russian Fed countries - rules are contained by Russia. French + // Indian Ocean country rules are contained by Réunion. + String regionCode = getRegionCodeForCountryCode(countryCode); + if (!isValidRegionCode(regionCode, countryCode, nationalSignificantNumber)) { + return nationalSignificantNumber; + } + String formattedExtension = maybeGetFormattedExtension(number, regionCode); + return formatNumberByFormat(countryCode, numberFormat, + formatNationalNumber(nationalSignificantNumber, + regionCode, + numberFormat), + formattedExtension); + } + + /** + * Formats a phone number in the specified format using client-defined formatting rules. Note that + * if the phone number has a country code of zero or an otherwise invalid country code, we cannot + * work out things like whether there should be a national prefix applied, or how to format + * extensions, so we return the national significant number with no formatting applied. + * + * @param number the phone number to be formatted + * @param numberFormat the format the phone number should be formatted into + * @param userDefinedFormats formatting rules specified by clients + * @return the formatted phone number + */ + public String formatByPattern(PhoneNumber number, + PhoneNumberFormat numberFormat, + List userDefinedFormats) { + int countryCode = number.getCountryCode(); + // For performance reasons, we use US to represent NANPA countries here. This means that + // the extension symbol will be chosen from the US metadata. + String regionCode = getRegionCodeForCountryCode(countryCode); + String nationalSignificantNumber = getUnformattedNationalNumber(number); + if (!isValidRegionCode(regionCode, countryCode, nationalSignificantNumber)) { + return nationalSignificantNumber; + } + int size = userDefinedFormats.size(); + for (int i = 0; i < size; i++) { + NumberFormat numFormat = userDefinedFormats.get(i); + String nationalPrefixFormattingRule = numFormat.getNationalPrefixFormattingRule(); + if (nationalPrefixFormattingRule.length() > 0) { + String nationalPrefix = getMetadataForRegion(regionCode).getNationalPrefix(); + // Replace $NP with national prefix and $FG with the first group ($1). + nationalPrefixFormattingRule = + nationalPrefixFormattingRule.replaceFirst("\\$NP", nationalPrefix) + .replaceFirst("\\$FG", "\\$1"); + userDefinedFormats.set(i, NumberFormat.newBuilder(numFormat) + .setNationalPrefixFormattingRule(nationalPrefixFormattingRule).build()); + } + } + + String formattedExtension = maybeGetFormattedExtension(number, regionCode); + return formatNumberByFormat(countryCode, + numberFormat, + formatAccordingToFormats(nationalSignificantNumber, + userDefinedFormats, + numberFormat), + formattedExtension); + } + + /** + * Formats a phone number for out-of-country dialing purpose. If no countryCallingFrom + * is supplied, we format the number in its INTERNATIONAL format. If the countryCallingFrom is + * the same as the country where the number is from, then NATIONAL formatting will be applied. + * + * If the number itself has a country code of zero or an otherwise invalid country code, then we + * return the number with no formatting applied. + * + * Note this function takes care of the case for calling inside of NANPA and between Russia and + * Kazakhstan (who share the same country code). In those cases, no international prefix is used. + * For countries which have multiple international prefixes, the number in its INTERNATIONAL + * format will be returned instead. + * + * @param number the phone number to be formatted + * @param countryCallingFrom the ISO 3166-1 two-letter country code that denotes the foreign + * country where the call is being placed + * @return the formatted phone number + */ + public String formatOutOfCountryCallingNumber(PhoneNumber number, + String countryCallingFrom) { + if (countryCallingFrom == null || countryCallingFrom.equals("ZZ")) { + LOGGER.log(Level.WARNING, + "Trying to format number from invalid region. International formatting applied."); + return format(number, PhoneNumberFormat.INTERNATIONAL); + } + int countryCode = number.getCountryCode(); + if (countryCode == NANPA_COUNTRY_CODE && nanpaCountries.contains(countryCallingFrom)) { + // For NANPA countries, return the national format for these countries but prefix it with the + // country code. + return countryCode + " " + format(number, PhoneNumberFormat.NATIONAL); + } + if (countryCode == FRENCH_INDIAN_OCEAN_COUNTRY_CODE && + frenchIndianOceanTerritories.contains(countryCallingFrom)) { + // For dialling between FRENCH_INDIAN_OCEAN countries, the 10 digit number is all we need. + // Technically this is the case for dialling from la Réunion to other overseas departments of + // France (French Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover this + // edge case for now and for those cases return the version including country code. + // Details here: http://www.petitfute.com/voyage/225-info-pratiques-reunion + return format(number, PhoneNumberFormat.NATIONAL); + } + // If the country code is the Russian Fed country code, we check the number itself to determine + // which region code it is for. We don't do this for NANPA countries because of performance + // reasons, and instead use US rules for all NANPA numbers. Also, NANPA countries share the + // same national and international prefixes, which is not the case for Russian Fed countries. + // There is also a special case for toll-free and premium rate numbers dialled within Russian + // Fed countries. + String regionCode; + if (countryCode == RUSSIAN_FED_COUNTRY_CODE) { + if (russiaFederationCountries.contains(countryCallingFrom)) { + // For toll-free numbers and premium rate numbers dialled from within Russian Fed countries, + // we should format them as if they are local numbers. + // A toll-free number would be dialled from KZ as 8-800-080-7777 but from Russia as + // 0-800-080-7777. (Confirmation on government websites such as e.gov.kz). + PhoneNumberType numberType = getNumberType(number); + if (numberType == PhoneNumberType.TOLL_FREE || numberType == PhoneNumberType.PREMIUM_RATE) { + return format(number, PhoneNumberFormat.NATIONAL); + } + } + // Otherwise, we should find out what region the number really belongs to before continuing, + // since they have different formatting rules. + regionCode = getRegionCodeForNumber(number); + } else { + regionCode = getRegionCodeForCountryCode(countryCode); + } + String nationalSignificantNumber = getUnformattedNationalNumber(number); + if (!isValidRegionCode(regionCode, countryCode, nationalSignificantNumber)) { + return nationalSignificantNumber; + } + if (regionCode.equals(countryCallingFrom)) { + return format(number, PhoneNumberFormat.NATIONAL); + } + String formattedNationalNumber = + formatNationalNumber(nationalSignificantNumber, + regionCode, PhoneNumberFormat.INTERNATIONAL); + PhoneMetadata metadata = getMetadataForRegion(countryCallingFrom); + String internationalPrefix = metadata.getInternationalPrefix(); + String formattedExtension = maybeGetFormattedExtension(number, regionCode); + // For countries that have multiple international prefixes, the international format of the + // number is returned, unless there is a preferred international prefix. + String internationalPrefixForFormatting = ""; + if (UNIQUE_INTERNATIONAL_PREFIX.matcher(internationalPrefix).matches()) { + internationalPrefixForFormatting = internationalPrefix; + } else if (metadata.hasPreferredInternationalPrefix()) { + internationalPrefixForFormatting = metadata.getPreferredInternationalPrefix(); + } + return !internationalPrefixForFormatting.equals("") + ? internationalPrefixForFormatting + " " + countryCode + " " + formattedNationalNumber + + formattedExtension + : formatNumberByFormat(countryCode, + PhoneNumberFormat.INTERNATIONAL, + formattedNationalNumber, + formattedExtension); + } + + static String getUnformattedNationalNumber(PhoneNumber number) { + // The leading zero in the national (significant) number of an Italian phone number has a + // special meaning. Unlike the rest of the world, it indicates the number is a landline + // number. There have been plans to migrate landline numbers to start with the digit two since + // December 2000, but it has not yet happened. + // See http://en.wikipedia.org/wiki/%2B39 for more details. + // Cote d'Ivoire also uses this for some of their mobile numbers. + StringBuffer nationalNumber = new StringBuffer( + ((number.getCountryCode() == 39 || number.getCountryCode() == 225) && + number.hasItalianLeadingZero() && + number.getItalianLeadingZero()) + ? "0" : "" + ); + nationalNumber.append(number.getNationalNumber()); + return nationalNumber.toString(); + } + + /** + * A helper function that is used by format and formatByPattern. + */ + private String formatNumberByFormat(int countryCode, + PhoneNumberFormat numberFormat, + String formattedNationalNumber, + String formattedExtension) { + switch (numberFormat) { + case E164: + return String.valueOf(PLUS_SIGN) + countryCode + formattedNationalNumber + + formattedExtension; + case INTERNATIONAL: + return String.valueOf(PLUS_SIGN) + countryCode + " " + formattedNationalNumber + + formattedExtension; + case NATIONAL: + default: + return formattedNationalNumber + formattedExtension; + } + } + + // Note in some countries, the national number can be written in two completely different ways + // depending on whether it forms part of the NATIONAL format or INTERNATIONAL format. The + // numberFormat parameter here is used to specify which format to use for those cases. + private String formatNationalNumber(String number, + String regionCode, + PhoneNumberFormat numberFormat) { + PhoneMetadata metadata = getMetadataForRegion(regionCode); + List intlNumberFormats = metadata.getIntlNumberFormatList(); + // When the intlNumberFormats exists, we use that to format national number for the + // INTERNATIONAL format instead of using the numberDesc.numberFormats. + List availableFormats = + (intlNumberFormats.size() == 0 || numberFormat == PhoneNumberFormat.NATIONAL) + ? metadata.getNumberFormatList() + : metadata.getIntlNumberFormatList(); + return formatAccordingToFormats(number, availableFormats, numberFormat); + } + + private String formatAccordingToFormats(String nationalNumber, + List availableFormats, + PhoneNumberFormat numberFormat) { + for (NumberFormat numFormat : availableFormats) { + if (!numFormat.hasLeadingDigits() || + Pattern.compile(numFormat.getLeadingDigits()).matcher(nationalNumber).lookingAt()) { + String patternToMatch = numFormat.getPattern(); + if (nationalNumber.matches(patternToMatch)) { + String nationalPrefixFormattingRule = numFormat.getNationalPrefixFormattingRule(); + if (numberFormat == PhoneNumberFormat.NATIONAL && + nationalPrefixFormattingRule != null && + nationalPrefixFormattingRule.length() > 0) { + return nationalNumber.replaceAll( + patternToMatch, + numFormat.getFormat().replaceFirst("(\\$1)", nationalPrefixFormattingRule)); + } else { + return nationalNumber.replaceAll(patternToMatch, numFormat.getFormat()); + } + } + } + } + + // If no pattern above is matched, we format the number as a whole. + return nationalNumber; + } + + /** + * Gets a valid number for the specified country. + * + * @param regionCode the ISO 3166-1 two-letter country code that denotes the country for which + * an example number is needed + * @return a valid fixed-line number for the specified country. Returns null when the metadata + * does not contain such information. + */ + public PhoneNumber getExampleNumber(String regionCode) { + return getExampleNumberForType(regionCode, PhoneNumberType.FIXED_LINE); + } + + /** + * Gets a valid number for the specified country and number type. + * + * @param regionCode the ISO 3166-1 two-letter country code that denotes the country for which + * an example number is needed + * @param type the type of number that is needed + * @return a valid number for the specified country and type. Returns null when the metadata + * does not contain such information. + */ + public PhoneNumber getExampleNumberForType(String regionCode, PhoneNumberType type) { + PhoneNumberDesc desc = getNumberDescByType(getMetadataForRegion(regionCode), type); + try { + if (desc.hasExampleNumber()) { + return parse(desc.getExampleNumber(), regionCode); + } + } catch (NumberParseException e) { + LOGGER.log(Level.SEVERE, e.toString()); + } + return null; + } + + /** + * Gets the formatted extension of a phone number, if the phone number had an extension specified. + * If not, it returns an empty string. + */ + private String maybeGetFormattedExtension(PhoneNumber number, String regionCode) { + if (!number.hasExtension()) { + return ""; + } else { + return formatExtension(number.getExtension(), regionCode); + } + } + + /** + * Formats the extension part of the phone number by prefixing it with the appropriate extension + * prefix. This will be the default extension prefix, unless overridden by a preferred + * extension prefix for this country. + */ + private String formatExtension(String extensionDigits, String regionCode) { + PhoneMetadata metadata = getMetadataForRegion(regionCode); + if (metadata.hasPreferredExtnPrefix()) { + return metadata.getPreferredExtnPrefix() + extensionDigits; + } else { + return DEFAULT_EXTN_PREFIX + extensionDigits; + } + } + + PhoneNumberDesc getNumberDescByType(PhoneMetadata metadata, PhoneNumberType type) { + switch (type) { + case PREMIUM_RATE: + return metadata.getPremiumRate(); + case TOLL_FREE: + return metadata.getTollFree(); + case MOBILE: + return metadata.getMobile(); + case FIXED_LINE: + case FIXED_LINE_OR_MOBILE: + return metadata.getFixedLine(); + case SHARED_COST: + return metadata.getSharedCost(); + case VOIP: + return metadata.getVoip(); + case PERSONAL_NUMBER: + return metadata.getPersonalNumber(); + default: + return metadata.getGeneralDesc(); + } + } + + /** + * Gets the type of a phone number. + * + * @param number the phone number that we want to know the type + * @return the type of the phone number + */ + public PhoneNumberType getNumberType(PhoneNumber number) { + String regionCode = getRegionCodeForNumber(number); + String nationalSignificantNumber = getUnformattedNationalNumber(number); + if (!isValidRegionCode(regionCode, number.getCountryCode(), nationalSignificantNumber)) { + return PhoneNumberType.UNKNOWN; + } + PhoneMetadata metadata = getMetadataForRegion(regionCode); + return getNumberTypeHelper(nationalSignificantNumber, metadata); + } + + private PhoneNumberType getNumberTypeHelper(String nationalNumber, PhoneMetadata metadata) { + PhoneNumberDesc generalNumberDesc = metadata.getGeneralDesc(); + if (!generalNumberDesc.hasNationalNumberPattern() || + !isNumberMatchingDesc(nationalNumber, generalNumberDesc)) { + LOGGER.log(Level.FINEST, + "Number type unknown - doesn't match general national number pattern."); + return PhoneNumberType.UNKNOWN; + } + + if (isNumberMatchingDesc(nationalNumber, metadata.getPremiumRate())) { + LOGGER.log(Level.FINEST, "Number is a premium number."); + return PhoneNumberType.PREMIUM_RATE; + } + if (isNumberMatchingDesc(nationalNumber, metadata.getTollFree())) { + LOGGER.log(Level.FINEST, "Number is a toll-free number."); + return PhoneNumberType.TOLL_FREE; + } + if (isNumberMatchingDesc(nationalNumber, metadata.getSharedCost())) { + LOGGER.log(Level.FINEST, "Number is a shared cost number."); + return PhoneNumberType.SHARED_COST; + } + if (isNumberMatchingDesc(nationalNumber, metadata.getVoip())) { + LOGGER.log(Level.FINEST, "Number is a VOIP (Voice over IP) number."); + return PhoneNumberType.VOIP; + } + if (isNumberMatchingDesc(nationalNumber, metadata.getPersonalNumber())) { + LOGGER.log(Level.FINEST, "Number is a personal number."); + return PhoneNumberType.PERSONAL_NUMBER; + } + + boolean isFixedLine = isNumberMatchingDesc(nationalNumber, metadata.getFixedLine()); + if (isFixedLine) { + if (metadata.getSameMobileAndFixedLinePattern()) { + LOGGER.log(Level.FINEST, + "Fixed-line and mobile patterns equal, number is fixed-line or mobile"); + return PhoneNumberType.FIXED_LINE_OR_MOBILE; + } else if (isNumberMatchingDesc(nationalNumber, metadata.getMobile())) { + LOGGER.log(Level.FINEST, + "Fixed-line and mobile patterns differ, but number is " + + "still fixed-line or mobile"); + return PhoneNumberType.FIXED_LINE_OR_MOBILE; + } + LOGGER.log(Level.FINEST, "Number is a fixed line number."); + return PhoneNumberType.FIXED_LINE; + } + // Otherwise, test to see if the number is mobile. Only do this if certain that the patterns for + // mobile and fixed line aren't the same. + if (!metadata.getSameMobileAndFixedLinePattern() && + isNumberMatchingDesc(nationalNumber, metadata.getMobile())) { + LOGGER.log(Level.FINEST, "Number is a mobile number."); + return PhoneNumberType.MOBILE; + } + LOGGER.log(Level.FINEST, + "Number type unknown - doesn't match any specific number type pattern."); + return PhoneNumberType.UNKNOWN; + } + + PhoneMetadata getMetadataForRegion(String regionCode) { + if (regionCode == null) { + return null; + } + return countryToMetadataMap.get(regionCode); + } + + private boolean isNumberMatchingDesc(String nationalNumber, PhoneNumberDesc numberDesc) { + String possiblePattern = numberDesc.getPossibleNumberPattern(); + if (!nationalNumber.matches(possiblePattern)) { + return false; + } + + String validPattern = numberDesc.getNationalNumberPattern(); + return nationalNumber.matches(validPattern); + } + + /** + * Tests whether a phone number matches a valid pattern. Note this doesn't verify the number + * is actually in use, which is impossible to tell by just looking at a number itself. + * + * @param number the phone number that we want to validate + * @return a boolean that indicates whether the number is of a valid pattern + */ + public boolean isValidNumber(PhoneNumber number) { + String regionCode = getRegionCodeForNumber(number); + return isValidRegionCode(regionCode, number.getCountryCode(), + getUnformattedNationalNumber(number)) + && isValidNumberForRegion(number, regionCode); + } + + /** + * Tests whether a phone number is valid for a certain region. Note this doesn't verify the number + * is actually in use, which is impossible to tell by just looking at a number itself. If the + * country code is not the same as the country code for the region, this immediately exits with + * false. After this, the specific number pattern rules for the region are examined. This is + * useful for determining for example whether a particular number is valid for Canada, rather than + * just a valid NANPA number. + * + * @param number the phone number that we want to validate + * @param regionCode the ISO 3166-1 two-letter country code that denotes the region/country + * that we want to validate the phone number for + * @return a boolean that indicates whether the number is of a valid pattern + */ + public boolean isValidNumberForRegion(PhoneNumber number, String regionCode) { + if (number.getCountryCode() != getCountryCodeForRegion(regionCode)) { + return false; + } + PhoneMetadata metadata = getMetadataForRegion(regionCode); + PhoneNumberDesc generalNumDesc = metadata.getGeneralDesc(); + String nationalSignificantNumber = getUnformattedNationalNumber(number); + + // For countries where we don't have meta-data for PhoneNumberDesc, we treat any number passed + // in as a valid number if its national significant number is between the minimum and maximum + // lengths defined by ITU for a national significant number. + if (!generalNumDesc.hasNationalNumberPattern()) { + LOGGER.log(Level.FINER, "Validating number with incomplete metadata."); + int numberLength = nationalSignificantNumber.length(); + return numberLength > MIN_LENGTH_FOR_NSN && numberLength <= MAX_LENGTH_FOR_NSN; + } + return isNumberMatchingDesc(nationalSignificantNumber, generalNumDesc) + && getNumberTypeHelper(nationalSignificantNumber, metadata) != PhoneNumberType.UNKNOWN; + } + + /** + * Returns the country/region where a phone number is from. This could be used for geo-coding in + * the country/region level. + * + * @param number the phone number whose origin we want to know + * @return the country/region where the phone number is from + */ + public String getRegionCodeForNumber(PhoneNumber number) { + int countryCode = number.getCountryCode(); + String regionCode; + switch (countryCode) { + case NANPA_COUNTRY_CODE: + // Override this and try the US case first, since it is more likely than other countries, + // for performance reasons. + if (isValidNumberForRegion(number, "US")) { + return "US"; + } + Set nanpaExceptUS = new HashSet(nanpaCountries); + nanpaExceptUS.remove("US"); + regionCode = getRegionCodeForNumberFromRegionList(number, nanpaExceptUS); + return regionCode; + case RUSSIAN_FED_COUNTRY_CODE: + regionCode = getRegionCodeForNumberFromRegionList(number, russiaFederationCountries); + return regionCode; + case FRENCH_INDIAN_OCEAN_COUNTRY_CODE: + regionCode = getRegionCodeForNumberFromRegionList(number, frenchIndianOceanTerritories); + return regionCode; + default: + return getRegionCodeForCountryCode(countryCode); + } + } + + private String getRegionCodeForNumberFromRegionList(PhoneNumber number, + Set regionCodes) { + String nationalNumber = String.valueOf(number.getNationalNumber()); + for (String regionCode : regionCodes) { + PhoneMetadata metadata = getMetadataForRegion(regionCode); + if (getNumberTypeHelper(nationalNumber, metadata) != PhoneNumberType.UNKNOWN) { + return regionCode; + } + } + return null; + } + + /** + * Returns the region code that matches the specific country code. In the case of no region code + * being found, ZZ will be returned. + */ + String getRegionCodeForCountryCode(int countryCode) { + String regionCode = countryCodeToRegionCodeMap.get(countryCode); + return regionCode == null ? "ZZ" : regionCode; + } + + /** + * Returns the country calling code for a specific region. For example, this would be 1 for the + * United States, and 64 for New Zealand. + * + * @param regionCode the ISO 3166-1 two-letter country code that denotes the country/region that + * we want to get the country code for + * @return the country calling code for the country/region denoted by regionCode + */ + public int getCountryCodeForRegion(String regionCode) { + if (regionCode == null || regionCode.equals("ZZ")) { + LOGGER.log(Level.SEVERE, "Invalid or missing country code provided."); + return 0; + } + PhoneMetadata metadata = getMetadataForRegion(regionCode); + return metadata.getCountryCode(); + } + + /** + * Gets a set which contains all the countries under the North American Numbering Plan + * Administration (NANPA). + * + * @return the set that contains the countries under NANPA + */ + public Set getNANPACountries() { + return new HashSet(nanpaCountries); + } + + /** + * Convenience wrapper around isPossibleNumberWithReason. Instead of returning the reason for + * failure, this method returns a boolean value. + * @param number the number that needs to be checked + * @return true if the number is possible + */ + public boolean isPossibleNumber(PhoneNumber number) { + return isPossibleNumberWithReason(number) == ValidationResult.IS_POSSIBLE; + } + + /** + * Check whether a phone number is a possible number. It provides a more lenient check than + * isValidNumber in the following sense: + * 1. It only checks the length of phone numbers. In particular, it doesn't check starting + * digits of the number. + * 2. It doesn't attempt to figure out the type of the number, but uses general rules which + * applies to all types of phone numbers in a country. Therefore, it is much faster than + * isValidNumber. + * 3. For fixed line numbers, many countries have the concept of area code, which together with + * subscriber number constitute the national significant number. It is sometimes okay to dial + * the subscriber number only when dialing in the same area. This function will return + * true if the subscriber-number-only version is passed in. On the other hand, because + * isValidNumber validates using information on both starting digits (for fixed line + * numbers, that would most likely be area codes) and length (obviously includes the + * length of area codes for fixed line numbers), it will return false for the + * subscriber-number-only version. + * + * @param number the number that needs to be checked + * @return a ValidationResult object which indicates whether the number is possible + */ + public ValidationResult isPossibleNumberWithReason(PhoneNumber number) { + String nationalNumber = String.valueOf(number.getNationalNumber()); + int countryCode = number.getCountryCode(); + // Note: For Russian Fed and NANPA numbers, we just use the rules from the default region (US or + // Russia) since the getRegionCodeForNumber will not work if the number is possible but not + // valid. This would need to be revisited if the possible number pattern ever differed between + // various countries within those plans. + String regionCode = getRegionCodeForCountryCode(countryCode); + if (!isValidRegionCode(regionCode, countryCode, nationalNumber)) { + return ValidationResult.INVALID_COUNTRY_CODE; + } + PhoneNumberDesc generalNumDesc = getMetadataForRegion(regionCode).getGeneralDesc(); + String possibleNumberPattern = generalNumDesc.getPossibleNumberPattern(); + Matcher m = Pattern.compile(possibleNumberPattern).matcher(nationalNumber); + if (m.lookingAt()) { + return (m.end() == nationalNumber.length()) ? ValidationResult.IS_POSSIBLE + : ValidationResult.TOO_LONG; + } else { + return ValidationResult.TOO_SHORT; + } + } + + /** + * Check whether a phone number is a possible number given a number in the form of a string, and + * the country where the number could be dialed from. It provides a more lenient check than + * isValidNumber. See isPossibleNumber(PhoneNumber number) for details. + * + * This method first parses the number, then invokes isPossibleNumber(PhoneNumber number) with the + * resultant PhoneNumber object. + * + * @param number the number that needs to be checked, in the form of a string + * @param countryDialingFrom the ISO 3166-1 two-letter country code that denotes + * the country that we are expecting the number to be dialed from. + * Note this is different from the country where the number belongs. + * For example, the number +1 650 253 0000 is a number that belongs to US. + * When written in this form, it could be dialed from any country. + * When it is written as 00 1 650 253 0000, it could be dialed from + * any country which has international prefix 00. When it is written as + * 650 253 0000, it could only be dialed from US, and when written as + * 253 0000, it could only be dialed from US (Mountain View, CA, to be + * more specific). + * @return true if the number is possible + */ + public boolean isPossibleNumber(String number, String countryDialingFrom) { + try { + return isPossibleNumber(parse(number, countryDialingFrom)); + } catch (NumberParseException e) { + return false; + } + } + + /** + * Gets an AsYouTypeFormatter for the specific country. Note this function doesn't attempt to + * figure out the types of phone number being entered on the fly due to performance reasons. + * Instead, it tries to apply a standard format to all types of phone numbers. For countries + * where different types of phone numbers follow different formats, the formatter returned + * will do no formatting but output exactly what is fed into the inputDigit method. + * + * If the type of the phone number being entered is known beforehand, use + * getAsYouTypeFormatterByType instead. + * + * @param regionCode the ISO 3166-1 two-letter country code that denotes the country/region + * where the phone number is being entered + * @return an AsYouTypeFormatter object, which could be used to format phone numbers in the + * specific country "as you type" + */ + public AsYouTypeFormatter getAsYouTypeFormatter(String regionCode) { + return new AsYouTypeFormatter(regionCode); + } + + // Extracts country code from fullNumber, returns it and places the remaining number in + // nationalNumber. It assumes that the leading plus sign or IDD has already been removed. Returns + // 0 if fullNumber doesn't start with a valid country code, and leaves nationalNumber unmodified. + int extractCountryCode(StringBuffer fullNumber, StringBuffer nationalNumber) { + int potentialCountryCode; + for (int i = 1; i <= 3; i++) { + potentialCountryCode = Integer.parseInt(fullNumber.substring(0, i)); + if (countryCodeToRegionCodeMap.containsKey(potentialCountryCode)) { + nationalNumber.append(fullNumber.substring(i)); + return potentialCountryCode; + } + } + return 0; + } + + /** + * Tries to extract a country code from a number. This method will return zero if no country code + * is considered to be present. Country codes are extracted in the following ways: + * - by stripping the international dialing prefix of the country the person is dialing from, + * if this is present in the number, and looking at the next digits + * - by stripping the '+' sign if present and then looking at the next digits + * - by comparing the start of the number and the country code of the default region. If the + * number is not considered possible for the numbering plan of the default region initially, + * but starts with the country code of this region, validation will be reattempted after + * stripping this country code. If this number is considered a possible number, then the + * first digits will be considered the country code and removed as such. + * + * It will throw a NumberParseException if the number starts with a '+' but the country code + * supplied after this does not match that of any known country. + * + * @param number non-normalized telephone number that we wish to extract a country + * code from - may begin with '+' + * @param defaultRegionMetadata metadata about the region this number may be from + * @param nationalNumber a string buffer to store the national significant number in, in the case + * that a country code was extracted. The number is appended to any existing contents. If no + * country code was extracted, this will be left unchanged. + * @return the country code extracted or 0 if none could be extracted + */ + @VisibleForTesting + int maybeExtractCountryCode(String number, PhoneMetadata defaultRegionMetadata, + StringBuffer nationalNumber) + throws NumberParseException { + StringBuffer fullNumber = new StringBuffer(number); + // Set the default prefix to be something that will never match. + String possibleCountryIddPrefix = "NonMatch"; + if (defaultRegionMetadata != null) { + possibleCountryIddPrefix = defaultRegionMetadata.getInternationalPrefix(); + } + if (maybeStripInternationalPrefixAndNormalize(fullNumber, possibleCountryIddPrefix)) { + if (fullNumber.length() < MIN_LENGTH_FOR_NSN) { + throw new NumberParseException(NumberParseException.ErrorType.TOO_SHORT_AFTER_IDD, + "Phone number had an IDD, but after this was not " + + "long enough to be a viable phone number."); + } + int potentialCountryCode = extractCountryCode(fullNumber, nationalNumber); + if (potentialCountryCode != 0) { + return potentialCountryCode; + } + + // If this fails, they must be using a strange country code that we don't recognize, or + // that doesn't exist. + throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + "Country code supplied was not recognised."); + } else if (defaultRegionMetadata != null) { + // Check to see if the number is valid for the default region already. If not, we check to + // see if the country code for the default region is present at the start of the number. + Pattern validNumberPattern = + Pattern.compile(defaultRegionMetadata.getGeneralDesc().getNationalNumberPattern()); + if (!validNumberPattern.matcher(fullNumber).matches()) { + int defaultCountryCode = defaultRegionMetadata.getCountryCode(); + String defaultCountryCodeString = String.valueOf(defaultCountryCode); + String normalizedNumber = fullNumber.toString(); + if (normalizedNumber.startsWith(defaultCountryCodeString)) { + // If so, strip this, and see if the resultant number is valid. + StringBuffer potentialNationalNumber = + new StringBuffer(normalizedNumber.substring(defaultCountryCodeString.length())); + maybeStripNationalPrefix( + potentialNationalNumber, + defaultRegionMetadata.getNationalPrefixForParsing(), + defaultRegionMetadata.getNationalPrefixTransformRule(), + validNumberPattern); + if (validNumberPattern.matcher(potentialNationalNumber).matches()) { + nationalNumber.append(potentialNationalNumber); + return defaultCountryCode; + } + } + } + } + // No country code present. + return 0; + } + + /** + * Strips the IDD from the start of the number if present. Helper function used by + * maybeStripInternationalPrefixAndNormalize. + */ + private boolean parsePrefixAsIdd(Pattern iddPattern, + StringBuffer number) { + Matcher m = iddPattern.matcher(number); + if (m.lookingAt()) { + int matchEnd = m.end(); + // Only strip this if the first digit after the match is not a 0, since country codes cannot + // begin with 0. + Matcher digitMatcher = CAPTURING_DIGIT_PATTERN.matcher(number.substring(matchEnd)); + if (digitMatcher.find()) { + String normalizedGroup = normalize(digitMatcher.group(1)); + if (normalizedGroup.equals("0")) { + return false; + } + } + number.delete(0, matchEnd); + return true; + } + return false; + } + + /** + * Strips any international prefix (such as +, 00, 011) present in the number provided, normalizes + * the resulting number, and indicates if an international prefix was present. + * + * @param number the non-normalized telephone number that we wish to strip any international + * dialing prefix from + * @param possibleIddPrefix the international direct dialing prefix from the country we + * think this number may be dialed in + * @return true if an international dialing prefix could be removed from the number, otherwise + * false if the number did not seem to be in international format + */ + @VisibleForTesting + boolean maybeStripInternationalPrefixAndNormalize(StringBuffer number, String possibleIddPrefix) { + if (number.length() == 0) { + return false; + } + if (number.charAt(0) == PLUS_SIGN) { + number.deleteCharAt(0); + // Can now normalize the rest of the number since we've consumed the "+" sign at the start. + normalize(number); + return true; + } + // Attempt to parse the first digits as an international prefix. + Pattern iddPattern = Pattern.compile(possibleIddPrefix); + if (parsePrefixAsIdd(iddPattern, number)) { + normalize(number); + return true; + } + // If still not found, then try and normalize the number and then try again. This shouldn't be + // done before, since non-numeric characters (+ and ~) may legally be in the international + // prefix. + normalize(number); + return parsePrefixAsIdd(iddPattern, number); + } + + /** + * Strips any national prefix (such as 0, 1) present in the number provided. + * + * @param number the normalized telephone number that we wish to strip any national + * dialing prefix from + * @param possibleNationalPrefix a regex that represents the national direct dialing prefix + * from the country we think this number may be dialed in + * @param transformRule the string that specifies how number should be transformed according + * to the regex specified in possibleNationalPrefix + * @param nationalNumberRule a regular expression that specifies what a valid phonenumber from + * this region should look like after any national prefix was stripped or transformed + */ + @VisibleForTesting + void maybeStripNationalPrefix(StringBuffer number, String possibleNationalPrefix, + String transformRule, Pattern nationalNumberRule) { + int numberLength = number.length(); + if (numberLength == 0 || possibleNationalPrefix.equals("")) { + // Early return for numbers of zero length. + return; + } + // Attempt to parse the first digits as a national prefix. + Matcher m = Pattern.compile(possibleNationalPrefix).matcher(number); + if (m.lookingAt()) { + // m.group(1) == null implies nothing was captured by the capturing groups in + // possibleNationalPrefix; therefore, no transformation is necessary, and we + // just remove the national prefix. + if (transformRule == null || transformRule.equals("") || m.group(1) == null) { + // Check that the resultant number is viable. If not, return. + Matcher nationalNumber = nationalNumberRule.matcher(number.substring(m.end())); + if (!nationalNumber.matches()) { + return; + } + number.delete(0, m.end()); + } else { + // Check that the resultant number is viable. If not, return. Check this by copying the + // string buffer and making the transformation on the copy first. + StringBuffer transformedNumber = new StringBuffer(number); + transformedNumber.replace(0, numberLength, m.replaceFirst(transformRule)); + Matcher nationalNumber = nationalNumberRule.matcher(transformedNumber.toString()); + if (!nationalNumber.matches()) { + return; + } + number.replace(0, number.length(), transformedNumber.toString()); + } + } + } + + /** + * Strips any extension (as in, the part of the number dialled after the call is connected, + * usually indicated with extn, ext, x or similar) from the end of the number, and returns it. + * + * @param number the non-normalized telephone number that we wish to strip the extension from + * @return the phone extension + */ + @VisibleForTesting + String maybeStripExtension(StringBuffer number) { + Matcher m = EXTN_PATTERN.matcher(number); + // If we find a potential extension, and the number preceding this is a viable number, we assume + // it is an extension. + if (m.find() && isViablePhoneNumber(number.substring(0, m.start()))) { + // The numbers are captured into groups in the regular expression. + for (int i = 1; i <= m.groupCount(); i++) { + if (m.group(i) != null) { + // We go through the capturing groups until we find one that captured some digits. If none + // did, then we will return the empty string. + String extension = m.group(i); + number.delete(m.start(), number.length()); + return extension; + } + } + } + return ""; + } + + /** + * Parses a string and returns it in proto buffer format. This method will throw a + * NumberParseException exception if the number is not considered to be a possible number. Note + * that validation of whether the number is actually a valid number for a particular + * country/region is not performed. This can be done separately with isValidNumber. + * + * @param numberToParse number that we are attempting to parse. This can contain formatting + * such as +, ( and -, as well as a phone number extension. + * @param defaultCountry the ISO 3166-1 two-letter country code that denotes the country that + * we are expecting the number to be from. This is only used + * if the number being parsed is not written in international format. + * The country code for the number in this case would be stored as that + * of the default country supplied. + * @return a phone number proto buffer filled with the parsed number + * @throws NumberParseException if the string is not considered to be a viable phone number or if + * no default country was supplied + */ + public PhoneNumber parse(String numberToParse, String defaultCountry) + throws NumberParseException { + if (defaultCountry == null || defaultCountry.equals("ZZ")) { + throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + "No default country was supplied."); + } + return parseHelper(numberToParse, defaultCountry); + } + + /** + * Parses a string and returns it in proto buffer format. This method is the same as the public + * parse() method, with the exception that it allows the default country to be null, for use by + * isNumberMatch(). + */ + private PhoneNumber parseHelper(String numberToParse, String defaultCountry) + throws NumberParseException { + // Extract a possible number from the string passed in (this strips leading characters that + // could not be the start of a phone number.) + String number = extractPossibleNumber(numberToParse); + if (!isViablePhoneNumber(number)) { + throw new NumberParseException(NumberParseException.ErrorType.NOT_A_NUMBER, + "The string supplied did not seem to be a phone number."); + } + + PhoneNumber.Builder phoneNumber = PhoneNumber.newBuilder(); + StringBuffer nationalNumber = new StringBuffer(number); + // Attempt to parse extension first, since it doesn't require country-specific data and we want + // to have the non-normalised number here. + String extension = maybeStripExtension(nationalNumber); + if (!extension.equals("")) { + phoneNumber.setExtension(extension); + } + + PhoneMetadata countryMetadata = getMetadataForRegion(defaultCountry); + // Check to see if the number is given in international format so we know whether this number is + // from the default country or not. + StringBuffer normalizedNationalNumber = new StringBuffer(); + // been created, and just remove the prefix, rather than taking in a string and then outputting + // a string buffer. + int countryCode = maybeExtractCountryCode(nationalNumber.toString(), countryMetadata, + normalizedNationalNumber); + if (countryCode != 0) { + String phoneNumberRegion = getRegionCodeForCountryCode(countryCode); + countryMetadata = getMetadataForRegion(phoneNumberRegion); + } else { + // If no extracted country code, use the region supplied instead. The national number is just + // the normalized version of the number we were given to parse. + normalize(nationalNumber); + normalizedNationalNumber.append(nationalNumber); + if (defaultCountry != null) { + countryCode = countryMetadata.getCountryCode(); + } + } + if (normalizedNationalNumber.length() < MIN_LENGTH_FOR_NSN) { + throw new NumberParseException(NumberParseException.ErrorType.TOO_SHORT_NSN, + "The string supplied is too short to be a phone number."); + } + if (countryMetadata != null) { + Pattern validNumberPattern = + Pattern.compile(countryMetadata.getGeneralDesc().getNationalNumberPattern()); + maybeStripNationalPrefix(normalizedNationalNumber, + countryMetadata.getNationalPrefixForParsing(), + countryMetadata.getNationalPrefixTransformRule(), + validNumberPattern); + } + phoneNumber.setCountryCode(countryCode); + // The ItalianLeadingZero is valid only for numbers from IT and CI. + if ((countryCode == 39 || countryCode == 225) && normalizedNationalNumber.charAt(0) == '0') { + phoneNumber.setItalianLeadingZero(true); + } + int lengthOfNationalNumber = normalizedNationalNumber.length(); + if (lengthOfNationalNumber < MIN_LENGTH_FOR_NSN) { + throw new NumberParseException(NumberParseException.ErrorType.TOO_SHORT_NSN, + "The string supplied is too short to be a " + + "phone number."); + } + if (lengthOfNationalNumber > MAX_LENGTH_FOR_NSN) { + throw new NumberParseException(NumberParseException.ErrorType.TOO_LONG, + "The string supplied is too long to be a " + + "phone number."); + } + phoneNumber.setNationalNumber(Long.parseLong(normalizedNationalNumber.toString())); + return phoneNumber.build(); + } + + /** + * Takes two phone numbers and compares them for equality. + * + * Returns EXACT_MATCH if the country code, NSN, presence of a leading zero for Italian numbers + * and any extension present are the same. + * Returns NSN_MATCH if either or both has no country specified, and the NSNs and extensions are + * the same. + * Returns SHORT_NSN_MATCH if either or both has no country specified, or the country specified + * is the same, and one NSN could be a shorter version of the other number. This includes the case + * where one has an extension specified, and the other does not. + * Returns NO_MATCH otherwise. + * For example, the numbers +1 345 657 1234 and 657 1234 are a SHORT_NSN_MATCH. + * The numbers +1 345 657 1234 and 345 657 are a NO_MATCH. + * + * @param firstNumberIn first number to compare + * @param secondNumberIn second number to compare + * + * @return NO_MATCH, SHORT_NSN_MATCH, NSN_MATCH or EXACT_MATCH depending on the level of equality + * of the two numbers, described in the method definition. + */ + public MatchType isNumberMatch(PhoneNumber firstNumberIn, PhoneNumber secondNumberIn) { + // Make copies of the phone number so that the numbers passed in are not edited. + PhoneNumber.Builder firstNumber = PhoneNumber.newBuilder(); + firstNumber.mergeFrom(firstNumberIn); + PhoneNumber.Builder secondNumber = PhoneNumber.newBuilder(); + secondNumber.mergeFrom(secondNumberIn); + // First clear any empty-string extensions so that we can use the proto-buffer equality method. + + if (firstNumber.hasExtension() && + firstNumber.getExtension().equals("")) { + firstNumber.clearExtension(); + } + if (secondNumber.hasExtension() && + secondNumber.getExtension().equals("")) { + secondNumber.clearExtension(); + } + + PhoneNumber number1 = firstNumber.build(); + PhoneNumber number2 = secondNumber.build(); + + // Early exit if both had extensions and these are different. + if (number1.hasExtension() && number2.hasExtension() && + !number1.getExtension().equals(number2.getExtension())) { + return MatchType.NO_MATCH; + } + int firstNumberCountryCode = number1.getCountryCode(); + int secondNumberCountryCode = number2.getCountryCode(); + // Both had country code specified. + if (firstNumberCountryCode != 0 && secondNumberCountryCode != 0) { + if (number1.equals(number2)) { + return MatchType.EXACT_MATCH; + } else if (firstNumberCountryCode == secondNumberCountryCode) { + // A SHORT_NSN_MATCH occurs if there is a difference because of the presence or absence of + // an 'Italian leading zero', the presence or absence of an extension, or one NSN being a + // shorter variant of the other. + String firstNumberNationalNumber = String.valueOf(number1.getNationalNumber()); + String secondNumberNationalNumber = String.valueOf(number2.getNationalNumber()); + // Note that endsWith returns true if the numbers are equal. + if (firstNumberNationalNumber.endsWith(secondNumberNationalNumber) || + secondNumberNationalNumber.endsWith(firstNumberNationalNumber)) { + return MatchType.SHORT_NSN_MATCH; + } + } + // This is not a match. + return MatchType.NO_MATCH; + } + // Checks cases where one or both country codes were not specified. To make equality checks + // easier, we first set the country codes to be equal. + PhoneNumber newNumber = + PhoneNumber.newBuilder(number1).setCountryCode(secondNumberCountryCode).build(); + // If all else was the same, then this is an NSN_MATCH. + if (newNumber.equals(number2)) { + return MatchType.NSN_MATCH; + } + String firstNumberNationalNumber = String.valueOf(newNumber.getNationalNumber()); + String secondNumberNationalNumber = String.valueOf(number2.getNationalNumber()); + // Note that endsWith returns true if the numbers are equal. + if (firstNumberNationalNumber.endsWith(secondNumberNationalNumber) || + secondNumberNationalNumber.endsWith(firstNumberNationalNumber)) { + return MatchType.SHORT_NSN_MATCH; + } + return MatchType.NO_MATCH; + } + + /** + * Takes two phone numbers as strings and compares them for equality. This is a convenience + * wrapper for isNumberMatch(PhoneNumber firstNumber, PhoneNumber secondNumber). No default region + * is known. + * + * @param firstNumber first number to compare. Can contain formatting, and can have country code + * specified with + at the start. + * @param secondNumber second number to compare. Can contain formatting, and can have country + * code specified with + at the start. + * @return NO_MATCH, SHORT_NSN_MATCH, NSN_MATCH, EXACT_MATCH. See isNumberMatch(PhoneNumber + * firstNumber, PhoneNumber secondNumber) for more details. + * @throws NumberParseException if either number is not considered to be a viable phone + * number + */ + public MatchType isNumberMatch(String firstNumber, String secondNumber) + throws NumberParseException { + return isNumberMatch(parseHelper(firstNumber, null), parseHelper(secondNumber, null)); + } + + /** + * Takes two phone numbers and compares them for equality. This is a convenience wrapper for + * isNumberMatch(PhoneNumber firstNumber, PhoneNumber secondNumber). No default region is known. + * + * @param firstNumber first number to compare in proto buffer format. + * @param secondNumber second number to compare. Can contain formatting, and can have country + * code specified with + at the start. + * @return NO_MATCH, SHORT_NSN_MATCH, NSN_MATCH, EXACT_MATCH. See isNumberMatch(PhoneNumber + * firstNumber, PhoneNumber secondNumber) for more details. + * @throws NumberParseException if the second number is not considered to be a viable phone + * number + */ + public MatchType isNumberMatch(PhoneNumber firstNumber, String secondNumber) + throws NumberParseException { + return isNumberMatch(firstNumber, parseHelper(secondNumber, null)); + } +} diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java b/java/src/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java new file mode 100644 index 000000000..af962d011 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java @@ -0,0 +1,1449 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.i18n.phonenumbers; + +import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; +import junit.framework.TestCase; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * Unit tests for PhoneNumberUtil.java + * + * Note that these tests use the metadata contained in the file specified by TEST_META_DATA_FILE, + * not the normal metadata file, so should not be used for regression test purposes - these tests + * are illustrative only and test functionality. + * + * @author Shaopeng Jia + * @author Lara Rennie + */ +public class PhoneNumberUtilTest extends TestCase { + private PhoneNumberUtil phoneUtil; + private static final String TEST_META_DATA_FILE = + "/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting"; + + public PhoneNumberUtilTest() { + PhoneNumberUtil.resetInstance(); + InputStream in = PhoneNumberUtilTest.class.getResourceAsStream(TEST_META_DATA_FILE); + phoneUtil = PhoneNumberUtil.getInstance(in); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testGetInstanceLoadUSMetadata() { + PhoneMetadata metadata = phoneUtil.getPhoneMetadata("US"); + assertEquals("US", metadata.getId()); + assertEquals(1, metadata.getCountryCode()); + assertEquals("011", metadata.getInternationalPrefix()); + assertFalse(metadata.hasNationalPrefix()); + assertEquals(2, metadata.getNumberFormatCount()); + assertEquals("(\\d{3})(\\d{3})(\\d{4})", + metadata.getNumberFormat(0).getPattern()); + assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat()); + assertEquals("[13-9]\\d{9}|2[0-35-9]\\d{8}", + metadata.getGeneralDesc().getNationalNumberPattern()); + assertEquals("\\d{7,10}", metadata.getGeneralDesc().getPossibleNumberPattern()); + assertEquals(metadata.getGeneralDesc(), metadata.getFixedLine()); + assertEquals("\\d{10}", metadata.getTollFree().getPossibleNumberPattern()); + assertEquals("900\\d{7}", metadata.getPremiumRate().getNationalNumberPattern()); + // No shared-cost data is available, so it should be initialised to "NA". + assertEquals("NA", metadata.getSharedCost().getNationalNumberPattern()); + assertEquals("NA", metadata.getSharedCost().getPossibleNumberPattern()); + } + + public void testGetInstanceLoadDEMetadata() { + PhoneMetadata metadata = phoneUtil.getPhoneMetadata("DE"); + assertEquals("DE", metadata.getId()); + assertEquals(49, metadata.getCountryCode()); + assertEquals("00", metadata.getInternationalPrefix()); + assertEquals("0", metadata.getNationalPrefix()); + assertEquals(6, metadata.getNumberFormatCount()); + assertEquals("9009", metadata.getNumberFormat(5).getLeadingDigits()); + assertEquals("(\\d{3})(\\d{4})(\\d{4})", + metadata.getNumberFormat(5).getPattern()); + assertEquals("$1 $2 $3", metadata.getNumberFormat(5).getFormat()); + assertEquals("(?:[24-6]\\d{2}|3[03-9]\\d|[789](?:[1-9]\\d|0[2-9]))\\d{3,8}", + metadata.getFixedLine().getNationalNumberPattern()); + assertEquals("\\d{2,14}", metadata.getFixedLine().getPossibleNumberPattern()); + assertEquals("30123456", metadata.getFixedLine().getExampleNumber()); + assertEquals("\\d{10}", metadata.getTollFree().getPossibleNumberPattern()); + assertEquals("900([135]\\d{6}|9\\d{7})", metadata.getPremiumRate().getNationalNumberPattern()); + } + + public void testGetInstanceLoadARMetadata() { + PhoneMetadata metadata = phoneUtil.getPhoneMetadata("AR"); + assertEquals("AR", metadata.getId()); + assertEquals(54, metadata.getCountryCode()); + assertEquals("00", metadata.getInternationalPrefix()); + assertEquals("0", metadata.getNationalPrefix()); + assertEquals("0(?:(11|343|3715)15)?", metadata.getNationalPrefixForParsing()); + assertEquals("9$1", metadata.getNationalPrefixTransformRule()); + assertEquals("9(\\d{4})(\\d{2})(\\d{4})", + metadata.getNumberFormat(3).getPattern()); + assertEquals("$1 15 $2-$3", metadata.getNumberFormat(3).getFormat()); + assertEquals("(9)(\\d{4})(\\d{2})(\\d{4})", + metadata.getIntlNumberFormat(3).getPattern()); + assertEquals("$1 $2 $3 $4", metadata.getIntlNumberFormat(3).getFormat()); + } + + public void testGetExampleNumber() { + PhoneNumber deNumber = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(30123456).build(); + assertEquals(deNumber, phoneUtil.getExampleNumber("DE")); + + assertEquals(deNumber, + phoneUtil.getExampleNumberForType("DE", + PhoneNumberUtil.PhoneNumberType.FIXED_LINE)); + assertEquals(null, + phoneUtil.getExampleNumberForType("DE", + PhoneNumberUtil.PhoneNumberType.MOBILE)); + // For the US, the example number is placed under general description, and hence should be used + // for both fixed line and mobile, so neither of these should return null. + assertNotNull(phoneUtil.getExampleNumberForType("US", + PhoneNumberUtil.PhoneNumberType.FIXED_LINE)); + assertNotNull(phoneUtil.getExampleNumberForType("US", + PhoneNumberUtil.PhoneNumberType.MOBILE)); + } + + public void testNormaliseRemovePunctuation() { + String inputNumber = "034-56&+#234"; + String expectedOutput = "03456234"; + assertEquals("Conversion did not correctly remove punctuation", + expectedOutput, + PhoneNumberUtil.normalize(inputNumber)); + } + + public void testNormaliseReplaceAlphaCharacters() { + String inputNumber = "034-I-am-HUNGRY"; + String expectedOutput = "034426486479"; + assertEquals("Conversion did not correctly replace alpha characters", + expectedOutput, + PhoneNumberUtil.normalize(inputNumber)); + } + + public void testNormaliseOtherDigits() { + String inputNumber = "\uFF125\u0665"; + String expectedOutput = "255"; + assertEquals("Conversion did not correctly replace non-latin digits", + expectedOutput, + PhoneNumberUtil.normalize(inputNumber)); + } + + public void testNormaliseStripAlphaCharacters() { + String inputNumber = "034-56&+a#234"; + String expectedOutput = "03456234"; + assertEquals("Conversion did not correctly remove alpha character", + expectedOutput, + PhoneNumberUtil.normalizeDigitsOnly(inputNumber)); + } + + public void testFormatUSNumber() { + PhoneNumber usNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502530000L).build(); + assertEquals("650 253 0000", phoneUtil.format(usNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+1 650 253 0000", + phoneUtil.format(usNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber usNumber2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(8002530000L).build(); + assertEquals("800 253 0000", phoneUtil.format(usNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+1 800 253 0000", + phoneUtil.format(usNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber usNumber3 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(9002530000L).build(); + assertEquals("900 253 0000", phoneUtil.format(usNumber3, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+1 900 253 0000", + phoneUtil.format(usNumber3, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + } + + public void testFormatBSNumber() { + PhoneNumber bsNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2421234567L).build(); + assertEquals("242 123 4567", phoneUtil.format(bsNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+1 242 123 4567", + phoneUtil.format(bsNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber bsNumber2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(8002530000L).build(); + assertEquals("800 253 0000", phoneUtil.format(bsNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+1 800 253 0000", + phoneUtil.format(bsNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber bsNumber3 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(9002530000L).build(); + assertEquals("900 253 0000", phoneUtil.format(bsNumber3, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+1 900 253 0000", + phoneUtil.format(bsNumber3, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + } + + public void testFormatGBNumber() { + PhoneNumber gbNumber1 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(2087389353L).build(); + assertEquals("(020) 8738 9353", phoneUtil.format(gbNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+44 20 8738 9353", + phoneUtil.format(gbNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber gbNumber2 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(7912345678L).build(); + assertEquals("(07912) 345 678", phoneUtil.format(gbNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+44 7912 345 678", + phoneUtil.format(gbNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + } + + public void testFormatDENumber() { + PhoneNumber deNumber1 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(301234L).build(); + assertEquals("030 1234", phoneUtil.format(deNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+49 30 1234", + phoneUtil.format(deNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber deNumber2 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(291123L).build(); + assertEquals("0291 123", phoneUtil.format(deNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+49 291 123", + phoneUtil.format(deNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber deNumber3 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(29112345678L).build(); + assertEquals("0291 12345678", phoneUtil.format(deNumber3, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+49 291 12345678", + phoneUtil.format(deNumber3, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + + PhoneNumber deNumber4 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(9123123L).build(); + assertEquals("09123 123", phoneUtil.format(deNumber4, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+49 9123 123", + phoneUtil.format(deNumber4, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + PhoneNumber deNumber5 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(1234L).build(); + // Note this number is correctly formatted without national prefix. Most of the numbers that + // are treated as invalid numbers by the library are short numbers, and they are usually not + // dialed with national prefix. + assertEquals("1234", phoneUtil.format(deNumber5, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+49 1234", + phoneUtil.format(deNumber5, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + } + + public void testFormatITNumber() { + PhoneNumber itNumber1 = + PhoneNumber.newBuilder() + .setCountryCode(39).setNationalNumber(236618300L).setItalianLeadingZero(true).build(); + assertEquals("02 3661 8300", phoneUtil.format(itNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+39 02 3661 8300", + phoneUtil.format(itNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+390236618300", + phoneUtil.format(itNumber1, + PhoneNumberUtil.PhoneNumberFormat.E164)); + + PhoneNumber itNumber2 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(345678901L).build(); + assertEquals("345 678 901", phoneUtil.format(itNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+39 345 678 901", + phoneUtil.format(itNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+39345678901", + phoneUtil.format(itNumber2, + PhoneNumberUtil.PhoneNumberFormat.E164)); + } + + public void testFormatAUNumber() { + PhoneNumber auNumber1 = + PhoneNumber.newBuilder().setCountryCode(61).setNationalNumber(236618300L).build(); + assertEquals("02 3661 8300", phoneUtil.format(auNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+61 2 3661 8300", + phoneUtil.format(auNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+61236618300", + phoneUtil.format(auNumber1, + PhoneNumberUtil.PhoneNumberFormat.E164)); + + PhoneNumber auNumber2 = + PhoneNumber.newBuilder().setCountryCode(61).setNationalNumber(1800123456L).build(); + assertEquals("1800 123 456", phoneUtil.format(auNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+61 1800 123 456", + phoneUtil.format(auNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+611800123456", + phoneUtil.format(auNumber2, + PhoneNumberUtil.PhoneNumberFormat.E164)); + } + + public void testFormatARNumber() { + PhoneNumber arNumber1 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(1187654321L).build(); + assertEquals("011 8765-4321", phoneUtil.format(arNumber1, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+54 11 8765-4321", + phoneUtil.format(arNumber1, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+541187654321", + phoneUtil.format(arNumber1, + PhoneNumberUtil.PhoneNumberFormat.E164)); + + PhoneNumber arNumber2 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(91187654321L).build(); + assertEquals("011 15 8765-4321", phoneUtil.format(arNumber2, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals("+54 9 11 8765 4321", + phoneUtil.format(arNumber2, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+5491187654321", + phoneUtil.format(arNumber2, + PhoneNumberUtil.PhoneNumberFormat.E164)); + } + + public void testFormatOutOfCountryCallingNumber() { + PhoneNumber usNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(9002530000L).build(); + assertEquals("00 1 900 253 0000", + phoneUtil.formatOutOfCountryCallingNumber(usNumber1, "DE")); + + PhoneNumber usNumber2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502530000L).build(); + assertEquals("1 650 253 0000", + phoneUtil.formatOutOfCountryCallingNumber(usNumber2, "BS")); + + assertEquals("0~0 1 650 253 0000", + phoneUtil.formatOutOfCountryCallingNumber(usNumber2, "PL")); + + PhoneNumber gbNumber = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(7912345678L).build(); + assertEquals("011 44 7912 345 678", + phoneUtil.formatOutOfCountryCallingNumber(gbNumber, "US")); + + PhoneNumber deNumber = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(1234L).build(); + assertEquals("00 49 1234", + phoneUtil.formatOutOfCountryCallingNumber(deNumber, "GB")); + // Note this number is correctly formatted without national prefix. Most of the numbers that + // are treated as invalid numbers by the library are short numbers, and they are usually not + // dialed with national prefix. + assertEquals("1234", + phoneUtil.formatOutOfCountryCallingNumber(deNumber, "DE")); + + PhoneNumber itNumber = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(236618300L) + .setItalianLeadingZero(true).build(); + assertEquals("011 39 02 3661 8300", + phoneUtil.formatOutOfCountryCallingNumber(itNumber, "US")); + assertEquals("02 3661 8300", + phoneUtil.formatOutOfCountryCallingNumber(itNumber, "IT")); + assertEquals("+39 02 3661 8300", + phoneUtil.formatOutOfCountryCallingNumber(itNumber, "SG")); + + PhoneNumber sgNumber = + PhoneNumber.newBuilder().setCountryCode(65).setNationalNumber(94777892L).build(); + assertEquals("9477 7892", + phoneUtil.formatOutOfCountryCallingNumber(sgNumber, "SG")); + + PhoneNumber arNumber1 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(91187654321L).build(); + assertEquals("011 54 9 11 8765 4321", + phoneUtil.formatOutOfCountryCallingNumber(arNumber1, "US")); + + PhoneNumber arNumber2 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(91187654321L) + .setExtension("1234").build(); + assertEquals("011 54 9 11 8765 4321 ext. 1234", + phoneUtil.formatOutOfCountryCallingNumber(arNumber2, "US")); + assertEquals("0011 54 9 11 8765 4321 ext. 1234", + phoneUtil.formatOutOfCountryCallingNumber(arNumber2, "AU")); + assertEquals("011 15 8765-4321 ext. 1234", + phoneUtil.formatOutOfCountryCallingNumber(arNumber2, "AR")); + } + + public void testFormatOutOfCountryWithPreferredIntlPrefix() { + PhoneNumber.Builder itNumber = PhoneNumber.newBuilder(); + itNumber.setCountryCode(39).setNationalNumber(236618300L).setItalianLeadingZero(true); + // This should use 0011, since that is the preferred international prefix (both 0011 and 0012 + // are accepted as possible international prefixes in our test metadta.) + assertEquals("0011 39 02 3661 8300", + phoneUtil.formatOutOfCountryCallingNumber(itNumber.build(), "AU")); + } + + public void testFormatByPattern() { + PhoneNumber usNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502530000L).build(); + NumberFormat newNumFormat1 = + NumberFormat.newBuilder().setPattern("(\\d{3})(\\d{3})(\\d{4})") + .setFormat("($1) $2-$3").build(); + List newNumberFormats = new ArrayList(); + newNumberFormats.add(newNumFormat1); + + assertEquals("(650) 253-0000", + phoneUtil.formatByPattern(usNumber, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL, + newNumberFormats)); + assertEquals("+1 (650) 253-0000", + phoneUtil.formatByPattern(usNumber, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL, + newNumberFormats)); + + PhoneNumber itNumber = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(236618300L) + .setItalianLeadingZero(true).build(); + NumberFormat newNumFormat2 = + NumberFormat.newBuilder().setPattern("(\\d{2})(\\d{5})(\\d{3})") + .setFormat("$1-$2 $3").build(); + newNumberFormats.set(0, newNumFormat2); + + assertEquals("02-36618 300", + phoneUtil.formatByPattern(itNumber, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL, + newNumberFormats)); + assertEquals("+39 02-36618 300", + phoneUtil.formatByPattern(itNumber, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL, + newNumberFormats)); + + PhoneNumber gbNumber = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(2012345678L).build(); + + NumberFormat newNumFormat3 = + NumberFormat.newBuilder().setNationalPrefixFormattingRule("$NP$FG") + .setPattern("(\\d{2})(\\d{4})(\\d{4})").setFormat("$1 $2 $3").build(); + newNumberFormats.set(0, newNumFormat3); + assertEquals("020 1234 5678", + phoneUtil.formatByPattern(gbNumber, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL, + newNumberFormats)); + + NumberFormat newNumFormat4 = + NumberFormat.newBuilder(newNumFormat3).setNationalPrefixFormattingRule("($NP$FG)").build(); + newNumberFormats.set(0, newNumFormat4); + assertEquals("(020) 1234 5678", + phoneUtil.formatByPattern(gbNumber, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL, + newNumberFormats)); + NumberFormat newNumFormat5 = + NumberFormat.newBuilder(newNumFormat4).setNationalPrefixFormattingRule("").build(); + newNumberFormats.set(0, newNumFormat5); + assertEquals("20 1234 5678", + phoneUtil.formatByPattern(gbNumber, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL, + newNumberFormats)); + + NumberFormat newNumFormat6 = + NumberFormat.newBuilder(newNumFormat5).setNationalPrefixFormattingRule("").build(); + newNumberFormats.set(0, newNumFormat6); + assertEquals("+44 20 1234 5678", + phoneUtil.formatByPattern(gbNumber, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL, + newNumberFormats)); + } + + public void testFormatE164Number() { + PhoneNumber.Builder usNumber = PhoneNumber.newBuilder(); + usNumber.setCountryCode(1).setNationalNumber(6502530000L); + assertEquals("+16502530000", phoneUtil.format(usNumber.build(), + PhoneNumberUtil.PhoneNumberFormat.E164)); + PhoneNumber.Builder deNumber = PhoneNumber.newBuilder(); + deNumber.setCountryCode(49).setNationalNumber(301234L); + assertEquals("+49301234", phoneUtil.format(deNumber.build(), + PhoneNumberUtil.PhoneNumberFormat.E164)); + } + + public void testFormatNumberWithExtension() { + PhoneNumber.Builder nzNumber = PhoneNumber.newBuilder(); + nzNumber.setCountryCode(64).setNationalNumber(33316005L).setExtension("1234"); + // Uses default extension prefix: + assertEquals("03-331 6005 ext. 1234", + phoneUtil.format(nzNumber.build(), + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + // Extension prefix overridden in the territory information for the US: + PhoneNumber.Builder usNumber = PhoneNumber.newBuilder(); + usNumber.setCountryCode(1).setNationalNumber(6502530000L).setExtension("4567"); + assertEquals("650 253 0000 extn. 4567", + phoneUtil.format(usNumber.build(), + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + } + + public void testIsPremiumRate() { + PhoneNumber premiumRateNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(9004433030L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.PREMIUM_RATE, + phoneUtil.getNumberType(premiumRateNumber1)); + + PhoneNumber premiumRateNumber2 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(892123L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.PREMIUM_RATE, + phoneUtil.getNumberType(premiumRateNumber2)); + + PhoneNumber premiumRateNumber3 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(9187654321L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.PREMIUM_RATE, + phoneUtil.getNumberType(premiumRateNumber3)); + + PhoneNumber premiumRateNumber4 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(9001654321L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.PREMIUM_RATE, + phoneUtil.getNumberType(premiumRateNumber4)); + + PhoneNumber premiumRateNumber5 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(90091234567L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.PREMIUM_RATE, + phoneUtil.getNumberType(premiumRateNumber5)); + } + + public void testIsTollFree() { + PhoneNumber tollFreeNumber1 + = PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(8881234567L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.TOLL_FREE, + phoneUtil.getNumberType(tollFreeNumber1)); + + PhoneNumber tollFreeNumber2 + = PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(803123L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.TOLL_FREE, + phoneUtil.getNumberType(tollFreeNumber2)); + + PhoneNumber tollFreeNumber3 + = PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(8012345678L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.TOLL_FREE, + phoneUtil.getNumberType(tollFreeNumber3)); + + PhoneNumber tollFreeNumber4 + = PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(8001234567L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.TOLL_FREE, + phoneUtil.getNumberType(tollFreeNumber4)); + } + + public void testIsMobile() { + PhoneNumber mobileNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2423570000L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.MOBILE, + phoneUtil.getNumberType(mobileNumber1)); + + PhoneNumber mobileNumber2 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(312345678L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.MOBILE, + phoneUtil.getNumberType(mobileNumber2)); + + PhoneNumber mobileNumber3 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(7912345678L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.MOBILE, + phoneUtil.getNumberType(mobileNumber3)); + + PhoneNumber mobileNumber4 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(15123456789L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.MOBILE, + phoneUtil.getNumberType(mobileNumber4)); + + PhoneNumber mobileNumber5 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(91187654321L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.MOBILE, + phoneUtil.getNumberType(mobileNumber5)); + } + + public void testIsFixedLine() { + // A Bahama fixed-line number + PhoneNumber fixedLineNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2423651234L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE, + phoneUtil.getNumberType(fixedLineNumber1)); + + // An Italian fixed-line number + PhoneNumber fixedLineNumber2 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(236618300L) + .setItalianLeadingZero(true).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE, + phoneUtil.getNumberType(fixedLineNumber2)); + + PhoneNumber fixedLineNumber3 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(2012345678L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE, + phoneUtil.getNumberType(fixedLineNumber3)); + + PhoneNumber fixedLineNumber4 = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(301234L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE, + phoneUtil.getNumberType(fixedLineNumber4)); + } + + public void testIsFixedLineAndMobile() { + PhoneNumber fixedLineAndMobileNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502531111L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE, + phoneUtil.getNumberType(fixedLineAndMobileNumber1)); + + PhoneNumber fixedLineAndMobileNumber2 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(1987654321L).build(); + assertEquals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE, + phoneUtil.getNumberType(fixedLineAndMobileNumber2)); + } + + public void testIsSharedCost() { + PhoneNumber.Builder gbNumber = PhoneNumber.newBuilder(); + gbNumber.setCountryCode(44).setNationalNumber(8431231234L); + assertEquals(PhoneNumberUtil.PhoneNumberType.SHARED_COST, + phoneUtil.getNumberType(gbNumber.build())); + } + + public void testIsVoip() { + PhoneNumber.Builder gbNumber = PhoneNumber.newBuilder(); + gbNumber.setCountryCode(44).setNationalNumber(5631231234L); + assertEquals(PhoneNumberUtil.PhoneNumberType.VOIP, phoneUtil.getNumberType(gbNumber.build())); + } + + public void testIsPersonalNumber() { + PhoneNumber.Builder gbNumber = PhoneNumber.newBuilder(); + gbNumber.setCountryCode(44).setNationalNumber(7031231234L); + assertEquals(PhoneNumberUtil.PhoneNumberType.PERSONAL_NUMBER, + phoneUtil.getNumberType(gbNumber.build())); + } + + public void testIsUnknown() { + PhoneNumber.Builder unknownNumber = PhoneNumber.newBuilder(); + unknownNumber.setCountryCode(1).setNationalNumber(65025311111L); + assertEquals(PhoneNumberUtil.PhoneNumberType.UNKNOWN, + phoneUtil.getNumberType(unknownNumber.build())); + } + + public void testIsValidNumber() { + PhoneNumber.Builder usNumber = PhoneNumber.newBuilder(); + usNumber.setCountryCode(1).setNationalNumber(6502530000L); + assertTrue(phoneUtil.isValidNumber(usNumber.build())); + + PhoneNumber.Builder itNumber = PhoneNumber.newBuilder(); + itNumber.setCountryCode(39).setNationalNumber(236618300L).setItalianLeadingZero(true); + assertTrue(phoneUtil.isValidNumber(itNumber.build())); + + PhoneNumber.Builder gbNumber = PhoneNumber.newBuilder(); + gbNumber.setCountryCode(44).setNationalNumber(7912345678L); + assertTrue(phoneUtil.isValidNumber(gbNumber.build())); + + PhoneNumber.Builder nzNumber = PhoneNumber.newBuilder(); + nzNumber.setCountryCode(64).setNationalNumber(21387835L); + assertTrue(phoneUtil.isValidNumber(nzNumber.build())); + } + + public void testIsValidForRegion() { + // This number is valid for the Bahamas, but is not a valid US number. + PhoneNumber bsNumber1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2423232345L).build(); + assertTrue(phoneUtil.isValidNumber(bsNumber1)); + assertTrue(phoneUtil.isValidNumberForRegion(bsNumber1, "BS")); + assertFalse(phoneUtil.isValidNumberForRegion(bsNumber1, "US")); + PhoneNumber bsNumber2 = + PhoneNumber.newBuilder(bsNumber1).setNationalNumber(2421232345L).build(); + // This number is no longer valid. + assertFalse(phoneUtil.isValidNumber(bsNumber2)); + } + + public void testIsNotValidNumber() { + PhoneNumber.Builder usNumber = PhoneNumber.newBuilder(); + usNumber.setCountryCode(1).setNationalNumber(2530000L); + assertFalse(phoneUtil.isValidNumber(usNumber.build())); + + PhoneNumber.Builder itNumber = PhoneNumber.newBuilder(); + itNumber.setCountryCode(39).setNationalNumber(23661830000L).setItalianLeadingZero(true); + assertFalse(phoneUtil.isValidNumber(itNumber.build())); + + PhoneNumber.Builder gbNumber = PhoneNumber.newBuilder(); + gbNumber.setCountryCode(44).setNationalNumber(791234567L); + assertFalse(phoneUtil.isValidNumber(gbNumber.build())); + + PhoneNumber.Builder deNumber = PhoneNumber.newBuilder(); + deNumber.setCountryCode(49).setNationalNumber(1234L); + assertFalse(phoneUtil.isValidNumber(deNumber.build())); + + PhoneNumber.Builder nzNumber = PhoneNumber.newBuilder(); + nzNumber.setCountryCode(64).setNationalNumber(3316005L); + assertFalse(phoneUtil.isValidNumber(nzNumber.build())); + } + + public void testGetRegionCodeForCountryCode() { + assertEquals("US", phoneUtil.getRegionCodeForCountryCode(1)); + assertEquals("GB", phoneUtil.getRegionCodeForCountryCode(44)); + assertEquals("DE", phoneUtil.getRegionCodeForCountryCode(49)); + } + + public void testGetRegionCodeForNumber() { + PhoneNumber.Builder bsNumber = PhoneNumber.newBuilder(); + bsNumber.setCountryCode(1).setNationalNumber(2423027000L); + assertEquals("BS", phoneUtil.getRegionCodeForNumber(bsNumber.build())); + + PhoneNumber.Builder usNumber = PhoneNumber.newBuilder(); + usNumber.setCountryCode(1).setNationalNumber(6502530000L); + assertEquals("US", phoneUtil.getRegionCodeForNumber(usNumber.build())); + + PhoneNumber.Builder gbNumber = PhoneNumber.newBuilder(); + gbNumber.setCountryCode(44).setNationalNumber(7912345678L); + assertEquals("GB", phoneUtil.getRegionCodeForNumber(gbNumber.build())); + } + + public void testGetCountryCodeForRegion() { + assertEquals(1, phoneUtil.getCountryCodeForRegion("US")); + assertEquals(64, phoneUtil.getCountryCodeForRegion("NZ")); + } + + public void testGetNANPACountries() { + Set nanpaCountries = phoneUtil.getNANPACountries(); + assertEquals(2, nanpaCountries.size()); + assertTrue(nanpaCountries.contains("US")); + assertTrue(nanpaCountries.contains("BS")); + } + + public void testIsPossibleNumber() { + PhoneNumber number1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502530000L).build(); + assertTrue(phoneUtil.isPossibleNumber(number1)); + + PhoneNumber number2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2530000L).build(); + assertTrue(phoneUtil.isPossibleNumber(number2)); + + PhoneNumber number3 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(2070313000L).build(); + assertTrue(phoneUtil.isPossibleNumber(number3)); + + assertTrue(phoneUtil.isPossibleNumber("+1 650 253 0000", "US")); + assertTrue(phoneUtil.isPossibleNumber("+1 650 GOO OGLE", "US")); + assertTrue(phoneUtil.isPossibleNumber("(650) 253-0000", "US")); + assertTrue(phoneUtil.isPossibleNumber("253-0000", "US")); + assertTrue(phoneUtil.isPossibleNumber("+1 650 253 0000", "GB")); + assertTrue(phoneUtil.isPossibleNumber("+44 20 7031 3000", "GB")); + assertTrue(phoneUtil.isPossibleNumber("(020) 7031 3000", "GB")); + assertTrue(phoneUtil.isPossibleNumber("7031 3000", "GB")); + assertTrue(phoneUtil.isPossibleNumber("3331 6005", "NZ")); + } + + + public void testIsPossibleNumberWithReason() { + // FYI, national numbers for country code +1 that are within 7 to 10 digits are possible. + PhoneNumber number1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502530000L).build(); + assertEquals(PhoneNumberUtil.ValidationResult.IS_POSSIBLE, + phoneUtil.isPossibleNumberWithReason(number1)); + + PhoneNumber number2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2530000L).build(); + assertEquals(PhoneNumberUtil.ValidationResult.IS_POSSIBLE, + phoneUtil.isPossibleNumberWithReason(number2)); + + PhoneNumber number3 = + PhoneNumber.newBuilder().setCountryCode(0).setNationalNumber(2530000L).build(); + assertEquals(PhoneNumberUtil.ValidationResult.INVALID_COUNTRY_CODE, + phoneUtil.isPossibleNumberWithReason(number3)); + + PhoneNumber number4 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(253000L).build(); + assertEquals(PhoneNumberUtil.ValidationResult.TOO_SHORT, + phoneUtil.isPossibleNumberWithReason(number4)); + + PhoneNumber number5 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(65025300000L).build(); + assertEquals(PhoneNumberUtil.ValidationResult.TOO_LONG, + phoneUtil.isPossibleNumberWithReason(number5)); + } + + public void testIsNotPossibleNumber() { + PhoneNumber number1 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(65025300000L).build(); + assertFalse(phoneUtil.isPossibleNumber(number1)); + + PhoneNumber number2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(253000L).build(); + assertFalse(phoneUtil.isPossibleNumber(number2)); + + PhoneNumber number3 = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(300L).build(); + assertFalse(phoneUtil.isPossibleNumber(number3)); + + assertFalse(phoneUtil.isPossibleNumber("+1 650 253 00000", "US")); + assertFalse(phoneUtil.isPossibleNumber("(650) 253-00000", "US")); + assertFalse(phoneUtil.isPossibleNumber("I want a Pizza", "US")); + assertFalse(phoneUtil.isPossibleNumber("253-000", "US")); + assertFalse(phoneUtil.isPossibleNumber("1 3000", "GB")); + assertFalse(phoneUtil.isPossibleNumber("+44 300", "GB")); + } + + public void testIsViablePhoneNumber() { + // Only one or two digits before strange non-possible punctuation. + assertFalse(PhoneNumberUtil.isViablePhoneNumber("12. March")); + assertFalse(PhoneNumberUtil.isViablePhoneNumber("1+1+1")); + assertFalse(PhoneNumberUtil.isViablePhoneNumber("80+0")); + assertFalse(PhoneNumberUtil.isViablePhoneNumber("00")); + // Three digits is viable. + assertTrue(PhoneNumberUtil.isViablePhoneNumber("111")); + // Alpha numbers. + assertTrue(PhoneNumberUtil.isViablePhoneNumber("0800-4-pizza")); + assertTrue(PhoneNumberUtil.isViablePhoneNumber("0800-4-PIZZA")); + // Only one or two digits before possible punctuation followed by more digits. + assertTrue(PhoneNumberUtil.isViablePhoneNumber("1\u300034")); + assertFalse(PhoneNumberUtil.isViablePhoneNumber("1\u30003+4")); + // Unicode variants of possible starting character and other allowed punctuation/digits. + assertTrue(PhoneNumberUtil.isViablePhoneNumber("\uFF081\uFF09\u30003456789")); + // Testing a leading + is okay. + assertTrue(PhoneNumberUtil.isViablePhoneNumber("+1\uFF09\u30003456789")); + } + + public void testExtractPossibleNumber() { + // Removes preceding funky punctuation and letters but leaves the rest untouched. + assertEquals("0800-345-600", PhoneNumberUtil.extractPossibleNumber("Tel:0800-345-600")); + assertEquals("0800 FOR PIZZA", PhoneNumberUtil.extractPossibleNumber("Tel:0800 FOR PIZZA")); + // Should not remove plus sign + assertEquals("+800-345-600", PhoneNumberUtil.extractPossibleNumber("Tel:+800-345-600")); + // Should recognise wide digits as possible start values. + assertEquals("\uFF10\uFF12\uFF13", + PhoneNumberUtil.extractPossibleNumber("\uFF10\uFF12\uFF13")); + // Dashes are not possible start values and should be removed. + assertEquals("\uFF11\uFF12\uFF13", + PhoneNumberUtil.extractPossibleNumber("Num-\uFF11\uFF12\uFF13")); + // If not possible number present, return empty string. + assertEquals("", PhoneNumberUtil.extractPossibleNumber("Num-....")); + // Leading brackets are stripped - these are not used when parsing. + assertEquals("650) 253-0000", PhoneNumberUtil.extractPossibleNumber("(650) 253-0000")); + } + + public void testMaybeStripNationalPrefix() { + String nationalPrefix = "34"; + StringBuffer numberToStrip = new StringBuffer("34356778"); + String strippedNumber = "356778"; + String nationalRuleRegExp = "\\d{4,7}"; + Pattern nationalRule = Pattern.compile(nationalRuleRegExp); + phoneUtil.maybeStripNationalPrefix(numberToStrip, nationalPrefix, "", nationalRule); + assertEquals("Should have had national prefix stripped.", + strippedNumber, numberToStrip.toString()); + // Retry stripping - now the number should not start with the national prefix, so no more + // stripping should occur. + phoneUtil.maybeStripNationalPrefix(numberToStrip, nationalPrefix, "", nationalRule); + assertEquals("Should have had no change - no national prefix present.", + strippedNumber, numberToStrip.toString()); + // Some countries have no national prefix. Repeat test with none specified. + nationalPrefix = ""; + phoneUtil.maybeStripNationalPrefix(numberToStrip, nationalPrefix, "", nationalRule); + assertEquals("Should not strip anything with empty national prefix.", + strippedNumber, numberToStrip.toString()); + // If the resultant number doesn't match the national rule, it shouldn't be stripped. + nationalPrefix = "3"; + numberToStrip = new StringBuffer("3123"); + strippedNumber = "3123"; + phoneUtil.maybeStripNationalPrefix(numberToStrip, nationalPrefix, "", nationalRule); + assertEquals("Should have had no change - after stripping, it wouldn't have matched " + + "the national rule.", + strippedNumber, numberToStrip.toString()); + } + + public void testMaybeStripInternationalPrefix() { + String internationalPrefix = "00[39]"; + StringBuffer numberToStrip = new StringBuffer("0034567700-3898003"); + // Note the dash is removed as part of the normalization. + StringBuffer strippedNumber = new StringBuffer("45677003898003"); + assertEquals(true, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + assertEquals("The number supplied was not stripped of its international prefix.", + strippedNumber.toString(), numberToStrip.toString()); + // Now the number no longer starts with an IDD prefix, so it should now report false. + assertEquals(false, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + + numberToStrip = new StringBuffer("00945677003898003"); + assertEquals(true, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + assertEquals("The number supplied was not stripped of its international prefix.", + strippedNumber.toString(), numberToStrip.toString()); + // Test it works when the international prefix is broken up by spaces. + numberToStrip = new StringBuffer("00 9 45677003898003"); + assertEquals(true, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + assertEquals("The number supplied was not stripped of its international prefix.", + strippedNumber.toString(), numberToStrip.toString()); + // Now the number no longer starts with an IDD prefix, so it should now report false. + assertEquals(false, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + + // Test the + symbol is also recognised and stripped. + numberToStrip = new StringBuffer("+45677003898003"); + strippedNumber = new StringBuffer("45677003898003"); + assertEquals(true, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + assertEquals("The number supplied was not stripped of the plus symbol.", + strippedNumber.toString(), numberToStrip.toString()); + + // If the number afterwards is a zero, we should not strip this - no country code begins with 0. + numberToStrip = new StringBuffer("0090112-3123"); + strippedNumber = new StringBuffer("00901123123"); + assertEquals(false, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + assertEquals("The number supplied had a 0 after the match so shouldn't be stripped.", + strippedNumber.toString(), numberToStrip.toString()); + // Here the 0 is separated by a space from the IDD. + numberToStrip = new StringBuffer("009 0-112-3123"); + assertEquals(false, phoneUtil.maybeStripInternationalPrefixAndNormalize(numberToStrip, + internationalPrefix)); + } + + public void testMaybeExtractCountryCode() { + PhoneMetadata metadata = phoneUtil.getPhoneMetadata("US"); + // Note that for the US, the IDD is 011. + try { + String phoneNumber = "011112-3456789"; + String strippedNumber = "123456789"; + int countryCode = 1; + StringBuffer numberToFill = new StringBuffer(); + assertEquals("Did not extract country code " + countryCode + " correctly.", + countryCode, + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, numberToFill)); + // Should strip and normalize national significant number. + assertEquals("Did not strip off the country code correctly.", + strippedNumber, + numberToFill.toString()); + } catch (NumberParseException e) { + fail("Should not have thrown an exception: " + e.toString()); + } + try { + String phoneNumber = "+6423456789"; + int countryCode = 64; + StringBuffer numberToFill = new StringBuffer(); + assertEquals("Did not extract country code " + countryCode + " correctly.", + countryCode, + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, numberToFill)); + } catch (NumberParseException e) { + fail("Should not have thrown an exception: " + e.toString()); + } + try { + String phoneNumber = "2345-6789"; + StringBuffer numberToFill = new StringBuffer(); + assertEquals("Should not have extracted a country code - no international prefix present.", + 0, + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, numberToFill)); + } catch (NumberParseException e) { + fail("Should not have thrown an exception: " + e.toString()); + } + try { + String phoneNumber = "0119991123456789"; + StringBuffer numberToFill = new StringBuffer(); + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, + numberToFill); + fail("Should have thrown an exception, no valid country code present."); + } catch (NumberParseException e) { + // Expected. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + e.getErrorType()); + } + try { + String phoneNumber = "(1 610) 619 4466"; + int countryCode = 1; + StringBuffer numberToFill = new StringBuffer(); + assertEquals("Should have extracted the country code of the region passed in", + countryCode, + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, numberToFill)); + } catch (NumberParseException e) { + fail("Should not have thrown an exception: " + e.toString()); + } + try { + String phoneNumber = "(1 610) 619 446"; + StringBuffer numberToFill = new StringBuffer(); + assertEquals("Should not have extracted a country code - invalid number after extraction " + + "of uncertain country code.", + 0, + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, numberToFill)); + } catch (NumberParseException e) { + fail("Should not have thrown an exception: " + e.toString()); + } + try { + String phoneNumber = "(1 610) 619 43 446"; + StringBuffer numberToFill = new StringBuffer(); + assertEquals("Should not have extracted a country code - invalid number both before and " + + "after extraction of uncertain country code.", + 0, + phoneUtil.maybeExtractCountryCode(phoneNumber, metadata, + numberToFill)); + } catch (NumberParseException e) { + fail("Should not have thrown an exception: " + e.toString()); + } + } + + public void testParseNationalNumber() throws Exception { + PhoneNumber nzNumber = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(33316005L).build(); + + // National prefix attached. + assertEquals(nzNumber, phoneUtil.parse("033316005", "NZ")); + assertEquals(nzNumber, phoneUtil.parse("33316005", "NZ")); + // National prefix attached and some formatting present. + assertEquals(nzNumber, phoneUtil.parse("03-331 6005", "NZ")); + assertEquals(nzNumber, phoneUtil.parse("03 331 6005", "NZ")); + // Test case with alpha characters. + PhoneNumber tollfreeNumber = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(800332005L).build(); + assertEquals(tollfreeNumber, phoneUtil.parse("0800 DDA 005", "NZ")); + PhoneNumber premiumNumber = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(9003326005L).build(); + assertEquals(premiumNumber, phoneUtil.parse("0900 DDA 6005", "NZ")); + // Not enough alpha characters for them to be considered intentional, so they are stripped. + assertEquals(premiumNumber, phoneUtil.parse("0900 332 6005a", "NZ")); + assertEquals(premiumNumber, phoneUtil.parse("0900 332 600a5", "NZ")); + assertEquals(premiumNumber, phoneUtil.parse("0900 332 600A5", "NZ")); + assertEquals(premiumNumber, phoneUtil.parse("0900 a332 600A5", "NZ")); + + // Testing international prefixes. + // Should strip country code. + assertEquals(nzNumber, phoneUtil.parse("0064 3 331 6005", "NZ")); + // Try again, but this time we have an international number with Region Code US. It should + // recognise the country code and parse accordingly. + assertEquals(nzNumber, phoneUtil.parse("01164 3 331 6005", "US")); + assertEquals(nzNumber, phoneUtil.parse("+64 3 331 6005", "US")); + + // Test for http://b/issue?id=2247493 + PhoneNumber nzNumber2 = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(64123456L).build(); + assertEquals(nzNumber2, phoneUtil.parse("64(0)64123456", "NZ")); + + PhoneNumber usNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6503336000L).build(); + assertEquals(usNumber, phoneUtil.parse("+1 (650) 333-6000", "NZ")); + assertEquals(usNumber, phoneUtil.parse("1-650-333-6000", "US")); + // Calling the US number from Singapore by using different service providers + // 1st test: calling using SingTel IDD service (IDD is 001) + assertEquals(usNumber, phoneUtil.parse("0011-650-333-6000", "SG")); + // 2nd test: calling using StarHub IDD service (IDD is 008) + assertEquals(usNumber, phoneUtil.parse("0081-650-333-6000", "SG")); + // 3rd test: calling using SingTel V019 service (IDD is 019) + assertEquals(usNumber, phoneUtil.parse("0191-650-333-6000", "SG")); + // Calling the US number from Poland + assertEquals(usNumber, phoneUtil.parse("0~01-650-333-6000", "PL")); + // Check it doesn't use the '1' as a country code when parsing if the phone number was already + // possible. + + PhoneNumber usNumber2 = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(1234567890L).build(); + assertEquals(usNumber2, phoneUtil.parse("123-456-7890", "US")); + + PhoneNumber itNumber = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(236618300L) + .setItalianLeadingZero(true).build(); + assertEquals(itNumber, phoneUtil.parse("+39 02-36618 300", "NZ")); + assertEquals(itNumber, phoneUtil.parse("02-36618 300", "IT")); + + PhoneNumber itNumber2 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(312345678L).build(); + assertEquals(itNumber2, phoneUtil.parse("312 345 678", "IT")); + + // Check that using a "/" is fine in a phone number. + PhoneNumber deNumber = + PhoneNumber.newBuilder().setCountryCode(49).setNationalNumber(12345678L).build(); + assertEquals(deNumber, phoneUtil.parse("123/45678", "DE")); + + // Test parsing mobile numbers of Argentina. + PhoneNumber arNumber = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(93435551212L).build(); + assertEquals(arNumber, phoneUtil.parse("+54 9 343 555 1212", "AR")); + assertEquals(arNumber, phoneUtil.parse("0343 15 555 1212", "AR")); + + PhoneNumber arNumber2 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(93715654320L).build(); + assertEquals(arNumber2, phoneUtil.parse("+54 9 3715 65 4320", "AR")); + assertEquals(arNumber2, phoneUtil.parse("03715 15 65 4320", "AR")); + + // Test parsing fixed-line numbers of Argentina. + PhoneNumber arNumber3 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(1137970000L).build(); + assertEquals(arNumber3, phoneUtil.parse("+54 11 3797 0000", "AR")); + assertEquals(arNumber3, phoneUtil.parse("011 3797 0000", "AR")); + + PhoneNumber arNumber4 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(3715654321L).build(); + assertEquals(arNumber4, phoneUtil.parse("+54 3715 65 4321", "AR")); + assertEquals(arNumber4, phoneUtil.parse("03715 65 4321", "AR")); + + PhoneNumber arNumber5 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(2312340000L).build(); + assertEquals(arNumber5, phoneUtil.parse("+54 23 1234 0000", "AR")); + assertEquals(arNumber5, phoneUtil.parse("023 1234 0000", "AR")); + + // Test that having an 'x' in the phone number at the start is ok and that it just gets removed. + PhoneNumber arNumber6 = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(123456789L).build(); + assertEquals(arNumber6, phoneUtil.parse("0123456789", "AR")); + assertEquals(arNumber6, phoneUtil.parse("(0) 123456789", "AR")); + assertEquals(arNumber6, phoneUtil.parse("0 123456789", "AR")); + assertEquals(arNumber6, phoneUtil.parse("(0xx) 123456789", "AR")); + PhoneNumber arFromUs = + PhoneNumber.newBuilder().setCountryCode(54).setNationalNumber(81429712L).build(); + // This test is intentionally constructed such that the number of digit after xx is larger than + // 7, so that the number won't be mistakenly treated as an extension, as we allow extensions up + // to 7 digits. This assumption is okay for now as all the countries where a carrier selection + // code is written in the form of xx have a national significant number of length larger than 7. + assertEquals(arFromUs, phoneUtil.parse("011xx5481429712", "US")); + + // Test parsing fixed-line numbers of Mexico. + PhoneNumber mxNumber = + PhoneNumber.newBuilder().setCountryCode(52).setNationalNumber(4499780001L).build(); + assertEquals(mxNumber, phoneUtil.parse("+52 (449)978-0001", "MX")); + assertEquals(mxNumber, phoneUtil.parse("01 (449)978-0001", "MX")); + assertEquals(mxNumber, phoneUtil.parse("(449)978-0001", "MX")); + + // Test parsing mobile numbers of Mexico. + PhoneNumber mxNumber2 = + PhoneNumber.newBuilder().setCountryCode(52).setNationalNumber(13312345678L).build(); + assertEquals(mxNumber2, phoneUtil.parse("+52 1 33 1234-5678", "MX")); + assertEquals(mxNumber2, phoneUtil.parse("044 (33) 1234-5678", "MX")); + assertEquals(mxNumber2, phoneUtil.parse("045 33 1234-5678", "MX")); + + // Test that if a number has two extensions specified, we ignore the second. + PhoneNumber usWithTwoExtensionsNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(2121231234L) + .setExtension("508").build(); + assertEquals(usWithTwoExtensionsNumber, phoneUtil.parse("(212)123-1234 x508/x1234", + "US")); + assertEquals(usWithTwoExtensionsNumber, phoneUtil.parse("(212)123-1234 x508/ x1234", + "US")); + assertEquals(usWithTwoExtensionsNumber, phoneUtil.parse("(212)123-1234 x508\\x1234", + "US")); + + // Test parsing numbers in the form (645) 123-1234-910# works, where the last 3 digits before + // the # are an extension. + PhoneNumber usWithExtension = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6451231234L) + .setExtension("910").build(); + assertEquals(usWithExtension, phoneUtil.parse("+1 (645) 123 1234-910#", "US")); + } + + public void testFailedParseOnInvalidNumbers() { + try { + String sentencePhoneNumber = "This is not a phone number"; + phoneUtil.parse(sentencePhoneNumber, "NZ"); + fail("This should not parse without throwing an exception " + sentencePhoneNumber); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.NOT_A_NUMBER, + e.getErrorType()); + } + try { + String tooLongPhoneNumber = "01495 72553301873 810104"; + phoneUtil.parse(tooLongPhoneNumber, "GB"); + fail("This should not parse without throwing an exception " + tooLongPhoneNumber); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.TOO_LONG, + e.getErrorType()); + } + try { + String tooShortPhoneNumber = "+49 0"; + phoneUtil.parse(tooShortPhoneNumber, "DE"); + fail("This should not parse without throwing an exception " + tooShortPhoneNumber); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.TOO_SHORT_NSN, + e.getErrorType()); + } + try { + String invalidCountryCode = "+210 3456 56789"; + phoneUtil.parse(invalidCountryCode, "NZ"); + fail("This is not a recognised country code: should fail: " + invalidCountryCode); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + e.getErrorType()); + } + try { + String someNumber = "123 456 7890"; + phoneUtil.parse(someNumber, "ZZ"); + fail("'Unknown' country code not allowed: should fail."); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + e.getErrorType()); + } + try { + String someNumber = "123 456 7890"; + phoneUtil.parse(someNumber, null); + fail("Null country code not allowed: should fail."); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + e.getErrorType()); + } + try { + String someNumber = "0044------"; + phoneUtil.parse(someNumber, "GB"); + fail("No number provided, only country code: should fail"); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.TOO_SHORT_AFTER_IDD, + e.getErrorType()); + } + try { + String someNumber = "0044"; + phoneUtil.parse(someNumber, "GB"); + fail("No number provided, only country code: should fail"); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.TOO_SHORT_AFTER_IDD, + e.getErrorType()); + } + try { + String someNumber = "011"; + phoneUtil.parse(someNumber, "US"); + fail("Only IDD provided - should fail."); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.TOO_SHORT_AFTER_IDD, + e.getErrorType()); + } + try { + String someNumber = "0119"; + phoneUtil.parse(someNumber, "US"); + fail("Only IDD provided and then 9 - should fail."); + } catch (NumberParseException e) { + // Expected this exception. + assertEquals("Wrong error type stored in exception.", + NumberParseException.ErrorType.TOO_SHORT_AFTER_IDD, + e.getErrorType()); + } + } + + public void testParseExtensions() throws Exception { + PhoneNumber nzNumber = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(33316005L) + .setExtension("3456").build(); + assertEquals(nzNumber, phoneUtil.parse("03 331 6005 ext 3456", "NZ")); + assertEquals(nzNumber, phoneUtil.parse("03-3316005x3456", "NZ")); + assertEquals(nzNumber, phoneUtil.parse("03-3316005 int.3456", "NZ")); + assertEquals(nzNumber, phoneUtil.parse("03 3316005 #3456", "NZ")); + // Test the following do not extract extensions: + PhoneNumber nonExtnNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(180074935247L).build(); + assertEquals(nonExtnNumber, phoneUtil.parse("1800 six-flags", "US")); + assertEquals(nonExtnNumber, phoneUtil.parse("1800 SIX FLAGS", "US")); + assertEquals(nonExtnNumber, phoneUtil.parse("0~01 1800 7493 5247", "PL")); + assertEquals(nonExtnNumber, phoneUtil.parse("(1800) 7493.5247", "US")); + // Check that the last instance of an extension token is matched. + PhoneNumber extnNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(180074935247L) + .setExtension("1234").build(); + assertEquals(extnNumber, phoneUtil.parse("0~01 1800 7493 5247 ~1234", "PL")); + // Verifying bug-fix where the last digit of a number was previously omitted if it was a 0 when + // extracting the extension. Also verifying a few different cases of extensions. + PhoneNumber ukNumber = + PhoneNumber.newBuilder().setCountryCode(44).setNationalNumber(2034567890L) + .setExtension("456").build(); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890x456", "NZ")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890x456", "GB")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890 x456", "GB")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890 X456", "GB")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890 X 456", "GB")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890 X 456", "GB")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890 x 456 ", "GB")); + assertEquals(ukNumber, phoneUtil.parse("+44 2034567890 X 456", "GB")); + + PhoneNumber usWithExtension = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(8009013355L) + .setExtension("7246433").build(); + assertEquals(usWithExtension, phoneUtil.parse("(800) 901-3355 x 7246433", "US")); + assertEquals(usWithExtension, phoneUtil.parse("(800) 901-3355 , ext 7246433", "US")); + assertEquals(usWithExtension, + phoneUtil.parse("(800) 901-3355 ,extension 7246433", "US")); + assertEquals(usWithExtension, phoneUtil.parse("(800) 901-3355 , 7246433", "US")); + assertEquals(usWithExtension, phoneUtil.parse("(800) 901-3355 ext: 7246433", "US")); + } + + public void testCountryWithNoNumberDesc() { + // Andorra is a country where we don't have PhoneNumberDesc info in the meta data. + PhoneNumber adNumber = + PhoneNumber.newBuilder().setCountryCode(376).setNationalNumber(12345L).build(); + assertEquals("+376 12345", phoneUtil.format(adNumber, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); + assertEquals("+37612345", phoneUtil.format(adNumber, + PhoneNumberUtil.PhoneNumberFormat.E164)); + assertEquals("12345", phoneUtil.format(adNumber, + PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); + assertEquals(PhoneNumberUtil.PhoneNumberType.UNKNOWN, + phoneUtil.getNumberType(adNumber)); + assertTrue(phoneUtil.isValidNumber(adNumber)); + + // Test dialing a US number from within Andorra. + PhoneNumber usNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(6502530000L).build(); + assertEquals("00 1 650 253 0000", + phoneUtil.formatOutOfCountryCallingNumber(usNumber, "AD")); + } + + public void testUnknownCountryCallingCodeForValidation() { + PhoneNumber.Builder invalidNumber = PhoneNumber.newBuilder(); + invalidNumber.setCountryCode(0).setNationalNumber(1234L); + assertFalse(phoneUtil.isValidNumber(invalidNumber.build())); + } + + public void testIsNumberMatchMatches() throws Exception { + // Test simple matches where formatting is different, or leading zeroes, or country code has + // been specified. + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+64 3 331 6005", "+64 03 331 6005")); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+64 03 331-6005", "+64 03331 6005")); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+643 331-6005", "+64033316005")); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+643 331-6005", "+6433316005")); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005", "+6433316005")); + // Test alpha numbers. + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+1800 siX-Flags", "+1 800 7493 5247")); + // Test numbers with extensions. + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005 extn 1234", "+6433316005#1234")); + // Test proto buffers. + PhoneNumber nzNumber = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(33316005L) + .setExtension("3456").build(); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch(nzNumber, "+643 331 6005 ext 3456")); + PhoneNumber nzNumber2 = PhoneNumber.newBuilder(nzNumber).clearExtension().build(); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch(nzNumber2, "+6403 331 6005")); + // Check empty extensions are ignored. + PhoneNumber nzNumber3 = PhoneNumber.newBuilder(nzNumber).setExtension("").build(); + assertEquals(PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch(nzNumber3, "+6403 331 6005")); + // Check variant with two proto buffers. + PhoneNumber nzNumber4 = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(33316005L).build(); + assertEquals("Number " + nzNumber.toString() + " did not match " + nzNumber4.toString(), + PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch(nzNumber3, nzNumber4)); + } + + public void testIsNumberMatchNonMatches() throws Exception { + // Non-matches. + assertEquals(PhoneNumberUtil.MatchType.NO_MATCH, + phoneUtil.isNumberMatch("03 331 6005", "03 331 6006")); + // Different country code, partial number match. + assertEquals(PhoneNumberUtil.MatchType.NO_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005", "+16433316005")); + // Different country code, same number. + assertEquals(PhoneNumberUtil.MatchType.NO_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005", "+6133316005")); + // Extension different, all else the same. + assertEquals(PhoneNumberUtil.MatchType.NO_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005 extn 1234", "0116433316005#1235")); + // NSN matches, but extension is different - not the same number. + assertEquals(PhoneNumberUtil.MatchType.NO_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005 ext.1235", "3 331 6005#1234")); + } + + public void testIsNumberMatchNsnMatches() throws Exception { + // NSN matches. + assertEquals(PhoneNumberUtil.MatchType.NSN_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005", "03 331 6005")); + assertEquals(PhoneNumberUtil.MatchType.NSN_MATCH, + phoneUtil.isNumberMatch("3 331-6005", "03 331 6005")); + PhoneNumber nzNumber = + PhoneNumber.newBuilder().setCountryCode(64).setNationalNumber(33316005L) + .setExtension("").build(); + assertEquals(PhoneNumberUtil.MatchType.NSN_MATCH, + phoneUtil.isNumberMatch(nzNumber, "03 331 6005")); + PhoneNumber unchangedNzNumber = PhoneNumber.newBuilder().setCountryCode(64) + .setNationalNumber(33316005L).setExtension("").build(); + // Check the phone number proto was not edited during the method call. + assertEquals(unchangedNzNumber, nzNumber); + } + + public void testIsNumberMatchShortNsnMatches() throws Exception { + // Short NSN matches with the country not specified for either one or both numbers. + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005", "331 6005")); + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("3 331-6005", "331 6005")); + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("3 331-6005", "+64 331 6005")); + // Short NSN match with the country specified. + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("03 331-6005", "331 6005")); + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("1 234 345 6789", "345 6789")); + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("+1 (234) 345 6789", "345 6789")); + // NSN matches, country code omitted for one number, extension missing for one. + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch("+64 3 331-6005", "3 331 6005#1234")); + // One has Italian leading zero, one does not. + PhoneNumber italianNumber1 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(1234L) + .setItalianLeadingZero(true).build(); + PhoneNumber italianNumber2 = + PhoneNumber.newBuilder().setCountryCode(39).setNationalNumber(1234L).build(); + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch(italianNumber1, italianNumber2)); + // One has an extension, the other has an extension of "". + PhoneNumber italianNumber3 = + PhoneNumber.newBuilder(italianNumber1).setExtension("1234") + .clearItalianLeadingZero().build(); + PhoneNumber italianNumber4 = + PhoneNumber.newBuilder(italianNumber2).setExtension("").build(); + assertEquals(PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch(italianNumber3, italianNumber4)); + } +} diff --git a/java/src/com/google/i18n/phonenumbers/Phonemetadata.java b/java/src/com/google/i18n/phonenumbers/Phonemetadata.java new file mode 100644 index 000000000..6f6a66ce8 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/Phonemetadata.java @@ -0,0 +1,2603 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: phonemetadata.proto + +package com.google.i18n.phonenumbers; + +public final class Phonemetadata { + private Phonemetadata() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public static final class NumberFormat extends + com.google.protobuf.GeneratedMessage { + // Use NumberFormat.newBuilder() to construct. + private NumberFormat() { + initFields(); + } + private NumberFormat(boolean noInit) {} + + private static final NumberFormat defaultInstance; + public static NumberFormat getDefaultInstance() { + return defaultInstance; + } + + public NumberFormat getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_NumberFormat_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_NumberFormat_fieldAccessorTable; + } + + // required string pattern = 1; + public static final int PATTERN_FIELD_NUMBER = 1; + private boolean hasPattern; + private java.lang.String pattern_ = ""; + public boolean hasPattern() { return hasPattern; } + public java.lang.String getPattern() { return pattern_; } + + // required string format = 2; + public static final int FORMAT_FIELD_NUMBER = 2; + private boolean hasFormat; + private java.lang.String format_ = ""; + public boolean hasFormat() { return hasFormat; } + public java.lang.String getFormat() { return format_; } + + // optional string leading_digits = 3; + public static final int LEADING_DIGITS_FIELD_NUMBER = 3; + private boolean hasLeadingDigits; + private java.lang.String leadingDigits_ = ""; + public boolean hasLeadingDigits() { return hasLeadingDigits; } + public java.lang.String getLeadingDigits() { return leadingDigits_; } + + // optional string national_prefix_formatting_rule = 4; + public static final int NATIONAL_PREFIX_FORMATTING_RULE_FIELD_NUMBER = 4; + private boolean hasNationalPrefixFormattingRule; + private java.lang.String nationalPrefixFormattingRule_ = ""; + public boolean hasNationalPrefixFormattingRule() { return hasNationalPrefixFormattingRule; } + public java.lang.String getNationalPrefixFormattingRule() { return nationalPrefixFormattingRule_; } + + private void initFields() { + } + public final boolean isInitialized() { + if (!hasPattern) return false; + if (!hasFormat) return false; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasPattern()) { + output.writeString(1, getPattern()); + } + if (hasFormat()) { + output.writeString(2, getFormat()); + } + if (hasLeadingDigits()) { + output.writeString(3, getLeadingDigits()); + } + if (hasNationalPrefixFormattingRule()) { + output.writeString(4, getNationalPrefixFormattingRule()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasPattern()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(1, getPattern()); + } + if (hasFormat()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(2, getFormat()); + } + if (hasLeadingDigits()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(3, getLeadingDigits()); + } + if (hasNationalPrefixFormattingRule()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(4, getNationalPrefixFormattingRule()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.NumberFormat parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.google.i18n.phonenumbers.Phonemetadata.NumberFormat prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private com.google.i18n.phonenumbers.Phonemetadata.NumberFormat result; + + // Construct using com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new com.google.i18n.phonenumbers.Phonemetadata.NumberFormat(); + return builder; + } + + protected com.google.i18n.phonenumbers.Phonemetadata.NumberFormat internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new com.google.i18n.phonenumbers.Phonemetadata.NumberFormat(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.getDescriptor(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat getDefaultInstanceForType() { + return com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private com.google.i18n.phonenumbers.Phonemetadata.NumberFormat buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + com.google.i18n.phonenumbers.Phonemetadata.NumberFormat returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.i18n.phonenumbers.Phonemetadata.NumberFormat) { + return mergeFrom((com.google.i18n.phonenumbers.Phonemetadata.NumberFormat)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.i18n.phonenumbers.Phonemetadata.NumberFormat other) { + if (other == com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.getDefaultInstance()) return this; + if (other.hasPattern()) { + setPattern(other.getPattern()); + } + if (other.hasFormat()) { + setFormat(other.getFormat()); + } + if (other.hasLeadingDigits()) { + setLeadingDigits(other.getLeadingDigits()); + } + if (other.hasNationalPrefixFormattingRule()) { + setNationalPrefixFormattingRule(other.getNationalPrefixFormattingRule()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 10: { + setPattern(input.readString()); + break; + } + case 18: { + setFormat(input.readString()); + break; + } + case 26: { + setLeadingDigits(input.readString()); + break; + } + case 34: { + setNationalPrefixFormattingRule(input.readString()); + break; + } + } + } + } + + + // required string pattern = 1; + public boolean hasPattern() { + return result.hasPattern(); + } + public java.lang.String getPattern() { + return result.getPattern(); + } + public Builder setPattern(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasPattern = true; + result.pattern_ = value; + return this; + } + public Builder clearPattern() { + result.hasPattern = false; + result.pattern_ = getDefaultInstance().getPattern(); + return this; + } + + // required string format = 2; + public boolean hasFormat() { + return result.hasFormat(); + } + public java.lang.String getFormat() { + return result.getFormat(); + } + public Builder setFormat(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasFormat = true; + result.format_ = value; + return this; + } + public Builder clearFormat() { + result.hasFormat = false; + result.format_ = getDefaultInstance().getFormat(); + return this; + } + + // optional string leading_digits = 3; + public boolean hasLeadingDigits() { + return result.hasLeadingDigits(); + } + public java.lang.String getLeadingDigits() { + return result.getLeadingDigits(); + } + public Builder setLeadingDigits(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasLeadingDigits = true; + result.leadingDigits_ = value; + return this; + } + public Builder clearLeadingDigits() { + result.hasLeadingDigits = false; + result.leadingDigits_ = getDefaultInstance().getLeadingDigits(); + return this; + } + + // optional string national_prefix_formatting_rule = 4; + public boolean hasNationalPrefixFormattingRule() { + return result.hasNationalPrefixFormattingRule(); + } + public java.lang.String getNationalPrefixFormattingRule() { + return result.getNationalPrefixFormattingRule(); + } + public Builder setNationalPrefixFormattingRule(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasNationalPrefixFormattingRule = true; + result.nationalPrefixFormattingRule_ = value; + return this; + } + public Builder clearNationalPrefixFormattingRule() { + result.hasNationalPrefixFormattingRule = false; + result.nationalPrefixFormattingRule_ = getDefaultInstance().getNationalPrefixFormattingRule(); + return this; + } + + // @@protoc_insertion_point(builder_scope:i18n.phonenumbers.NumberFormat) + } + + static { + defaultInstance = new NumberFormat(true); + com.google.i18n.phonenumbers.Phonemetadata.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:i18n.phonenumbers.NumberFormat) + } + + public static final class PhoneNumberDesc extends + com.google.protobuf.GeneratedMessage { + // Use PhoneNumberDesc.newBuilder() to construct. + private PhoneNumberDesc() { + initFields(); + } + private PhoneNumberDesc(boolean noInit) {} + + private static final PhoneNumberDesc defaultInstance; + public static PhoneNumberDesc getDefaultInstance() { + return defaultInstance; + } + + public PhoneNumberDesc getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_PhoneNumberDesc_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_PhoneNumberDesc_fieldAccessorTable; + } + + // optional string national_number_pattern = 2; + public static final int NATIONAL_NUMBER_PATTERN_FIELD_NUMBER = 2; + private boolean hasNationalNumberPattern; + private java.lang.String nationalNumberPattern_ = ""; + public boolean hasNationalNumberPattern() { return hasNationalNumberPattern; } + public java.lang.String getNationalNumberPattern() { return nationalNumberPattern_; } + + // optional string possible_number_pattern = 3; + public static final int POSSIBLE_NUMBER_PATTERN_FIELD_NUMBER = 3; + private boolean hasPossibleNumberPattern; + private java.lang.String possibleNumberPattern_ = ""; + public boolean hasPossibleNumberPattern() { return hasPossibleNumberPattern; } + public java.lang.String getPossibleNumberPattern() { return possibleNumberPattern_; } + + // optional string example_number = 6; + public static final int EXAMPLE_NUMBER_FIELD_NUMBER = 6; + private boolean hasExampleNumber; + private java.lang.String exampleNumber_ = ""; + public boolean hasExampleNumber() { return hasExampleNumber; } + public java.lang.String getExampleNumber() { return exampleNumber_; } + + private void initFields() { + } + public final boolean isInitialized() { + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasNationalNumberPattern()) { + output.writeString(2, getNationalNumberPattern()); + } + if (hasPossibleNumberPattern()) { + output.writeString(3, getPossibleNumberPattern()); + } + if (hasExampleNumber()) { + output.writeString(6, getExampleNumber()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasNationalNumberPattern()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(2, getNationalNumberPattern()); + } + if (hasPossibleNumberPattern()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(3, getPossibleNumberPattern()); + } + if (hasExampleNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(6, getExampleNumber()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc result; + + // Construct using com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc(); + return builder; + } + + protected com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDescriptor(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getDefaultInstanceForType() { + return com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc) { + return mergeFrom((com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc other) { + if (other == com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) return this; + if (other.hasNationalNumberPattern()) { + setNationalNumberPattern(other.getNationalNumberPattern()); + } + if (other.hasPossibleNumberPattern()) { + setPossibleNumberPattern(other.getPossibleNumberPattern()); + } + if (other.hasExampleNumber()) { + setExampleNumber(other.getExampleNumber()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 18: { + setNationalNumberPattern(input.readString()); + break; + } + case 26: { + setPossibleNumberPattern(input.readString()); + break; + } + case 50: { + setExampleNumber(input.readString()); + break; + } + } + } + } + + + // optional string national_number_pattern = 2; + public boolean hasNationalNumberPattern() { + return result.hasNationalNumberPattern(); + } + public java.lang.String getNationalNumberPattern() { + return result.getNationalNumberPattern(); + } + public Builder setNationalNumberPattern(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasNationalNumberPattern = true; + result.nationalNumberPattern_ = value; + return this; + } + public Builder clearNationalNumberPattern() { + result.hasNationalNumberPattern = false; + result.nationalNumberPattern_ = getDefaultInstance().getNationalNumberPattern(); + return this; + } + + // optional string possible_number_pattern = 3; + public boolean hasPossibleNumberPattern() { + return result.hasPossibleNumberPattern(); + } + public java.lang.String getPossibleNumberPattern() { + return result.getPossibleNumberPattern(); + } + public Builder setPossibleNumberPattern(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasPossibleNumberPattern = true; + result.possibleNumberPattern_ = value; + return this; + } + public Builder clearPossibleNumberPattern() { + result.hasPossibleNumberPattern = false; + result.possibleNumberPattern_ = getDefaultInstance().getPossibleNumberPattern(); + return this; + } + + // optional string example_number = 6; + public boolean hasExampleNumber() { + return result.hasExampleNumber(); + } + public java.lang.String getExampleNumber() { + return result.getExampleNumber(); + } + public Builder setExampleNumber(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasExampleNumber = true; + result.exampleNumber_ = value; + return this; + } + public Builder clearExampleNumber() { + result.hasExampleNumber = false; + result.exampleNumber_ = getDefaultInstance().getExampleNumber(); + return this; + } + + // @@protoc_insertion_point(builder_scope:i18n.phonenumbers.PhoneNumberDesc) + } + + static { + defaultInstance = new PhoneNumberDesc(true); + com.google.i18n.phonenumbers.Phonemetadata.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:i18n.phonenumbers.PhoneNumberDesc) + } + + public static final class PhoneMetadata extends + com.google.protobuf.GeneratedMessage { + // Use PhoneMetadata.newBuilder() to construct. + private PhoneMetadata() { + initFields(); + } + private PhoneMetadata(boolean noInit) {} + + private static final PhoneMetadata defaultInstance; + public static PhoneMetadata getDefaultInstance() { + return defaultInstance; + } + + public PhoneMetadata getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_PhoneMetadata_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_PhoneMetadata_fieldAccessorTable; + } + + // required .i18n.phonenumbers.PhoneNumberDesc general_desc = 1; + public static final int GENERAL_DESC_FIELD_NUMBER = 1; + private boolean hasGeneralDesc; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc generalDesc_; + public boolean hasGeneralDesc() { return hasGeneralDesc; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getGeneralDesc() { return generalDesc_; } + + // required .i18n.phonenumbers.PhoneNumberDesc fixed_line = 2; + public static final int FIXED_LINE_FIELD_NUMBER = 2; + private boolean hasFixedLine; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc fixedLine_; + public boolean hasFixedLine() { return hasFixedLine; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getFixedLine() { return fixedLine_; } + + // required .i18n.phonenumbers.PhoneNumberDesc mobile = 3; + public static final int MOBILE_FIELD_NUMBER = 3; + private boolean hasMobile; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc mobile_; + public boolean hasMobile() { return hasMobile; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getMobile() { return mobile_; } + + // required .i18n.phonenumbers.PhoneNumberDesc toll_free = 4; + public static final int TOLL_FREE_FIELD_NUMBER = 4; + private boolean hasTollFree; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc tollFree_; + public boolean hasTollFree() { return hasTollFree; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getTollFree() { return tollFree_; } + + // required .i18n.phonenumbers.PhoneNumberDesc premium_rate = 5; + public static final int PREMIUM_RATE_FIELD_NUMBER = 5; + private boolean hasPremiumRate; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc premiumRate_; + public boolean hasPremiumRate() { return hasPremiumRate; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getPremiumRate() { return premiumRate_; } + + // required .i18n.phonenumbers.PhoneNumberDesc shared_cost = 6; + public static final int SHARED_COST_FIELD_NUMBER = 6; + private boolean hasSharedCost; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc sharedCost_; + public boolean hasSharedCost() { return hasSharedCost; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getSharedCost() { return sharedCost_; } + + // required .i18n.phonenumbers.PhoneNumberDesc personal_number = 7; + public static final int PERSONAL_NUMBER_FIELD_NUMBER = 7; + private boolean hasPersonalNumber; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc personalNumber_; + public boolean hasPersonalNumber() { return hasPersonalNumber; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getPersonalNumber() { return personalNumber_; } + + // required .i18n.phonenumbers.PhoneNumberDesc voip = 8; + public static final int VOIP_FIELD_NUMBER = 8; + private boolean hasVoip; + private com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc voip_; + public boolean hasVoip() { return hasVoip; } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getVoip() { return voip_; } + + // required string id = 9; + public static final int ID_FIELD_NUMBER = 9; + private boolean hasId; + private java.lang.String id_ = ""; + public boolean hasId() { return hasId; } + public java.lang.String getId() { return id_; } + + // required int32 country_code = 10; + public static final int COUNTRY_CODE_FIELD_NUMBER = 10; + private boolean hasCountryCode; + private int countryCode_ = 0; + public boolean hasCountryCode() { return hasCountryCode; } + public int getCountryCode() { return countryCode_; } + + // required string international_prefix = 11; + public static final int INTERNATIONAL_PREFIX_FIELD_NUMBER = 11; + private boolean hasInternationalPrefix; + private java.lang.String internationalPrefix_ = ""; + public boolean hasInternationalPrefix() { return hasInternationalPrefix; } + public java.lang.String getInternationalPrefix() { return internationalPrefix_; } + + // optional string preferred_international_prefix = 17; + public static final int PREFERRED_INTERNATIONAL_PREFIX_FIELD_NUMBER = 17; + private boolean hasPreferredInternationalPrefix; + private java.lang.String preferredInternationalPrefix_ = ""; + public boolean hasPreferredInternationalPrefix() { return hasPreferredInternationalPrefix; } + public java.lang.String getPreferredInternationalPrefix() { return preferredInternationalPrefix_; } + + // optional string national_prefix = 12; + public static final int NATIONAL_PREFIX_FIELD_NUMBER = 12; + private boolean hasNationalPrefix; + private java.lang.String nationalPrefix_ = ""; + public boolean hasNationalPrefix() { return hasNationalPrefix; } + public java.lang.String getNationalPrefix() { return nationalPrefix_; } + + // optional string preferred_extn_prefix = 13; + public static final int PREFERRED_EXTN_PREFIX_FIELD_NUMBER = 13; + private boolean hasPreferredExtnPrefix; + private java.lang.String preferredExtnPrefix_ = ""; + public boolean hasPreferredExtnPrefix() { return hasPreferredExtnPrefix; } + public java.lang.String getPreferredExtnPrefix() { return preferredExtnPrefix_; } + + // optional string national_prefix_for_parsing = 15; + public static final int NATIONAL_PREFIX_FOR_PARSING_FIELD_NUMBER = 15; + private boolean hasNationalPrefixForParsing; + private java.lang.String nationalPrefixForParsing_ = ""; + public boolean hasNationalPrefixForParsing() { return hasNationalPrefixForParsing; } + public java.lang.String getNationalPrefixForParsing() { return nationalPrefixForParsing_; } + + // optional string national_prefix_transform_rule = 16; + public static final int NATIONAL_PREFIX_TRANSFORM_RULE_FIELD_NUMBER = 16; + private boolean hasNationalPrefixTransformRule; + private java.lang.String nationalPrefixTransformRule_ = ""; + public boolean hasNationalPrefixTransformRule() { return hasNationalPrefixTransformRule; } + public java.lang.String getNationalPrefixTransformRule() { return nationalPrefixTransformRule_; } + + // optional bool same_mobile_and_fixed_line_pattern = 18 [default = false]; + public static final int SAME_MOBILE_AND_FIXED_LINE_PATTERN_FIELD_NUMBER = 18; + private boolean hasSameMobileAndFixedLinePattern; + private boolean sameMobileAndFixedLinePattern_ = false; + public boolean hasSameMobileAndFixedLinePattern() { return hasSameMobileAndFixedLinePattern; } + public boolean getSameMobileAndFixedLinePattern() { return sameMobileAndFixedLinePattern_; } + + // repeated .i18n.phonenumbers.NumberFormat number_format = 19; + public static final int NUMBER_FORMAT_FIELD_NUMBER = 19; + private java.util.List numberFormat_ = + java.util.Collections.emptyList(); + public java.util.List getNumberFormatList() { + return numberFormat_; + } + public int getNumberFormatCount() { return numberFormat_.size(); } + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat getNumberFormat(int index) { + return numberFormat_.get(index); + } + + // repeated .i18n.phonenumbers.NumberFormat intl_number_format = 20; + public static final int INTL_NUMBER_FORMAT_FIELD_NUMBER = 20; + private java.util.List intlNumberFormat_ = + java.util.Collections.emptyList(); + public java.util.List getIntlNumberFormatList() { + return intlNumberFormat_; + } + public int getIntlNumberFormatCount() { return intlNumberFormat_.size(); } + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat getIntlNumberFormat(int index) { + return intlNumberFormat_.get(index); + } + + // optional string national_prefix_formatting_rule = 21; + public static final int NATIONAL_PREFIX_FORMATTING_RULE_FIELD_NUMBER = 21; + private boolean hasNationalPrefixFormattingRule; + private java.lang.String nationalPrefixFormattingRule_ = ""; + public boolean hasNationalPrefixFormattingRule() { return hasNationalPrefixFormattingRule; } + public java.lang.String getNationalPrefixFormattingRule() { return nationalPrefixFormattingRule_; } + + private void initFields() { + generalDesc_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + fixedLine_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + mobile_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + tollFree_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + premiumRate_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + sharedCost_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + personalNumber_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + voip_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + } + public final boolean isInitialized() { + if (!hasGeneralDesc) return false; + if (!hasFixedLine) return false; + if (!hasMobile) return false; + if (!hasTollFree) return false; + if (!hasPremiumRate) return false; + if (!hasSharedCost) return false; + if (!hasPersonalNumber) return false; + if (!hasVoip) return false; + if (!hasId) return false; + if (!hasCountryCode) return false; + if (!hasInternationalPrefix) return false; + for (com.google.i18n.phonenumbers.Phonemetadata.NumberFormat element : getNumberFormatList()) { + if (!element.isInitialized()) return false; + } + for (com.google.i18n.phonenumbers.Phonemetadata.NumberFormat element : getIntlNumberFormatList()) { + if (!element.isInitialized()) return false; + } + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasGeneralDesc()) { + output.writeMessage(1, getGeneralDesc()); + } + if (hasFixedLine()) { + output.writeMessage(2, getFixedLine()); + } + if (hasMobile()) { + output.writeMessage(3, getMobile()); + } + if (hasTollFree()) { + output.writeMessage(4, getTollFree()); + } + if (hasPremiumRate()) { + output.writeMessage(5, getPremiumRate()); + } + if (hasSharedCost()) { + output.writeMessage(6, getSharedCost()); + } + if (hasPersonalNumber()) { + output.writeMessage(7, getPersonalNumber()); + } + if (hasVoip()) { + output.writeMessage(8, getVoip()); + } + if (hasId()) { + output.writeString(9, getId()); + } + if (hasCountryCode()) { + output.writeInt32(10, getCountryCode()); + } + if (hasInternationalPrefix()) { + output.writeString(11, getInternationalPrefix()); + } + if (hasNationalPrefix()) { + output.writeString(12, getNationalPrefix()); + } + if (hasPreferredExtnPrefix()) { + output.writeString(13, getPreferredExtnPrefix()); + } + if (hasNationalPrefixForParsing()) { + output.writeString(15, getNationalPrefixForParsing()); + } + if (hasNationalPrefixTransformRule()) { + output.writeString(16, getNationalPrefixTransformRule()); + } + if (hasPreferredInternationalPrefix()) { + output.writeString(17, getPreferredInternationalPrefix()); + } + if (hasSameMobileAndFixedLinePattern()) { + output.writeBool(18, getSameMobileAndFixedLinePattern()); + } + for (com.google.i18n.phonenumbers.Phonemetadata.NumberFormat element : getNumberFormatList()) { + output.writeMessage(19, element); + } + for (com.google.i18n.phonenumbers.Phonemetadata.NumberFormat element : getIntlNumberFormatList()) { + output.writeMessage(20, element); + } + if (hasNationalPrefixFormattingRule()) { + output.writeString(21, getNationalPrefixFormattingRule()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasGeneralDesc()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, getGeneralDesc()); + } + if (hasFixedLine()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getFixedLine()); + } + if (hasMobile()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getMobile()); + } + if (hasTollFree()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, getTollFree()); + } + if (hasPremiumRate()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, getPremiumRate()); + } + if (hasSharedCost()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, getSharedCost()); + } + if (hasPersonalNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, getPersonalNumber()); + } + if (hasVoip()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, getVoip()); + } + if (hasId()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(9, getId()); + } + if (hasCountryCode()) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(10, getCountryCode()); + } + if (hasInternationalPrefix()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(11, getInternationalPrefix()); + } + if (hasNationalPrefix()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(12, getNationalPrefix()); + } + if (hasPreferredExtnPrefix()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(13, getPreferredExtnPrefix()); + } + if (hasNationalPrefixForParsing()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(15, getNationalPrefixForParsing()); + } + if (hasNationalPrefixTransformRule()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(16, getNationalPrefixTransformRule()); + } + if (hasPreferredInternationalPrefix()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(17, getPreferredInternationalPrefix()); + } + if (hasSameMobileAndFixedLinePattern()) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(18, getSameMobileAndFixedLinePattern()); + } + for (com.google.i18n.phonenumbers.Phonemetadata.NumberFormat element : getNumberFormatList()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(19, element); + } + for (com.google.i18n.phonenumbers.Phonemetadata.NumberFormat element : getIntlNumberFormatList()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(20, element); + } + if (hasNationalPrefixFormattingRule()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(21, getNationalPrefixFormattingRule()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata result; + + // Construct using com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata(); + return builder; + } + + protected com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.getDescriptor(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata getDefaultInstanceForType() { + return com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + if (result.numberFormat_ != java.util.Collections.EMPTY_LIST) { + result.numberFormat_ = + java.util.Collections.unmodifiableList(result.numberFormat_); + } + if (result.intlNumberFormat_ != java.util.Collections.EMPTY_LIST) { + result.intlNumberFormat_ = + java.util.Collections.unmodifiableList(result.intlNumberFormat_); + } + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata) { + return mergeFrom((com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata other) { + if (other == com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.getDefaultInstance()) return this; + if (other.hasGeneralDesc()) { + mergeGeneralDesc(other.getGeneralDesc()); + } + if (other.hasFixedLine()) { + mergeFixedLine(other.getFixedLine()); + } + if (other.hasMobile()) { + mergeMobile(other.getMobile()); + } + if (other.hasTollFree()) { + mergeTollFree(other.getTollFree()); + } + if (other.hasPremiumRate()) { + mergePremiumRate(other.getPremiumRate()); + } + if (other.hasSharedCost()) { + mergeSharedCost(other.getSharedCost()); + } + if (other.hasPersonalNumber()) { + mergePersonalNumber(other.getPersonalNumber()); + } + if (other.hasVoip()) { + mergeVoip(other.getVoip()); + } + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasCountryCode()) { + setCountryCode(other.getCountryCode()); + } + if (other.hasInternationalPrefix()) { + setInternationalPrefix(other.getInternationalPrefix()); + } + if (other.hasPreferredInternationalPrefix()) { + setPreferredInternationalPrefix(other.getPreferredInternationalPrefix()); + } + if (other.hasNationalPrefix()) { + setNationalPrefix(other.getNationalPrefix()); + } + if (other.hasPreferredExtnPrefix()) { + setPreferredExtnPrefix(other.getPreferredExtnPrefix()); + } + if (other.hasNationalPrefixForParsing()) { + setNationalPrefixForParsing(other.getNationalPrefixForParsing()); + } + if (other.hasNationalPrefixTransformRule()) { + setNationalPrefixTransformRule(other.getNationalPrefixTransformRule()); + } + if (other.hasSameMobileAndFixedLinePattern()) { + setSameMobileAndFixedLinePattern(other.getSameMobileAndFixedLinePattern()); + } + if (!other.numberFormat_.isEmpty()) { + if (result.numberFormat_.isEmpty()) { + result.numberFormat_ = new java.util.ArrayList(); + } + result.numberFormat_.addAll(other.numberFormat_); + } + if (!other.intlNumberFormat_.isEmpty()) { + if (result.intlNumberFormat_.isEmpty()) { + result.intlNumberFormat_ = new java.util.ArrayList(); + } + result.intlNumberFormat_.addAll(other.intlNumberFormat_); + } + if (other.hasNationalPrefixFormattingRule()) { + setNationalPrefixFormattingRule(other.getNationalPrefixFormattingRule()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 10: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasGeneralDesc()) { + subBuilder.mergeFrom(getGeneralDesc()); + } + input.readMessage(subBuilder, extensionRegistry); + setGeneralDesc(subBuilder.buildPartial()); + break; + } + case 18: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasFixedLine()) { + subBuilder.mergeFrom(getFixedLine()); + } + input.readMessage(subBuilder, extensionRegistry); + setFixedLine(subBuilder.buildPartial()); + break; + } + case 26: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasMobile()) { + subBuilder.mergeFrom(getMobile()); + } + input.readMessage(subBuilder, extensionRegistry); + setMobile(subBuilder.buildPartial()); + break; + } + case 34: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasTollFree()) { + subBuilder.mergeFrom(getTollFree()); + } + input.readMessage(subBuilder, extensionRegistry); + setTollFree(subBuilder.buildPartial()); + break; + } + case 42: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasPremiumRate()) { + subBuilder.mergeFrom(getPremiumRate()); + } + input.readMessage(subBuilder, extensionRegistry); + setPremiumRate(subBuilder.buildPartial()); + break; + } + case 50: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasSharedCost()) { + subBuilder.mergeFrom(getSharedCost()); + } + input.readMessage(subBuilder, extensionRegistry); + setSharedCost(subBuilder.buildPartial()); + break; + } + case 58: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasPersonalNumber()) { + subBuilder.mergeFrom(getPersonalNumber()); + } + input.readMessage(subBuilder, extensionRegistry); + setPersonalNumber(subBuilder.buildPartial()); + break; + } + case 66: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(); + if (hasVoip()) { + subBuilder.mergeFrom(getVoip()); + } + input.readMessage(subBuilder, extensionRegistry); + setVoip(subBuilder.buildPartial()); + break; + } + case 74: { + setId(input.readString()); + break; + } + case 80: { + setCountryCode(input.readInt32()); + break; + } + case 90: { + setInternationalPrefix(input.readString()); + break; + } + case 98: { + setNationalPrefix(input.readString()); + break; + } + case 106: { + setPreferredExtnPrefix(input.readString()); + break; + } + case 122: { + setNationalPrefixForParsing(input.readString()); + break; + } + case 130: { + setNationalPrefixTransformRule(input.readString()); + break; + } + case 138: { + setPreferredInternationalPrefix(input.readString()); + break; + } + case 144: { + setSameMobileAndFixedLinePattern(input.readBool()); + break; + } + case 154: { + com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addNumberFormat(subBuilder.buildPartial()); + break; + } + case 162: { + com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addIntlNumberFormat(subBuilder.buildPartial()); + break; + } + case 170: { + setNationalPrefixFormattingRule(input.readString()); + break; + } + } + } + } + + + // required .i18n.phonenumbers.PhoneNumberDesc general_desc = 1; + public boolean hasGeneralDesc() { + return result.hasGeneralDesc(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getGeneralDesc() { + return result.getGeneralDesc(); + } + public Builder setGeneralDesc(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasGeneralDesc = true; + result.generalDesc_ = value; + return this; + } + public Builder setGeneralDesc(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasGeneralDesc = true; + result.generalDesc_ = builderForValue.build(); + return this; + } + public Builder mergeGeneralDesc(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasGeneralDesc() && + result.generalDesc_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.generalDesc_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.generalDesc_).mergeFrom(value).buildPartial(); + } else { + result.generalDesc_ = value; + } + result.hasGeneralDesc = true; + return this; + } + public Builder clearGeneralDesc() { + result.hasGeneralDesc = false; + result.generalDesc_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc fixed_line = 2; + public boolean hasFixedLine() { + return result.hasFixedLine(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getFixedLine() { + return result.getFixedLine(); + } + public Builder setFixedLine(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasFixedLine = true; + result.fixedLine_ = value; + return this; + } + public Builder setFixedLine(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasFixedLine = true; + result.fixedLine_ = builderForValue.build(); + return this; + } + public Builder mergeFixedLine(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasFixedLine() && + result.fixedLine_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.fixedLine_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.fixedLine_).mergeFrom(value).buildPartial(); + } else { + result.fixedLine_ = value; + } + result.hasFixedLine = true; + return this; + } + public Builder clearFixedLine() { + result.hasFixedLine = false; + result.fixedLine_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc mobile = 3; + public boolean hasMobile() { + return result.hasMobile(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getMobile() { + return result.getMobile(); + } + public Builder setMobile(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasMobile = true; + result.mobile_ = value; + return this; + } + public Builder setMobile(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasMobile = true; + result.mobile_ = builderForValue.build(); + return this; + } + public Builder mergeMobile(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasMobile() && + result.mobile_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.mobile_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.mobile_).mergeFrom(value).buildPartial(); + } else { + result.mobile_ = value; + } + result.hasMobile = true; + return this; + } + public Builder clearMobile() { + result.hasMobile = false; + result.mobile_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc toll_free = 4; + public boolean hasTollFree() { + return result.hasTollFree(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getTollFree() { + return result.getTollFree(); + } + public Builder setTollFree(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasTollFree = true; + result.tollFree_ = value; + return this; + } + public Builder setTollFree(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasTollFree = true; + result.tollFree_ = builderForValue.build(); + return this; + } + public Builder mergeTollFree(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasTollFree() && + result.tollFree_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.tollFree_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.tollFree_).mergeFrom(value).buildPartial(); + } else { + result.tollFree_ = value; + } + result.hasTollFree = true; + return this; + } + public Builder clearTollFree() { + result.hasTollFree = false; + result.tollFree_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc premium_rate = 5; + public boolean hasPremiumRate() { + return result.hasPremiumRate(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getPremiumRate() { + return result.getPremiumRate(); + } + public Builder setPremiumRate(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasPremiumRate = true; + result.premiumRate_ = value; + return this; + } + public Builder setPremiumRate(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasPremiumRate = true; + result.premiumRate_ = builderForValue.build(); + return this; + } + public Builder mergePremiumRate(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasPremiumRate() && + result.premiumRate_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.premiumRate_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.premiumRate_).mergeFrom(value).buildPartial(); + } else { + result.premiumRate_ = value; + } + result.hasPremiumRate = true; + return this; + } + public Builder clearPremiumRate() { + result.hasPremiumRate = false; + result.premiumRate_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc shared_cost = 6; + public boolean hasSharedCost() { + return result.hasSharedCost(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getSharedCost() { + return result.getSharedCost(); + } + public Builder setSharedCost(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasSharedCost = true; + result.sharedCost_ = value; + return this; + } + public Builder setSharedCost(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasSharedCost = true; + result.sharedCost_ = builderForValue.build(); + return this; + } + public Builder mergeSharedCost(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasSharedCost() && + result.sharedCost_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.sharedCost_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.sharedCost_).mergeFrom(value).buildPartial(); + } else { + result.sharedCost_ = value; + } + result.hasSharedCost = true; + return this; + } + public Builder clearSharedCost() { + result.hasSharedCost = false; + result.sharedCost_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc personal_number = 7; + public boolean hasPersonalNumber() { + return result.hasPersonalNumber(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getPersonalNumber() { + return result.getPersonalNumber(); + } + public Builder setPersonalNumber(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasPersonalNumber = true; + result.personalNumber_ = value; + return this; + } + public Builder setPersonalNumber(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasPersonalNumber = true; + result.personalNumber_ = builderForValue.build(); + return this; + } + public Builder mergePersonalNumber(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasPersonalNumber() && + result.personalNumber_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.personalNumber_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.personalNumber_).mergeFrom(value).buildPartial(); + } else { + result.personalNumber_ = value; + } + result.hasPersonalNumber = true; + return this; + } + public Builder clearPersonalNumber() { + result.hasPersonalNumber = false; + result.personalNumber_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required .i18n.phonenumbers.PhoneNumberDesc voip = 8; + public boolean hasVoip() { + return result.hasVoip(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc getVoip() { + return result.getVoip(); + } + public Builder setVoip(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasVoip = true; + result.voip_ = value; + return this; + } + public Builder setVoip(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder builderForValue) { + result.hasVoip = true; + result.voip_ = builderForValue.build(); + return this; + } + public Builder mergeVoip(com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc value) { + if (result.hasVoip() && + result.voip_ != com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance()) { + result.voip_ = + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.newBuilder(result.voip_).mergeFrom(value).buildPartial(); + } else { + result.voip_ = value; + } + result.hasVoip = true; + return this; + } + public Builder clearVoip() { + result.hasVoip = false; + result.voip_ = com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.getDefaultInstance(); + return this; + } + + // required string id = 9; + public boolean hasId() { + return result.hasId(); + } + public java.lang.String getId() { + return result.getId(); + } + public Builder setId(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasId = true; + result.id_ = value; + return this; + } + public Builder clearId() { + result.hasId = false; + result.id_ = getDefaultInstance().getId(); + return this; + } + + // required int32 country_code = 10; + public boolean hasCountryCode() { + return result.hasCountryCode(); + } + public int getCountryCode() { + return result.getCountryCode(); + } + public Builder setCountryCode(int value) { + result.hasCountryCode = true; + result.countryCode_ = value; + return this; + } + public Builder clearCountryCode() { + result.hasCountryCode = false; + result.countryCode_ = 0; + return this; + } + + // required string international_prefix = 11; + public boolean hasInternationalPrefix() { + return result.hasInternationalPrefix(); + } + public java.lang.String getInternationalPrefix() { + return result.getInternationalPrefix(); + } + public Builder setInternationalPrefix(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasInternationalPrefix = true; + result.internationalPrefix_ = value; + return this; + } + public Builder clearInternationalPrefix() { + result.hasInternationalPrefix = false; + result.internationalPrefix_ = getDefaultInstance().getInternationalPrefix(); + return this; + } + + // optional string preferred_international_prefix = 17; + public boolean hasPreferredInternationalPrefix() { + return result.hasPreferredInternationalPrefix(); + } + public java.lang.String getPreferredInternationalPrefix() { + return result.getPreferredInternationalPrefix(); + } + public Builder setPreferredInternationalPrefix(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasPreferredInternationalPrefix = true; + result.preferredInternationalPrefix_ = value; + return this; + } + public Builder clearPreferredInternationalPrefix() { + result.hasPreferredInternationalPrefix = false; + result.preferredInternationalPrefix_ = getDefaultInstance().getPreferredInternationalPrefix(); + return this; + } + + // optional string national_prefix = 12; + public boolean hasNationalPrefix() { + return result.hasNationalPrefix(); + } + public java.lang.String getNationalPrefix() { + return result.getNationalPrefix(); + } + public Builder setNationalPrefix(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasNationalPrefix = true; + result.nationalPrefix_ = value; + return this; + } + public Builder clearNationalPrefix() { + result.hasNationalPrefix = false; + result.nationalPrefix_ = getDefaultInstance().getNationalPrefix(); + return this; + } + + // optional string preferred_extn_prefix = 13; + public boolean hasPreferredExtnPrefix() { + return result.hasPreferredExtnPrefix(); + } + public java.lang.String getPreferredExtnPrefix() { + return result.getPreferredExtnPrefix(); + } + public Builder setPreferredExtnPrefix(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasPreferredExtnPrefix = true; + result.preferredExtnPrefix_ = value; + return this; + } + public Builder clearPreferredExtnPrefix() { + result.hasPreferredExtnPrefix = false; + result.preferredExtnPrefix_ = getDefaultInstance().getPreferredExtnPrefix(); + return this; + } + + // optional string national_prefix_for_parsing = 15; + public boolean hasNationalPrefixForParsing() { + return result.hasNationalPrefixForParsing(); + } + public java.lang.String getNationalPrefixForParsing() { + return result.getNationalPrefixForParsing(); + } + public Builder setNationalPrefixForParsing(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasNationalPrefixForParsing = true; + result.nationalPrefixForParsing_ = value; + return this; + } + public Builder clearNationalPrefixForParsing() { + result.hasNationalPrefixForParsing = false; + result.nationalPrefixForParsing_ = getDefaultInstance().getNationalPrefixForParsing(); + return this; + } + + // optional string national_prefix_transform_rule = 16; + public boolean hasNationalPrefixTransformRule() { + return result.hasNationalPrefixTransformRule(); + } + public java.lang.String getNationalPrefixTransformRule() { + return result.getNationalPrefixTransformRule(); + } + public Builder setNationalPrefixTransformRule(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasNationalPrefixTransformRule = true; + result.nationalPrefixTransformRule_ = value; + return this; + } + public Builder clearNationalPrefixTransformRule() { + result.hasNationalPrefixTransformRule = false; + result.nationalPrefixTransformRule_ = getDefaultInstance().getNationalPrefixTransformRule(); + return this; + } + + // optional bool same_mobile_and_fixed_line_pattern = 18 [default = false]; + public boolean hasSameMobileAndFixedLinePattern() { + return result.hasSameMobileAndFixedLinePattern(); + } + public boolean getSameMobileAndFixedLinePattern() { + return result.getSameMobileAndFixedLinePattern(); + } + public Builder setSameMobileAndFixedLinePattern(boolean value) { + result.hasSameMobileAndFixedLinePattern = true; + result.sameMobileAndFixedLinePattern_ = value; + return this; + } + public Builder clearSameMobileAndFixedLinePattern() { + result.hasSameMobileAndFixedLinePattern = false; + result.sameMobileAndFixedLinePattern_ = false; + return this; + } + + // repeated .i18n.phonenumbers.NumberFormat number_format = 19; + public java.util.List getNumberFormatList() { + return java.util.Collections.unmodifiableList(result.numberFormat_); + } + public int getNumberFormatCount() { + return result.getNumberFormatCount(); + } + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat getNumberFormat(int index) { + return result.getNumberFormat(index); + } + public Builder setNumberFormat(int index, com.google.i18n.phonenumbers.Phonemetadata.NumberFormat value) { + if (value == null) { + throw new NullPointerException(); + } + result.numberFormat_.set(index, value); + return this; + } + public Builder setNumberFormat(int index, com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder builderForValue) { + result.numberFormat_.set(index, builderForValue.build()); + return this; + } + public Builder addNumberFormat(com.google.i18n.phonenumbers.Phonemetadata.NumberFormat value) { + if (value == null) { + throw new NullPointerException(); + } + if (result.numberFormat_.isEmpty()) { + result.numberFormat_ = new java.util.ArrayList(); + } + result.numberFormat_.add(value); + return this; + } + public Builder addNumberFormat(com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder builderForValue) { + if (result.numberFormat_.isEmpty()) { + result.numberFormat_ = new java.util.ArrayList(); + } + result.numberFormat_.add(builderForValue.build()); + return this; + } + public Builder addAllNumberFormat( + java.lang.Iterable values) { + if (result.numberFormat_.isEmpty()) { + result.numberFormat_ = new java.util.ArrayList(); + } + super.addAll(values, result.numberFormat_); + return this; + } + public Builder clearNumberFormat() { + result.numberFormat_ = java.util.Collections.emptyList(); + return this; + } + + // repeated .i18n.phonenumbers.NumberFormat intl_number_format = 20; + public java.util.List getIntlNumberFormatList() { + return java.util.Collections.unmodifiableList(result.intlNumberFormat_); + } + public int getIntlNumberFormatCount() { + return result.getIntlNumberFormatCount(); + } + public com.google.i18n.phonenumbers.Phonemetadata.NumberFormat getIntlNumberFormat(int index) { + return result.getIntlNumberFormat(index); + } + public Builder setIntlNumberFormat(int index, com.google.i18n.phonenumbers.Phonemetadata.NumberFormat value) { + if (value == null) { + throw new NullPointerException(); + } + result.intlNumberFormat_.set(index, value); + return this; + } + public Builder setIntlNumberFormat(int index, com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder builderForValue) { + result.intlNumberFormat_.set(index, builderForValue.build()); + return this; + } + public Builder addIntlNumberFormat(com.google.i18n.phonenumbers.Phonemetadata.NumberFormat value) { + if (value == null) { + throw new NullPointerException(); + } + if (result.intlNumberFormat_.isEmpty()) { + result.intlNumberFormat_ = new java.util.ArrayList(); + } + result.intlNumberFormat_.add(value); + return this; + } + public Builder addIntlNumberFormat(com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder builderForValue) { + if (result.intlNumberFormat_.isEmpty()) { + result.intlNumberFormat_ = new java.util.ArrayList(); + } + result.intlNumberFormat_.add(builderForValue.build()); + return this; + } + public Builder addAllIntlNumberFormat( + java.lang.Iterable values) { + if (result.intlNumberFormat_.isEmpty()) { + result.intlNumberFormat_ = new java.util.ArrayList(); + } + super.addAll(values, result.intlNumberFormat_); + return this; + } + public Builder clearIntlNumberFormat() { + result.intlNumberFormat_ = java.util.Collections.emptyList(); + return this; + } + + // optional string national_prefix_formatting_rule = 21; + public boolean hasNationalPrefixFormattingRule() { + return result.hasNationalPrefixFormattingRule(); + } + public java.lang.String getNationalPrefixFormattingRule() { + return result.getNationalPrefixFormattingRule(); + } + public Builder setNationalPrefixFormattingRule(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasNationalPrefixFormattingRule = true; + result.nationalPrefixFormattingRule_ = value; + return this; + } + public Builder clearNationalPrefixFormattingRule() { + result.hasNationalPrefixFormattingRule = false; + result.nationalPrefixFormattingRule_ = getDefaultInstance().getNationalPrefixFormattingRule(); + return this; + } + + // @@protoc_insertion_point(builder_scope:i18n.phonenumbers.PhoneMetadata) + } + + static { + defaultInstance = new PhoneMetadata(true); + com.google.i18n.phonenumbers.Phonemetadata.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:i18n.phonenumbers.PhoneMetadata) + } + + public static final class PhoneMetadataCollection extends + com.google.protobuf.GeneratedMessage { + // Use PhoneMetadataCollection.newBuilder() to construct. + private PhoneMetadataCollection() { + initFields(); + } + private PhoneMetadataCollection(boolean noInit) {} + + private static final PhoneMetadataCollection defaultInstance; + public static PhoneMetadataCollection getDefaultInstance() { + return defaultInstance; + } + + public PhoneMetadataCollection getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_PhoneMetadataCollection_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.i18n.phonenumbers.Phonemetadata.internal_static_i18n_phonenumbers_PhoneMetadataCollection_fieldAccessorTable; + } + + // repeated .i18n.phonenumbers.PhoneMetadata metadata = 1; + public static final int METADATA_FIELD_NUMBER = 1; + private java.util.List metadata_ = + java.util.Collections.emptyList(); + public java.util.List getMetadataList() { + return metadata_; + } + public int getMetadataCount() { return metadata_.size(); } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata getMetadata(int index) { + return metadata_.get(index); + } + + private void initFields() { + } + public final boolean isInitialized() { + for (com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata element : getMetadataList()) { + if (!element.isInitialized()) return false; + } + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata element : getMetadataList()) { + output.writeMessage(1, element); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + for (com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata element : getMetadataList()) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, element); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection result; + + // Construct using com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection(); + return builder; + } + + protected com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection.getDescriptor(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection getDefaultInstanceForType() { + return com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + if (result.metadata_ != java.util.Collections.EMPTY_LIST) { + result.metadata_ = + java.util.Collections.unmodifiableList(result.metadata_); + } + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection) { + return mergeFrom((com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection other) { + if (other == com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection.getDefaultInstance()) return this; + if (!other.metadata_.isEmpty()) { + if (result.metadata_.isEmpty()) { + result.metadata_ = new java.util.ArrayList(); + } + result.metadata_.addAll(other.metadata_); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 10: { + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.Builder subBuilder = com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addMetadata(subBuilder.buildPartial()); + break; + } + } + } + } + + + // repeated .i18n.phonenumbers.PhoneMetadata metadata = 1; + public java.util.List getMetadataList() { + return java.util.Collections.unmodifiableList(result.metadata_); + } + public int getMetadataCount() { + return result.getMetadataCount(); + } + public com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata getMetadata(int index) { + return result.getMetadata(index); + } + public Builder setMetadata(int index, com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata value) { + if (value == null) { + throw new NullPointerException(); + } + result.metadata_.set(index, value); + return this; + } + public Builder setMetadata(int index, com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.Builder builderForValue) { + result.metadata_.set(index, builderForValue.build()); + return this; + } + public Builder addMetadata(com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata value) { + if (value == null) { + throw new NullPointerException(); + } + if (result.metadata_.isEmpty()) { + result.metadata_ = new java.util.ArrayList(); + } + result.metadata_.add(value); + return this; + } + public Builder addMetadata(com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.Builder builderForValue) { + if (result.metadata_.isEmpty()) { + result.metadata_ = new java.util.ArrayList(); + } + result.metadata_.add(builderForValue.build()); + return this; + } + public Builder addAllMetadata( + java.lang.Iterable values) { + if (result.metadata_.isEmpty()) { + result.metadata_ = new java.util.ArrayList(); + } + super.addAll(values, result.metadata_); + return this; + } + public Builder clearMetadata() { + result.metadata_ = java.util.Collections.emptyList(); + return this; + } + + // @@protoc_insertion_point(builder_scope:i18n.phonenumbers.PhoneMetadataCollection) + } + + static { + defaultInstance = new PhoneMetadataCollection(true); + com.google.i18n.phonenumbers.Phonemetadata.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:i18n.phonenumbers.PhoneMetadataCollection) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_i18n_phonenumbers_NumberFormat_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_i18n_phonenumbers_NumberFormat_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_i18n_phonenumbers_PhoneNumberDesc_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_i18n_phonenumbers_PhoneNumberDesc_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_i18n_phonenumbers_PhoneMetadata_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_i18n_phonenumbers_PhoneMetadata_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_i18n_phonenumbers_PhoneMetadataCollection_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_i18n_phonenumbers_PhoneMetadataCollection_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\023phonemetadata.proto\022\021i18n.phonenumbers" + + "\"p\n\014NumberFormat\022\017\n\007pattern\030\001 \002(\t\022\016\n\006for" + + "mat\030\002 \002(\t\022\026\n\016leading_digits\030\003 \001(\t\022\'\n\037nat" + + "ional_prefix_formatting_rule\030\004 \001(\t\"k\n\017Ph" + + "oneNumberDesc\022\037\n\027national_number_pattern" + + "\030\002 \001(\t\022\037\n\027possible_number_pattern\030\003 \001(\t\022" + + "\026\n\016example_number\030\006 \001(\t\"\214\007\n\rPhoneMetadat" + + "a\0228\n\014general_desc\030\001 \002(\0132\".i18n.phonenumb" + + "ers.PhoneNumberDesc\0226\n\nfixed_line\030\002 \002(\0132" + + "\".i18n.phonenumbers.PhoneNumberDesc\0222\n\006m", + "obile\030\003 \002(\0132\".i18n.phonenumbers.PhoneNum" + + "berDesc\0225\n\ttoll_free\030\004 \002(\0132\".i18n.phonen" + + "umbers.PhoneNumberDesc\0228\n\014premium_rate\030\005" + + " \002(\0132\".i18n.phonenumbers.PhoneNumberDesc" + + "\0227\n\013shared_cost\030\006 \002(\0132\".i18n.phonenumber" + + "s.PhoneNumberDesc\022;\n\017personal_number\030\007 \002" + + "(\0132\".i18n.phonenumbers.PhoneNumberDesc\0220" + + "\n\004voip\030\010 \002(\0132\".i18n.phonenumbers.PhoneNu" + + "mberDesc\022\n\n\002id\030\t \002(\t\022\024\n\014country_code\030\n \002" + + "(\005\022\034\n\024international_prefix\030\013 \002(\t\022&\n\036pref", + "erred_international_prefix\030\021 \001(\t\022\027\n\017nati" + + "onal_prefix\030\014 \001(\t\022\035\n\025preferred_extn_pref" + + "ix\030\r \001(\t\022#\n\033national_prefix_for_parsing\030" + + "\017 \001(\t\022&\n\036national_prefix_transform_rule\030" + + "\020 \001(\t\0221\n\"same_mobile_and_fixed_line_patt" + + "ern\030\022 \001(\010:\005false\0226\n\rnumber_format\030\023 \003(\0132" + + "\037.i18n.phonenumbers.NumberFormat\022;\n\022intl" + + "_number_format\030\024 \003(\0132\037.i18n.phonenumbers" + + ".NumberFormat\022\'\n\037national_prefix_formatt" + + "ing_rule\030\025 \001(\t\"M\n\027PhoneMetadataCollectio", + "n\0222\n\010metadata\030\001 \003(\0132 .i18n.phonenumbers." + + "PhoneMetadataB\036\n\034com.google.i18n.phonenu" + + "mbers" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_i18n_phonenumbers_NumberFormat_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_i18n_phonenumbers_NumberFormat_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_i18n_phonenumbers_NumberFormat_descriptor, + new java.lang.String[] { "Pattern", "Format", "LeadingDigits", "NationalPrefixFormattingRule", }, + com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.class, + com.google.i18n.phonenumbers.Phonemetadata.NumberFormat.Builder.class); + internal_static_i18n_phonenumbers_PhoneNumberDesc_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_i18n_phonenumbers_PhoneNumberDesc_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_i18n_phonenumbers_PhoneNumberDesc_descriptor, + new java.lang.String[] { "NationalNumberPattern", "PossibleNumberPattern", "ExampleNumber", }, + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.class, + com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc.Builder.class); + internal_static_i18n_phonenumbers_PhoneMetadata_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_i18n_phonenumbers_PhoneMetadata_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_i18n_phonenumbers_PhoneMetadata_descriptor, + new java.lang.String[] { "GeneralDesc", "FixedLine", "Mobile", "TollFree", "PremiumRate", "SharedCost", "PersonalNumber", "Voip", "Id", "CountryCode", "InternationalPrefix", "PreferredInternationalPrefix", "NationalPrefix", "PreferredExtnPrefix", "NationalPrefixForParsing", "NationalPrefixTransformRule", "SameMobileAndFixedLinePattern", "NumberFormat", "IntlNumberFormat", "NationalPrefixFormattingRule", }, + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.class, + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata.Builder.class); + internal_static_i18n_phonenumbers_PhoneMetadataCollection_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_i18n_phonenumbers_PhoneMetadataCollection_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_i18n_phonenumbers_PhoneMetadataCollection_descriptor, + new java.lang.String[] { "Metadata", }, + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection.class, + com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection.Builder.class); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + public static void internalForceInit() {} + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/java/src/com/google/i18n/phonenumbers/Phonenumber.java b/java/src/com/google/i18n/phonenumbers/Phonenumber.java new file mode 100644 index 000000000..84e794989 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/Phonenumber.java @@ -0,0 +1,462 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: phonenumber.proto + +package com.google.i18n.phonenumbers; + +public final class Phonenumber { + private Phonenumber() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public static final class PhoneNumber extends + com.google.protobuf.GeneratedMessage { + // Use PhoneNumber.newBuilder() to construct. + private PhoneNumber() { + initFields(); + } + private PhoneNumber(boolean noInit) {} + + private static final PhoneNumber defaultInstance; + public static PhoneNumber getDefaultInstance() { + return defaultInstance; + } + + public PhoneNumber getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.i18n.phonenumbers.Phonenumber.internal_static_i18n_phonenumbers_PhoneNumber_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.i18n.phonenumbers.Phonenumber.internal_static_i18n_phonenumbers_PhoneNumber_fieldAccessorTable; + } + + // required int32 country_code = 1; + public static final int COUNTRY_CODE_FIELD_NUMBER = 1; + private boolean hasCountryCode; + private int countryCode_ = 0; + public boolean hasCountryCode() { return hasCountryCode; } + public int getCountryCode() { return countryCode_; } + + // required uint64 national_number = 2; + public static final int NATIONAL_NUMBER_FIELD_NUMBER = 2; + private boolean hasNationalNumber; + private long nationalNumber_ = 0L; + public boolean hasNationalNumber() { return hasNationalNumber; } + public long getNationalNumber() { return nationalNumber_; } + + // optional string extension = 3; + public static final int EXTENSION_FIELD_NUMBER = 3; + private boolean hasExtension; + private java.lang.String extension_ = ""; + public boolean hasExtension() { return hasExtension; } + public java.lang.String getExtension() { return extension_; } + + // optional bool italian_leading_zero = 4; + public static final int ITALIAN_LEADING_ZERO_FIELD_NUMBER = 4; + private boolean hasItalianLeadingZero; + private boolean italianLeadingZero_ = false; + public boolean hasItalianLeadingZero() { return hasItalianLeadingZero; } + public boolean getItalianLeadingZero() { return italianLeadingZero_; } + + private void initFields() { + } + public final boolean isInitialized() { + if (!hasCountryCode) return false; + if (!hasNationalNumber) return false; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (hasCountryCode()) { + output.writeInt32(1, getCountryCode()); + } + if (hasNationalNumber()) { + output.writeUInt64(2, getNationalNumber()); + } + if (hasExtension()) { + output.writeString(3, getExtension()); + } + if (hasItalianLeadingZero()) { + output.writeBool(4, getItalianLeadingZero()); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasCountryCode()) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, getCountryCode()); + } + if (hasNationalNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(2, getNationalNumber()); + } + if (hasExtension()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(3, getExtension()); + } + if (hasItalianLeadingZero()) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(4, getItalianLeadingZero()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.google.i18n.phonenumbers.Phonenumber.PhoneNumber parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.google.i18n.phonenumbers.Phonenumber.PhoneNumber prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder { + private com.google.i18n.phonenumbers.Phonenumber.PhoneNumber result; + + // Construct using com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.newBuilder() + private Builder() {} + + private static Builder create() { + Builder builder = new Builder(); + builder.result = new com.google.i18n.phonenumbers.Phonenumber.PhoneNumber(); + return builder; + } + + protected com.google.i18n.phonenumbers.Phonenumber.PhoneNumber internalGetResult() { + return result; + } + + public Builder clear() { + if (result == null) { + throw new IllegalStateException( + "Cannot call clear() after build()."); + } + result = new com.google.i18n.phonenumbers.Phonenumber.PhoneNumber(); + return this; + } + + public Builder clone() { + return create().mergeFrom(result); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.getDescriptor(); + } + + public com.google.i18n.phonenumbers.Phonenumber.PhoneNumber getDefaultInstanceForType() { + return com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.getDefaultInstance(); + } + + public boolean isInitialized() { + return result.isInitialized(); + } + public com.google.i18n.phonenumbers.Phonenumber.PhoneNumber build() { + if (result != null && !isInitialized()) { + throw newUninitializedMessageException(result); + } + return buildPartial(); + } + + private com.google.i18n.phonenumbers.Phonenumber.PhoneNumber buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + if (!isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public com.google.i18n.phonenumbers.Phonenumber.PhoneNumber buildPartial() { + if (result == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } + com.google.i18n.phonenumbers.Phonenumber.PhoneNumber returnMe = result; + result = null; + return returnMe; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.i18n.phonenumbers.Phonenumber.PhoneNumber) { + return mergeFrom((com.google.i18n.phonenumbers.Phonenumber.PhoneNumber)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.i18n.phonenumbers.Phonenumber.PhoneNumber other) { + if (other == com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.getDefaultInstance()) return this; + if (other.hasCountryCode()) { + setCountryCode(other.getCountryCode()); + } + if (other.hasNationalNumber()) { + setNationalNumber(other.getNationalNumber()); + } + if (other.hasExtension()) { + setExtension(other.getExtension()); + } + if (other.hasItalianLeadingZero()) { + setItalianLeadingZero(other.getItalianLeadingZero()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + return this; + } + break; + } + case 8: { + setCountryCode(input.readInt32()); + break; + } + case 16: { + setNationalNumber(input.readUInt64()); + break; + } + case 26: { + setExtension(input.readString()); + break; + } + case 32: { + setItalianLeadingZero(input.readBool()); + break; + } + } + } + } + + + // required int32 country_code = 1; + public boolean hasCountryCode() { + return result.hasCountryCode(); + } + public int getCountryCode() { + return result.getCountryCode(); + } + public Builder setCountryCode(int value) { + result.hasCountryCode = true; + result.countryCode_ = value; + return this; + } + public Builder clearCountryCode() { + result.hasCountryCode = false; + result.countryCode_ = 0; + return this; + } + + // required uint64 national_number = 2; + public boolean hasNationalNumber() { + return result.hasNationalNumber(); + } + public long getNationalNumber() { + return result.getNationalNumber(); + } + public Builder setNationalNumber(long value) { + result.hasNationalNumber = true; + result.nationalNumber_ = value; + return this; + } + public Builder clearNationalNumber() { + result.hasNationalNumber = false; + result.nationalNumber_ = 0L; + return this; + } + + // optional string extension = 3; + public boolean hasExtension() { + return result.hasExtension(); + } + public java.lang.String getExtension() { + return result.getExtension(); + } + public Builder setExtension(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasExtension = true; + result.extension_ = value; + return this; + } + public Builder clearExtension() { + result.hasExtension = false; + result.extension_ = getDefaultInstance().getExtension(); + return this; + } + + // optional bool italian_leading_zero = 4; + public boolean hasItalianLeadingZero() { + return result.hasItalianLeadingZero(); + } + public boolean getItalianLeadingZero() { + return result.getItalianLeadingZero(); + } + public Builder setItalianLeadingZero(boolean value) { + result.hasItalianLeadingZero = true; + result.italianLeadingZero_ = value; + return this; + } + public Builder clearItalianLeadingZero() { + result.hasItalianLeadingZero = false; + result.italianLeadingZero_ = false; + return this; + } + + // @@protoc_insertion_point(builder_scope:i18n.phonenumbers.PhoneNumber) + } + + static { + defaultInstance = new PhoneNumber(true); + com.google.i18n.phonenumbers.Phonenumber.internalForceInit(); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:i18n.phonenumbers.PhoneNumber) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_i18n_phonenumbers_PhoneNumber_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_i18n_phonenumbers_PhoneNumber_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\021phonenumber.proto\022\021i18n.phonenumbers\"m" + + "\n\013PhoneNumber\022\024\n\014country_code\030\001 \002(\005\022\027\n\017n" + + "ational_number\030\002 \002(\004\022\021\n\textension\030\003 \001(\t\022" + + "\034\n\024italian_leading_zero\030\004 \001(\010B\036\n\034com.goo" + + "gle.i18n.phonenumbers" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_i18n_phonenumbers_PhoneNumber_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_i18n_phonenumbers_PhoneNumber_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_i18n_phonenumbers_PhoneNumber_descriptor, + new java.lang.String[] { "CountryCode", "NationalNumber", "Extension", "ItalianLeadingZero", }, + com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.class, + com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.Builder.class); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + public static void internalForceInit() {} + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/java/src/com/google/i18n/phonenumbers/phonemetadata.proto b/java/src/com/google/i18n/phonenumbers/phonemetadata.proto new file mode 100644 index 000000000..d0be4af37 --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/phonemetadata.proto @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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. + */ + +// Definition of protocol buffer for holding metadata for international +// telephone numbers. +// @author Shaopeng Jia + +syntax = "proto2"; + +option java_package = "com.google.i18n.phonenumbers"; +package i18n.phonenumbers; + +message NumberFormat { + // pattern is a regex that is used to match the national (significant) + // number. For example, the pattern "(20)(\d{4})(\d{4})" will match number + // "2070313000", which is the national (significant) number for Google London. + // Note the presence of the parentheses, which are capturing groups what + // specifies the grouping of numbers. + required string pattern = 1; + + // format specifies how the national (significant) number matched by + // pattern should be formatted. + // Using the same example as above, format could contain "$1 $2 $3", + // meaning that the number should be formatted as "20 7031 3000". + // Each $x are replaced by the numbers captured by group x in the + // regex specified by pattern. + required string format = 2; + + // leadingDigits is a regex that is used to match up to the first four digits + // of the national (significant) number. When the match is successful, the + // accompanying pattern and format should be used to format this number. For + // example, if leading_digits="[1-3]|44", then all the national numbers + // starting with 1, 2, 3 or 44 should be formatted using the accompanying + // pattern and format. + optional string leading_digits = 3; + + // This field specifies how the national prefix ($NP) together with the first + // group ($FG) in the national significant number should be formatted in + // the NATIONAL format when a national prefix exists for a certain country. + // When present, it overrides the national_prefix_formatting_rule specified + // in PhoneNumberDesc. See the comment in PhoneNumberDesc for more information + // on how this rule is specified. + optional string national_prefix_formatting_rule = 4; +} + +message PhoneNumberDesc { + // The national_number_pattern is the pattern that a valid national + // significant number would match. This specifies information such as its + // total length and leading digits. + optional string national_number_pattern = 2; + + // The possible_number_pattern represents what a potentially valid phone + // number for this region may be written as. This is a superset of the + // national_number_pattern above and includes numbers that have the area code + // omitted. Typically the only restrictions here are in the number of digits. + // This could be used to highlight tokens in a text that may be a phone + // number, or to quickly prune numbers that could not possibly be a phone + // number for this locale. + optional string possible_number_pattern = 3; + + // An example national significant number for the specific type. It should + // not contain any formatting information. + optional string example_number = 6; +} + +message PhoneMetadata { + // The general_desc contains information which is a superset of descriptions + // for all types of phone numbers. If any element is missing in the + // description of a specific type in the XML file, the element will inherit + // from its counterpart in the general_desc. Every locale is assumed to have + // fixed line and mobile numbers - if these types are missing in the XML + // file, they will inherit all fields from the general_desc. For all other + // types, if the whole type is missing in the xml file, it will be given a + // national_number_pattern of "NA" and a possible_number_pattern of "NA". + required PhoneNumberDesc general_desc = 1; + required PhoneNumberDesc fixed_line = 2; + required PhoneNumberDesc mobile = 3; + required PhoneNumberDesc toll_free = 4; + required PhoneNumberDesc premium_rate = 5; + required PhoneNumberDesc shared_cost = 6; + required PhoneNumberDesc personal_number = 7; + required PhoneNumberDesc voip = 8; + + // The ISO 3166-1 alpha-2 representation of a country/region + required string id = 9; + + // The country calling code that one would dial from overseas when trying to + // dial a phone number in this country. For example, this would be "64" for + // New Zealand. + required int32 country_code = 10; + + // The international_prefix of country A is the number that needs to be + // dialled from country A to another country (country B). This is followed + // by the country code for country B. Note that some countries may have more + // than one international prefix, and for those cases, a regular expression + // matching the international prefixes will be stored in this field. + required string international_prefix = 11; + + // If more than one international prefix is present, a preferred prefix can + // be specified here for out-of-country formatting purposes. If this field is + // not present, and multiple international prefixes are present, then "+" + // will be used instead. + optional string preferred_international_prefix = 17; + + // The national prefix of country A is the number that needs to be dialled + // before the national significant number when dialling internally. This + // would not be dialled when dialling internationally. For example, in New + // Zealand, the number that would be locally dialled as 09 345 3456 would be + // dialled from overseas as +64 9 345 3456. In this case, 0 is the national + // prefix. + optional string national_prefix = 12; + + // The preferred prefix when specifying an extension in this country. This is + // used for formatting only, and if this is not specified, a suitable default + // should be used instead. For example, if you wanted extensions to be + // formatted in the following way: + // 1 (365) 345 445 ext. 2345 + // " ext. " should be the preferred extension prefix. + optional string preferred_extn_prefix = 13; + + // This field is used for cases where the national prefix of a country + // contains a carrier selection code, and is written in the form of a + // regular expression. For example, to dial the number 2222-2222 in + // Fortaleza, Brazil (area code 85) using the long distance carrier Oi + // (selection code 31), one would dial 0 31 85 2222 2222. Assuming the + // only other possible carrier selection code is 32, the field will + // contain "03[12]". + // + // When it is missing from the XML file, this field inherits the value of + // national_prefix, if that is present. + optional string national_prefix_for_parsing = 15; + + // This field is only populated and used under very rare situations. + // For example, mobile numbers in Argentina are written in two completely + // different ways when dialed in-country and out-of-country + // (e.g. 0343 15 555 1212 is exactly the same number as +54 9 343 555 1212). + // This field is used together with national_prefix_for_parsing to transform + // the number into a particular representation for storing in the phonenumber + // proto buffer in those rare cases. + optional string national_prefix_transform_rule = 16; + + // Specifies whether the mobile and fixed-line patterns are the same or not. + // This is used to speed up determining phone number type in countries where + // these two types of phone numbers can never be distinguished. + optional bool same_mobile_and_fixed_line_pattern = 18 [default=false]; + + // Note that the number format here is used for formatting only, not parsing. + // Hence all the varied ways a user *may* write a number need not be recorded + // - just the ideal way we would like to format it for them. When this element + // is absent, the national significant number will be formatted as a whole + // without any formatting applied. + // + // When formatting, the library goes through the list of formats from the + // beginning and the first successful match is used to do the formatting. + // A match is successful if the phone number being formatted starts with + // digits matching the leadingDigits and the number itself matches the + // corresponding pattern. However, AsYouTypeFormatter goes through the whole + // list and selects formats whose leadingDigits match what has been typed + // so far. Therefore, having more specific leadingDigits improves the + // performance of AsYouTypeFormatter in terms of speed. + repeated NumberFormat number_format = 19; + + // This field is populated only when the national significant number is + // formatted differently when it forms part of the INTERNATIONAL format + // and NATIONAL format. A case in point is mobile numbers in Argentina: + // The number, which would be written in INTERNATIONAL format as + // +54 9 343 555 1212, will be written as 0343 15 555 1212 for NATIONAL + // format. In this case, the prefix 9 is inserted when dialling from + // overseas, but otherwise the prefix 0 and the carrier selection code + // 15 (inserted after the area code of 343) is used. + repeated NumberFormat intl_number_format = 20; + + // This field specifies how the national prefix ($NP) together with the first + // group ($FG) in the national significant number should be formatted in + // the NATIONAL format when a national prefix exists for a certain country. + // For example, when this field contains "($NP$FG)", a number from Beijing, + // China (whose $NP = 0), which would by default be formatted without + // national prefix as 10 1234 5678 in NATIONAL format, will instead be + // formatted as (010) 1234 5678; to format it as (0)10 1234 5678, the field + // would contain "($NP)$FG". Note $FG should always be present in this field, + // but $NP can be omitted. For example, having "$FG" could indicate the + // number should be formatted in NATIONAL format without the national prefix. + // This is commonly used to override the rule from generalDesc. + // + // When this field is missing, a number will be formatted without national + // prefix in NATIONAL format. This field does not affect how a number + // is formatted in other formats, such as INTERNATIONAL. + // + // This field applies to all numberFormats unless overridden by the + // national_prefix_formatting_rule in a specific numberFormat. + optional string national_prefix_formatting_rule = 21; +} + +message PhoneMetadataCollection { + repeated PhoneMetadata metadata = 1; +} diff --git a/java/src/com/google/i18n/phonenumbers/phonenumber.proto b/java/src/com/google/i18n/phonenumbers/phonenumber.proto new file mode 100644 index 000000000..eee21a82d --- /dev/null +++ b/java/src/com/google/i18n/phonenumbers/phonenumber.proto @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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. + */ + +// Definition of protocol buffer for representing international telephone numbers. +// @author Shaopeng Jia + +syntax = "proto2"; + +option java_package = "com.google.i18n.phonenumbers"; +package i18n.phonenumbers; + +message PhoneNumber { + required int32 country_code = 1; + +// National (significant) Number is defined in International Telecommunication Union Recommendation +// E.164. It is a language/country-neutral representation of a phone number at a country level. For +// countries which have the concept of Area Code, the National (significant) Number contains the +// area code. It contains a maximum number of digits which equal to 15 - n, where n is the number of +// digits of the country code. Take note that National (significant) Number does not contain +// National(trunk) prefix. Obviously, as a uint64, it will never contain any formatting (hypens, +// spaces, parentheses), nor any alphanumeric spellings. + required uint64 national_number = 2; + +// Extension is not standardized in ITU recommendations, except for being defined as a series of +// numbers with a maximum length of 40 digits. It is defined as a string here to accommodate for the +// possible use of a leading zero in the extension (organizations have complete freedom to do so, +// as there is no standard defined). However, only ASCII digits should be stored here. + optional string extension = 3; + +// The leading zero in the national (significant) number of an Italian phone number has a special +// meaning. Unlike the rest of the world, it indicates the number is a fixed-line number. There have +// been plans to migrate fixed-line numbers to start with the digit two since December 2000, but it +// has not happened yet. See http://en.wikipedia.org/wiki/%2B39 for more details. +// +// This field can be safely ignored (no need to set it) if you are not dealing with Italian +// phone numbers. For an Italian phone number, if its national (significant) number starts +// with the digit zero, set this flag to true. + optional bool italian_leading_zero = 4; +} + +// Examples +// +// Google MTV, +1 650-253-0000, (650) 253-0000 +// country_code: 1 +// national_number: 6502530000 +// +// Google Paris, +33 (0)1 42 68 53 00, 01 42 68 53 00 +// country_code: 33 +// national_number: 142685300 +// +// Google Beijing, +86-10-62503000, (010) 62503000 +// country_code: 86 +// national_number: 1062503000 +// +// Google Italy, +39 02-36618 300, 02-36618 300 +// country_code: 39 +// national_number: 236618300 +// italian_leading_zero: true