diff --git a/java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml b/java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml index b83058eba..0e72c87b8 100644 --- a/java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml +++ b/java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml @@ -237,8 +237,60 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 + $1 $2 + $1 $2 + $1 $2 + + + [2-57]\d{7}|6\d{8}|8\d{5,7}|9\d{5} + \d{5,9} + + + (?:2(?:[168][1-9]|[247]\d|9[1-7])|3(?:1[1-3]|[2-6]\d|[79][1-8]|8[1-9])|4\d{2}|5(?:1[1-4]|[2-578]\d|6[1-5]|9[1-7])|8(?:[19][1-5]|[2-6]\d|[78][1-7]))\d{5} + \d{5,8} + 22345678 + + + 6[6-9]\d{7} + \d{9} + 661234567 + + + 800\d{4} + \d{7} + 8001234 + + + + 900\d{3} + \d{6} + 900123 + + + 808\d{3} + \d{6} + 808123 + + + 700\d{5} + \d{8} + 70012345 + @@ -251,8 +303,24 @@ - + + + + $1 $2 $3 + + + [29]\d{8} + \d{9} + + + 2\d(?:[26-9]\d|\d[26-9])\d{5} + 222123456 + + + + 9[1-3]\d{7} + 923123456 + @@ -514,11 +582,11 @@ \d{8,9} - (?:1(?:(?:2[3-5]|36|8\d|9)\d|02|1[0-589]|3[358]|4[013-79]|5[0-479]|6[0236-9]|7[0-24-8])|2(?:16|2\d|3[0-24]|4[1468]|55|6[56]|79))\d{5} + (?:1(?:(?:[28]\d|36|9)\d|02|1[0-589]|3[358]|4[013-79]|5[0-479]|6[0236-9]|7[0-24-8])|2(?:16|2\d|3[0-24]|4[1468]|55|6[56]|79))\d{5} 123123456 - (?:40|5[015]|7[07])\d{7}|60540\d{4} + (?:4[04]|5[015]|60|7[07])\d{7} \d{9} 401234567 @@ -617,7 +685,26 @@ + + + + $1 $2 $3 $4 + + + [2457]\d{7} + \d{8} + + + + (?:20(?:49|5[23]|9[016-9])|40(?:4[569]|55|7[0179])|50[34]\d)\d{4} + 20491234 + + + 7(?:[0168]\d|2[0-4]|4[01]|5[01346-9])\d{5} + 70123456 + @@ -665,11 +752,70 @@ + + + $1 $2 $3 $4 + + + [27]\d{7} + \d{8} + + + 22(?:2[0-7]|[3-5]0)\d{4} + 22201234 + + + + + (?:2955|7(?:9(?:5[6-9]|[19]\d)|(?:6[269]|77|8\d)\d))\d{4} + 79561234 + + + + $1 $2 $3 $4 + + + + [289]\d{7}|7\d{3} + \d{4,8} + + + + 2(?:02|1[037]|2[45]|3[68])\d{5} + \d{8} + 20211234 + + + + 9(?:0[069]|[35][0-2457-9]|7[014-9])\d{5} + \d{8} + 90011234 + + + 7[3-5]\d{2} + \d{4} + 7312 + + + + 857[58]\d{4} + \d{8} + 85751234 + @@ -685,11 +831,11 @@ \d{7,10} - 441(?:2(?:02|23|[3479]\d)|[46]\d{2}|5(?:40|89)|824)\d{4} + 441(?:2(?:02|23|61|[3479]\d)|[46]\d{2}|5(?:4\d|60|89)|824)\d{4} 4412345678 - 441(?:[37]\d{2}|5(?:[0-3]\d|9[09]))\d{4} + 441(?:[37]\d|5[0-39])\d{5} \d{10} 4413701234 @@ -906,11 +1052,50 @@ + + + $1 $2 $3 $4 + + + [278]\d{7} + \d{8} + + + 2[12]\d{6} + 21612345 + + + 7[0257]\d{6} + 70012345 + + + 8776\d{4} + 87761234 + + + + $1 $2 + + + [24-68]\d{6} + \d{7} + + + + (?:2[1-589]|8\d)\d{5} + 2123456 + + + [4-6]\d{6} + 5012345 + @@ -1158,11 +1343,58 @@ + + + $1 $2 $3 + + + [29]\d{6} + \d{7} + + + 2(?:2[1-7]|3[0-8]|4[12]|5[1256]|6\d|7[1-3]|8[1-5])\d{4} + 2211234 + + + 9[189]\d{5} + 9911234 + + + + + $1 $2 + + + [27-9]\d{7} + \d{8} + + + 2[2-6]\d{6} + 22345678 + + + + 7777\d{4}|9(?:[69]\d|7[67])\d{5} + 96123456 + + + 8000\d{4} + 80001234 + + + 9009\d{4} + 90091234 + + + 700\d{5} + 70012345 + @@ -1273,7 +1505,30 @@ + + + + $1 $2 $3 + + + [2-8]\d{5} + \d{6} + + + + (?:25|3[0-6]|42)\d{4} + 251234 + + + + (?:[5-7]\d|8[0-7])\d{4} + 601234 + @@ -1454,8 +1709,27 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + $1 $2 $3 + + + [178]\d{6} + \d{6,7} + + + 1(?:1[12568]|20|40|55|6[146])\d{4}|8\d{6} + 8370362 + + + + 17[1-3]\d{4}|7\d{6} + \d{7} + 7123456 + @@ -1495,8 +1769,27 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + $1 $2 $3 + + + [1-59]\d{8} + \d{7,9} + + + (?:11(?:1(?:1[124]|2[2-57]|3[1-5]|5[5-8]|8[6-8])|2(?:13|3[6-8]|5[89]|7[05-9]|8[2-6])|3(?:2[01]|3[0-289]|4[1289]|7[1-4]|87)|4(?:1[69]|3[2-49]|4[0-23]|6[5-8])|5(?:1[57]|44|5[0-4])|6(?:18|2[69]|4[5-7]|5[1-5]|6[0-59]|8[015-8]))|2(?:2(?:11[1-9]|22[0-7]|33\d|44[1467]|66[1-68])|5(?:11[124-6]|33[2-8]|44[1467]|55[14]|66[1-3679]|77[124-79]|880))|3(?:3(?:11[0-46-8]|22[0-6]|33[0134689]|44[04]|55[0-6]|66[01467])|4(?:44[0-8]|55[0-69]|66[0-3]|77[1-5]))|4(?:6(?:22[0-24-7]|33[1-5]|44[13-69]|55[14-689]|660|88[1-4])|7(?:11[1-9]|22[1-9]|33[13-7]|44[13-6]|55[1-689]))|5(?:7(?:227|55[05]|(?:66|77)[14-8])|8(?:11[149]|22[013-79]|33[0-68]|44[013-8]|550|66[1-5]|77\d)))\d{4} + 111112345 + + + + 91(?:1(?:[146]\d|2[0-5]|3[4-6]|50|7[2-6]|8[46-9])|31\d|4(?:3[0-2489]|7[0-3])|5(?:3[23]|7[3-5])|6(?:58|8[23])|7(?:5[57]|8[01])|8(?:3[45]|7[67]))\d{4} + \d{9} + 911123456 + @@ -1593,7 +1886,35 @@ + + + $1 $2 $3 + $1 $2 $3 $4 + + + [4-9]\d{5}|0\d{7} + \d{6,8} + + + (?:4(?:[04-8]\d|2[04])|(?:5[04-689]|6[024-9]|7\d|8[236]|9[02368])\d)\d{3} + \d{6} + 441234 + + + + + 0(?:5(?:0[89]|3[0-4]|8[0-26]|9[238])|6(?:0[3-7]|1[01]|2[0-7]|6[0-589]|71|83|9[57])|7(?:1[2-5]|2[89]|3[35-9]|4[01]|5[0-347-9]|[67]\d|8[457-9]|9[0146]))\d{4} + \d{8} + 06031234 + @@ -1750,6 +2071,7 @@ + @@ -1758,7 +2080,7 @@ $1 $2 $1 $2 $3 - $1 $2 $3 + $1 $2 $3 $1 $2 $3 @@ -1774,7 +2096,7 @@ 251234 - 2(?:755\d{4}|(?:4|08)\d{6}|[368]\d{7})|54\d{7} + 2(?:755\d{4}|(?:4\d?|08)\d{6}|[368]\d{7})|54\d{7} \d{8,9} 27551234 @@ -1790,7 +2112,23 @@ + + + $1 $2 + + + [3-9]\d{6} + \d{7} + + + (4(?:[23]\d{2}|4(?:1[024679]|[6-9]\d))|5(?:54[0-7]|6(?:[67]\d)|7(?:1[04]|2[035]|3[58]|48))|8\d{3})\d{3} + 5661234 + + + [3679]\d{6} + 3012345 + @@ -1803,7 +2141,25 @@ + + + + $1 $2 $3 + + + [0256]\d{5} + \d{6} + + + 0[46-9]\d{4} + 041234 + + + [256]\d{5} + 212345 + @@ -1884,7 +2240,23 @@ + + + $1 $2 + + + [3567]\d{6} + \d{7} + + + 3(?:2[0125]|3[1245]|4[12]|5[1-4]|70|9[1-467])\d{4} + 3201234 + + + [5-7]\d{6} + 5012345 + @@ -2086,12 +2458,13 @@ + - $1 $2 $3 + $1 $2 $3 $1 $2 $1 $2 @@ -2126,7 +2499,7 @@ people dial, allowing them to easily dial Northern Ireland. We support these numbers here, although technically they are numbers for the UK. --> - (?:2[24-9]|4(?:0[24]|7)|5(?:0[45]|8)|6[237-9]|9[3-9])\d{5}|(?:45|[569]1|818)\d{6}|(?:1|4[12469]|5[3679]|6[56]|7[14]|9[04])\d{7}|21\d{6,7}|(?:23|4[34]|52|64)\d{5,7}|48\d{8} + 1\d{7,8}|(?:2[24-9]|4(?:0[24]|7)|5(?:0[45]|8)|6[237-9]|9[3-9])\d{5}|(?:45|[569]1|818)\d{6}|(?:4[12469]|5[3679]|6[56]|7[14]|9[04])\d{7}|21\d{6,7}|(?:23|4[34]|52|64)\d{5,7}|48\d{8} \d{5,10} 2212345 @@ -2141,7 +2514,7 @@ 1800123456 - 15(?:1[2-9]|[2-9]0|59)\d{6} + 15(?:1[2-9]|[2-8]0|59|9[089])\d{6} \d{10} 1520123456 @@ -2690,7 +3063,25 @@ + + + $1 $2 $3 + + + [37]\d{6} + \d{7} + + + + 7(?:6[0-37-9]|7[0-57-9])\d{4} + 7712345 + + + 3[23]\d{5} + 3212345 + @@ -2987,8 +3378,32 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + + $1 $2 $3 + $1 $2 $3 + + + (?:[27]\d|[4-6])\d{6} + \d{7,8} + + + 2\d{7} + \d{8} + 21234567 + + + + (?:4[67]|5\d|7\d{2}|6[4-8])\d{5} + 4612345 + @@ -3079,8 +3494,36 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + $1 $2 $3 + $1 $2 $3 + $1 $2 + + + [256-9]\d{7} + \d{8} + + + (?:2(?:1[0569]|2\d|3[015-7]|4[1-46-9]|5[0-24689]|6[2-589]|7[1-37]|9[1347-9])|5(?:33|5[257]))\d{5} + \d{5,8} + 22212345 + + + (?:6(?:50|7[12]|8[0-7]|9\d)|7(?:80|9\d))\d{5} + 65012345 + + + 800\d{5} + 80012345 + + + 900\d{5} + 90012345 + @@ -3089,8 +3532,31 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + $1 $2 $3 $4 + + + [23]\d{8} + \d{7,9} + + + + 2(?:0(?:(?:2\d|4[47]|5[3467]|6[279]|8[268]|9[245])\d|7(?:2[29]|[35]\d))|210\d)\d{4} + 202123456 + + + + 3[02-4]\d{7} + \d{9} + 301234567 + @@ -3099,13 +3565,73 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 $4 + + + [2-578]\d{7} + \d{8} + + + (?:2\d|3[1-4]|4[2-8])\d{6} + \d{6,8} + 22212345 + + + 7\d{7} + 72345678 + + + 800\d{5} + 80012345 + + + 5[02-9]\d{6} + 50012345 + + + 8(?:0[1-9]|[1-9]\d)\d{5} + 80123456 + + + + + $1 $2 $3 $4 + + + [246-8]\d{7} + \d{8} + + + + (?:2(?:0(?:2[0-589]|7[027-9])|1(?:2[5-7]|[3-9]\d))|442\d)\d{4} + 20212345 + + + (?:6(?:[569]\d)|7(?:[359][0-4]|4[014-7]|6\d|8[1-9]))\d{5} + 65012345 + + + + 800\d{5} + 80012345 + @@ -3230,8 +3756,24 @@ - + + + + + $1 $2 $3 + + + [2-7]\d{6} + \d{7} + + + 5(?:1[035]|2[0-69]|3[0348]|4[468]|5[02-467]|6[39]|7[4-69])\d{4} + 5131234 + + + (?:[23][0-4]|4[3-5]|6\d|7[0-7])\d{5} + 3123456 + @@ -3286,12 +3828,15 @@ (?:2(?:[034789]\d|1[0-8]|2[0-79])|4(?:[013-8]\d|2[4-7])|[56]\d{2}|8(?:14|3[129]))\d{4} + 2012345 (?:25\d|4(?:2[12389]|9\d)|7\d{2}|87[15-7]|9[13-8]\d)\d{4} + 2512345 80[012]\d{4} + 8001234 30\d{5} + 3012345 @@ -3308,7 +3854,37 @@ - + + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + + (?:[13-5]|[27]\d{2}|[89](?:\d{2})?)\d{6} + \d{7,9} + + + (?:1[2-9]|21\d{2})\d{5} + 1234567 + + + (?:[3-5]|77|8(?:8\d)?|9(?:9\d)?)\d{6} + 991234567 + @@ -3433,13 +4009,81 @@ - + + + + $1 $2 $3 + $1 $2 $3 + $1 $2 $3 + + + [28]\d{7,8} + \d{8,9} + + + 2(?:[1346]\d|5[0-2]|[78][12]|93)\d{5} + \d{8} + 21123456 + + + 8[24]\d{7} + \d{9} + 821234567 + + + + 800\d{6} + \d{9} + 800123456 + + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + $1 $2 $3 + + $1 $2 + $1 $2 $3 + + $1 $2 + $1 $2 + $1 $2 + $1 $2 $3 + + + [68]\d{5,9} + \d{4,10} + + + 6(?:1(?:[136]|2\d?)\d|2(?:[25]\d?|[134678])\d|3(?:2\d{0,3}|4\d{1,2}|[135-8]\d?)|4(?:[13-8]\d|2\d{1,2})|(?:5(?:[16-7]\d|[3-58]\d?|2\d{1,2}))|6\d{0,4}|7\d{0,3})\d{4} + 612012345 + + + 8(?:1(?:1[0-2]|[23]\d|50)|5\d{2})\d{5} + \d{9} + 811012345 + + + 88\d{6} + \d{8} + 88123456 + @@ -3756,23 +4400,28 @@ - $1 $2 - $1 $2 $3 + $1 $2 + $1 $2 $3 [1-9]\d{6,7} \d{7,8} - (?:3\d|47|[56]4|73|85|9[78])\d{5} + + (?:3\d{2}|4[257]\d|5[34]\d|6[24]9|85[02-46-9]|9[78]\d)\d{4} \d{7} 3123456 - (?:6[357-9]|7[126]\d)\d{5} + + (?:68|7(?:[126]\d|3[34689]))\d{5} \d{7,8} - 6345678 + 6812345 180\d{4} @@ -4187,15 +4836,19 @@ 25\d{7} + 250123456 7[258]\d{7} + 720123456 800\d{6} + 800123456 900\d{6} + 900123456 @@ -4474,8 +5127,23 @@ - + + + + $1 $2 + + + [29]\d{6} + \d{7} + + + 22\d{5} + 2221234 + + + 9[89]\d{5} + 9812345 + @@ -4511,7 +5179,32 @@ + + + + [2-7]\d{6,7} + \d{7,8} + + + $1 $2 + $1 $2 + + + + 2?(?:2(?:0[07]|[13]7|2[57])|3(?:0[34]|[1278]3|3[23]|[46][34])|(?:40[4-69]|16|2[12]|3[57]|[4578]2|67)|5(?:0[5-7]|1[6-9]|[23][78]|48|5[01]))\d{4} + 2171234 + + + + (?:6|7[67])\d{6} + 6012345 + @@ -4549,7 +5242,29 @@ - + + + + + $1 $2 $3 + + + [2369]\d{6} + \d{7} + + + 2(?:5[0-4]|6[89])\d{4} + 2511234 + + + + (?:3[0-7]|6[27]|9\d)\d{5} + 6201234 + @@ -4558,7 +5273,24 @@ + + + $1 $2 $3 + + + [02-9]\d{6} + \d{7} + + + (?:2[2-7]|3[23]|44|55|66|77)\d{5} + 2212345 + + + + (?:0[1-9]|7[56]|8[1-7]|9\d)\d{5} + 0112345 + @@ -5069,6 +5801,10 @@ $1 $2 $3 $1 $2 $3 + $1 $2 + $1 $2 $1 $2 $3 $1 $2 - [1-9]\d{6,9} + 8\d{5,8}|[1-79]\d{7,9} \d{7,10} - [2-8]\d{6,9} + (?:2(?:[025-79]|1[0189]|[348][01])|3(?:[0136-9]|[25][01])|[48]\d|5(?:[01][01]|[2-9])|6(?:[0-46-8]|5[01])|7(?:[02-79]|[18][01]))\d{7}|(?:80|69[3-5])\d{5} \d{7,10} - 2123456 + 2101234567 - (?:9[0-8]|1(?:2[1-369]|6[46-9]|99))\d{7} + (?:9[0-8]|1(?:2\d|6[46-9]|99))\d{7} \d{9,10} 912345678 @@ -5182,8 +5918,32 @@ + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + + [268]\d{8} + \d{9} + + + 2696[0-4]\d{4} + 269601234 + + + 639\d{6} + 639123456 + + + + 80\d{7} + 801234567 + @@ -5225,9 +5985,25 @@ - + + + nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> + + $1 $2 + + + [29]\d{8} + \d{9} + + + 21[1-8]\d{6} + 211234567 + + + 9(?:55|66|7[7-9])\d{6} + 955123456 + diff --git a/java/resources/com/google/i18n/phonenumbers/src/generated_files/PhoneNumberMetadataProto b/java/resources/com/google/i18n/phonenumbers/src/generated_files/PhoneNumberMetadataProto index 1c6227f1e..2cad06486 100644 Binary files a/java/resources/com/google/i18n/phonenumbers/src/generated_files/PhoneNumberMetadataProto and b/java/resources/com/google/i18n/phonenumbers/src/generated_files/PhoneNumberMetadataProto differ diff --git a/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java index 4e0935726..12bb94681 100644 --- a/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java +++ b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java @@ -16,8 +16,8 @@ package com.google.i18n.phonenumbers; -import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; +import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; import java.util.ArrayList; import java.util.List; @@ -42,10 +42,11 @@ public class AsYouTypeFormatter { private String formattingTemplate; private StringBuffer accruedInput; private StringBuffer accruedInputWithoutFormatting; - private boolean ableToFormat; + private boolean ableToFormat = true; + private boolean isInternationalFormatting = false; private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); private String defaultCountry; - private PhoneMetadata defaultMetaData; + private Phonemetadata.PhoneMetadata defaultMetaData; private PhoneMetadata currentMetaData; // The digits that have not been entered yet will be represented by a \u2008, the punctuation @@ -69,7 +70,6 @@ public class AsYouTypeFormatter { accruedInputWithoutFormatting = new StringBuffer(); currentOutput = new StringBuffer(); prefixBeforeNationalNumber = new StringBuffer(); - ableToFormat = true; nationalNumber = new StringBuffer(); defaultCountry = regionCode; initializeCountrySpecificInfo(defaultCountry); @@ -101,7 +101,10 @@ public class AsYouTypeFormatter { private List getAvailableFormats(String leadingFourDigits) { List matchedList = new ArrayList(); - List formatList = currentMetaData.getNumberFormatList(); + List formatList = + (isInternationalFormatting && currentMetaData.getIntlNumberFormatCount() > 0) + ? currentMetaData.getIntlNumberFormatList() + : currentMetaData.getNumberFormatList(); for (NumberFormat format : formatList) { if (format.hasLeadingDigits()) { Pattern leadingDigitsPattern = Pattern.compile(format.getLeadingDigits()); @@ -164,6 +167,7 @@ public class AsYouTypeFormatter { prefixBeforeNationalNumber = new StringBuffer(); nationalNumber = new StringBuffer(); ableToFormat = true; + isInternationalFormatting = false; if (!currentMetaData.equals(defaultMetaData)) { initializeCountrySpecificInfo(defaultCountry); } @@ -269,6 +273,7 @@ public class AsYouTypeFormatter { nationalNumber = new StringBuffer(); Matcher iddMatcher = internationalPrefix.matcher(accruedInputWithoutFormatting); if (iddMatcher.lookingAt()) { + isInternationalFormatting = true; int startOfCountryCode = iddMatcher.end(); StringBuffer numberIncludeCountryCode = new StringBuffer(accruedInputWithoutFormatting.substring(startOfCountryCode)); diff --git a/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java b/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java index 96e820813..2b895f15c 100644 --- a/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java +++ b/java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java @@ -18,6 +18,7 @@ package com.google.i18n.phonenumbers; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; @@ -49,9 +50,9 @@ import java.util.regex.Pattern; * @author Lara Rennie */ public class PhoneNumberUtil { - // The maximum and minimum length of the national significant number. - private static final int MAX_LENGTH_FOR_NSN = 15; + // The minimum and maximum length of the national significant number. private static final int MIN_LENGTH_FOR_NSN = 3; + private static final int MAX_LENGTH_FOR_NSN = 15; private static final String META_DATA_FILE = "/com/google/i18n/phonenumbers/src/generated_files/PhoneNumberMetadataProto"; private static final Logger LOGGER = Logger.getLogger(PhoneNumberUtil.class.getName()); @@ -154,6 +155,17 @@ public class PhoneNumberUtil { .putAll(DIGIT_MAPPINGS) .build(); + // A list of all country codes where national significant numbers (excluding any national prefix) + // exist that start with a leading zero. + private static final Set LEADING_ZERO_COUNTRIES = + new ImmutableSet.Builder() + .add(39) // Italy + .add(225) // C™te d'Ivoire + .add(228) // Togo + .add(240) // Equatorial Guinea + .add(241) // Gabon + .build(); + // Pattern that makes it easy to distinguish whether a country has a unique international dialing // prefix or not. If a country has a unique international prefix (e.g. 011 in USA), it will be // represented as a string that contains a sequence of ASCII digits. If there are multiple @@ -161,15 +173,15 @@ public class PhoneNumberUtil { // always contains character(s) other than ASCII digits. // Note this regex also includes tilde, which signals waiting for the tone. private static final Pattern UNIQUE_INTERNATIONAL_PREFIX = - Pattern.compile("[\\d]+([~\u2053\u223C\uFF5E][\\d]+)?"); + Pattern.compile("[\\d]+(?:[~\u2053\u223C\uFF5E][\\d]+)?"); // Regular expression of acceptable punctuation found in phone numbers. This excludes punctuation // found as a leading character only. // This consists of dash characters, white space characters, full stops, slashes, // square brackets, parentheses and tildes. It also includes the letter 'x' as that is found as a // placeholder for carrier information in some phone numbers. - private static final String VALID_PUNCTUATION = "[-x\u2010-\u2015\u2212\uFF0D-\uFF0F " + - "\u00A0\u200B\u2060\u3000()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E]"; + private static final String VALID_PUNCTUATION = "-x\u2010-\u2015\u2212\uFF0D-\uFF0F " + + "\u00A0\u200B\u2060\u3000()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E"; // Digits accepted in phone numbers private static final String VALID_DIGITS = @@ -199,16 +211,25 @@ public class PhoneNumberUtil { private static final String SECOND_NUMBER_START = "[\\\\/] *x"; private static final Pattern SECOND_NUMBER_START_PATTERN = Pattern.compile(SECOND_NUMBER_START); + // Regular expression of trailing characters that we want to remove. We remove all characters that + // are not alpha or numerical characters. The hash character is retained here, as it may signify + // the previous block was an extension. + private static final String UNWANTED_END_CHARS = "[[\\P{N}&&\\P{L}]&&[^#]]+$"; + private static final Pattern UNWANTED_END_CHAR_PATTERN = Pattern.compile(UNWANTED_END_CHARS); + + // We use this pattern to check if the phone number has at least three letters in it - if so, then + // we treat it as a number where some phone-number digits are represented by letters. + private static final Pattern VALID_ALPHA_PHONE_PATTERN = Pattern.compile("(?:.*?[A-Za-z]){3}.*"); + // Regular expression of viable phone numbers. This is location independent. Checks we have at // least three leading digits, and only valid punctuation, alpha characters and - // digits in the phone number. Does not include extension data - this is read in from the - // PhoneNumberMetaData.xml file at initialisation time. + // digits in the phone number. Does not include extension data. // The symbol 'x' is allowed here as valid punctuation since it is often used as a placeholder for // carrier codes, for example in Brazilian phone numbers. // Corresponds to the following: // plus_sign?([punctuation]*[digits]){3,}([punctuation]|[digits]|[alpha])* private static final String VALID_PHONE_NUMBER = - "[" + PLUS_CHARS + "]?(?:" + VALID_PUNCTUATION + "*[" + VALID_DIGITS + "]){3,}[" + + "[" + PLUS_CHARS + "]?(?:[" + VALID_PUNCTUATION + "]*[" + VALID_DIGITS + "]){3,}[" + VALID_ALPHA + VALID_PUNCTUATION + VALID_DIGITS + "]*"; // Default extension prefix to use when formatting. This will be put in front of any extension @@ -225,9 +246,9 @@ public class PhoneNumberUtil { // the extension is written with a hash at the end, such as "- 503#". // Note that the only capturing groups should be around the digits that you want to capture as // part of the extension, or else parsing will fail! - private static final String KNOWN_EXTN_PATTERNS = "[ \\u00A0\\t,]*(?:ext(?:ensio)?n?|" + - "\\uFF45\\uFF58\\uFF54\\uFF4E?|[,x\\uFF58#\\uFF03~\\uFF5E]|int|\\uFF49\\uFF4E\\uFF54)" + - "[:\\.\\uFF0E]?[ \\u00A0\\t,]*([" + VALID_DIGITS + "]{1,7})|[- ]+([" + VALID_DIGITS + + private static final String KNOWN_EXTN_PATTERNS = "[ \u00A0\\t,]*(?:ext(?:ensio)?n?|" + + "\uFF45\uFF58\uFF54\uFF4E?|[,x\uFF58#\uFF03~\uFF5E]|int|\uFF49\uFF4E\uFF54)" + + "[:\\.\uFF0E]?[ \u00A0\\t,]*([" + VALID_DIGITS + "]{1,7})|[- ]+([" + VALID_DIGITS + "]{1,5})#"; // Regexp of all known extension prefixes used by different countries followed by 1 or more valid @@ -239,7 +260,7 @@ public class PhoneNumberUtil { // We append optionally the extension pattern to the end here, as a valid phone number may // have an extension prefix appended, followed by 1 or more digits. private static final Pattern VALID_PHONE_NUMBER_PATTERN = - Pattern.compile(VALID_PHONE_NUMBER + "(?:" + EXTN_PATTERN + ")?", + Pattern.compile(VALID_PHONE_NUMBER + "(?:" + KNOWN_EXTN_PATTERNS + ")?", Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); private static PhoneNumberUtil instance = null; @@ -367,8 +388,12 @@ public class PhoneNumberUtil { */ @VisibleForTesting static String extractPossibleNumber(String number) { - // Remove leading and trailing whitespace. - number = number.trim(); + // Remove trailing non-alpha non-numerical characters. + Matcher trailingCharsMatcher = UNWANTED_END_CHAR_PATTERN.matcher(number); + if (trailingCharsMatcher.find()) { + number = number.substring(0, trailingCharsMatcher.start()); + LOGGER.log(Level.FINER, "Stripped trailing characters: " + number); + } Matcher m = VALID_START_CHAR_PATTERN.matcher(number); if (m.find()) { number = number.substring(m.start()); @@ -417,7 +442,8 @@ public class PhoneNumberUtil { * @return the normalized string version of the phone number */ static String normalize(String number) { - if (number.matches("(?:.*?[A-Za-z]){3}.*")) { + Matcher m = VALID_ALPHA_PHONE_PATTERN.matcher(number); + if (m.matches()) { return normalizeHelper(number, ALL_NORMALIZATION_MAPPINGS, true); } else { return normalizeHelper(number, DIGIT_MAPPINGS, true); @@ -438,7 +464,7 @@ public class PhoneNumberUtil { /** * Normalizes a string of characters representing a phone number. This converts wide-ascii and - * arabic-indic numerals to European numerals, and strips punctuation and alpha characters, + * arabic-indic numerals to European numerals, and strips punctuation and alpha characters. * * @param number a string of characters representing a phone number * @return the normalized string version of the phone number @@ -503,15 +529,6 @@ public class PhoneNumberUtil { instance = null; } - @VisibleForTesting - PhoneMetadata getPhoneMetadata(String regionCode) { - PhoneMetadata metadata = countryToMetadataMap.get(regionCode); - // makes a defensive copy - PhoneMetadata.Builder metadataCopy = metadata.newBuilder(); - metadataCopy.mergeFrom(metadata); - return metadataCopy.build(); - } - /** * Convenience method to enable tests to get a list of what countries the library has metadata * for. @@ -541,8 +558,8 @@ public class PhoneNumberUtil { } /** - * Helper function to check region code is not unknown or null. The number supplied is used only - * for the resultant log message. + * Helper function to check region code is not unknown or null. The countryCode and number + * supplied is used only for the resultant log message. */ private boolean isValidRegionCode(String regionCode, int countryCode, String number) { if (regionCode == null || regionCode.equals("ZZ")) { @@ -605,8 +622,9 @@ public class PhoneNumberUtil { PhoneNumberFormat numberFormat, List userDefinedFormats) { int countryCode = number.getCountryCode(); - // For performance reasons, we use US to represent NANPA countries here. This means that - // the extension symbol will be chosen from the US metadata. + // Note getRegionCodeForCountryCode() is used because formatting information for countries which + // share a country code is contained by only one country for performance reasons. For example, + // for NANPA countries it will be contained in the metadata for US. String regionCode = getRegionCodeForCountryCode(countryCode); String nationalSignificantNumber = getUnformattedNationalNumber(number); if (!isValidRegionCode(regionCode, countryCode, nationalSignificantNumber)) { @@ -662,7 +680,7 @@ public class PhoneNumberUtil { return format(number, PhoneNumberFormat.INTERNATIONAL); } int countryCode = number.getCountryCode(); - if (countryCode == NANPA_COUNTRY_CODE && nanpaCountries.contains(countryCallingFrom)) { + if (countryCode == NANPA_COUNTRY_CODE && isNANPACountry(countryCallingFrom)) { // For NANPA countries, return the national format for these countries but prefix it with the // country code. return countryCode + " " + format(number, PhoneNumberFormat.NATIONAL); @@ -736,9 +754,9 @@ public class PhoneNumberUtil { // number. There have been plans to migrate landline numbers to start with the digit two since // December 2000, but it has not yet happened. // See http://en.wikipedia.org/wiki/%2B39 for more details. - // Cote d'Ivoire also uses this for some of their mobile numbers. + // Other countries such as Cote d'Ivoire and Gabon use this for their mobile numbers. StringBuffer nationalNumber = new StringBuffer( - ((number.getCountryCode() == 39 || number.getCountryCode() == 225) && + (isLeadingZeroCountry(number.getCountryCode()) && number.hasItalianLeadingZero() && number.getItalianLeadingZero()) ? "0" : "" @@ -903,8 +921,7 @@ public class PhoneNumberUtil { if (!isValidRegionCode(regionCode, number.getCountryCode(), nationalSignificantNumber)) { return PhoneNumberType.UNKNOWN; } - PhoneMetadata metadata = getMetadataForRegion(regionCode); - return getNumberTypeHelper(nationalSignificantNumber, metadata); + return getNumberTypeHelper(nationalSignificantNumber, getMetadataForRegion(regionCode)); } private PhoneNumberType getNumberTypeHelper(String nationalNumber, PhoneMetadata metadata) { @@ -972,13 +989,8 @@ public class PhoneNumberUtil { } private boolean isNumberMatchingDesc(String nationalNumber, PhoneNumberDesc numberDesc) { - String possiblePattern = numberDesc.getPossibleNumberPattern(); - if (!nationalNumber.matches(possiblePattern)) { - return false; - } - - String validPattern = numberDesc.getNationalNumberPattern(); - return nationalNumber.matches(validPattern); + return nationalNumber.matches(numberDesc.getPossibleNumberPattern()) && + nationalNumber.matches(numberDesc.getNationalNumberPattern()); } /** @@ -1016,7 +1028,7 @@ public class PhoneNumberUtil { PhoneNumberDesc generalNumDesc = metadata.getGeneralDesc(); String nationalSignificantNumber = getUnformattedNationalNumber(number); - // For countries where we don't have meta-data for PhoneNumberDesc, we treat any number passed + // For countries where we don't have metadata for PhoneNumberDesc, we treat any number passed // in as a valid number if its national significant number is between the minimum and maximum // lengths defined by ITU for a national significant number. if (!generalNumDesc.hasNationalNumberPattern()) { @@ -1037,7 +1049,6 @@ public class PhoneNumberUtil { */ public String getRegionCodeForNumber(PhoneNumber number) { int countryCode = number.getCountryCode(); - String regionCode; switch (countryCode) { case NANPA_COUNTRY_CODE: // Override this and try the US case first, since it is more likely than other countries, @@ -1047,14 +1058,11 @@ public class PhoneNumberUtil { } Set nanpaExceptUS = new HashSet(nanpaCountries); nanpaExceptUS.remove("US"); - regionCode = getRegionCodeForNumberFromRegionList(number, nanpaExceptUS); - return regionCode; + return getRegionCodeForNumberFromRegionList(number, nanpaExceptUS); case RUSSIAN_FED_COUNTRY_CODE: - regionCode = getRegionCodeForNumberFromRegionList(number, russiaFederationCountries); - return regionCode; + return getRegionCodeForNumberFromRegionList(number, russiaFederationCountries); case FRENCH_INDIAN_OCEAN_COUNTRY_CODE: - regionCode = getRegionCodeForNumberFromRegionList(number, frenchIndianOceanTerritories); - return regionCode; + return getRegionCodeForNumberFromRegionList(number, frenchIndianOceanTerritories); default: return getRegionCodeForCountryCode(countryCode); } @@ -1064,8 +1072,8 @@ public class PhoneNumberUtil { Set regionCodes) { String nationalNumber = String.valueOf(number.getNationalNumber()); for (String regionCode : regionCodes) { - PhoneMetadata metadata = getMetadataForRegion(regionCode); - if (getNumberTypeHelper(nationalNumber, metadata) != PhoneNumberType.UNKNOWN) { + if (getNumberTypeHelper(nationalNumber, getMetadataForRegion(regionCode)) != + PhoneNumberType.UNKNOWN) { return regionCode; } } @@ -1095,17 +1103,21 @@ public class PhoneNumberUtil { return 0; } PhoneMetadata metadata = getMetadataForRegion(regionCode); + if (metadata == null) { + LOGGER.log(Level.SEVERE, "Unsupported country code provided."); + return 0; + } return metadata.getCountryCode(); } /** - * Gets a set which contains all the countries under the North American Numbering Plan + * Check if a country is one of the countries under the North American Numbering Plan * Administration (NANPA). * - * @return the set that contains the countries under NANPA + * @return true if regionCode is one of the countries under NANPA */ - public Set getNANPACountries() { - return new HashSet(nanpaCountries); + public boolean isNANPACountry(String regionCode) { + return nanpaCountries.contains(regionCode); } /** @@ -1118,6 +1130,14 @@ public class PhoneNumberUtil { return isPossibleNumberWithReason(number) == ValidationResult.IS_POSSIBLE; } + /** + * Check whether countryCode represents the country calling code from a country whose national + * significant number could contain a leading zero. An example of such a country is Italy. + */ + public static boolean isLeadingZeroCountry(int countryCode) { + return LEADING_ZERO_COUNTRIES.contains(countryCode); + } + /** * Check whether a phone number is a possible number. It provides a more lenient check than * isValidNumber in the following sense: @@ -1139,7 +1159,7 @@ public class PhoneNumberUtil { * @return a ValidationResult object which indicates whether the number is possible */ public ValidationResult isPossibleNumberWithReason(PhoneNumber number) { - String nationalNumber = String.valueOf(number.getNationalNumber()); + String nationalNumber = getUnformattedNationalNumber(number); int countryCode = number.getCountryCode(); // Note: For Russian Fed and NANPA numbers, we just use the rules from the default region (US or // Russia) since the getRegionCodeForNumber will not work if the number is possible but not @@ -1313,7 +1333,7 @@ public class PhoneNumberUtil { // begin with 0. Matcher digitMatcher = CAPTURING_DIGIT_PATTERN.matcher(number.substring(matchEnd)); if (digitMatcher.find()) { - String normalizedGroup = normalize(digitMatcher.group(1)); + String normalizedGroup = normalizeHelper(digitMatcher.group(1), DIGIT_MAPPINGS, true); if (normalizedGroup.equals("0")) { return false; } @@ -1456,7 +1476,31 @@ public class PhoneNumberUtil { throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE, "No default country was supplied."); } - return parseHelper(numberToParse, defaultCountry); + return parseHelper(numberToParse, defaultCountry, false); + } + + /** + * Parses a string and returns it in proto buffer format. This method differs from parse() in that + * it always populates the raw_input field of the protocol buffer with numberToParse. + * + * @param numberToParse number that we are attempting to parse. This can contain formatting + * such as +, ( and -, as well as a phone number extension. + * @param defaultCountry the ISO 3166-1 two-letter country code that denotes the country that + * we are expecting the number to be from. This is only used + * if the number being parsed is not written in international format. + * The country code for the number in this case would be stored as that + * of the default country supplied. + * @return a phone number proto buffer filled with the parsed number + * @throws NumberParseException if the string is not considered to be a viable phone number or if + * no default country was supplied + */ + public PhoneNumber parseAndKeepRawInput(String numberToParse, String defaultCountry) + throws NumberParseException { + if (defaultCountry == null || defaultCountry.equals("ZZ")) { + throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE, + "No default country was supplied."); + } + return parseHelper(numberToParse, defaultCountry, true); } /** @@ -1489,7 +1533,8 @@ public class PhoneNumberUtil { * parse() method, with the exception that it allows the default country to be null, for use by * isNumberMatch(). */ - private PhoneNumber parseHelper(String numberToParse, String defaultCountry) + private PhoneNumber parseHelper(String numberToParse, String defaultCountry, + Boolean keepRawInput) throws NumberParseException { // Extract a possible number from the string passed in (this strips leading characters that // could not be the start of a phone number.) @@ -1500,6 +1545,9 @@ public class PhoneNumberUtil { } PhoneNumber.Builder phoneNumber = PhoneNumber.newBuilder(); + if (keepRawInput) { + phoneNumber.setRawInput(numberToParse); + } StringBuffer nationalNumber = new StringBuffer(number); // Attempt to parse extension first, since it doesn't require country-specific data and we want // to have the non-normalised number here. @@ -1541,8 +1589,8 @@ public class PhoneNumberUtil { validNumberPattern); } phoneNumber.setCountryCode(countryCode); - // The ItalianLeadingZero is valid only for numbers from IT and CI. - if ((countryCode == 39 || countryCode == 225) && normalizedNationalNumber.charAt(0) == '0') { + if (isLeadingZeroCountry(countryCode) && + normalizedNationalNumber.charAt(0) == '0') { phoneNumber.setItalianLeadingZero(true); } int lengthOfNationalNumber = normalizedNationalNumber.length(); @@ -1586,8 +1634,10 @@ public class PhoneNumberUtil { firstNumber.mergeFrom(firstNumberIn); PhoneNumber.Builder secondNumber = PhoneNumber.newBuilder(); secondNumber.mergeFrom(secondNumberIn); - // First clear any empty-string extensions so that we can use the proto-buffer equality method. - + // First clear raw_input field and any empty-string extensions so that we can use the + // proto-buffer equality method. + firstNumber.clearRawInput(); + secondNumber.clearRawInput(); if (firstNumber.hasExtension() && firstNumber.getExtension().equals("")) { firstNumber.clearExtension(); @@ -1611,17 +1661,12 @@ public class PhoneNumberUtil { if (firstNumberCountryCode != 0 && secondNumberCountryCode != 0) { if (areSameMessages(number1, number2)) { return MatchType.EXACT_MATCH; - } else if (firstNumberCountryCode == secondNumberCountryCode) { + } else if (firstNumberCountryCode == secondNumberCountryCode && + isNationalNumberSuffixOfTheOther(number1, number2)) { // A SHORT_NSN_MATCH occurs if there is a difference because of the presence or absence of // an 'Italian leading zero', the presence or absence of an extension, or one NSN being a // shorter variant of the other. - String firstNumberNationalNumber = String.valueOf(number1.getNationalNumber()); - String secondNumberNationalNumber = String.valueOf(number2.getNationalNumber()); - // Note that endsWith returns true if the numbers are equal. - if (firstNumberNationalNumber.endsWith(secondNumberNationalNumber) || - secondNumberNationalNumber.endsWith(firstNumberNationalNumber)) { - return MatchType.SHORT_NSN_MATCH; - } + return MatchType.SHORT_NSN_MATCH; } // This is not a match. return MatchType.NO_MATCH; @@ -1634,16 +1679,22 @@ public class PhoneNumberUtil { if (areSameMessages(newNumber, number2)) { return MatchType.NSN_MATCH; } - String firstNumberNationalNumber = String.valueOf(newNumber.getNationalNumber()); - String secondNumberNationalNumber = String.valueOf(number2.getNationalNumber()); - // Note that endsWith returns true if the numbers are equal. - if (firstNumberNationalNumber.endsWith(secondNumberNationalNumber) || - secondNumberNationalNumber.endsWith(firstNumberNationalNumber)) { + if (isNationalNumberSuffixOfTheOther(newNumber, number2)) { return MatchType.SHORT_NSN_MATCH; } return MatchType.NO_MATCH; } + // Returns true when one national number is the suffix of the other or both are the same. + private boolean isNationalNumberSuffixOfTheOther(PhoneNumber firstNumber, + PhoneNumber secondNumber) { + String firstNumberNationalNumber = String.valueOf(firstNumber.getNationalNumber()); + String secondNumberNationalNumber = String.valueOf(secondNumber.getNationalNumber()); + // Note that endsWith returns true if the numbers are equal. + return firstNumberNationalNumber.endsWith(secondNumberNationalNumber) || + secondNumberNationalNumber.endsWith(firstNumberNationalNumber); + } + /** * Takes two phone numbers as strings and compares them for equality. This is a convenience * wrapper for isNumberMatch(PhoneNumber firstNumber, PhoneNumber secondNumber). No default region @@ -1660,7 +1711,8 @@ public class PhoneNumberUtil { */ public MatchType isNumberMatch(String firstNumber, String secondNumber) throws NumberParseException { - return isNumberMatch(parseHelper(firstNumber, null), parseHelper(secondNumber, null)); + return isNumberMatch(parseHelper(firstNumber, null, false), + parseHelper(secondNumber, null, false)); } /** @@ -1677,6 +1729,6 @@ public class PhoneNumberUtil { */ public MatchType isNumberMatch(PhoneNumber firstNumber, String secondNumber) throws NumberParseException { - return isNumberMatch(firstNumber, parseHelper(secondNumber, null)); + return isNumberMatch(firstNumber, parseHelper(secondNumber, null, false)); } } diff --git a/java/src/com/google/i18n/phonenumbers/Phonenumber.java b/java/src/com/google/i18n/phonenumbers/Phonenumber.java index c3b508912..3a0b16724 100644 --- a/java/src/com/google/i18n/phonenumbers/Phonenumber.java +++ b/java/src/com/google/i18n/phonenumbers/Phonenumber.java @@ -53,6 +53,13 @@ public final class Phonenumber { public boolean hasItalianLeadingZero() { return hasItalianLeadingZero; } public boolean getItalianLeadingZero() { return italianLeadingZero_; } + // optional string raw_input = 5; + public static final int RAW_INPUT_FIELD_NUMBER = 5; + private boolean hasRawInput; + private java.lang.String rawInput_ = ""; + public boolean hasRawInput() { return hasRawInput; } + public java.lang.String getRawInput() { return rawInput_; } + private void initFields() { } public final boolean isInitialized() { @@ -76,6 +83,9 @@ public final class Phonenumber { if (hasItalianLeadingZero()) { output.writeBool(4, getItalianLeadingZero()); } + if (hasRawInput()) { + output.writeString(5, getRawInput()); + } } private int memoizedSerializedSize = -1; @@ -100,6 +110,10 @@ public final class Phonenumber { size += com.google.protobuf.CodedOutputStream .computeBoolSize(4, getItalianLeadingZero()); } + if (hasRawInput()) { + size += com.google.protobuf.CodedOutputStream + .computeStringSize(5, getRawInput()); + } memoizedSerializedSize = size; return size; } @@ -256,6 +270,9 @@ public final class Phonenumber { if (other.hasItalianLeadingZero()) { setItalianLeadingZero(other.getItalianLeadingZero()); } + if (other.hasRawInput()) { + setRawInput(other.getRawInput()); + } return this; } @@ -290,6 +307,10 @@ public final class Phonenumber { setItalianLeadingZero(input.readBool()); break; } + case 42: { + setRawInput(input.readString()); + break; + } } } } @@ -370,6 +391,27 @@ public final class Phonenumber { return this; } + // optional string raw_input = 5; + public boolean hasRawInput() { + return result.hasRawInput(); + } + public java.lang.String getRawInput() { + return result.getRawInput(); + } + public Builder setRawInput(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + result.hasRawInput = true; + result.rawInput_ = value; + return this; + } + public Builder clearRawInput() { + result.hasRawInput = false; + result.rawInput_ = getDefaultInstance().getRawInput(); + return this; + } + // @@protoc_insertion_point(builder_scope:i18n.phonenumbers.PhoneNumber) } diff --git a/java/src/com/google/i18n/phonenumbers/phonenumber.proto b/java/src/com/google/i18n/phonenumbers/phonenumber.proto index 42382746b..27b5996f8 100644 --- a/java/src/com/google/i18n/phonenumbers/phonenumber.proto +++ b/java/src/com/google/i18n/phonenumbers/phonenumber.proto @@ -50,6 +50,11 @@ message PhoneNumber { // phone numbers. For an Italian phone number, if its national (significant) number starts // with the digit zero, set this flag to true. optional bool italian_leading_zero = 4; + +// This field is used to store the raw input string containing phone numbers before it was +// canonicalized by the library. For example, it could be used to store alphanumerical numbers +// such as "1-800-GOOG-411". + optional string raw_input = 5; } // Examples diff --git a/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java b/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java index 6eb77f949..4c114a57b 100644 --- a/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java +++ b/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java @@ -112,6 +112,25 @@ public class AsYouTypeFormatterTest extends TestCase { assertEquals("011 44 6 123 123 12\u2008", formatter.inputDigit('2')); assertEquals("011 44 6 123 123 123", formatter.inputDigit('3')); + 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 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\u2008\u2008\u2008 \u2008\u2008\u2008\u2008", + formatter.inputDigit('2')); + assertEquals("011 54 9 11 23\u2008\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("011 54 9 11 231\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("011 54 9 11 2312 \u2008\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("011 54 9 11 2312 1\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("011 54 9 11 2312 12\u2008\u2008", formatter.inputDigit('2')); + assertEquals("011 54 9 11 2312 123\u2008", formatter.inputDigit('3')); + assertEquals("011 54 9 11 2312 1234", formatter.inputDigit('4')); + formatter.clear(); assertEquals("+", formatter.inputDigit('+')); assertEquals("+1", formatter.inputDigit('1')); @@ -229,4 +248,38 @@ public class AsYouTypeFormatterTest extends TestCase { assertEquals("030123", formatter.inputDigit('3')); assertEquals("0301234", formatter.inputDigit('4')); } + + public void testAsYouTypeFormatterAR() { + 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 703\u2008-\u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("011 7031-\u2008\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("011 7031-3\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("011 7031-30\u2008\u2008", formatter.inputDigit('0')); + assertEquals("011 7031-300\u2008", formatter.inputDigit('0')); + assertEquals("011 7031-3000", formatter.inputDigit('0')); + } + + public void testAsYouTypeFormatterARMobile() { + 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\u2008\u2008\u2008 \u2008\u2008\u2008\u2008", + formatter.inputDigit('2')); + assertEquals("+54 9 11 23\u2008\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('3')); + assertEquals("+54 9 11 231\u2008 \u2008\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("+54 9 11 2312 \u2008\u2008\u2008\u2008", formatter.inputDigit('2')); + assertEquals("+54 9 11 2312 1\u2008\u2008\u2008", formatter.inputDigit('1')); + assertEquals("+54 9 11 2312 12\u2008\u2008", formatter.inputDigit('2')); + assertEquals("+54 9 11 2312 123\u2008", formatter.inputDigit('3')); + assertEquals("+54 9 11 2312 1234", formatter.inputDigit('4')); + } } diff --git a/java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java b/java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java index 0721fa619..746a3ed94 100644 --- a/java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java +++ b/java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.regex.Pattern; /** @@ -65,7 +64,7 @@ public class PhoneNumberUtilTest extends TestCase { } public void testGetInstanceLoadUSMetadata() throws IOException { - PhoneMetadata metadata = phoneUtil.getPhoneMetadata("US"); + PhoneMetadata metadata = phoneUtil.getMetadataForRegion("US"); assertEquals("US", metadata.getId()); assertEquals(1, metadata.getCountryCode()); assertEquals("011", metadata.getInternationalPrefix()); @@ -86,7 +85,7 @@ public class PhoneNumberUtilTest extends TestCase { } public void testGetInstanceLoadDEMetadata() { - PhoneMetadata metadata = phoneUtil.getPhoneMetadata("DE"); + PhoneMetadata metadata = phoneUtil.getMetadataForRegion("DE"); assertEquals("DE", metadata.getId()); assertEquals(49, metadata.getCountryCode()); assertEquals("00", metadata.getInternationalPrefix()); @@ -105,7 +104,7 @@ public class PhoneNumberUtilTest extends TestCase { } public void testGetInstanceLoadARMetadata() { - PhoneMetadata metadata = phoneUtil.getPhoneMetadata("AR"); + PhoneMetadata metadata = phoneUtil.getMetadataForRegion("AR"); assertEquals("AR", metadata.getId()); assertEquals(54, metadata.getCountryCode()); assertEquals("00", metadata.getInternationalPrefix()); @@ -740,13 +739,15 @@ public class PhoneNumberUtilTest extends TestCase { public void testGetCountryCodeForRegion() { assertEquals(1, phoneUtil.getCountryCodeForRegion("US")); assertEquals(64, phoneUtil.getCountryCodeForRegion("NZ")); + assertEquals(0, phoneUtil.getCountryCodeForRegion(null)); + assertEquals(0, phoneUtil.getCountryCodeForRegion("ZZ")); + // CS is already deprecated so the library doesn't support it. + assertEquals(0, phoneUtil.getCountryCodeForRegion("CS")); } - public void testGetNANPACountries() { - Set nanpaCountries = phoneUtil.getNANPACountries(); - assertEquals(2, nanpaCountries.size()); - assertTrue(nanpaCountries.contains("US")); - assertTrue(nanpaCountries.contains("BS")); + public void testIsNANPACountry() { + assertTrue(phoneUtil.isNANPACountry("US")); + assertTrue(phoneUtil.isNANPACountry("BS")); } public void testIsPossibleNumber() { @@ -857,6 +858,12 @@ public class PhoneNumberUtilTest extends TestCase { assertEquals("", PhoneNumberUtil.extractPossibleNumber("Num-....")); // Leading brackets are stripped - these are not used when parsing. assertEquals("650) 253-0000", PhoneNumberUtil.extractPossibleNumber("(650) 253-0000")); + + // Trailing non-alpha-numeric characters should be removed. + assertEquals("650) 253-0000", PhoneNumberUtil.extractPossibleNumber("(650) 253-0000..- ..")); + assertEquals("650) 253-0000", PhoneNumberUtil.extractPossibleNumber("(650) 253-0000.")); + // This case has a trailing RTL char. + assertEquals("650) 253-0000", PhoneNumberUtil.extractPossibleNumber("(650) 253-0000\u200F")); } public void testMaybeStripNationalPrefix() { @@ -938,7 +945,7 @@ public class PhoneNumberUtilTest extends TestCase { } public void testMaybeExtractCountryCode() { - PhoneMetadata metadata = phoneUtil.getPhoneMetadata("US"); + PhoneMetadata metadata = phoneUtil.getMetadataForRegion("US"); // Note that for the US, the IDD is 011. try { String phoneNumber = "011112-3456789"; @@ -1312,6 +1319,14 @@ public class PhoneNumberUtilTest extends TestCase { assertEquals(usWithExtension, phoneUtil.parse("(800) 901-3355 ext: 7246433", "US")); } + public void testParseAndKeepRaw() throws Exception { + PhoneNumber alphaNumericNumber = + PhoneNumber.newBuilder().setCountryCode(1).setNationalNumber(180074935247L) + .setRawInput("1800 six-flags").build(); + assertEquals(alphaNumericNumber, + phoneUtil.parseAndKeepRawInput("1800 six-flags", "US")); + } + public void testCountryWithNoNumberDesc() { // Andorra is a country where we don't have PhoneNumberDesc info in the meta data. PhoneNumber adNumber =