Browse Source

Major improvement in AsYouTypeFormatter to start formatting earlier. New TrancateTooLongNumber functionality in PhoneNumberUtil. Also fixes formatByPattern and Japanese AsYouTypeFormatter bugs.

pull/567/head
Shaopeng Jia 16 years ago
committed by Mihaela Rosca
parent
commit
b5bb575ac2
11 changed files with 2872 additions and 1300 deletions
  1. +48
    -37
      java/resources/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java
  2. +15
    -16
      java/resources/com/google/i18n/phonenumbers/proto/phonemetadata.proto
  3. +1888
    -876
      java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml
  4. +326
    -79
      java/resources/com/google/i18n/phonenumbers/test/PhoneNumberMetaDataForTesting.xml
  5. +189
    -138
      java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java
  6. BIN
      java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto
  7. +67
    -19
      java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
  8. +47
    -13
      java/src/com/google/i18n/phonenumbers/Phonemetadata.java
  9. +204
    -109
      java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java
  10. BIN
      java/test/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting
  11. +88
    -13
      java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java

+ 48
- 37
java/resources/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java View File

@ -23,11 +23,9 @@ 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.io.ObjectOutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -35,7 +33,6 @@ import java.util.regex.Pattern;
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
@ -55,37 +52,30 @@ public class BuildMetadataProtoFromXml {
private static final Logger LOGGER = Logger.getLogger(BuildMetadataProtoFromXml.class.getName());
private static Boolean liteBuild;
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
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 metadataCollection = new PhoneMetadataCollection();
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);
}
ObjectOutputStream out = new ObjectOutputStream(output);
metadataCollection.writeExternal(out);
out.close();
} 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());
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 metadataCollection = new PhoneMetadataCollection();
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);
}
ObjectOutputStream out = new ObjectOutputStream(output);
metadataCollection.writeExternal(out);
out.close();
}
private static String validateRE(String regex) {
@ -154,11 +144,16 @@ public class BuildMetadataProtoFromXml {
} else {
format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
}
if (numberFormatElement.hasAttribute("leadingDigits")) {
format.setLeadingDigits(validateRE(numberFormatElement.getAttribute("leadingDigits")));
}
setLeadingDigitsPatterns(numberFormatElement, format);
format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
format.setFormat(validateRE(numberFormatElement.getFirstChild().getNodeValue()));
NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
if (formatPattern.getLength() != 1) {
LOGGER.log(Level.SEVERE,
"Only one format pattern for a numberFormat element should be defined.");
throw new RuntimeException("Invalid number of format patterns for country: " +
regionCode.toString());
}
format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
metadata.addNumberFormat(format);
}
}
@ -169,11 +164,16 @@ public class BuildMetadataProtoFromXml {
for (int i = 0; i < numOfIntlFormatElements; i++) {
Element numberFormatElement = (Element) intlNumberFormatElements.item(i);
NumberFormat format = new NumberFormat();
if (numberFormatElement.hasAttribute("leadingDigits")) {
format.setLeadingDigits(validateRE(numberFormatElement.getAttribute("leadingDigits")));
}
setLeadingDigitsPatterns(numberFormatElement, format);
format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
format.setFormat(validateRE(numberFormatElement.getFirstChild().getNodeValue()));
NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
if (formatPattern.getLength() != 1) {
LOGGER.log(Level.SEVERE,
"Only one format pattern for a numberFormat element should be defined.");
throw new RuntimeException("Invalid number of format patterns for country: " +
regionCode.toString());
}
format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
format.setDomesticCarrierCodeFormattingRule(validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
@ -204,6 +204,17 @@ public class BuildMetadataProtoFromXml {
return metadata;
}
private static void setLeadingDigitsPatterns(Element numberFormatElement, NumberFormat format) {
NodeList leadingDigitsPatternNodes = numberFormatElement.getElementsByTagName("leadingDigits");
int numOfLeadingDigitsPatterns = leadingDigitsPatternNodes.getLength();
if (numOfLeadingDigitsPatterns > 0) {
for (int i = 0; i < numOfLeadingDigitsPatterns; i++) {
format.addLeadingDigitsPattern(
validateRE((leadingDigitsPatternNodes.item(i)).getFirstChild().getNodeValue()));
}
}
}
private static String getNationalPrefixFormattingRuleFromElement(Element element,
String nationalPrefix) {
String nationalPrefixFormattingRule = element.getAttribute("nationalPrefixFormattingRule");


+ 15
- 16
java/resources/com/google/i18n/phonenumbers/proto/phonemetadata.proto View File

@ -40,13 +40,21 @@ message NumberFormat {
// 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 is a regex that is used to match a certain number of digits
// at the beginning 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.
//
// The first leadingDigitsPattern matches up to the first three digits of the
// national (significant) number; the next one matches the first four digits,
// then the first five and so on, until the leadingDigitsPattern can uniquely
// identify one pattern and format to be used to format the number.
//
// In the case when only one formatting pattern exists, no
// leading_digits_pattern is needed.
repeated string leading_digits_pattern = 3;
// This field specifies how the national prefix ($NP) together with the first
// group ($FG) in the national significant number should be formatted in
@ -178,15 +186,6 @@ message PhoneMetadata {
// - 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


+ 1888
- 876
java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml
File diff suppressed because it is too large
View File


+ 326
- 79
java/resources/com/google/i18n/phonenumbers/test/PhoneNumberMetaDataForTesting.xml View File

@ -26,6 +26,29 @@
<territory id="AD" countryCode="376" internationalPrefix="00">
</territory>
<!-- Angola -->
<!-- http://www.itu.int/oth/T0202000006/en -->
<territory id="AO" countryCode="244" internationalPrefix="00">
<availableFormats>
<numberFormat pattern="(\d{3})(\d{3})(\d{3})">
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[29]\d{8}</nationalNumberPattern>
<possibleNumberPattern>\d{9}</possibleNumberPattern>
</generalDesc>
<fixedLine>
<nationalNumberPattern>2\d(?:[26-9]\d|\d[26-9])\d{5}</nationalNumberPattern>
<exampleNumber>222123456</exampleNumber>
</fixedLine>
<mobile>
<!-- Expanded the 92 prefix possibilities to matchnumbers found online. -->
<nationalNumberPattern>9[1-3]\d{7}</nationalNumberPattern>
<exampleNumber>923123456</exampleNumber>
</mobile>
</territory>
<!-- Argentina -->
<territory id="AR" countryCode="54" internationalPrefix="00"
nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"
@ -35,16 +58,47 @@
front of carrier selection code 15, is captured to replace $1 in
nationalPrefixTransformRule -->
<availableFormats>
<numberFormat leadingDigits="11" pattern="(\d{2})(\d{4})(\d{4})">$1 $2-$3</numberFormat>
<numberFormat leadingDigits="1[02-9]|[23]" pattern="(\d{4})(\d{2})(\d{4})">$1 $2-$3</numberFormat>
<numberFormat leadingDigits="911" pattern="9(11)(\d{4})(\d{4})">$1 15 $2-$3</numberFormat>
<numberFormat leadingDigits="9(?:1[02-9]|[23])" pattern="9(\d{4})(\d{2})(\d{4})" carrierCodeFormattingRule="$FG $CC">$1 $2-$3</numberFormat>
<numberFormat leadingDigits="[68]" pattern="(\d{3})(\d{3})(\d{4})">$1-$2-$3</numberFormat>
<intlNumberFormat leadingDigits="11" pattern="(\d{2})(\d{4})(\d{4})">$1 $2-$3</intlNumberFormat>
<intlNumberFormat leadingDigits="1[02-9]|[23]" pattern="(\d{4})(\d{2})(\d{4})">$1 $2-$3</intlNumberFormat>
<intlNumberFormat leadingDigits="911" pattern="(9)(11)(\d{4})(\d{4})">$1 $2 $3 $4</intlNumberFormat>
<intlNumberFormat leadingDigits="9(?:1[02-9]|[23])" pattern="(9)(\d{4})(\d{2})(\d{4})">$1 $2 $3 $4</intlNumberFormat>
<intlNumberFormat leadingDigits="[68]" pattern="(\d{3})(\d{3})(\d{4})">$1-$2-$3</intlNumberFormat>
<numberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>11</leadingDigits>
<format>$1 $2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{4})(\d{2})(\d{4})">
<leadingDigits>1[02-9]|[23]</leadingDigits>
<format>$1 $2-$3</format>
</numberFormat>
<numberFormat pattern="9(11)(\d{4})(\d{4})">
<leadingDigits>911</leadingDigits>
<format>$1 15 $2-$3</format>
</numberFormat>
<numberFormat pattern="9(\d{4})(\d{2})(\d{4})"
carrierCodeFormattingRule="$FG $CC">
<leadingDigits>9(?:1[02-9]|[23])</leadingDigits>
<format>$1 $2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>[68]</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<intlNumberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>11</leadingDigits>
<format>$1 $2-$3</format>
</intlNumberFormat>
<intlNumberFormat pattern="(\d{4})(\d{2})(\d{4})">
<leadingDigits>1[02-9]|[23]</leadingDigits>
<format>$1 $2-$3</format>
</intlNumberFormat>
<intlNumberFormat pattern="(9)(11)(\d{4})(\d{4})">
<leadingDigits>911</leadingDigits>
<format>$1 $2 $3 $4</format>
</intlNumberFormat>
<intlNumberFormat pattern="(9)(\d{4})(\d{2})(\d{4})">
<leadingDigits>9(?:1[02-9]|[23])</leadingDigits>
<format>$1 $2 $3 $4</format>
</intlNumberFormat>
<intlNumberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>[68]</leadingDigits>
<format>$1-$2-$3</format>
</intlNumberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[1-3689]\d{9,10}</nationalNumberPattern>
@ -73,8 +127,13 @@
nationalPrefix="0" preferredInternationalPrefix="0011"
nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat nationalPrefixFormattingRule="$FG" leadingDigits="1" pattern="(\d{4})(\d{3})(\d{3})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="[2-478]" pattern="(\d{1})(\d{4})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat nationalPrefixFormattingRule="$FG" leadingDigits="1" pattern="(\d{4})(\d{3})(\d{3})">
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{1})(\d{4})(\d{4})">
<leadingDigits>[2-478]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
<generalDesc >
<nationalNumberPattern>[1-578]\d{4,14}</nationalNumberPattern>
@ -100,9 +159,6 @@
<!-- Bahamas -->
<territory id="BS" countryCode="1" internationalPrefix="011">
<availableFormats>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>(242|8(00|66|77|88)|900)\d{7}</nationalNumberPattern>
<possibleNumberPattern>\d{7,10}</possibleNumberPattern>
@ -128,12 +184,27 @@
<territory id="DE" countryCode="49" internationalPrefix="00"
nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat leadingDigits="2|3[3-9]|906|[4-9][1-9]1" pattern="(\d{3})(\d{3,8})">$1 $2</numberFormat>
<numberFormat leadingDigits="[34]0|[68]9" pattern="(\d{2})(\d{4,9})">$1 $2</numberFormat>
<numberFormat leadingDigits="[4-9]" pattern="([4-9]\d{3})(\d{2,7})">$1 $2</numberFormat>
<numberFormat leadingDigits="800" pattern="(\d{3})(\d{1})(\d{6})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="900[135]" pattern="(\d{3})(\d{3})(d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="9009" pattern="(\d{3})(\d{4})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat pattern="(\d{3})(\d{3,8})">
<leadingDigits>2|3[3-9]|906|[4-9][1-9]1</leadingDigits>
<format>$1 $2</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{4,9})">
<leadingDigits>[34]0|[68]9</leadingDigits>
<format>$1 $2</format>
</numberFormat>
<numberFormat pattern="([4-9]\d{3})(\d{2,7})">
<leadingDigits>[4-9]</leadingDigits>
<leadingDigits>[4-6]|[7-9](?:\d[1-9]|[1-9]\d)</leadingDigits>
<format>$1 $2</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{1})(\d{6})">
<leadingDigits>800</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3,4})(\d{4})">
<leadingDigits>900</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>\d{4,14}</nationalNumberPattern>
@ -161,14 +232,22 @@
<territory id="GB" countryCode="44" internationalPrefix="00"
nationalPrefix="0" nationalPrefixFormattingRule="($NP$FG)">
<availableFormats>
<numberFormat leadingDigits="[1-59]|[78]0"
pattern="(\d{2})(\d{4})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="6"
pattern="(\d)(\d{3})(\d{3})(\d{3})">$1 $2 $3 $4</numberFormat>
<numberFormat leadingDigits="7[1-57-9]"
pattern="(\d{4})(\d{3})(\d{3})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="8[47]"
pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>[1-59]|[78]0</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d)(\d{3})(\d{3})(\d{3})">
<leadingDigits>6</leadingDigits>
<format>$1 $2 $3 $4</format>
</numberFormat>
<numberFormat pattern="(\d{4})(\d{3})(\d{3})">
<leadingDigits>7[1-57-9]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>8[47]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>\d{10}</nationalNumberPattern>
@ -207,10 +286,22 @@
<!-- http://en.wikipedia.org/wiki/%2B39 -->
<territory id="IT" countryCode="39" internationalPrefix="00">
<availableFormats>
<numberFormat leadingDigits="0[26]" pattern="(\d{2})(\d{4})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="0[13-57-9]" pattern="(\d{3})(\d{4})(\d{3,4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="3" pattern="(\d{3})(\d{3})(\d{3,4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="8" pattern="(\d{3})(\d{3,6})">$1 $2</numberFormat>
<numberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>0[26]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{4})(\d{3,4})">
<leadingDigits>0[13-57-9]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{3,4})">
<leadingDigits>3</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3,6})">
<leadingDigits>8</leadingDigits>
<format>$1 $2</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[0389]\d{5,10}</nationalNumberPattern>
@ -234,6 +325,37 @@
</premiumRate>
</territory>
<!-- Japan -->
<!-- The metadata here is added to unit test AsYouTypeFormatter for JP, which requires switching
patterns as digits beyond the third one are entered. As a result, only a few fake
formatting rules are added. -->
<territory id="JP" countryCode="81" internationalPrefix="010"
nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>[57-9]0</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{3})(\d{4})">
<leadingDigits>222|333</leadingDigits>
<leadingDigits>(?:222|333)1</leadingDigits>
<leadingDigits>(?:222|333)11</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{4})(\d)(\d{4})">
<leadingDigits>222|333</leadingDigits>
<leadingDigits>2221|3332</leadingDigits>
<leadingDigits>22212|3332</leadingDigits>
<leadingDigits>222120|3332</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{2})(\d{4})">
<leadingDigits>[23]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
</territory>
<!-- Korea (Rep. of) -->
<!-- http://www.itu.int/oth/T0202000072/en -->
<!-- http://en.wikipedia.org/wiki/%2B82 -->
@ -249,35 +371,101 @@
nationalPrefix="0" nationalPrefixForParsing="0(?:8[1-46-8]|85\d{2})?"
nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat leadingDigits="1(?:0|1[19]|[69]9|5(?:44|59|8))|[57]0"
pattern="(\d{2})(\d{4})(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="1(?:[169][2-8]|[78]|5(?:[1-3]|4[56]))|[68]0|[3-9][1-9][2-9]"
pattern="(\d{2})(\d{3})(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="1312"
pattern="(\d{3})(\d)(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="131[13-9]"
pattern="(\d{3})(\d{2})(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="13[2-9]"
pattern="(\d{3})(\d{3})(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="30"
pattern="(\d{2})(\d{2})(\d{3})(\d{4})">$1-$2-$3-$4</numberFormat>
<numberFormat leadingDigits="2(?:[26]|3(?:01|1[45]|2[17-9]|39|4|6[67]|7[078]))"
pattern="(\d)(\d{4})(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="2(?:3(?:0[02-9]|1[0-36-9]|2[02-6]|3[0-8]|6[0-589]|7[1-69]|[589])|[457-9])"
pattern="(\d)(\d{3})(\d{4})">$1-$2-$3</numberFormat>
<numberFormat leadingDigits="21(?:[0-247-9]|3[124]|6[1269])"
pattern="(\d)(\d{3})">$1-$2</numberFormat>
<numberFormat leadingDigits="21(?:3[035-9]|6[03-578])"
pattern="(\d)(\d{4})">$1-$2</numberFormat>
<numberFormat leadingDigits="[3-9][1-9]1(?:[0-247-9]|3[124]|6[1269])"
pattern="(\d{2})(\d{3})">$1-$2</numberFormat>
<numberFormat leadingDigits="[3-9][1-9]1(?:3[035-9]|6[03-578])"
pattern="(\d{2})(\d{4})">$1-$2</numberFormat>
<numberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>1(?:0|1[19]|[69]9|5[458])|[57]0</leadingDigits>
<leadingDigits>1(?:0|1[19]|[69]9|5(?:44|59|8))|[57]0</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{3})(\d{4})">
<leadingDigits>1(?:[169][2-8]|[78]|5[1-4])|[68]0|[3-9][1-9][2-9]</leadingDigits>
<leadingDigits>1(?:[169][2-8]|[78]|5(?:[1-3]|4[56]))|[68]0|[3-9][1-9][2-9]</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d)(\d{4})">
<leadingDigits>131</leadingDigits>
<leadingDigits>1312</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{2})(\d{4})">
<leadingDigits>131</leadingDigits>
<leadingDigits>131[13-9]</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>13[2-9]</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{2})(\d{3})(\d{4})">
<leadingDigits>30</leadingDigits>
<format>$1-$2-$3-$4</format>
</numberFormat>
<numberFormat pattern="(\d)(\d{4})(\d{4})">
<leadingDigits>2(?:[26]|3[0-467])</leadingDigits>
<leadingDigits>2(?:[26]|3(?:01|1[45]|2[17-9]|39|4|6[67]|7[078]))</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d)(\d{3})(\d{4})">
<leadingDigits>2(?:3[0-35-9]|[457-9])</leadingDigits>
<leadingDigits>2(?:3(?:0[02-9]|1[0-36-9]|2[02-6]|3[0-8]|6[0-589]|7[1-69]|[589])|[457-9])</leadingDigits>
<format>$1-$2-$3</format>
</numberFormat>
<numberFormat pattern="(\d)(\d{3})">
<leadingDigits>21[0-46-9]</leadingDigits>
<leadingDigits>21(?:[0-247-9]|3[124]|6[1269])</leadingDigits>
<format>$1-$2</format>
</numberFormat>
<numberFormat pattern="(\d)(\d{4})">
<leadingDigits>21[36]</leadingDigits>
<leadingDigits>21(?:3[035-9]|6[03-578])</leadingDigits>
<format>$1-$2</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{3})">
<leadingDigits>[3-9][1-9]1</leadingDigits>
<leadingDigits>[3-9][1-9]1(?:[0-46-9])</leadingDigits>
<leadingDigits>[3-9][1-9]1(?:[0-247-9]|3[124]|6[1269])</leadingDigits>
<format>$1-$2</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{4})">
<leadingDigits>[3-9][1-9]1</leadingDigits>
<leadingDigits>[3-9][1-9]1[36]</leadingDigits>
<leadingDigits>[3-9][1-9]1(?:3[035-9]|6[03-578])</leadingDigits>
<format>$1-$2</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[1-79]\d{3,9}|8\d{8}</nationalNumberPattern>
<possibleNumberPattern>\d{4,10}</possibleNumberPattern>
</generalDesc>
<fixedLine>
<nationalNumberPattern>(?:2|[34][1-3]|5[1-5]|6[1-4])(?:1\d{2,3}|[2-9]\d{6,7})</nationalNumberPattern>
<possibleNumberPattern>\d{4,10}</possibleNumberPattern>
<exampleNumber>22123456</exampleNumber>
</fixedLine>
<mobile>
<nationalNumberPattern>1[0-25-9]\d{7,8}</nationalNumberPattern>
<possibleNumberPattern>\d{9,10}</possibleNumberPattern>
<exampleNumber>1023456789</exampleNumber>
</mobile>
<tollFree>
<nationalNumberPattern>80\d{7}</nationalNumberPattern>
<possibleNumberPattern>\d{9}</possibleNumberPattern>
<exampleNumber>801234567</exampleNumber>
</tollFree>
<premiumRate>
<nationalNumberPattern>60[2-9]\d{6}</nationalNumberPattern>
<possibleNumberPattern>\d{9}</possibleNumberPattern>
<exampleNumber>602345678</exampleNumber>
</premiumRate>
<personalNumber>
<nationalNumberPattern>50\d{8}</nationalNumberPattern>
<possibleNumberPattern>\d{10}</possibleNumberPattern>
<exampleNumber>5012345678</exampleNumber>
</personalNumber>
<voip>
<nationalNumberPattern>70\d{8}</nationalNumberPattern>
<possibleNumberPattern>\d{10}</possibleNumberPattern>
<exampleNumber>7012345678</exampleNumber>
</voip>
</territory>
<!-- Mexico -->
@ -285,16 +473,46 @@
nationalPrefix="01" nationalPrefixForParsing="01|04[45](\d{10})"
nationalPrefixTransformRule="1$1">
<availableFormats>
<numberFormat leadingDigits="[89]00" pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="33|55|81" pattern="(\d{2})(\d{4})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="[2467]|3[0-24-9]|5[0-46-9]|8[2-9]|9[1-9]" pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="1(?:33|55|81)" pattern="1(\d{2})(\d{4})(\d{4})">045 $1 $2 $3</numberFormat>
<numberFormat leadingDigits="1(?:[124579]|3[0-24-9]|5[0-46-9]|8[02-9])" pattern="1(\d{3})(\d{3})(\d{4})">045 $1 $2 $3</numberFormat>
<intlNumberFormat leadingDigits="[89]00" pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</intlNumberFormat>
<intlNumberFormat leadingDigits="33|55|81" pattern="(\d{2})(\d{4})(\d{4})">$1 $2 $3</intlNumberFormat>
<intlNumberFormat leadingDigits="[2467]|3[0-24-9]|5[0-46-9]|8[2-9]|9[1-9]" pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</intlNumberFormat>
<intlNumberFormat leadingDigits="1(?:33|55|81)" pattern="(1)(\d{2})(\d{4})(\d{4})">$1 $2 $3 $4</intlNumberFormat>
<intlNumberFormat leadingDigits="1(?:[124579]|3[0-24-9]|5[0-46-9]|8[02-9])" pattern="(1)(\d{3})(\d{3})(\d{4})">$1 $2 $3 $4</intlNumberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>[89]00</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>33|55|81</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>[2467]|3[0-24-9]|5[0-46-9]|8[2-9]|9[1-9]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="1(\d{2})(\d{4})(\d{4})">
<leadingDigits>1(?:33|55|81)</leadingDigits>
<format>045 $1 $2 $3</format>
</numberFormat>
<numberFormat pattern="1(\d{3})(\d{3})(\d{4})">
<leadingDigits>1(?:[124579]|3[0-24-9]|5[0-46-9]|8[02-9])</leadingDigits>
<format>045 $1 $2 $3</format>
</numberFormat>
<intlNumberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>[89]00</leadingDigits>
<format>$1 $2 $3</format>
</intlNumberFormat>
<intlNumberFormat pattern="(\d{2})(\d{4})(\d{4})">
<leadingDigits>33|55|81</leadingDigits>
<format>$1 $2 $3</format>
</intlNumberFormat>
<intlNumberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>[2467]|3[0-24-9]|5[0-46-9]|8[2-9]|9[1-9]</leadingDigits>
<format>$1 $2 $3</format>
</intlNumberFormat>
<intlNumberFormat pattern="(1)(\d{2})(\d{4})(\d{4})">
<leadingDigits>1(?:33|55|81)</leadingDigits>
<format>$1 $2 $3 $4</format>
</intlNumberFormat>
<intlNumberFormat pattern="(1)(\d{3})(\d{3})(\d{4})">
<leadingDigits>1(?:[124579]|3[0-24-9]|5[0-46-9]|8[02-9])</leadingDigits>
<format>$1 $2 $3 $4</format>
</intlNumberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[1-9]\d{9,10}</nationalNumberPattern>
@ -322,9 +540,18 @@
<territory id="NZ" countryCode="64" internationalPrefix="00"
nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat leadingDigits="24|[34679]" pattern="(\d)(\d{3})(\d{4})">$1-$2 $3</numberFormat>
<numberFormat leadingDigits="2[179]" pattern="(\d)(\d{3})(\d{3,5})">$1-$2 $3</numberFormat>
<numberFormat leadingDigits="[89]" pattern="(\d{3})(\d{3})(\d{3,4})">$1 $2 $3</numberFormat>
<numberFormat pattern="(\d)(\d{3})(\d{4})">
<leadingDigits>24|[34679]</leadingDigits>
<format>$1-$2 $3</format>
</numberFormat>
<numberFormat pattern="(\d)(\d{3})(\d{3,5})">
<leadingDigits>2[179]</leadingDigits>
<format>$1-$2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{3,4})">
<leadingDigits>[89]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[2-9]\d{7,9}</nationalNumberPattern>
@ -353,7 +580,9 @@
<territory id="PL" countryCode="48" internationalPrefix="0~0"
nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat pattern="(\d{2})(\d{3})(\d{2})(\d{2})">$1 $2 $3 $4</numberFormat>
<numberFormat pattern="(\d{2})(\d{3})(\d{2})(\d{2})">
<format>$1 $2 $3 $4</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[1-9]\d{8}</nationalNumberPattern>
@ -377,7 +606,9 @@
<territory id="RE" countryCode="262" leadingDigits="262|6(?:9[23]|47)|8"
internationalPrefix="00" nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat pattern="([268]\d{2})(\d{2})(\d{2})(\d{2})">$1 $2 $3 $4</numberFormat>
<numberFormat pattern="([268]\d{2})(\d{2})(\d{2})(\d{2})">
<format>$1 $2 $3 $4</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[268]\d{8}</nationalNumberPattern>
@ -409,9 +640,18 @@
<!-- http://www.ida.gov.sg/policies%20and%20regulation/20060508120124.aspx -->
<territory id="SG" countryCode="65" internationalPrefix="0[0-3][0-9]">
<availableFormats>
<numberFormat leadingDigits="[369]|8[1-9]" pattern="(\d{4})(\d{4})">$1 $2</numberFormat>
<numberFormat leadingDigits="1[89]" pattern="(\d{4})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat leadingDigits="800" pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat pattern="(\d{4})(\d{4})">
<leadingDigits>[369]|8[1-9]</leadingDigits>
<format>$1 $2</format>
</numberFormat>
<numberFormat pattern="(\d{4})(\d{3})(\d{4})">
<leadingDigits>1[89]</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<leadingDigits>800</leadingDigits>
<format>$1 $2 $3</format>
</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[13689]\d{7,10}</nationalNumberPattern>
@ -440,11 +680,18 @@
<!-- For testing purposes, numbers starting with 24 are not considered US
numbers.-->
<territory id="US" countryCode="1" internationalPrefix="011"
preferredExtnPrefix=" extn. "
mainCountryForCode="true">
preferredExtnPrefix=" extn. " nationalPrefix="1"
mainCountryForCode="true" >
<availableFormats>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat pattern="(\d{3})(\d{4})">$1 $2</numberFormat>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">
<format>$1 $2 $3</format>
</numberFormat>
<numberFormat pattern="(\d{3})(\d{4})">
<format>$1 $2</format>
</numberFormat>
<intlNumberFormat pattern="(\d{3})(\d{3})(\d{4})">
<format>$1 $2 $3</format>
</intlNumberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[13-9]\d{9}|2[0-35-9]\d{8}</nationalNumberPattern>


+ 189
- 138
java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java View File

@ -20,6 +20,7 @@ import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -38,15 +39,18 @@ import java.util.regex.Pattern;
* @author Shaopeng Jia
*/
public class AsYouTypeFormatter {
private StringBuffer currentOutput;
private String formattingTemplate;
private StringBuffer accruedInput;
private StringBuffer accruedInputWithoutFormatting;
private String currentOutput = "";
private StringBuffer formattingTemplate = new StringBuffer();
// The pattern from numberFormat that is currently used to create formattingTemplate.
private String currentFormattingPattern = "";
private StringBuffer accruedInput = new StringBuffer();
private StringBuffer accruedInputWithoutFormatting = new StringBuffer();
private boolean ableToFormat = true;
private boolean isInternationalFormatting = false;
private boolean isExpectingCountryCode = false;
private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
private String defaultCountry;
private Phonemetadata.PhoneMetadata defaultMetaData;
private PhoneMetadata defaultMetaData;
private PhoneMetadata currentMetaData;
// A pattern that is used to match character classes in regular expressions. An example of a
@ -59,21 +63,30 @@ public class AsYouTypeFormatter {
// the phone number can be as long as 15 digits.
private static final Pattern STANDALONE_DIGIT_PATTERN = Pattern.compile("\\d(?=[^,}][^,}])");
// This is the minimum length of national number accrued that is required to trigger the
// formatter. The first element of the leadingDigitsPattern of each numberFormat contains a
// regular expression that matches up to this number of digits.
private static final int MIN_LEADING_DIGITS_LENGTH = 3;
// The digits that have not been entered yet will be represented by a \u2008, the punctuation
// space.
private String digitPlaceholder = "\u2008";
private Pattern digitPattern = Pattern.compile(digitPlaceholder);
private int lastMatchPosition = 0;
// The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
// found in the current output.
private int positionRemembered = 0;
// The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
// found in the original sequence of characters the user entered.
private int originalPosition = 0;
// The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
// found in accruedInputWithoutFormatting.
private int positionToRemember = 0;
private Pattern nationalPrefixForParsing;
private Pattern internationalPrefix;
private StringBuffer prefixBeforeNationalNumber;
private StringBuffer nationalNumber;
private StringBuffer prefixBeforeNationalNumber = new StringBuffer();
private StringBuffer nationalNumber = new StringBuffer();
private List<NumberFormat> possibleFormats = new ArrayList<NumberFormat>();
// A cache for frequently used country-specific regular expressions.
private RegexCache regexCache = new RegexCache(64);
/**
* Constructs a light-weight formatter which does no formatting, but outputs exactly what is
@ -82,11 +95,6 @@ public class AsYouTypeFormatter {
* @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();
nationalNumber = new StringBuffer();
defaultCountry = regionCode;
initializeCountrySpecificInfo(defaultCountry);
defaultMetaData = currentMetaData;
@ -95,44 +103,55 @@ public class AsYouTypeFormatter {
private void initializeCountrySpecificInfo(String regionCode) {
currentMetaData = phoneUtil.getMetadataForRegion(regionCode);
nationalPrefixForParsing =
Pattern.compile(currentMetaData.getNationalPrefixForParsing());
regexCache.getPatternForRegex(currentMetaData.getNationalPrefixForParsing());
internationalPrefix =
Pattern.compile("\\+|" + currentMetaData.getInternationalPrefix());
regexCache.getPatternForRegex("\\+|" + currentMetaData.getInternationalPrefix());
}
private void chooseFormatAndCreateTemplate(String leadingFourDigitsOfNationalNumber) {
List<NumberFormat> 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);
// Returns true if a new template is created as opposed to reusing the existing template.
private boolean maybeCreateNewTemplate() {
// When there are multiple available formats, the formatter uses the first format where a
// formatting template could be created.
for (NumberFormat numberFormat : possibleFormats) {
String pattern = numberFormat.getPattern();
if (currentFormattingPattern.equals(pattern)) {
return false;
}
if (createFormattingTemplate(numberFormat)) {
currentFormattingPattern = pattern;
return true;
}
}
ableToFormat = false;
return false;
}
private List<NumberFormat> getAvailableFormats(String leadingFourDigits) {
List<NumberFormat> matchedList = new ArrayList<NumberFormat>();
private void getAvailableFormats(String leadingThreeDigits) {
List<NumberFormat> formatList =
(isInternationalFormatting && currentMetaData.getIntlNumberFormatCount() > 0)
? currentMetaData.getIntlNumberFormatList()
: 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);
possibleFormats.addAll(formatList);
narrowDownPossibleFormats(leadingThreeDigits);
}
private void narrowDownPossibleFormats(String leadingDigits) {
int lengthOfLeadingDigits = leadingDigits.length();
int indexOfLeadingDigitsPattern = lengthOfLeadingDigits - MIN_LEADING_DIGITS_LENGTH;
Iterator<NumberFormat> it = possibleFormats.iterator();
while (it.hasNext()) {
NumberFormat format = it.next();
if (format.getLeadingDigitsPatternCount() > indexOfLeadingDigitsPattern) {
Pattern leadingDigitsPattern =
regexCache.getPatternForRegex(
format.getLeadingDigitsPattern(indexOfLeadingDigitsPattern));
Matcher m = leadingDigitsPattern.matcher(leadingDigits);
if (!m.lookingAt()) {
it.remove();
}
} else {
matchedList.add(format);
}
} // else the particular format has no more specific leadingDigitsPattern, and it should be
// retained.
}
return matchedList;
}
private boolean createFormattingTemplate(NumberFormat format) {
@ -150,8 +169,8 @@ public class AsYouTypeFormatter {
// Replace any standalone digit (not the one in d{}) with \d
numberPattern = STANDALONE_DIGIT_PATTERN.matcher(numberPattern).replaceAll("\\\\d");
formattingTemplate = getFormattingTemplate(numberPattern, numberFormat);
formattingTemplate.setLength(0);
formattingTemplate.append(getFormattingTemplate(numberPattern, numberFormat));
return true;
}
@ -161,7 +180,7 @@ public class AsYouTypeFormatter {
// 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);
Matcher m = regexCache.getPatternForRegex(numberPattern).matcher(longestPhoneNumber);
m.find(); // this will always succeed
String aPhoneNumber = m.group();
// Formats the number according to numberFormat
@ -175,23 +194,36 @@ public class AsYouTypeFormatter {
* Clears the internal state of the formatter, so it could be reused.
*/
public void clear() {
currentOutput = "";
accruedInput.setLength(0);
accruedInputWithoutFormatting.setLength(0);
currentOutput.setLength(0);
formattingTemplate.setLength(0);
lastMatchPosition = 0;
currentFormattingPattern = "";
prefixBeforeNationalNumber.setLength(0);
nationalNumber.setLength(0);
ableToFormat = true;
positionRemembered = 0;
originalPosition = 0;
positionToRemember = 0;
originalPosition = 0;
isInternationalFormatting = false;
isExpectingCountryCode = false;
possibleFormats.clear();
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.
*/
public String inputDigit(char nextChar) {
return inputDigitWithOptionToRememberPosition(nextChar, false);
currentOutput = inputDigitWithOptionToRememberPosition(nextChar, false);
return currentOutput;
}
/**
@ -200,14 +232,15 @@ public class AsYouTypeFormatter {
* adjusted if additional formatting characters are later inserted/removed in front of nextChar.
*/
public String inputDigitAndRememberPosition(char nextChar) {
return inputDigitWithOptionToRememberPosition(nextChar, true);
currentOutput = inputDigitWithOptionToRememberPosition(nextChar, true);
return currentOutput;
}
@SuppressWarnings(value = "fallthrough")
private String inputDigitWithOptionToRememberPosition(char nextChar, boolean rememberPosition) {
accruedInput.append(nextChar);
if (rememberPosition) {
positionRemembered = accruedInput.length();
originalPosition = positionRemembered;
originalPosition = accruedInput.length();
}
// We do formatting on-the-fly only when each character entered is either a plus sign or a
// digit.
@ -215,46 +248,71 @@ public class AsYouTypeFormatter {
ableToFormat = false;
}
if (!ableToFormat) {
resetPositionOnFailureToFormat();
return accruedInput.toString();
}
nextChar = normalizeAndAccrueDigitsAndPlusSign(nextChar);
nextChar = normalizeAndAccrueDigitsAndPlusSign(nextChar, rememberPosition);
// 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.
// We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits (the plus
// sign is counted as a digit as well for this purpose) have been entered.
switch (accruedInputWithoutFormatting.length()) {
case 0: // this is the case where the first few inputs are neither digits nor the plus sign.
case 1:
case 2:
return accruedInput.toString();
case 3:
if (attemptToExtractIdd()) {
isExpectingCountryCode = true;
} else { // No IDD or plus sign is found, must be entering in national format.
removeNationalPrefixFromNationalNumber();
return attemptToChooseFormattingPattern();
}
case 4:
case 5:
return accruedInput.toString();
if (isExpectingCountryCode) {
if (attemptToExtractCountryCode()) {
isExpectingCountryCode = false;
}
return prefixBeforeNationalNumber + nationalNumber.toString();
}
// We make a last attempt to extract a country code at the 6th digit because the maximum
// length of IDD and country code are both 3.
case 6:
if (!extractIddAndValidCountryCode()) {
if (isExpectingCountryCode && !attemptToExtractCountryCode()) {
ableToFormat = false;
return accruedInput.toString();
}
removeNationalPrefixFromNationalNumber();
return attemptToChooseFormattingPattern(rememberPosition);
default:
if (nationalNumber.length() > 4) { // The formatting pattern is already chosen.
String temp = inputDigitHelper(nextChar, rememberPosition);
if (possibleFormats.size() > 0) { // The formatting pattern is already chosen.
String tempNationalNumber = inputDigitHelper(nextChar);
// See if the accrued digits can be formatted properly already. If not, use the results
// from inputDigitHelper, which does formatting based on the formatting pattern chosen.
String formattedNumber = attemptToFormatAccruedDigits();
if (formattedNumber.length() > 0) {
return formattedNumber;
}
narrowDownPossibleFormats(nationalNumber.toString());
if (maybeCreateNewTemplate()) {
return inputAccruedNationalNumber();
}
return ableToFormat
? prefixBeforeNationalNumber + temp
: temp;
? prefixBeforeNationalNumber + tempNationalNumber
: tempNationalNumber;
} else {
return attemptToChooseFormattingPattern(rememberPosition);
return attemptToChooseFormattingPattern();
}
}
}
private void resetPositionOnFailureToFormat() {
if (positionRemembered > 0) {
positionRemembered = originalPosition;
currentOutput.setLength(0);
String attemptToFormatAccruedDigits() {
for (NumberFormat numFormat : possibleFormats) {
Matcher m = regexCache.getPatternForRegex(numFormat.getPattern()).matcher(nationalNumber);
if (m.matches()) {
String formattedNumber = m.replaceAll(numFormat.getFormat());
return prefixBeforeNationalNumber + formattedNumber;
}
}
return "";
}
/**
@ -262,48 +320,49 @@ public class AsYouTypeFormatter {
* previously passed in as the parameter of inputDigitAndRememberPosition().
*/
public int getRememberedPosition() {
return positionRemembered;
if (!ableToFormat) {
return originalPosition;
}
int accruedInputIndex = 0, currentOutputIndex = 0;
while (accruedInputIndex < positionToRemember) {
if (accruedInputWithoutFormatting.charAt(accruedInputIndex) ==
currentOutput.charAt(currentOutputIndex)) {
accruedInputIndex++;
currentOutputIndex++;
} else {
currentOutputIndex++;
}
}
return currentOutputIndex;
}
// Attempts to set the formatting template and returns a string which contains the formatted
// version of the digits entered so far.
private String attemptToChooseFormattingPattern(boolean rememberPosition) {
// 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(rememberPosition);
private String attemptToChooseFormattingPattern() {
// We start to attempt to format only when as least MIN_LEADING_DIGITS_LENGTH digits of national
// number (excluding national prefix) have been entered.
if (nationalNumber.length() >= MIN_LEADING_DIGITS_LENGTH) {
getAvailableFormats(nationalNumber.substring(0, MIN_LEADING_DIGITS_LENGTH));
maybeCreateNewTemplate();
return inputAccruedNationalNumber();
} else {
if (rememberPosition) {
positionRemembered = prefixBeforeNationalNumber.length() + nationalNumber.length();
}
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(boolean rememberPosition) {
private String inputAccruedNationalNumber() {
int lengthOfNationalNumber = nationalNumber.length();
if (lengthOfNationalNumber > 0) {
// The positionRemembered should be only adjusted once in the loop that follows.
boolean positionAlreadyAdjusted = false;
String tempNationalNumber = "";
for (int i = 0; i < lengthOfNationalNumber; i++) {
tempNationalNumber = inputDigitHelper(nationalNumber.charAt(i), rememberPosition);
if (!positionAlreadyAdjusted &&
positionRemembered - prefixBeforeNationalNumber.length() == i + 1) {
positionRemembered = prefixBeforeNationalNumber.length() + tempNationalNumber.length();
positionAlreadyAdjusted = true;
}
tempNationalNumber = inputDigitHelper(nationalNumber.charAt(i));
}
return ableToFormat
? prefixBeforeNationalNumber + tempNationalNumber
: tempNationalNumber;
} else {
if (rememberPosition) {
positionRemembered = prefixBeforeNationalNumber.length();
}
return prefixBeforeNationalNumber.toString();
}
}
@ -313,11 +372,7 @@ public class AsYouTypeFormatter {
if (currentMetaData.getCountryCode() == 1 && nationalNumber.charAt(0) == '1') {
startOfNationalNumber = 1;
prefixBeforeNationalNumber.append("1 ");
// Since a space is inserted after the national prefix in this case, we increase the
// remembered position by 1 for anything that is after the national prefix.
if (positionRemembered > prefixBeforeNationalNumber.length() - 1) {
positionRemembered++;
}
isInternationalFormatting = true;
} else if (currentMetaData.hasNationalPrefix()) {
Matcher m = nationalPrefixForParsing.matcher(nationalNumber);
if (m.lookingAt()) {
@ -329,51 +384,53 @@ public class AsYouTypeFormatter {
}
/**
* Extracts IDD, plus sign and country code to prefixBeforeNationalNumber when they are available,
* and places the remaining input into nationalNumber.
* Extracts IDD and plus sign 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.
* @return true when accruedInputWithoutFormatting begins with the plus sign or valid IDD for
* defaultCountry.
*/
private boolean extractIddAndValidCountryCode() {
nationalNumber.setLength(0);
private boolean attemptToExtractIdd() {
Matcher iddMatcher = internationalPrefix.matcher(accruedInputWithoutFormatting);
if (iddMatcher.lookingAt()) {
isInternationalFormatting = true;
int startOfCountryCode = iddMatcher.end();
StringBuffer numberIncludeCountryCode =
new StringBuffer(accruedInputWithoutFormatting.substring(startOfCountryCode));
int countryCode = phoneUtil.extractCountryCode(numberIncludeCountryCode, nationalNumber);
nationalNumber.setLength(0);
nationalNumber.append(accruedInputWithoutFormatting.substring(startOfCountryCode));
prefixBeforeNationalNumber.append(
accruedInputWithoutFormatting.substring(0, startOfCountryCode));
if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN) {
prefixBeforeNationalNumber.append(" ");
}
return true;
}
return false;
}
/**
* Extracts country code from the beginning of nationalNumber to prefixBeforeNationalNumber when
* they are available, and places the remaining input into nationalNumber.
*
* @return true when a valid country code can be found.
*/
private boolean attemptToExtractCountryCode() {
if (nationalNumber.length() == 0) {
return false;
}
StringBuffer numberWithoutCountryCode = new StringBuffer();
int countryCode = phoneUtil.extractCountryCode(nationalNumber, numberWithoutCountryCode);
if (countryCode == 0) {
return false;
} else {
nationalNumber.setLength(0);
nationalNumber.append(numberWithoutCountryCode);
String newRegionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
if (!newRegionCode.equals(defaultCountry)) {
initializeCountrySpecificInfo(newRegionCode);
}
prefixBeforeNationalNumber.append(
accruedInputWithoutFormatting.substring(0, startOfCountryCode));
if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN ) {
if (positionRemembered > prefixBeforeNationalNumber.length()) {
// Since a space will be inserted in front of the country code in this case, we increase
// the remembered position by 1.
positionRemembered++;
}
prefixBeforeNationalNumber.append(" ");
}
String countryCodeString = Integer.toString(countryCode);
if (positionRemembered > prefixBeforeNationalNumber.length() + countryCodeString.length()) {
// Since a space will be inserted after the country code in this case, we increase the
// remembered position by 1.
positionRemembered++;
}
prefixBeforeNationalNumber.append(countryCodeString).append(" ");
}
} else {
nationalNumber.setLength(0);
nationalNumber.append(accruedInputWithoutFormatting);
}
return true;
}
@ -381,36 +438,30 @@ public class AsYouTypeFormatter {
// 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) {
private char normalizeAndAccrueDigitsAndPlusSign(char nextChar, boolean rememberPosition) {
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);
}
if (rememberPosition) {
positionToRemember = accruedInputWithoutFormatting.length();
}
return nextChar;
}
private String inputDigitHelper(char nextChar, boolean rememberPosition) {
if (!PhoneNumberUtil.DIGIT_MAPPINGS.containsKey(nextChar)) {
return currentOutput.toString();
}
Matcher digitMatcher = digitPattern.matcher(currentOutput);
private String inputDigitHelper(char nextChar) {
Matcher digitMatcher = digitPattern.matcher(formattingTemplate);
if (digitMatcher.find(lastMatchPosition)) {
currentOutput = new StringBuffer(digitMatcher.replaceFirst(Character.toString(nextChar)));
String tempTemplate = digitMatcher.replaceFirst(Character.toString(nextChar));
formattingTemplate.replace(0, tempTemplate.length(), tempTemplate);
lastMatchPosition = digitMatcher.start();
if (rememberPosition) {
positionRemembered = prefixBeforeNationalNumber.length() + lastMatchPosition + 1;
}
return currentOutput.substring(0, lastMatchPosition + 1);
return formattingTemplate.substring(0, lastMatchPosition + 1);
} else { // More digits are entered than we could handle.
currentOutput.append(nextChar);
ableToFormat = false;
resetPositionOnFailureToFormat();
return accruedInput.toString();
}
}


BIN
java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto View File


+ 67
- 19
java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java View File

@ -498,7 +498,7 @@ public class PhoneNumberUtil {
* subscriber number should be diallable, at least on some devices. An example of how this could
* be used:
*
* PhoneNumberUtil phoneUtil.PhoneNumberUtil.getInstance();
* PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
* PhoneNumber number = phoneUtil.parse("16502530000", RegionCode.US);
* String nationalSignificantNumber = PhoneNumberUtil.getNationalSignificantNumber(number);
* String areaCode;
@ -540,7 +540,7 @@ public class PhoneNumberUtil {
return 0;
}
PhoneNumberType type = getNumberTypeHelper(String.valueOf(number.getNationalNumber()),
PhoneNumberType type = getNumberTypeHelper(getNationalSignificantNumber(number),
metadata);
// Most numbers other than the two types below have to be dialled in full.
if (type != PhoneNumberType.FIXED_LINE && type != PhoneNumberType.FIXED_LINE_OR_MOBILE) {
@ -567,9 +567,6 @@ public class PhoneNumberUtil {
if (numberGroups.length <= 3) {
return 0;
}
// Note all countries that use leading zero in national number don't use national prefix, so
// they won't have an area code, which means clients don't need to worry about appending the
// leading zero to the geographical area code they derive from the length we return here.
return numberGroups[2].length();
}
@ -725,24 +722,40 @@ public Set<String> getSupportedCountries() {
if (!isValidRegionCode(regionCode)) {
return nationalSignificantNumber;
}
List<NumberFormat> userDefinedFormatsCopy =
new ArrayList<NumberFormat>(userDefinedFormats.size());
int size = userDefinedFormats.size();
for (int i = 0; i < size; i++) {
NumberFormat numFormat = userDefinedFormats.get(i);
String nationalPrefixFormattingRule = numFormat.getNationalPrefixFormattingRule();
if (nationalPrefixFormattingRule.length() > 0) {
// 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 = new NumberFormat();
numFormatCopy.mergeFrom(numFormat);
String nationalPrefix = getMetadataForRegion(regionCode).getNationalPrefix();
// 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");
numFormat.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
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.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
} else {
// We don't want to have a rule for how to format the national prefix if there isn't one.
numFormatCopy.clearNationalPrefixFormattingRule();
}
userDefinedFormatsCopy.add(numFormatCopy);
} else {
// Otherwise, we just add the original rule to the modified list of formats.
userDefinedFormatsCopy.add(numFormat);
}
}
StringBuffer formattedNumber =
new StringBuffer(formatAccordingToFormats(nationalSignificantNumber,
userDefinedFormats,
userDefinedFormatsCopy,
numberFormat));
maybeGetFormattedExtension(number, regionCode, formattedNumber);
formatNumberByFormat(countryCode, numberFormat, formattedNumber);
@ -953,9 +966,10 @@ public Set<String> getSupportedCountries() {
PhoneNumberFormat numberFormat,
String carrierCode) {
for (NumberFormat numFormat : availableFormats) {
if (!numFormat.hasLeadingDigits() ||
regexCache.getPatternForRegex(numFormat.getLeadingDigits()).matcher(nationalNumber)
.lookingAt()) {
int size = numFormat.getLeadingDigitsPatternCount();
if (size == 0 || regexCache.getPatternForRegex(
// We always use the last leading_digits_pattern, as it is the most detailed.
numFormat.getLeadingDigitsPattern(size - 1)).matcher(nationalNumber).lookingAt()) {
Matcher m = regexCache.getPatternForRegex(numFormat.getPattern()).matcher(nationalNumber);
String numberFormatRule = numFormat.getFormat();
if (m.matches()) {
@ -1372,6 +1386,32 @@ public Set<String> getSupportedCountries() {
}
}
/**
* Attempts to extract a valid number from a phone number that is too long to be valid, and resets
* the PhoneNumber object passed in to that valid version. If no valid number could be extracted,
* the PhoneNumber object passed in will not be modified.
* @param number a PhoneNumber object which contains a number that is too long to be valid.
* @return true if a valid phone number can be successfully extracted.
*/
public boolean truncateTooLongNumber(PhoneNumber number) {
if (isValidNumber(number)) {
return true;
}
PhoneNumber numberCopy = new PhoneNumber();
numberCopy.mergeFrom(number);
long nationalNumber = number.getNationalNumber();
do {
nationalNumber /= 10;
numberCopy.setNationalNumber(nationalNumber);
if (isPossibleNumberWithReason(numberCopy) == ValidationResult.TOO_SHORT ||
nationalNumber == 0) {
return false;
}
} while (!isValidNumber(numberCopy));
number.setNationalNumber(nationalNumber);
return true;
}
/**
* 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.
@ -1396,7 +1436,8 @@ public Set<String> getSupportedCountries() {
// 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++) {
int numberLength = fullNumber.length();
for (int i = 1; i <= 3 && i <= numberLength; i++) {
potentialCountryCode = Integer.parseInt(fullNumber.substring(0, i));
if (countryCodeToRegionCodeMap.containsKey(potentialCountryCode)) {
nationalNumber.append(fullNumber.substring(i));
@ -1472,9 +1513,9 @@ public Set<String> getSupportedCountries() {
} 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.
PhoneNumberDesc generalDesc = defaultRegionMetadata.getGeneralDesc();
Pattern validNumberPattern =
regexCache.getPatternForRegex(defaultRegionMetadata.getGeneralDesc()
.getNationalNumberPattern());
regexCache.getPatternForRegex(generalDesc.getNationalNumberPattern());
if (!validNumberPattern.matcher(fullNumber).matches()) {
int defaultCountryCode = defaultRegionMetadata.getCountryCode();
String defaultCountryCodeString = String.valueOf(defaultCountryCode);
@ -1488,7 +1529,14 @@ public Set<String> getSupportedCountries() {
defaultRegionMetadata.getNationalPrefixForParsing(),
defaultRegionMetadata.getNationalPrefixTransformRule(),
validNumberPattern);
if (validNumberPattern.matcher(potentialNationalNumber).matches()) {
Matcher possibleNumberMatcher =
regexCache.getPatternForRegex(generalDesc.getPossibleNumberPattern()).matcher(
potentialNationalNumber);
// If the resultant number is either valid, or still too long even with the country code
// stripped, we consider this a better result and keep the potential national number.
if (validNumberPattern.matcher(potentialNationalNumber).matches() ||
(possibleNumberMatcher.lookingAt() &&
possibleNumberMatcher.end() != potentialNationalNumber.length())) {
nationalNumber.append(potentialNationalNumber);
if (storeCountryCodeSource) {
phoneNumber.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN);


+ 47
- 13
java/src/com/google/i18n/phonenumbers/Phonemetadata.java View File

@ -55,14 +55,20 @@ public final class Phonemetadata {
return this;
}
// optional string leading_digits = 3;
private boolean hasLeadingDigits;
private String leadingDigits_ = "";
public boolean hasLeadingDigits() { return hasLeadingDigits; }
public String getLeadingDigits() { return leadingDigits_; }
public NumberFormat setLeadingDigits(String value) {
hasLeadingDigits = true;
leadingDigits_ = value;
// repeated string leading_digits_pattern = 3;
private java.util.List<String> leadingDigitsPattern_ = new java.util.ArrayList<String>();
public java.util.List<String> getLeadingDigitsPatternList() {
return leadingDigitsPattern_;
}
public int getLeadingDigitsPatternCount() { return leadingDigitsPattern_.size(); }
public String getLeadingDigitsPattern(int index) {
return leadingDigitsPattern_.get(index);
}
public NumberFormat addLeadingDigitsPattern(String value) {
if (value == null) {
throw new NullPointerException();
}
leadingDigitsPattern_.add(value);
return this;
}
@ -76,6 +82,11 @@ public final class Phonemetadata {
nationalPrefixFormattingRule_ = value;
return this;
}
public NumberFormat clearNationalPrefixFormattingRule() {
hasNationalPrefixFormattingRule = false;
nationalPrefixFormattingRule_ = "";
return this;
}
// optional string domestic_carrier_code_formatting_rule = 5;
private boolean hasDomesticCarrierCodeFormattingRule;
@ -90,13 +101,35 @@ public final class Phonemetadata {
return this;
}
public NumberFormat mergeFrom(NumberFormat other) {
if (other.hasPattern()) {
setPattern(other.getPattern());
}
if (other.hasFormat()) {
setFormat(other.getFormat());
}
int leadingDigitsPatternSize = other.getLeadingDigitsPatternCount();
for (int i = 0; i < leadingDigitsPatternSize; i++) {
addLeadingDigitsPattern(other.getLeadingDigitsPattern(i));
}
if (other.hasNationalPrefixFormattingRule()) {
setNationalPrefixFormattingRule(other.getNationalPrefixFormattingRule());
}
if (other.hasDomesticCarrierCodeFormattingRule()) {
setDomesticCarrierCodeFormattingRule(other.getDomesticCarrierCodeFormattingRule());
}
return this;
}
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeUTF(pattern_);
objectOutput.writeUTF(format_);
objectOutput.writeBoolean(hasLeadingDigits);
if (hasLeadingDigits) {
objectOutput.writeUTF(leadingDigits_);
int leadingDigitsPatternSize = getLeadingDigitsPatternCount();
objectOutput.writeInt(leadingDigitsPatternSize);
for (int i = 0; i < leadingDigitsPatternSize; i++) {
objectOutput.writeUTF(leadingDigitsPattern_.get(i));
}
objectOutput.writeBoolean(hasNationalPrefixFormattingRule);
if (hasNationalPrefixFormattingRule) {
objectOutput.writeUTF(nationalPrefixFormattingRule_);
@ -110,8 +143,9 @@ public final class Phonemetadata {
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
setPattern(objectInput.readUTF());
setFormat(objectInput.readUTF());
if (objectInput.readBoolean()) {
setLeadingDigits(objectInput.readUTF());
int leadingDigitsPatternSize = objectInput.readInt();
for (int i = 0; i < leadingDigitsPatternSize; i++) {
leadingDigitsPattern_.add(objectInput.readUTF());
}
if (objectInput.readBoolean()) {
setNationalPrefixFormattingRule(objectInput.readUTF());


+ 204
- 109
java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java View File

@ -35,20 +35,21 @@ public class AsYouTypeFormatterTest extends TestCase {
"/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting";
public AsYouTypeFormatterTest() {
PhoneNumberUtil.resetInstance();
PhoneNumberUtil.resetInstance();
InputStream in = PhoneNumberUtilTest.class.getResourceAsStream(TEST_META_DATA_FILE);
phoneUtil = PhoneNumberUtil.getInstance(in);
}
public void testAsYouTypeFormatterUS() {
public void testAYTFUS() {
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 2", formatter.inputDigit('2'));
assertEquals("650 25", formatter.inputDigit('5'));
assertEquals("650 253", formatter.inputDigit('3'));
assertEquals("650 253 2", formatter.inputDigit('2'));
// Note this is how a US local number (without area code) should be formatted.
assertEquals("650 2532", formatter.inputDigit('2'));
assertEquals("650 253 22", formatter.inputDigit('2'));
assertEquals("650 253 222", formatter.inputDigit('2'));
assertEquals("650 253 2222", formatter.inputDigit('2'));
@ -56,9 +57,9 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("1", formatter.inputDigit('1'));
assertEquals("16", formatter.inputDigit('6'));
assertEquals("165", formatter.inputDigit('5'));
assertEquals("1650", formatter.inputDigit('0'));
assertEquals("16502", formatter.inputDigit('2'));
assertEquals("1 65", formatter.inputDigit('5'));
assertEquals("1 650", formatter.inputDigit('0'));
assertEquals("1 650 2", formatter.inputDigit('2'));
assertEquals("1 650 25", formatter.inputDigit('5'));
assertEquals("1 650 253", formatter.inputDigit('3'));
assertEquals("1 650 253 2", formatter.inputDigit('2'));
@ -69,12 +70,12 @@ public class AsYouTypeFormatterTest extends TestCase {
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 ", formatter.inputDigit('1'));
assertEquals("011 4", formatter.inputDigit('4'));
assertEquals("011 44 ", 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 12", formatter.inputDigit('2'));
assertEquals("011 44 6 123", formatter.inputDigit('3'));
assertEquals("011 44 6 123 1", formatter.inputDigit('1'));
assertEquals("011 44 6 123 12", formatter.inputDigit('2'));
@ -86,14 +87,13 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0115", formatter.inputDigit('5'));
assertEquals("01154", formatter.inputDigit('4'));
assertEquals("011 ", formatter.inputDigit('1'));
assertEquals("011 5", formatter.inputDigit('5'));
assertEquals("011 54 ", formatter.inputDigit('4'));
assertEquals("011 54 9", formatter.inputDigit('9'));
assertEquals("011 54 91", formatter.inputDigit('1'));
assertEquals("011 54 911", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2",
formatter.inputDigit('2'));
assertEquals("011 54 9 11", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2", formatter.inputDigit('2'));
assertEquals("011 54 9 11 23", formatter.inputDigit('3'));
assertEquals("011 54 9 11 231", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2312", formatter.inputDigit('2'));
@ -102,13 +102,30 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("011 54 9 11 2312 123", formatter.inputDigit('3'));
assertEquals("011 54 9 11 2312 1234", formatter.inputDigit('4'));
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011 ", formatter.inputDigit('1'));
assertEquals("011 2", formatter.inputDigit('2'));
assertEquals("011 24", formatter.inputDigit('4'));
assertEquals("011 244 ", formatter.inputDigit('4'));
assertEquals("011 244 2", formatter.inputDigit('2'));
assertEquals("011 244 28", formatter.inputDigit('8'));
assertEquals("011 244 280", formatter.inputDigit('0'));
assertEquals("011 244 280 0", formatter.inputDigit('0'));
assertEquals("011 244 280 00", formatter.inputDigit('0'));
assertEquals("011 244 280 000", formatter.inputDigit('0'));
assertEquals("011 244 280 000 0", formatter.inputDigit('0'));
assertEquals("011 244 280 000 00", formatter.inputDigit('0'));
assertEquals("011 244 280 000 000", formatter.inputDigit('0'));
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 ", formatter.inputDigit('8'));
assertEquals("+48 8", formatter.inputDigit('8'));
assertEquals("+48 88", formatter.inputDigit('8'));
assertEquals("+48 88 1", formatter.inputDigit('1'));
assertEquals("+48 88 12", formatter.inputDigit('2'));
assertEquals("+48 88 123", formatter.inputDigit('3'));
assertEquals("+48 88 123 1", formatter.inputDigit('1'));
@ -117,21 +134,21 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("+48 88 123 12 12", formatter.inputDigit('2'));
}
public void testAsYouTypeFormatterUSFullWidthCharacters() {
public void testAYTFUSFullWidthCharacters() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
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", formatter.inputDigit('\uFF10'));
assertEquals("650 2", formatter.inputDigit('\uFF12'));
assertEquals("650 25", formatter.inputDigit('\uFF15'));
assertEquals("650 253", formatter.inputDigit('\uFF13'));
assertEquals("650 253 2", formatter.inputDigit('\uFF12'));
assertEquals("650 2532", formatter.inputDigit('\uFF12'));
assertEquals("650 253 22", formatter.inputDigit('\uFF12'));
assertEquals("650 253 222", formatter.inputDigit('\uFF12'));
assertEquals("650 253 2222", formatter.inputDigit('\uFF12'));
}
public void testAsYouTypeFormatterUSMobileShortCode() {
public void testAYTFUSMobileShortCode() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("*", formatter.inputDigit('*'));
assertEquals("*1", formatter.inputDigit('1'));
@ -140,7 +157,7 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("*121#", formatter.inputDigit('#'));
}
public void testAsYouTypeFormatterUSVanityNumber() {
public void testAYTFUSVanityNumber() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("8", formatter.inputDigit('8'));
assertEquals("80", formatter.inputDigit('0'));
@ -156,16 +173,16 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("800 MY APPLE", formatter.inputDigit('E'));
}
public void testAsYouTypeFormatterAndRememberPositionUS() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
public void testAYTFAndRememberPositionUS() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("1", formatter.inputDigitAndRememberPosition('1'));
assertEquals(1, formatter.getRememberedPosition());
assertEquals("16", formatter.inputDigit('6'));
assertEquals("165", formatter.inputDigit('5'));
assertEquals("1 65", formatter.inputDigit('5'));
assertEquals(1, formatter.getRememberedPosition());
assertEquals("1650", formatter.inputDigitAndRememberPosition('0'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("16502", formatter.inputDigit('2'));
assertEquals("1 650", formatter.inputDigitAndRememberPosition('0'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("1 650 2", formatter.inputDigit('2'));
assertEquals("1 650 25", formatter.inputDigit('5'));
// Note the remembered position for digit "0" changes from 4 to 5, because a space is now
// inserted in the front.
@ -185,39 +202,40 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("1", formatter.inputDigit('1'));
assertEquals("16", formatter.inputDigit('6'));
assertEquals("165", formatter.inputDigitAndRememberPosition('5'));
assertEquals("1650", formatter.inputDigit('0'));
assertEquals("16", formatter.inputDigitAndRememberPosition('6'));
assertEquals(2, formatter.getRememberedPosition());
assertEquals("1 65", formatter.inputDigit('5'));
assertEquals("1 650", formatter.inputDigit('0'));
assertEquals(3, formatter.getRememberedPosition());
assertEquals("16502", formatter.inputDigit('2'));
assertEquals("1 650 2", formatter.inputDigit('2'));
assertEquals("1 650 25", formatter.inputDigit('5'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals(3, formatter.getRememberedPosition());
assertEquals("1 650 253", formatter.inputDigit('3'));
assertEquals("1 650 253 2", formatter.inputDigit('2'));
assertEquals("1 650 253 22", formatter.inputDigit('2'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals(3, formatter.getRememberedPosition());
assertEquals("1 650 253 222", formatter.inputDigit('2'));
assertEquals("1 650 253 2222", formatter.inputDigit('2'));
assertEquals("165025322222", formatter.inputDigit('2'));
assertEquals(3, formatter.getRememberedPosition());
assertEquals(2, formatter.getRememberedPosition());
assertEquals("1650253222222", formatter.inputDigit('2'));
assertEquals(3, formatter.getRememberedPosition());
assertEquals(2, formatter.getRememberedPosition());
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.inputDigitAndRememberPosition('5'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("650 2", formatter.inputDigit('2'));
assertEquals("650 25", formatter.inputDigit('5'));
assertEquals("650 253", formatter.inputDigit('3'));
assertEquals(6, formatter.getRememberedPosition());
assertEquals("650 253 2", formatter.inputDigit('2'));
assertEquals("650 2532", formatter.inputDigitAndRememberPosition('2'));
assertEquals(8, formatter.getRememberedPosition());
assertEquals("650 253 22", formatter.inputDigit('2'));
assertEquals(9, formatter.getRememberedPosition());
assertEquals("650 253 222", formatter.inputDigit('2'));
// No more formatting when semicolon is entered.
assertEquals("650253222;", formatter.inputDigit(';'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals(7, formatter.getRememberedPosition());
assertEquals("650253222;2", formatter.inputDigit('2'));
formatter.clear();
@ -241,14 +259,14 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0114", formatter.inputDigitAndRememberPosition('4'));
assertEquals("01148", formatter.inputDigit('8'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("011 ", formatter.inputDigit('1'));
assertEquals("011 4", formatter.inputDigitAndRememberPosition('4'));
assertEquals("011 48 ", formatter.inputDigit('8'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("011 48 8", formatter.inputDigit('8'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("011 48 88", formatter.inputDigit('8'));
assertEquals("011 48 881", formatter.inputDigit('1'));
assertEquals("011 48 88 1", formatter.inputDigit('1'));
assertEquals("011 48 88 12", formatter.inputDigit('2'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("011 48 88 123", formatter.inputDigit('3'));
@ -260,10 +278,10 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+1", formatter.inputDigit('1'));
assertEquals("+16", formatter.inputDigitAndRememberPosition('6'));
assertEquals("+165", formatter.inputDigit('5'));
assertEquals("+1650", formatter.inputDigit('0'));
assertEquals(3, formatter.getRememberedPosition());
assertEquals("+1 6", formatter.inputDigitAndRememberPosition('6'));
assertEquals("+1 65", formatter.inputDigit('5'));
assertEquals("+1 650", formatter.inputDigit('0'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("+1 650 2", formatter.inputDigit('2'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("+1 650 25", formatter.inputDigit('5'));
@ -276,10 +294,10 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+1", formatter.inputDigit('1'));
assertEquals("+16", formatter.inputDigitAndRememberPosition('6'));
assertEquals("+165", formatter.inputDigit('5'));
assertEquals("+1650", formatter.inputDigit('0'));
assertEquals(3, formatter.getRememberedPosition());
assertEquals("+1 6", formatter.inputDigitAndRememberPosition('6'));
assertEquals("+1 65", formatter.inputDigit('5'));
assertEquals("+1 650", formatter.inputDigit('0'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("+1 650 2", formatter.inputDigit('2'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("+1 650 25", formatter.inputDigit('5'));
@ -291,14 +309,14 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals(3, formatter.getRememberedPosition());
}
public void testAsYouTypeFormatterGBFixedLine() {
public void testAYTFGBFixedLine() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("GB");
assertEquals("0", formatter.inputDigit('0'));
assertEquals("02", formatter.inputDigit('2'));
assertEquals("020", formatter.inputDigit('0'));
assertEquals("0207", formatter.inputDigitAndRememberPosition('7'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("02070", formatter.inputDigit('0'));
assertEquals("020 7", formatter.inputDigitAndRememberPosition('7'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("020 70", formatter.inputDigit('0'));
assertEquals("020 703", formatter.inputDigit('3'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("020 7031", formatter.inputDigit('1'));
@ -308,13 +326,13 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("020 7031 3000", formatter.inputDigit('0'));
}
public void testAsYouTypeFormatterGBTollFree() {
public void testAYTFGBTollFree() {
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 7", formatter.inputDigit('7'));
assertEquals("080 70", formatter.inputDigit('0'));
assertEquals("080 703", formatter.inputDigit('3'));
assertEquals("080 7031", formatter.inputDigit('1'));
assertEquals("080 7031 3", formatter.inputDigit('3'));
@ -323,13 +341,13 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("080 7031 3000", formatter.inputDigit('0'));
}
public void testAsYouTypeFormatterGBPremiumRate() {
public void testAYTFGBPremiumRate() {
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 7", formatter.inputDigit('7'));
assertEquals("090 70", formatter.inputDigit('0'));
assertEquals("090 703", formatter.inputDigit('3'));
assertEquals("090 7031", formatter.inputDigit('1'));
assertEquals("090 7031 3", formatter.inputDigit('3'));
@ -338,13 +356,13 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("090 7031 3000", formatter.inputDigit('0'));
}
public void testAsYouTypeFormatterNZMobile() {
public void testAYTFNZMobile() {
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("02-11", formatter.inputDigit('1'));
assertEquals("02-112", formatter.inputDigit('2'));
// Note the unittest is using fake metadata which might produce non-ideal results.
assertEquals("02-112 3", formatter.inputDigit('3'));
assertEquals("02-112 34", formatter.inputDigit('4'));
@ -352,24 +370,52 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("02-112 3456", formatter.inputDigit('6'));
}
public void testAsYouTypeFormatterDE() {
public void testAYTFDE() {
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("030 1", formatter.inputDigit('1'));
assertEquals("030 12", formatter.inputDigit('2'));
assertEquals("030 123", formatter.inputDigit('3'));
assertEquals("030 1234", formatter.inputDigit('4'));
// 08021 2345
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("08", formatter.inputDigit('8'));
assertEquals("080", formatter.inputDigit('0'));
assertEquals("0802", formatter.inputDigit('2'));
assertEquals("08021", formatter.inputDigit('1'));
assertEquals("08021 2", formatter.inputDigit('2'));
assertEquals("08021 23", formatter.inputDigit('3'));
assertEquals("08021 234", formatter.inputDigit('4'));
assertEquals("08021 2345", formatter.inputDigit('5'));
// 00 1 650 253 2250
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("00", formatter.inputDigit('0'));
assertEquals("00 1 ", formatter.inputDigit('1'));
assertEquals("00 1 6", formatter.inputDigit('6'));
assertEquals("00 1 65", formatter.inputDigit('5'));
assertEquals("00 1 650", formatter.inputDigit('0'));
assertEquals("00 1 650 2", formatter.inputDigit('2'));
assertEquals("00 1 650 25", formatter.inputDigit('5'));
assertEquals("00 1 650 253", formatter.inputDigit('3'));
assertEquals("00 1 650 253 2", formatter.inputDigit('2'));
assertEquals("00 1 650 253 22", formatter.inputDigit('2'));
assertEquals("00 1 650 253 222", formatter.inputDigit('2'));
assertEquals("00 1 650 253 2222", formatter.inputDigit('2'));
}
public void testAsYouTypeFormatterAR() {
public void testAYTFAR() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("AR");
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0117", formatter.inputDigit('7'));
assertEquals("01170", formatter.inputDigit('0'));
assertEquals("011 7", formatter.inputDigit('7'));
assertEquals("011 70", formatter.inputDigit('0'));
assertEquals("011 703", formatter.inputDigit('3'));
assertEquals("011 7031", formatter.inputDigit('1'));
assertEquals("011 7031-3", formatter.inputDigit('3'));
@ -378,15 +424,16 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("011 7031-3000", formatter.inputDigit('0'));
}
public void testAsYouTypeFormatterARMobile() {
public void testAYTFARMobile() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("AR");
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+5", formatter.inputDigit('5'));
assertEquals("+54", formatter.inputDigit('4'));
assertEquals("+549", formatter.inputDigit('9'));
assertEquals("+5491", formatter.inputDigit('1'));
assertEquals("+54 911", formatter.inputDigit('1'));
assertEquals("+54 9 11 2", formatter.inputDigit('2'));
assertEquals("+54 ", formatter.inputDigit('4'));
assertEquals("+54 9", formatter.inputDigit('9'));
assertEquals("+54 91", formatter.inputDigit('1'));
assertEquals("+54 9 11", formatter.inputDigit('1'));
assertEquals("+54 9 11 2",
formatter.inputDigit('2'));
assertEquals("+54 9 11 23", formatter.inputDigit('3'));
assertEquals("+54 9 11 231", formatter.inputDigit('1'));
assertEquals("+54 9 11 2312", formatter.inputDigit('2'));
@ -396,15 +443,15 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("+54 9 11 2312 1234", formatter.inputDigit('4'));
}
public void testAsYouTypeFormatterKR() {
public void testAYTFKR() {
// +82 51 234 5678
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("KR");
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+8", formatter.inputDigit('8'));
assertEquals("+82", formatter.inputDigit('2'));
assertEquals("+825", formatter.inputDigit('5'));
assertEquals("+8251", formatter.inputDigit('1'));
assertEquals("+82 512", formatter.inputDigit('2'));
assertEquals("+82 ", formatter.inputDigit('2'));
assertEquals("+82 5", formatter.inputDigit('5'));
assertEquals("+82 51", formatter.inputDigit('1'));
assertEquals("+82 51-2", formatter.inputDigit('2'));
assertEquals("+82 51-23", formatter.inputDigit('3'));
assertEquals("+82 51-234", formatter.inputDigit('4'));
assertEquals("+82 51-234-5", formatter.inputDigit('5'));
@ -416,10 +463,10 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+8", formatter.inputDigit('8'));
assertEquals("+82", formatter.inputDigit('2'));
assertEquals("+822", formatter.inputDigit('2'));
assertEquals("+8225", formatter.inputDigit('5'));
assertEquals("+82 253", formatter.inputDigit('3'));
assertEquals("+82 ", formatter.inputDigit('2'));
assertEquals("+82 2", formatter.inputDigit('2'));
assertEquals("+82 25", formatter.inputDigit('5'));
assertEquals("+82 2-53", formatter.inputDigit('3'));
assertEquals("+82 2-531", formatter.inputDigit('1'));
assertEquals("+82 2-531-5", formatter.inputDigit('5'));
assertEquals("+82 2-531-56", formatter.inputDigit('6'));
@ -430,10 +477,10 @@ public class AsYouTypeFormatterTest extends TestCase {
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+8", formatter.inputDigit('8'));
assertEquals("+82", formatter.inputDigit('2'));
assertEquals("+822", formatter.inputDigit('2'));
assertEquals("+8223", formatter.inputDigit('3'));
assertEquals("+82 236", formatter.inputDigit('6'));
assertEquals("+82 ", formatter.inputDigit('2'));
assertEquals("+82 2", formatter.inputDigit('2'));
assertEquals("+82 23", formatter.inputDigit('3'));
assertEquals("+82 2-36", formatter.inputDigit('6'));
assertEquals("+82 2-366", formatter.inputDigit('6'));
assertEquals("+82 2-3665", formatter.inputDigit('5'));
assertEquals("+82 2-3665-5", formatter.inputDigit('5'));
@ -441,21 +488,21 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("+82 2-3665-567", formatter.inputDigit('7'));
assertEquals("+82 2-3665-5678", formatter.inputDigit('8'));
// 02-114 : This is too short to format. Checking that there are no side-effects.
// 02-114
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("02", formatter.inputDigit('2'));
assertEquals("021", formatter.inputDigit('1'));
assertEquals("0211", formatter.inputDigit('1'));
assertEquals("02114", formatter.inputDigit('4'));
assertEquals("02-11", formatter.inputDigit('1'));
assertEquals("02-114", formatter.inputDigit('4'));
// 02-1300
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("02", formatter.inputDigit('2'));
assertEquals("021", formatter.inputDigit('1'));
assertEquals("0213", formatter.inputDigit('3'));
assertEquals("02130", formatter.inputDigit('0'));
assertEquals("02-13", formatter.inputDigit('3'));
assertEquals("02-130", formatter.inputDigit('0'));
assertEquals("02-1300", formatter.inputDigit('0'));
// 011-456-7890
@ -463,8 +510,8 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0114", formatter.inputDigit('4'));
assertEquals("01145", formatter.inputDigit('5'));
assertEquals("011-4", formatter.inputDigit('4'));
assertEquals("011-45", formatter.inputDigit('5'));
assertEquals("011-456", formatter.inputDigit('6'));
assertEquals("011-456-7", formatter.inputDigit('7'));
assertEquals("011-456-78", formatter.inputDigit('8'));
@ -476,8 +523,8 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0119", formatter.inputDigit('9'));
assertEquals("01198", formatter.inputDigit('8'));
assertEquals("011-9", formatter.inputDigit('9'));
assertEquals("011-98", formatter.inputDigit('8'));
assertEquals("011-987", formatter.inputDigit('7'));
assertEquals("011-9876", formatter.inputDigit('6'));
assertEquals("011-9876-7", formatter.inputDigit('7'));
@ -485,4 +532,52 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("011-9876-789", formatter.inputDigit('9'));
assertEquals("011-9876-7890", formatter.inputDigit('0'));
}
public void testAYTFMultipleLeadingDigitPatterns() {
// +81 50 2345 6789
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("JP");
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+8", formatter.inputDigit('8'));
assertEquals("+81 ", formatter.inputDigit('1'));
assertEquals("+81 5", formatter.inputDigit('5'));
assertEquals("+81 50", formatter.inputDigit('0'));
assertEquals("+81 50 2", formatter.inputDigit('2'));
assertEquals("+81 50 23", formatter.inputDigit('3'));
assertEquals("+81 50 234", formatter.inputDigit('4'));
assertEquals("+81 50 2345", formatter.inputDigit('5'));
assertEquals("+81 50 2345 6", formatter.inputDigit('6'));
assertEquals("+81 50 2345 67", formatter.inputDigit('7'));
assertEquals("+81 50 2345 678", formatter.inputDigit('8'));
assertEquals("+81 50 2345 6789", formatter.inputDigit('9'));
// +81 222 12 5678
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+8", formatter.inputDigit('8'));
assertEquals("+81 ", formatter.inputDigit('1'));
assertEquals("+81 2", formatter.inputDigit('2'));
assertEquals("+81 22", formatter.inputDigit('2'));
assertEquals("+81 22 2", formatter.inputDigit('2'));
assertEquals("+81 22 21", formatter.inputDigit('1'));
assertEquals("+81 2221 2", formatter.inputDigit('2'));
assertEquals("+81 222 12 5", formatter.inputDigit('5'));
assertEquals("+81 222 12 56", formatter.inputDigit('6'));
assertEquals("+81 222 12 567", formatter.inputDigit('7'));
assertEquals("+81 222 12 5678", formatter.inputDigit('8'));
// +81 3332 2 5678
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+8", formatter.inputDigit('8'));
assertEquals("+81 ", formatter.inputDigit('1'));
assertEquals("+81 3", formatter.inputDigit('3'));
assertEquals("+81 33", formatter.inputDigit('3'));
assertEquals("+81 33 3", formatter.inputDigit('3'));
assertEquals("+81 3332", formatter.inputDigit('2'));
assertEquals("+81 3332 2", formatter.inputDigit('2'));
assertEquals("+81 3332 2 5", formatter.inputDigit('5'));
assertEquals("+81 3332 2 56", formatter.inputDigit('6'));
assertEquals("+81 3332 2 567", formatter.inputDigit('7'));
assertEquals("+81 3332 2 5678", formatter.inputDigit('8'));
}
}

BIN
java/test/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting View File


+ 88
- 13
java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java View File

@ -76,7 +76,7 @@ public class PhoneNumberUtilTest extends TestCase {
assertEquals("US", metadata.getId());
assertEquals(1, metadata.getCountryCode());
assertEquals("011", metadata.getInternationalPrefix());
assertFalse(metadata.hasNationalPrefix());
assertTrue(metadata.hasNationalPrefix());
assertEquals(2, metadata.getNumberFormatCount());
assertEquals("(\\d{3})(\\d{3})(\\d{4})",
metadata.getNumberFormat(0).getPattern());
@ -98,11 +98,12 @@ public class PhoneNumberUtilTest extends TestCase {
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(5, metadata.getNumberFormatCount());
assertEquals(1, metadata.getNumberFormat(4).getLeadingDigitsPatternCount());
assertEquals("900", metadata.getNumberFormat(4).getLeadingDigitsPattern(0));
assertEquals("(\\d{3})(\\d{3,4})(\\d{4})",
metadata.getNumberFormat(4).getPattern());
assertEquals("$1 $2 $3", metadata.getNumberFormat(4).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());
@ -331,6 +332,13 @@ public class PhoneNumberUtilTest extends TestCase {
phoneUtil.format(deNumber,
PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL));
deNumber.clear();
deNumber.setCountryCode(49).setNationalNumber(80212345L);
assertEquals("08021 2345", phoneUtil.format(deNumber,
PhoneNumberUtil.PhoneNumberFormat.NATIONAL));
assertEquals("+49 8021 2345",
phoneUtil.format(deNumber,
PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL));
deNumber.clear();
deNumber.setCountryCode(49).setNationalNumber(1234L);
// 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
@ -518,6 +526,21 @@ public class PhoneNumberUtilTest extends TestCase {
PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL,
newNumberFormats));
// $NP is set to '1' for the US. Here we check that for other NANPA countries the US rules are
// followed.
newNumFormat.setNationalPrefixFormattingRule("$NP ($FG)");
newNumFormat.setFormat("$1 $2-$3");
PhoneNumber bsNumber = new PhoneNumber();
bsNumber.setCountryCode(1).setNationalNumber(4168819999L);
assertEquals("1 (416) 881-9999",
phoneUtil.formatByPattern(bsNumber,
PhoneNumberUtil.PhoneNumberFormat.NATIONAL,
newNumberFormats));
assertEquals("+1 416 881-9999",
phoneUtil.formatByPattern(bsNumber,
PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL,
newNumberFormats));
PhoneNumber itNumber = new PhoneNumber();
itNumber.setCountryCode(39).setNationalNumber(236618300L).setItalianLeadingZero(true);
@ -952,6 +975,58 @@ public class PhoneNumberUtilTest extends TestCase {
assertFalse(phoneUtil.isPossibleNumber("+44 300", "GB"));
}
public void testTruncateTooLongNumber() {
// US number 650-253-0000, but entered with one additional digit at the end.
PhoneNumber tooLongNumber = new PhoneNumber();
tooLongNumber.setCountryCode(1).setNationalNumber(65025300001L);
PhoneNumber validNumber = new PhoneNumber();
validNumber.setCountryCode(1).setNationalNumber(6502530000L);
assertTrue(phoneUtil.truncateTooLongNumber(tooLongNumber));
assertEquals(validNumber, tooLongNumber);
// GB number 080 1234 5678, but entered with 4 extra digits at the end.
tooLongNumber.clear();
tooLongNumber.setCountryCode(44).setNationalNumber(80123456780123L);
validNumber.clear();
validNumber.setCountryCode(44).setNationalNumber(8012345678L);
assertTrue(phoneUtil.truncateTooLongNumber(tooLongNumber));
assertEquals(validNumber, tooLongNumber);
// IT number 022 3456 7890, but entered with 3 extra digits at the end.
tooLongNumber.clear();
tooLongNumber.setCountryCode(39).setNationalNumber(2234567890123L).setItalianLeadingZero(true);
validNumber.clear();
validNumber.setCountryCode(39).setNationalNumber(2234567890L).setItalianLeadingZero(true);
assertTrue(phoneUtil.truncateTooLongNumber(tooLongNumber));
assertEquals(validNumber, tooLongNumber);
// Tests what happens when a valid number is passed in.
PhoneNumber validNumberCopy = new PhoneNumber();
validNumberCopy.mergeFrom(validNumber);
assertTrue(phoneUtil.truncateTooLongNumber(validNumber));
// Tests the number is not modified.
assertEquals(validNumberCopy, validNumber);
// Tests what happens when a number with invalid prefix is passed in.
PhoneNumber numberWithInvalidPrefix = new PhoneNumber();
// The test metadata says US numbers cannot have prefix 240.
numberWithInvalidPrefix.setCountryCode(1).setNationalNumber(2401234567L);
PhoneNumber invalidNumberCopy = new PhoneNumber();
invalidNumberCopy.mergeFrom(numberWithInvalidPrefix);
assertFalse(phoneUtil.truncateTooLongNumber(numberWithInvalidPrefix));
// Tests the number is not modified.
assertEquals(invalidNumberCopy, numberWithInvalidPrefix);
// Tests what happens when a too short number is passed in.
PhoneNumber tooShortNumber = new PhoneNumber();
tooShortNumber.setCountryCode(1).setNationalNumber(1234L);
PhoneNumber tooShortNumberCopy = new PhoneNumber();
tooShortNumberCopy.mergeFrom(tooShortNumber);
assertFalse(phoneUtil.truncateTooLongNumber(tooShortNumber));
// Tests the number is not modified.
assertEquals(tooShortNumberCopy, tooShortNumber);
}
public void testIsViablePhoneNumber() {
// Only one or two digits before strange non-possible punctuation.
assertFalse(PhoneNumberUtil.isViablePhoneNumber("12. March"));
@ -1188,7 +1263,7 @@ public class PhoneNumberUtilTest extends TestCase {
}
number.clear();
try {
String phoneNumber = "(1 610) 619 43 446";
String phoneNumber = "(1 610) 619 43";
StringBuffer numberToFill = new StringBuffer();
assertEquals("Should not have extracted a country code - invalid number both before and " +
"after extraction of uncertain country code.",
@ -1487,15 +1562,15 @@ public class PhoneNumberUtilTest extends TestCase {
assertEquals(nzNumber, phoneUtil.parse("03 3316005 #3456", "NZ"));
// Test the following do not extract extensions:
PhoneNumber nonExtnNumber = new PhoneNumber();
nonExtnNumber.setCountryCode(1).setNationalNumber(180074935247L);
nonExtnNumber.setCountryCode(1).setNationalNumber(80074935247L);
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("0~0 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 = new PhoneNumber();
extnNumber.setCountryCode(1).setNationalNumber(180074935247L).setExtension("1234");
assertEquals(extnNumber, phoneUtil.parse("0~01 1800 7493 5247 ~1234", "PL"));
extnNumber.setCountryCode(1).setNationalNumber(80074935247L).setExtension("1234");
assertEquals(extnNumber, phoneUtil.parse("0~0 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 = new PhoneNumber();
@ -1539,10 +1614,10 @@ public class PhoneNumberUtilTest extends TestCase {
public void testParseAndKeepRaw() throws Exception {
PhoneNumber alphaNumericNumber = new PhoneNumber();
alphaNumericNumber.
setCountryCode(1).setNationalNumber(180074935247L).setRawInput("1800 six-flags").
setCountryCode(1).setNationalNumber(80074935247L).setRawInput("800 six-flags").
setCountryCodeSource(CountryCodeSource.FROM_DEFAULT_COUNTRY);
assertEquals(alphaNumericNumber,
phoneUtil.parseAndKeepRawInput("1800 six-flags", "US"));
phoneUtil.parseAndKeepRawInput("800 six-flags", "US"));
alphaNumericNumber.
setCountryCode(1).setNationalNumber(8007493524L).setRawInput("1800 six-flag").


Loading…
Cancel
Save