summaryrefslogtreecommitdiff
path: root/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java')
-rw-r--r--libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java375
1 files changed, 248 insertions, 127 deletions
diff --git a/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java b/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
index 0a0e455f..5a41d761 100644
--- a/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
+++ b/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
@@ -16,11 +16,11 @@
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.PhoneNumberDesc;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
-import com.google.i18n.phonenumbers.nano.Phonemetadata.NumberFormat;
-import com.google.i18n.phonenumbers.nano.Phonemetadata.PhoneMetadata;
-import com.google.i18n.phonenumbers.nano.Phonemetadata.PhoneNumberDesc;
import java.io.InputStream;
import java.util.ArrayList;
@@ -88,6 +88,19 @@ public class PhoneNumberUtil {
// be the length of the area code plus the length of the mobile token.
private static final Map<Integer, String> MOBILE_TOKEN_MAPPINGS;
+ // Set of country codes that have geographically assigned mobile numbers (see GEO_MOBILE_COUNTRIES
+ // below) which are not based on *area codes*. For example, in China mobile numbers start with a
+ // carrier indicator, and beyond that are geographically assigned: this carrier indicator is not
+ // considered to be an area code.
+ private static final Set<Integer> GEO_MOBILE_COUNTRIES_WITHOUT_MOBILE_AREA_CODES;
+
+ // Set of country calling codes that have geographically assigned mobile numbers. This may not be
+ // complete; we add calling codes case by case, as we find geographical mobile numbers or hear
+ // from user reports. Note that countries like the US, where we can't distinguish between
+ // fixed-line or mobile numbers, are not listed here, since we consider FIXED_LINE_OR_MOBILE to be
+ // a possibly geographically-related type anyway (like FIXED_LINE).
+ private static final Set<Integer> GEO_MOBILE_COUNTRIES;
+
// The PLUS_SIGN signifies the international prefix.
static final char PLUS_SIGN = '+';
@@ -119,6 +132,19 @@ public class PhoneNumberUtil {
mobileTokenMap.put(54, "9");
MOBILE_TOKEN_MAPPINGS = Collections.unmodifiableMap(mobileTokenMap);
+ HashSet<Integer> geoMobileCountriesWithoutMobileAreaCodes = new HashSet<Integer>();
+ geoMobileCountriesWithoutMobileAreaCodes.add(86); // China
+ GEO_MOBILE_COUNTRIES_WITHOUT_MOBILE_AREA_CODES =
+ Collections.unmodifiableSet(geoMobileCountriesWithoutMobileAreaCodes);
+
+ HashSet<Integer> geoMobileCountries = new HashSet<Integer>();
+ geoMobileCountries.add(52); // Mexico
+ geoMobileCountries.add(54); // Argentina
+ geoMobileCountries.add(55); // Brazil
+ geoMobileCountries.add(62); // Indonesia: some prefixes only (fixed CMDA wireless)
+ geoMobileCountries.addAll(geoMobileCountriesWithoutMobileAreaCodes);
+ GEO_MOBILE_COUNTRIES = Collections.unmodifiableSet(geoMobileCountries);
+
// Simple ASCII digits map used to populate ALPHA_PHONE_MAPPINGS and
// ALL_PLUS_NUMBER_GROUPING_SYMBOLS.
HashMap<Character, Character> asciiDigitMappings = new HashMap<Character, Character>();
@@ -587,24 +613,6 @@ public class PhoneNumberUtil {
}
/**
- * Returns a copy of the given NumberFormat object.
- */
- static NumberFormat copyNumberFormat(NumberFormat other) {
- NumberFormat copy = new NumberFormat();
- copy.pattern = other.pattern;
- copy.format = other.format;
- int leadingDigitsPatternSize = other.leadingDigitsPattern.length;
- copy.leadingDigitsPattern = new String[leadingDigitsPatternSize];
- for (int i = 0; i < leadingDigitsPatternSize; i++) {
- copy.leadingDigitsPattern[i] = other.leadingDigitsPattern[i];
- }
- copy.nationalPrefixFormattingRule = other.nationalPrefixFormattingRule;
- copy.domesticCarrierCodeFormattingRule = other.domesticCarrierCodeFormattingRule;
- copy.nationalPrefixOptionalWhenFormatting = other.nationalPrefixOptionalWhenFormatting;
- return copy;
- }
-
- /**
* Attempts to extract a possible number from the string passed in. This currently strips all
* leading characters that cannot 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
@@ -787,11 +795,21 @@ public class PhoneNumberUtil {
}
// If a country doesn't use a national prefix, and this number doesn't have an Italian leading
// zero, we assume it is a closed dialling plan with no area codes.
- if (metadata.nationalPrefix.equals("") && !number.isItalianLeadingZero()) {
+ if (!metadata.hasNationalPrefix() && !number.isItalianLeadingZero()) {
+ return 0;
+ }
+
+ PhoneNumberType type = getNumberType(number);
+ int countryCallingCode = number.getCountryCode();
+ if (type == PhoneNumberType.MOBILE
+ // Note this is a rough heuristic; it doesn't cover Indonesia well, for example, where area
+ // codes are present for some mobile phones but not for others. We have no better way of
+ // representing this in the metadata at this point.
+ && GEO_MOBILE_COUNTRIES_WITHOUT_MOBILE_AREA_CODES.contains(countryCallingCode)) {
return 0;
}
- if (!isNumberGeographical(number)) {
+ if (!isNumberGeographical(type, countryCallingCode)) {
return 0;
}
@@ -1006,18 +1024,23 @@ public class PhoneNumberUtil {
* Tests whether a phone number has a geographical association. It checks if the number is
* associated to a certain region in the country where it belongs to. Note that this doesn't
* verify if the number is actually in use.
+ */
+ public boolean isNumberGeographical(PhoneNumber phoneNumber) {
+ return isNumberGeographical(getNumberType(phoneNumber), phoneNumber.getCountryCode());
+ }
+
+ /**
+ * Tests whether a phone number has a geographical association, as represented by its type and the
+ * country it belongs to.
*
- * A similar method is implemented as PhoneNumberOfflineGeocoder.canBeGeocoded, which performs a
- * looser check, since it only prevents cases where prefixes overlap for geocodable and
- * non-geocodable numbers. Also, if new phone number types were added, we should check if this
- * other method should be updated too.
+ * This version of isNumberGeographical exists since calculating the phone number type is
+ * expensive; if we have already done this, we don't want to do it again.
*/
- boolean isNumberGeographical(PhoneNumber phoneNumber) {
- PhoneNumberType numberType = getNumberType(phoneNumber);
- // TODO: Include mobile phone numbers from countries like Indonesia, which has some
- // mobile numbers that are geographical.
- return numberType == PhoneNumberType.FIXED_LINE ||
- numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE;
+ public boolean isNumberGeographical(PhoneNumberType numberType, int countryCallingCode) {
+ return numberType == PhoneNumberType.FIXED_LINE
+ || numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE
+ || (GEO_MOBILE_COUNTRIES.contains(countryCallingCode)
+ && numberType == PhoneNumberType.MOBILE);
}
/**
@@ -1131,29 +1154,29 @@ public class PhoneNumberUtil {
StringBuilder formattedNumber = new StringBuilder(20);
NumberFormat formattingPattern =
- chooseFormattingPatternForNumber(userDefinedFormats.toArray(
- new NumberFormat[userDefinedFormats.size()]), nationalSignificantNumber);
+ chooseFormattingPatternForNumber(userDefinedFormats, nationalSignificantNumber);
if (formattingPattern == null) {
// If no pattern above is matched, we format the number as a whole.
formattedNumber.append(nationalSignificantNumber);
} else {
+ NumberFormat numFormatCopy = new NumberFormat();
// Before we do a replacement of the national prefix pattern $NP with the national prefix, we
// need to copy the rule so that subsequent replacements for different numbers have the
// appropriate national prefix.
- NumberFormat numFormatCopy = copyNumberFormat(formattingPattern);
- String nationalPrefixFormattingRule = formattingPattern.nationalPrefixFormattingRule;
+ numFormatCopy.mergeFrom(formattingPattern);
+ String nationalPrefixFormattingRule = formattingPattern.getNationalPrefixFormattingRule();
if (nationalPrefixFormattingRule.length() > 0) {
- String nationalPrefix = metadata.nationalPrefix;
+ String nationalPrefix = metadata.getNationalPrefix();
if (nationalPrefix.length() > 0) {
// Replace $NP with national prefix and $FG with the first group ($1).
nationalPrefixFormattingRule =
NP_PATTERN.matcher(nationalPrefixFormattingRule).replaceFirst(nationalPrefix);
nationalPrefixFormattingRule =
FG_PATTERN.matcher(nationalPrefixFormattingRule).replaceFirst("\\$1");
- numFormatCopy.nationalPrefixFormattingRule = nationalPrefixFormattingRule;
+ numFormatCopy.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
} else {
// We don't want to have a rule for how to format the national prefix if there isn't one.
- numFormatCopy.nationalPrefixFormattingRule = "";
+ numFormatCopy.clearNationalPrefixFormattingRule();
}
}
formattedNumber.append(
@@ -1369,16 +1392,16 @@ public class PhoneNumberUtil {
}
// Metadata cannot be null because we checked 'isValidRegionCode()' above.
PhoneMetadata metadataForRegionCallingFrom = getMetadataForRegion(regionCallingFrom);
- String internationalPrefix = metadataForRegionCallingFrom.internationalPrefix;
+ String internationalPrefix = metadataForRegionCallingFrom.getInternationalPrefix();
// For regions 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 (!metadataForRegionCallingFrom.preferredInternationalPrefix.equals("")) {
+ } else if (metadataForRegionCallingFrom.hasPreferredInternationalPrefix()) {
internationalPrefixForFormatting =
- metadataForRegionCallingFrom.preferredInternationalPrefix;
+ metadataForRegionCallingFrom.getPreferredInternationalPrefix();
}
String regionCode = getRegionCodeForCountryCode(countryCallingCode);
@@ -1464,7 +1487,7 @@ public class PhoneNumberUtil {
PhoneMetadata metadata = getMetadataForRegion(regionCode);
String nationalNumber = getNationalSignificantNumber(number);
NumberFormat formatRule =
- chooseFormattingPatternForNumber(metadata.numberFormat, nationalNumber);
+ chooseFormattingPatternForNumber(metadata.numberFormats(), nationalNumber);
// The format rule could still be null here if the national number was 0 and there was no
// raw input (this should not be possible for numbers generated by the phonenumber library
// as they would also not have a country calling code and we would have exited earlier).
@@ -1476,7 +1499,7 @@ public class PhoneNumberUtil {
// return the national format.
// TODO: Refactor the code below with the code in
// isNationalPrefixPresentIfRequired.
- String candidateNationalPrefixRule = formatRule.nationalPrefixFormattingRule;
+ String candidateNationalPrefixRule = formatRule.getNationalPrefixFormattingRule();
// We assume that the first-group symbol will never be _before_ the national prefix.
int indexOfFirstGroup = candidateNationalPrefixRule.indexOf("$1");
if (indexOfFirstGroup <= 0) {
@@ -1492,8 +1515,9 @@ public class PhoneNumberUtil {
break;
}
// Otherwise, we need to remove the national prefix from our output.
- NumberFormat numFormatCopy = copyNumberFormat(formatRule);
- numFormatCopy.nationalPrefixFormattingRule = "";
+ NumberFormat numFormatCopy = new NumberFormat();
+ numFormatCopy.mergeFrom(formatRule);
+ numFormatCopy.clearNationalPrefixFormattingRule();
List<NumberFormat> numberFormats = new ArrayList<NumberFormat>(1);
numberFormats.add(numFormatCopy);
formattedNumber = formatByPattern(number, PhoneNumberFormat.NATIONAL, numberFormats);
@@ -1550,7 +1574,7 @@ public class PhoneNumberUtil {
}
String nationalNumber = getNationalSignificantNumber(number);
NumberFormat formatRule =
- chooseFormattingPatternForNumber(metadata.numberFormat, nationalNumber);
+ chooseFormattingPatternForNumber(metadata.numberFormats(), nationalNumber);
return formatRule != null;
}
@@ -1614,17 +1638,18 @@ public class PhoneNumberUtil {
} else if (metadataForRegionCallingFrom != null &&
countryCode == getCountryCodeForValidRegion(regionCallingFrom)) {
NumberFormat formattingPattern =
- chooseFormattingPatternForNumber(metadataForRegionCallingFrom.numberFormat,
+ chooseFormattingPatternForNumber(metadataForRegionCallingFrom.numberFormats(),
nationalNumber);
if (formattingPattern == null) {
// If no pattern above is matched, we format the original input.
return rawInput;
}
- NumberFormat newFormat = copyNumberFormat(formattingPattern);
+ NumberFormat newFormat = new NumberFormat();
+ newFormat.mergeFrom(formattingPattern);
// The first group is the first group of digits that the user wrote together.
- newFormat.pattern = "(\\d+)(.*)";
+ newFormat.setPattern("(\\d+)(.*)");
// Here we just concatenate them back together after the national prefix has been fixed.
- newFormat.format = "$1$2";
+ newFormat.setFormat("$1$2");
// Now we format using this pattern instead of the default pattern, but with the national
// prefix prefixed if necessary.
// This will not work in the cases where the pattern (and not the leading digits) decide
@@ -1637,11 +1662,11 @@ public class PhoneNumberUtil {
// prefixes, the international format of the number is returned, unless there is a preferred
// international prefix.
if (metadataForRegionCallingFrom != null) {
- String internationalPrefix = metadataForRegionCallingFrom.internationalPrefix;
+ String internationalPrefix = metadataForRegionCallingFrom.getInternationalPrefix();
internationalPrefixForFormatting =
UNIQUE_INTERNATIONAL_PREFIX.matcher(internationalPrefix).matches()
? internationalPrefix
- : metadataForRegionCallingFrom.preferredInternationalPrefix;
+ : metadataForRegionCallingFrom.getPreferredInternationalPrefix();
}
StringBuilder formattedNumber = new StringBuilder(rawInput);
String regionCode = getRegionCodeForCountryCode(countryCode);
@@ -1723,27 +1748,27 @@ public class PhoneNumberUtil {
PhoneMetadata metadata,
PhoneNumberFormat numberFormat,
String carrierCode) {
- NumberFormat[] intlNumberFormats = metadata.intlNumberFormat;
+ List<NumberFormat> intlNumberFormats = metadata.intlNumberFormats();
// When the intlNumberFormats exists, we use that to format national number for the
// INTERNATIONAL format instead of using the numberDesc.numberFormats.
- NumberFormat[] availableFormats =
- (intlNumberFormats.length == 0 || numberFormat == PhoneNumberFormat.NATIONAL)
- ? metadata.numberFormat
- : metadata.intlNumberFormat;
+ List<NumberFormat> availableFormats =
+ (intlNumberFormats.size() == 0 || numberFormat == PhoneNumberFormat.NATIONAL)
+ ? metadata.numberFormats()
+ : metadata.intlNumberFormats();
NumberFormat formattingPattern = chooseFormattingPatternForNumber(availableFormats, number);
return (formattingPattern == null)
? number
: formatNsnUsingPattern(number, formattingPattern, numberFormat, carrierCode);
}
- NumberFormat chooseFormattingPatternForNumber(NumberFormat[] availableFormats,
+ NumberFormat chooseFormattingPatternForNumber(List<NumberFormat> availableFormats,
String nationalNumber) {
for (NumberFormat numFormat : availableFormats) {
- int size = numFormat.leadingDigitsPattern.length;
+ int size = numFormat.leadingDigitsPatternSize();
if (size == 0 || regexCache.getPatternForRegex(
// We always use the last leading_digits_pattern, as it is the most detailed.
- numFormat.leadingDigitsPattern[size - 1]).matcher(nationalNumber).lookingAt()) {
- Matcher m = regexCache.getPatternForRegex(numFormat.pattern).matcher(nationalNumber);
+ numFormat.getLeadingDigitsPattern(size - 1)).matcher(nationalNumber).lookingAt()) {
+ Matcher m = regexCache.getPatternForRegex(numFormat.getPattern()).matcher(nationalNumber);
if (m.matches()) {
return numFormat;
}
@@ -1765,15 +1790,15 @@ public class PhoneNumberUtil {
NumberFormat formattingPattern,
PhoneNumberFormat numberFormat,
String carrierCode) {
- String numberFormatRule = formattingPattern.format;
+ String numberFormatRule = formattingPattern.getFormat();
Matcher m =
- regexCache.getPatternForRegex(formattingPattern.pattern).matcher(nationalNumber);
+ regexCache.getPatternForRegex(formattingPattern.getPattern()).matcher(nationalNumber);
String formattedNationalNumber = "";
if (numberFormat == PhoneNumberFormat.NATIONAL &&
carrierCode != null && carrierCode.length() > 0 &&
- formattingPattern.domesticCarrierCodeFormattingRule.length() > 0) {
+ formattingPattern.getDomesticCarrierCodeFormattingRule().length() > 0) {
// Replace the $CC in the formatting rule with the desired carrier code.
- String carrierCodeFormattingRule = formattingPattern.domesticCarrierCodeFormattingRule;
+ String carrierCodeFormattingRule = formattingPattern.getDomesticCarrierCodeFormattingRule();
carrierCodeFormattingRule =
CC_PATTERN.matcher(carrierCodeFormattingRule).replaceFirst(carrierCode);
// Now replace the $FG in the formatting rule with the first group and the carrier code
@@ -1783,7 +1808,7 @@ public class PhoneNumberUtil {
formattedNationalNumber = m.replaceAll(numberFormatRule);
} else {
// Use the national prefix formatting rule instead.
- String nationalPrefixFormattingRule = formattingPattern.nationalPrefixFormattingRule;
+ String nationalPrefixFormattingRule = formattingPattern.getNationalPrefixFormattingRule();
if (numberFormat == PhoneNumberFormat.NATIONAL &&
nationalPrefixFormattingRule != null &&
nationalPrefixFormattingRule.length() > 0) {
@@ -1819,6 +1844,62 @@ public class PhoneNumberUtil {
}
/**
+ * Gets an invalid number for the specified region. This is useful for unit-testing purposes,
+ * where you want to test what will happen with an invalid number. Note that the number that is
+ * returned will always be able to be parsed and will have the correct country code. It may also
+ * be a valid *short* number/code for this region. Validity checking such numbers is handled with
+ * {@link com.google.i18n.phonenumbers.ShortNumberInfo}.
+ *
+ * @param regionCode the region for which an example number is needed
+ * @return an invalid number for the specified region. Returns null when an unsupported region or
+ * the region 001 (Earth) is passed in.
+ */
+ public PhoneNumber getInvalidExampleNumber(String regionCode) {
+ if (!isValidRegionCode(regionCode)) {
+ logger.log(Level.WARNING, "Invalid or unknown region code provided: " + regionCode);
+ return null;
+ }
+ // We start off with a valid fixed-line number since every country supports this. Alternatively
+ // we could start with a different number type, since fixed-line numbers typically have a wide
+ // breadth of valid number lengths and we may have to make it very short before we get an
+ // invalid number.
+ PhoneNumberDesc desc = getNumberDescByType(getMetadataForRegion(regionCode),
+ PhoneNumberType.FIXED_LINE);
+ if (!desc.hasExampleNumber()) {
+ // This shouldn't happen; we have a test for this.
+ return null;
+ }
+ String exampleNumber = desc.getExampleNumber();
+ // Try and make the number invalid. We do this by changing the length. We try reducing the
+ // length of the number, since currently no region has a number that is the same length as
+ // MIN_LENGTH_FOR_NSN. This is probably quicker than making the number longer, which is another
+ // alternative. We could also use the possible number pattern to extract the possible lengths of
+ // the number to make this faster, but this method is only for unit-testing so simplicity is
+ // preferred to performance. We don't want to return a number that can't be parsed, so we check
+ // the number is long enough. We try all possible lengths because phone number plans often have
+ // overlapping prefixes so the number 123456 might be valid as a fixed-line number, and 12345 as
+ // a mobile number. It would be faster to loop in a different order, but we prefer numbers that
+ // look closer to real numbers (and it gives us a variety of different lengths for the resulting
+ // phone numbers - otherwise they would all be MIN_LENGTH_FOR_NSN digits long.)
+ for (int phoneNumberLength = exampleNumber.length() - 1;
+ phoneNumberLength >= MIN_LENGTH_FOR_NSN;
+ phoneNumberLength--) {
+ String numberToTry = exampleNumber.substring(0, phoneNumberLength);
+ try {
+ PhoneNumber possiblyValidNumber = parse(numberToTry, regionCode);
+ if (!isValidNumber(possiblyValidNumber)) {
+ return possiblyValidNumber;
+ }
+ } catch (NumberParseException e) {
+ // Shouldn't happen: we have already checked the length, we know example numbers have
+ // only valid digits, and we know the region code is fine.
+ }
+ }
+ // We have a test to check that this doesn't happen for any of our supported regions.
+ return null;
+ }
+
+ /**
* Gets a valid number for the specified region and number type.
*
* @param regionCode the region for which an example number is needed
@@ -1836,8 +1917,8 @@ public class PhoneNumberUtil {
}
PhoneNumberDesc desc = getNumberDescByType(getMetadataForRegion(regionCode), type);
try {
- if (!desc.exampleNumber.equals("")) {
- return parse(desc.exampleNumber, regionCode);
+ if (desc.hasExampleNumber()) {
+ return parse(desc.getExampleNumber(), regionCode);
}
} catch (NumberParseException e) {
logger.log(Level.SEVERE, e.toString());
@@ -1846,6 +1927,37 @@ public class PhoneNumberUtil {
}
/**
+ * Gets a valid number for the specified number type (it may belong to any country).
+ *
+ * @param type the type of number that is needed
+ * @return a valid number for the specified type. Returns null when the metadata
+ * does not contain such information. This should only happen when no numbers of this type are
+ * allocated anywhere in the world anymore.
+ */
+ public PhoneNumber getExampleNumberForType(PhoneNumberType type) {
+ for (String regionCode : getSupportedRegions()) {
+ PhoneNumber exampleNumber = getExampleNumberForType(regionCode, type);
+ if (exampleNumber != null) {
+ return exampleNumber;
+ }
+ }
+ // If there wasn't an example number for a region, try the non-geographical entities.
+ for (int countryCallingCode : getSupportedGlobalNetworkCallingCodes()) {
+ PhoneNumberDesc desc = getNumberDescByType(
+ getMetadataForNonGeographicalRegion(countryCallingCode), type);
+ try {
+ if (desc.hasExampleNumber()) {
+ return parse("+" + countryCallingCode + desc.getExampleNumber(), UNKNOWN_REGION);
+ }
+ } catch (NumberParseException e) {
+ logger.log(Level.SEVERE, e.toString());
+ }
+ }
+ // There are no example numbers of this type for any country in the library.
+ return null;
+ }
+
+ /**
* Gets a valid number for the specified country calling code for a non-geographical entity.
*
* @param countryCallingCode the country calling code for a non-geographical entity
@@ -1856,10 +1968,10 @@ public class PhoneNumberUtil {
public PhoneNumber getExampleNumberForNonGeoEntity(int countryCallingCode) {
PhoneMetadata metadata = getMetadataForNonGeographicalRegion(countryCallingCode);
if (metadata != null) {
- PhoneNumberDesc desc = metadata.generalDesc;
+ PhoneNumberDesc desc = metadata.getGeneralDesc();
try {
- if (!desc.exampleNumber.equals("")) {
- return parse("+" + countryCallingCode + desc.exampleNumber, "ZZ");
+ if (desc.hasExampleNumber()) {
+ return parse("+" + countryCallingCode + desc.getExampleNumber(), UNKNOWN_REGION);
}
} catch (NumberParseException e) {
logger.log(Level.SEVERE, e.toString());
@@ -1882,8 +1994,8 @@ public class PhoneNumberUtil {
if (numberFormat == PhoneNumberFormat.RFC3966) {
formattedNumber.append(RFC3966_EXTN_PREFIX).append(number.getExtension());
} else {
- if (!metadata.preferredExtnPrefix.equals("")) {
- formattedNumber.append(metadata.preferredExtnPrefix).append(number.getExtension());
+ if (metadata.hasPreferredExtnPrefix()) {
+ formattedNumber.append(metadata.getPreferredExtnPrefix()).append(number.getExtension());
} else {
formattedNumber.append(DEFAULT_EXTN_PREFIX).append(number.getExtension());
}
@@ -1894,28 +2006,28 @@ public class PhoneNumberUtil {
PhoneNumberDesc getNumberDescByType(PhoneMetadata metadata, PhoneNumberType type) {
switch (type) {
case PREMIUM_RATE:
- return metadata.premiumRate;
+ return metadata.getPremiumRate();
case TOLL_FREE:
- return metadata.tollFree;
+ return metadata.getTollFree();
case MOBILE:
- return metadata.mobile;
+ return metadata.getMobile();
case FIXED_LINE:
case FIXED_LINE_OR_MOBILE:
- return metadata.fixedLine;
+ return metadata.getFixedLine();
case SHARED_COST:
- return metadata.sharedCost;
+ return metadata.getSharedCost();
case VOIP:
- return metadata.voip;
+ return metadata.getVoip();
case PERSONAL_NUMBER:
- return metadata.personalNumber;
+ return metadata.getPersonalNumber();
case PAGER:
- return metadata.pager;
+ return metadata.getPager();
case UAN:
- return metadata.uan;
+ return metadata.getUan();
case VOICEMAIL:
- return metadata.voicemail;
+ return metadata.getVoicemail();
default:
- return metadata.generalDesc;
+ return metadata.getGeneralDesc();
}
}
@@ -1936,48 +2048,48 @@ public class PhoneNumberUtil {
}
private PhoneNumberType getNumberTypeHelper(String nationalNumber, PhoneMetadata metadata) {
- if (!isNumberMatchingDesc(nationalNumber, metadata.generalDesc)) {
+ if (!isNumberMatchingDesc(nationalNumber, metadata.getGeneralDesc())) {
return PhoneNumberType.UNKNOWN;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.premiumRate)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getPremiumRate())) {
return PhoneNumberType.PREMIUM_RATE;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.tollFree)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getTollFree())) {
return PhoneNumberType.TOLL_FREE;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.sharedCost)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getSharedCost())) {
return PhoneNumberType.SHARED_COST;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.voip)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getVoip())) {
return PhoneNumberType.VOIP;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.personalNumber)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getPersonalNumber())) {
return PhoneNumberType.PERSONAL_NUMBER;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.pager)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getPager())) {
return PhoneNumberType.PAGER;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.uan)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getUan())) {
return PhoneNumberType.UAN;
}
- if (isNumberMatchingDesc(nationalNumber, metadata.voicemail)) {
+ if (isNumberMatchingDesc(nationalNumber, metadata.getVoicemail())) {
return PhoneNumberType.VOICEMAIL;
}
- boolean isFixedLine = isNumberMatchingDesc(nationalNumber, metadata.fixedLine);
+ boolean isFixedLine = isNumberMatchingDesc(nationalNumber, metadata.getFixedLine());
if (isFixedLine) {
- if (metadata.sameMobileAndFixedLinePattern) {
+ if (metadata.isSameMobileAndFixedLinePattern()) {
return PhoneNumberType.FIXED_LINE_OR_MOBILE;
- } else if (isNumberMatchingDesc(nationalNumber, metadata.mobile)) {
+ } else if (isNumberMatchingDesc(nationalNumber, metadata.getMobile())) {
return PhoneNumberType.FIXED_LINE_OR_MOBILE;
}
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.sameMobileAndFixedLinePattern &&
- isNumberMatchingDesc(nationalNumber, metadata.mobile)) {
+ if (!metadata.isSameMobileAndFixedLinePattern() &&
+ isNumberMatchingDesc(nationalNumber, metadata.getMobile())) {
return PhoneNumberType.MOBILE;
}
return PhoneNumberType.UNKNOWN;
@@ -2003,14 +2115,14 @@ public class PhoneNumberUtil {
boolean isNumberPossibleForDesc(String nationalNumber, PhoneNumberDesc numberDesc) {
Matcher possibleNumberPatternMatcher =
- regexCache.getPatternForRegex(numberDesc.possibleNumberPattern)
+ regexCache.getPatternForRegex(numberDesc.getPossibleNumberPattern())
.matcher(nationalNumber);
return possibleNumberPatternMatcher.matches();
}
boolean isNumberMatchingDesc(String nationalNumber, PhoneNumberDesc numberDesc) {
Matcher nationalNumberPatternMatcher =
- regexCache.getPatternForRegex(numberDesc.nationalNumberPattern)
+ regexCache.getPatternForRegex(numberDesc.getNationalNumberPattern())
.matcher(nationalNumber);
return isNumberPossibleForDesc(nationalNumber, numberDesc) &&
nationalNumberPatternMatcher.matches();
@@ -2089,8 +2201,8 @@ public class PhoneNumberUtil {
// If leadingDigits is present, use this. Otherwise, do full validation.
// Metadata cannot be null because the region codes come from the country calling code map.
PhoneMetadata metadata = getMetadataForRegion(regionCode);
- if (!metadata.leadingDigits.equals("")) {
- if (regexCache.getPatternForRegex(metadata.leadingDigits)
+ if (metadata.hasLeadingDigits()) {
+ if (regexCache.getPatternForRegex(metadata.getLeadingDigits())
.matcher(nationalNumber).lookingAt()) {
return regionCode;
}
@@ -2156,7 +2268,7 @@ public class PhoneNumberUtil {
if (metadata == null) {
throw new IllegalArgumentException("Invalid region code: " + regionCode);
}
- return metadata.countryCode;
+ return metadata.getCountryCode();
}
/**
@@ -2182,7 +2294,7 @@ public class PhoneNumberUtil {
+ ") provided.");
return null;
}
- String nationalPrefix = metadata.nationalPrefix;
+ String nationalPrefix = metadata.getNationalPrefix();
// If no national prefix was found, we return null.
if (nationalPrefix.length() == 0) {
return null;
@@ -2216,7 +2328,7 @@ public class PhoneNumberUtil {
if (mainMetadataForCallingCode == null) {
return false;
}
- return mainMetadataForCallingCode.leadingZeroPossible;
+ return mainMetadataForCallingCode.isLeadingZeroPossible();
}
/**
@@ -2273,7 +2385,7 @@ public class PhoneNumberUtil {
*/
private boolean isShorterThanPossibleNormalNumber(PhoneMetadata regionMetadata, String number) {
Pattern possibleNumberPattern = regexCache.getPatternForRegex(
- regionMetadata.generalDesc.possibleNumberPattern);
+ regionMetadata.getGeneralDesc().getPossibleNumberPattern());
return testNumberLengthAgainstPattern(possibleNumberPattern, number) ==
ValidationResult.TOO_SHORT;
}
@@ -2313,7 +2425,7 @@ public class PhoneNumberUtil {
// Metadata cannot be null because the country calling code is valid.
PhoneMetadata metadata = getMetadataForRegionOrCallingCode(countryCode, regionCode);
Pattern possibleNumberPattern =
- regexCache.getPatternForRegex(metadata.generalDesc.possibleNumberPattern);
+ regexCache.getPatternForRegex(metadata.getGeneralDesc().getPossibleNumberPattern());
return testNumberLengthAgainstPattern(possibleNumberPattern, nationalNumber);
}
@@ -2445,7 +2557,7 @@ public class PhoneNumberUtil {
// Set the default prefix to be something that will never match.
String possibleCountryIddPrefix = "NonMatch";
if (defaultRegionMetadata != null) {
- possibleCountryIddPrefix = defaultRegionMetadata.internationalPrefix;
+ possibleCountryIddPrefix = defaultRegionMetadata.getInternationalPrefix();
}
CountryCodeSource countryCodeSource =
@@ -2473,19 +2585,19 @@ public class PhoneNumberUtil {
// Check to see if the number starts with the country calling code for the default region. If
// so, we remove the country calling code, and do some checks on the validity of the number
// before and after.
- int defaultCountryCode = defaultRegionMetadata.countryCode;
+ int defaultCountryCode = defaultRegionMetadata.getCountryCode();
String defaultCountryCodeString = String.valueOf(defaultCountryCode);
String normalizedNumber = fullNumber.toString();
if (normalizedNumber.startsWith(defaultCountryCodeString)) {
StringBuilder potentialNationalNumber =
new StringBuilder(normalizedNumber.substring(defaultCountryCodeString.length()));
- PhoneNumberDesc generalDesc = defaultRegionMetadata.generalDesc;
+ PhoneNumberDesc generalDesc = defaultRegionMetadata.getGeneralDesc();
Pattern validNumberPattern =
- regexCache.getPatternForRegex(generalDesc.nationalNumberPattern);
+ regexCache.getPatternForRegex(generalDesc.getNationalNumberPattern());
maybeStripNationalPrefixAndCarrierCode(
potentialNationalNumber, defaultRegionMetadata, null /* Don't need the carrier code */);
Pattern possibleNumberPattern =
- regexCache.getPatternForRegex(generalDesc.possibleNumberPattern);
+ regexCache.getPatternForRegex(generalDesc.getPossibleNumberPattern());
// If the number was not valid before but is valid now, or if it was too long before, we
// consider the number with the country calling code stripped to be a better result and
// keep that instead.
@@ -2578,7 +2690,7 @@ public class PhoneNumberUtil {
boolean maybeStripNationalPrefixAndCarrierCode(
StringBuilder number, PhoneMetadata metadata, StringBuilder carrierCode) {
int numberLength = number.length();
- String possibleNationalPrefix = metadata.nationalPrefixForParsing;
+ String possibleNationalPrefix = metadata.getNationalPrefixForParsing();
if (numberLength == 0 || possibleNationalPrefix.length() == 0) {
// Early return for numbers of zero length.
return false;
@@ -2587,14 +2699,14 @@ public class PhoneNumberUtil {
Matcher prefixMatcher = regexCache.getPatternForRegex(possibleNationalPrefix).matcher(number);
if (prefixMatcher.lookingAt()) {
Pattern nationalNumberRule =
- regexCache.getPatternForRegex(metadata.generalDesc.nationalNumberPattern);
+ regexCache.getPatternForRegex(metadata.getGeneralDesc().getNationalNumberPattern());
// Check if the original number is viable.
boolean isViableOriginalNumber = nationalNumberRule.matcher(number).matches();
// prefixMatcher.group(numOfGroups) == null implies nothing was captured by the capturing
// groups in possibleNationalPrefix; therefore, no transformation is necessary, and we just
// remove the national prefix.
int numOfGroups = prefixMatcher.groupCount();
- String transformRule = metadata.nationalPrefixTransformRule;
+ String transformRule = metadata.getNationalPrefixTransformRule();
if (transformRule == null || transformRule.length() == 0 ||
prefixMatcher.group(numOfGroups) == null) {
// If the original number was viable, and the resultant number is not, we return.
@@ -2670,10 +2782,18 @@ public class PhoneNumberUtil {
}
/**
- * Parses a string and returns it in proto buffer format. This method will throw a
- * {@link com.google.i18n.phonenumbers.NumberParseException} 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 region is not performed. This can be done separately with {@link #isValidNumber}.
+ * Parses a string and returns it as a phone number in proto buffer format. The method is quite
+ * lenient and looks for a number in the input text (raw input) and does not check whether the
+ * string is definitely only a phone number. To do this, it ignores punctuation and white-space,
+ * as well as any text before the number (e.g. a leading "Tel: ") and trims the non-number bits.
+ * It will accept a number in any format (E164, national, international etc), assuming it can be
+ * interpreted with the defaultRegion supplied. It also attempts to convert any alpha characters
+ * into digits if it thinks this is a vanity number of the type "1800 MICROSOFT".
+ *
+ * <p> This method will throw a {@link com.google.i18n.phonenumbers.NumberParseException} 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 region is not performed. This can be done
+ * separately with {@link #isValidNumber}.
*
* @param numberToParse number that we are attempting to parse. This can contain formatting
* such as +, ( and -, as well as a phone number extension. It can also
@@ -2685,9 +2805,10 @@ public class PhoneNumberUtil {
* start with a '+' followed by the country calling code, then
* "ZZ" or null can be 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 region was supplied and the number is not in
- * international format (does not start with +)
+ * @throws NumberParseException if the string is not considered to be a viable phone number (e.g.
+ * too few or too many digits) or if no default region was supplied
+ * and the number is not in international format (does not start
+ * with +)
*/
public PhoneNumber parse(String numberToParse, String defaultRegion)
throws NumberParseException {
@@ -2880,7 +3001,7 @@ public class PhoneNumberUtil {
normalize(nationalNumber);
normalizedNationalNumber.append(nationalNumber);
if (defaultRegion != null) {
- countryCode = regionMetadata.countryCode;
+ countryCode = regionMetadata.getCountryCode();
phoneNumber.setCountryCode(countryCode);
} else if (keepRawInput) {
phoneNumber.clearCountryCodeSource();
@@ -3154,7 +3275,7 @@ public class PhoneNumberUtil {
return true;
}
String nationalSignificantNumber = getNationalSignificantNumber(number);
- return !isNumberMatchingDesc(nationalSignificantNumber, metadata.noInternationalDialling);
+ return !isNumberMatchingDesc(nationalSignificantNumber, metadata.getNoInternationalDialling());
}
/**
@@ -3170,6 +3291,6 @@ public class PhoneNumberUtil {
logger.log(Level.WARNING, "Invalid or unknown region code provided: " + regionCode);
return false;
}
- return metadata.mobileNumberPortableRegion;
+ return metadata.isMobileNumberPortableRegion();
}
}