2 * Copyright (C) 2011 The Libphonenumber Authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.google.i18n.phonenumbers;
19 import com.google.i18n.phonenumbers.PhoneNumberUtil.Leniency;
20 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
21 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.NoSuchElementException;
30 * Tests for {@link PhoneNumberMatcher}. This only tests basic functionality based on test metadata.
32 * @see PhoneNumberUtilTest {@link PhoneNumberUtilTest} for the origin of the test data
34 public class PhoneNumberMatcherTest extends TestMetadataTestCase {
36 public void testContainsMoreThanOneSlashInNationalNumber() throws Exception {
37 // A date should return true.
38 PhoneNumber number = new PhoneNumber();
39 number.setCountryCode(1);
40 number.setCountryCodeSource(CountryCodeSource.FROM_DEFAULT_COUNTRY);
41 String candidate = "1/05/2013";
42 assertTrue(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
44 // Here, the country code source thinks it started with a country calling code, but this is not
45 // the same as the part before the slash, so it's still true.
46 number = new PhoneNumber();
47 number.setCountryCode(274);
48 number.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN);
49 candidate = "27/4/2013";
50 assertTrue(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
52 // Now it should be false, because the first slash is after the country calling code.
53 number = new PhoneNumber();
54 number.setCountryCode(49);
55 number.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN);
56 candidate = "49/69/2013";
57 assertFalse(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
59 number = new PhoneNumber();
60 number.setCountryCode(49);
61 number.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN);
62 candidate = "+49/69/2013";
63 assertFalse(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
65 candidate = "+ 49/69/2013";
66 assertFalse(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
68 candidate = "+ 49/69/20/13";
69 assertTrue(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
71 // Here, the first group is not assumed to be the country calling code, even though it is the
72 // same as it, so this should return true.
73 number = new PhoneNumber();
74 number.setCountryCode(49);
75 number.setCountryCodeSource(CountryCodeSource.FROM_DEFAULT_COUNTRY);
76 candidate = "49/69/2013";
77 assertTrue(PhoneNumberMatcher.containsMoreThanOneSlashInNationalNumber(number, candidate));
80 /** See {@link PhoneNumberUtilTest#testParseNationalNumber()}. */
81 public void testFindNationalNumber() throws Exception {
82 // same cases as in testParseNationalNumber
83 doTestFindInContext("033316005", RegionCode.NZ);
84 // ("33316005", RegionCode.NZ) is omitted since the national prefix is obligatory for these
85 // types of numbers in New Zealand.
86 // National prefix attached and some formatting present.
87 doTestFindInContext("03-331 6005", RegionCode.NZ);
88 doTestFindInContext("03 331 6005", RegionCode.NZ);
89 // Testing international prefixes.
90 // Should strip country code.
91 doTestFindInContext("0064 3 331 6005", RegionCode.NZ);
92 // Try again, but this time we have an international number with Region Code US. It should
93 // recognize the country code and parse accordingly.
94 doTestFindInContext("01164 3 331 6005", RegionCode.US);
95 doTestFindInContext("+64 3 331 6005", RegionCode.US);
97 doTestFindInContext("64(0)64123456", RegionCode.NZ);
98 // Check that using a "/" is fine in a phone number.
99 // Note that real Polish numbers do *not* start with a 0.
100 doTestFindInContext("0123/456789", RegionCode.PL);
101 doTestFindInContext("123-456-7890", RegionCode.US);
104 /** See {@link PhoneNumberUtilTest#testParseWithInternationalPrefixes()}. */
105 public void testFindWithInternationalPrefixes() throws Exception {
106 doTestFindInContext("+1 (650) 333-6000", RegionCode.NZ);
107 doTestFindInContext("1-650-333-6000", RegionCode.US);
108 // Calling the US number from Singapore by using different service providers
109 // 1st test: calling using SingTel IDD service (IDD is 001)
110 doTestFindInContext("0011-650-333-6000", RegionCode.SG);
111 // 2nd test: calling using StarHub IDD service (IDD is 008)
112 doTestFindInContext("0081-650-333-6000", RegionCode.SG);
113 // 3rd test: calling using SingTel V019 service (IDD is 019)
114 doTestFindInContext("0191-650-333-6000", RegionCode.SG);
115 // Calling the US number from Poland
116 doTestFindInContext("0~01-650-333-6000", RegionCode.PL);
117 // Using "++" at the start.
118 doTestFindInContext("++1 (650) 333-6000", RegionCode.PL);
119 // Using a full-width plus sign.
120 doTestFindInContext("\uFF0B1 (650) 333-6000", RegionCode.SG);
121 // The whole number, including punctuation, is here represented in full-width form.
122 doTestFindInContext("\uFF0B\uFF11\u3000\uFF08\uFF16\uFF15\uFF10\uFF09" +
123 "\u3000\uFF13\uFF13\uFF13\uFF0D\uFF16\uFF10\uFF10\uFF10",
127 /** See {@link PhoneNumberUtilTest#testParseWithLeadingZero()}. */
128 public void testFindWithLeadingZero() throws Exception {
129 doTestFindInContext("+39 02-36618 300", RegionCode.NZ);
130 doTestFindInContext("02-36618 300", RegionCode.IT);
131 doTestFindInContext("312 345 678", RegionCode.IT);
134 /** See {@link PhoneNumberUtilTest#testParseNationalNumberArgentina()}. */
135 public void testFindNationalNumberArgentina() throws Exception {
136 // Test parsing mobile numbers of Argentina.
137 doTestFindInContext("+54 9 343 555 1212", RegionCode.AR);
138 doTestFindInContext("0343 15 555 1212", RegionCode.AR);
140 doTestFindInContext("+54 9 3715 65 4320", RegionCode.AR);
141 doTestFindInContext("03715 15 65 4320", RegionCode.AR);
143 // Test parsing fixed-line numbers of Argentina.
144 doTestFindInContext("+54 11 3797 0000", RegionCode.AR);
145 doTestFindInContext("011 3797 0000", RegionCode.AR);
147 doTestFindInContext("+54 3715 65 4321", RegionCode.AR);
148 doTestFindInContext("03715 65 4321", RegionCode.AR);
150 doTestFindInContext("+54 23 1234 0000", RegionCode.AR);
151 doTestFindInContext("023 1234 0000", RegionCode.AR);
154 /** See {@link PhoneNumberUtilTest#testParseWithXInNumber()}. */
155 public void testFindWithXInNumber() throws Exception {
156 doTestFindInContext("(0xx) 123456789", RegionCode.AR);
157 // A case where x denotes both carrier codes and extension symbol.
158 doTestFindInContext("(0xx) 123456789 x 1234", RegionCode.AR);
160 // This test is intentionally constructed such that the number of digit after xx is larger than
161 // 7, so that the number won't be mistakenly treated as an extension, as we allow extensions up
162 // to 7 digits. This assumption is okay for now as all the countries where a carrier selection
163 // code is written in the form of xx have a national significant number of length larger than 7.
164 doTestFindInContext("011xx5481429712", RegionCode.US);
167 /** See {@link PhoneNumberUtilTest#testParseNumbersMexico()}. */
168 public void testFindNumbersMexico() throws Exception {
169 // Test parsing fixed-line numbers of Mexico.
170 doTestFindInContext("+52 (449)978-0001", RegionCode.MX);
171 doTestFindInContext("01 (449)978-0001", RegionCode.MX);
172 doTestFindInContext("(449)978-0001", RegionCode.MX);
174 // Test parsing mobile numbers of Mexico.
175 doTestFindInContext("+52 1 33 1234-5678", RegionCode.MX);
176 doTestFindInContext("044 (33) 1234-5678", RegionCode.MX);
177 doTestFindInContext("045 33 1234-5678", RegionCode.MX);
180 /** See {@link PhoneNumberUtilTest#testParseNumbersWithPlusWithNoRegion()}. */
181 public void testFindNumbersWithPlusWithNoRegion() throws Exception {
182 // RegionCode.ZZ is allowed only if the number starts with a '+' - then the country code can be
184 doTestFindInContext("+64 3 331 6005", RegionCode.ZZ);
185 // Null is also allowed for the region code in these cases.
186 doTestFindInContext("+64 3 331 6005", null);
189 /** See {@link PhoneNumberUtilTest#testParseExtensions()}. */
190 public void testFindExtensions() throws Exception {
191 doTestFindInContext("03 331 6005 ext 3456", RegionCode.NZ);
192 doTestFindInContext("03-3316005x3456", RegionCode.NZ);
193 doTestFindInContext("03-3316005 int.3456", RegionCode.NZ);
194 doTestFindInContext("03 3316005 #3456", RegionCode.NZ);
195 doTestFindInContext("0~0 1800 7493 524", RegionCode.PL);
196 doTestFindInContext("(1800) 7493.524", RegionCode.US);
197 // Check that the last instance of an extension token is matched.
198 doTestFindInContext("0~0 1800 7493 524 ~1234", RegionCode.PL);
199 // Verifying bug-fix where the last digit of a number was previously omitted if it was a 0 when
200 // extracting the extension. Also verifying a few different cases of extensions.
201 doTestFindInContext("+44 2034567890x456", RegionCode.NZ);
202 doTestFindInContext("+44 2034567890x456", RegionCode.GB);
203 doTestFindInContext("+44 2034567890 x456", RegionCode.GB);
204 doTestFindInContext("+44 2034567890 X456", RegionCode.GB);
205 doTestFindInContext("+44 2034567890 X 456", RegionCode.GB);
206 doTestFindInContext("+44 2034567890 X 456", RegionCode.GB);
207 doTestFindInContext("+44 2034567890 X 456", RegionCode.GB);
209 doTestFindInContext("(800) 901-3355 x 7246433", RegionCode.US);
210 doTestFindInContext("(800) 901-3355 , ext 7246433", RegionCode.US);
211 doTestFindInContext("(800) 901-3355 ,extension 7246433", RegionCode.US);
212 // The next test differs from PhoneNumberUtil -> when matching we don't consider a lone comma to
213 // indicate an extension, although we accept it when parsing.
214 doTestFindInContext("(800) 901-3355 ,x 7246433", RegionCode.US);
215 doTestFindInContext("(800) 901-3355 ext: 7246433", RegionCode.US);
218 public void testFindInterspersedWithSpace() throws Exception {
219 doTestFindInContext("0 3 3 3 1 6 0 0 5", RegionCode.NZ);
223 * Test matching behavior when starting in the middle of a phone number.
225 public void testIntermediateParsePositions() throws Exception {
226 String text = "Call 033316005 or 032316005!";
230 // Iterate over all possible indices.
231 for (int i = 0; i <= 5; i++) {
232 assertEqualRange(text, i, 5, 14);
234 // 7 and 8 digits in a row are still parsed as number.
235 assertEqualRange(text, 6, 6, 14);
236 assertEqualRange(text, 7, 7, 14);
237 // Anything smaller is skipped to the second instance.
238 for (int i = 8; i <= 19; i++) {
239 assertEqualRange(text, i, 19, 28);
243 public void testFourMatchesInARow() throws Exception {
244 String number1 = "415-666-7777";
245 String number2 = "800-443-1223";
246 String number3 = "212-443-1223";
247 String number4 = "650-443-1223";
248 String text = number1 + " - " + number2 + " - " + number3 + " - " + number4;
250 Iterator<PhoneNumberMatch> iterator =
251 phoneUtil.findNumbers(text, RegionCode.US).iterator();
252 PhoneNumberMatch match = iterator.hasNext() ? iterator.next() : null;
253 assertMatchProperties(match, text, number1, RegionCode.US);
255 match = iterator.hasNext() ? iterator.next() : null;
256 assertMatchProperties(match, text, number2, RegionCode.US);
258 match = iterator.hasNext() ? iterator.next() : null;
259 assertMatchProperties(match, text, number3, RegionCode.US);
261 match = iterator.hasNext() ? iterator.next() : null;
262 assertMatchProperties(match, text, number4, RegionCode.US);
265 public void testMatchesFoundWithMultipleSpaces() throws Exception {
266 String number1 = "(415) 666-7777";
267 String number2 = "(800) 443-1223";
268 String text = number1 + " " + number2;
270 Iterator<PhoneNumberMatch> iterator =
271 phoneUtil.findNumbers(text, RegionCode.US).iterator();
272 PhoneNumberMatch match = iterator.hasNext() ? iterator.next() : null;
273 assertMatchProperties(match, text, number1, RegionCode.US);
275 match = iterator.hasNext() ? iterator.next() : null;
276 assertMatchProperties(match, text, number2, RegionCode.US);
279 public void testMatchWithSurroundingZipcodes() throws Exception {
280 String number = "415-666-7777";
281 String zipPreceding = "My address is CA 34215 - " + number + " is my number.";
283 Iterator<PhoneNumberMatch> iterator =
284 phoneUtil.findNumbers(zipPreceding, RegionCode.US).iterator();
285 PhoneNumberMatch match = iterator.hasNext() ? iterator.next() : null;
286 assertMatchProperties(match, zipPreceding, number, RegionCode.US);
288 // Now repeat, but this time the phone number has spaces in it. It should still be found.
289 number = "(415) 666 7777";
291 String zipFollowing = "My number is " + number + ". 34215 is my zip-code.";
292 iterator = phoneUtil.findNumbers(zipFollowing, RegionCode.US).iterator();
293 PhoneNumberMatch matchWithSpaces = iterator.hasNext() ? iterator.next() : null;
294 assertMatchProperties(matchWithSpaces, zipFollowing, number, RegionCode.US);
297 public void testIsLatinLetter() throws Exception {
298 assertTrue(PhoneNumberMatcher.isLatinLetter('c'));
299 assertTrue(PhoneNumberMatcher.isLatinLetter('C'));
300 assertTrue(PhoneNumberMatcher.isLatinLetter('\u00C9'));
301 assertTrue(PhoneNumberMatcher.isLatinLetter('\u0301')); // Combining acute accent
302 // Punctuation, digits and white-space are not considered "latin letters".
303 assertFalse(PhoneNumberMatcher.isLatinLetter(':'));
304 assertFalse(PhoneNumberMatcher.isLatinLetter('5'));
305 assertFalse(PhoneNumberMatcher.isLatinLetter('-'));
306 assertFalse(PhoneNumberMatcher.isLatinLetter('.'));
307 assertFalse(PhoneNumberMatcher.isLatinLetter(' '));
308 assertFalse(PhoneNumberMatcher.isLatinLetter('\u6211')); // Chinese character
309 assertFalse(PhoneNumberMatcher.isLatinLetter('\u306E')); // Hiragana letter no
312 public void testMatchesWithSurroundingLatinChars() throws Exception {
313 ArrayList<NumberContext> possibleOnlyContexts = new ArrayList<NumberContext>();
314 possibleOnlyContexts.add(new NumberContext("abc", "def"));
315 possibleOnlyContexts.add(new NumberContext("abc", ""));
316 possibleOnlyContexts.add(new NumberContext("", "def"));
317 // Latin capital letter e with an acute accent.
318 possibleOnlyContexts.add(new NumberContext("\u00C9", ""));
319 // e with an acute accent decomposed (with combining mark).
320 possibleOnlyContexts.add(new NumberContext("e\u0301", ""));
322 // Numbers should not be considered valid, if they are surrounded by Latin characters, but
323 // should be considered possible.
324 findMatchesInContexts(possibleOnlyContexts, false, true);
327 public void testMoneyNotSeenAsPhoneNumber() throws Exception {
328 ArrayList<NumberContext> possibleOnlyContexts = new ArrayList<NumberContext>();
329 possibleOnlyContexts.add(new NumberContext("$", ""));
330 possibleOnlyContexts.add(new NumberContext("", "$"));
331 possibleOnlyContexts.add(new NumberContext("\u00A3", "")); // Pound sign
332 possibleOnlyContexts.add(new NumberContext("\u00A5", "")); // Yen sign
333 findMatchesInContexts(possibleOnlyContexts, false, true);
336 public void testPercentageNotSeenAsPhoneNumber() throws Exception {
337 ArrayList<NumberContext> possibleOnlyContexts = new ArrayList<NumberContext>();
338 possibleOnlyContexts.add(new NumberContext("", "%"));
339 // Numbers followed by % should be dropped.
340 findMatchesInContexts(possibleOnlyContexts, false, true);
343 public void testPhoneNumberWithLeadingOrTrailingMoneyMatches() throws Exception {
344 // Because of the space after the 20 (or before the 100) these dollar amounts should not stop
345 // the actual number from being found.
346 ArrayList<NumberContext> contexts = new ArrayList<NumberContext>();
347 contexts.add(new NumberContext("$20 ", ""));
348 contexts.add(new NumberContext("", " 100$"));
349 findMatchesInContexts(contexts, true, true);
352 public void testMatchesWithSurroundingLatinCharsAndLeadingPunctuation() throws Exception {
353 // Contexts with trailing characters. Leading characters are okay here since the numbers we will
354 // insert start with punctuation, but trailing characters are still not allowed.
355 ArrayList<NumberContext> possibleOnlyContexts = new ArrayList<NumberContext>();
356 possibleOnlyContexts.add(new NumberContext("abc", "def"));
357 possibleOnlyContexts.add(new NumberContext("", "def"));
358 possibleOnlyContexts.add(new NumberContext("", "\u00C9"));
360 // Numbers should not be considered valid, if they have trailing Latin characters, but should be
361 // considered possible.
362 String numberWithPlus = "+14156667777";
363 String numberWithBrackets = "(415)6667777";
364 findMatchesInContexts(possibleOnlyContexts, false, true, RegionCode.US, numberWithPlus);
365 findMatchesInContexts(possibleOnlyContexts, false, true, RegionCode.US, numberWithBrackets);
367 ArrayList<NumberContext> validContexts = new ArrayList<NumberContext>();
368 validContexts.add(new NumberContext("abc", ""));
369 validContexts.add(new NumberContext("\u00C9", ""));
370 validContexts.add(new NumberContext("\u00C9", ".")); // Trailing punctuation.
371 validContexts.add(new NumberContext("\u00C9", " def")); // Trailing white-space.
373 // Numbers should be considered valid, since they start with punctuation.
374 findMatchesInContexts(validContexts, true, true, RegionCode.US, numberWithPlus);
375 findMatchesInContexts(validContexts, true, true, RegionCode.US, numberWithBrackets);
378 public void testMatchesWithSurroundingChineseChars() throws Exception {
379 ArrayList<NumberContext> validContexts = new ArrayList<NumberContext>();
380 validContexts.add(new NumberContext("\u6211\u7684\u7535\u8BDD\u53F7\u7801\u662F", ""));
381 validContexts.add(new NumberContext("", "\u662F\u6211\u7684\u7535\u8BDD\u53F7\u7801"));
382 validContexts.add(new NumberContext("\u8BF7\u62E8\u6253", "\u6211\u5728\u660E\u5929"));
384 // Numbers should be considered valid, since they are surrounded by Chinese.
385 findMatchesInContexts(validContexts, true, true);
388 public void testMatchesWithSurroundingPunctuation() throws Exception {
389 ArrayList<NumberContext> validContexts = new ArrayList<NumberContext>();
390 validContexts.add(new NumberContext("My number-", "")); // At end of text.
391 validContexts.add(new NumberContext("", ".Nice day.")); // At start of text.
392 validContexts.add(new NumberContext("Tel:", ".")); // Punctuation surrounds number.
393 validContexts.add(new NumberContext("Tel: ", " on Saturdays.")); // White-space is also fine.
395 // Numbers should be considered valid, since they are surrounded by punctuation.
396 findMatchesInContexts(validContexts, true, true);
399 public void testMatchesMultiplePhoneNumbersSeparatedByPhoneNumberPunctuation() throws Exception {
400 String text = "Call 650-253-4561 -- 455-234-3451";
401 String region = RegionCode.US;
403 PhoneNumber number1 = new PhoneNumber();
404 number1.setCountryCode(phoneUtil.getCountryCodeForRegion(region));
405 number1.setNationalNumber(6502534561L);
406 PhoneNumberMatch match1 = new PhoneNumberMatch(5, "650-253-4561", number1);
408 PhoneNumber number2 = new PhoneNumber();
409 number2.setCountryCode(phoneUtil.getCountryCodeForRegion(region));
410 number2.setNationalNumber(4552343451L);
411 PhoneNumberMatch match2 = new PhoneNumberMatch(21, "455-234-3451", number2);
413 Iterator<PhoneNumberMatch> matches = phoneUtil.findNumbers(text, region).iterator();
414 assertEquals(match1, matches.next());
415 assertEquals(match2, matches.next());
418 public void testDoesNotMatchMultiplePhoneNumbersSeparatedWithNoWhiteSpace() throws Exception {
419 // No white-space found between numbers - neither is found.
420 String text = "Call 650-253-4561--455-234-3451";
421 String region = RegionCode.US;
423 assertTrue(hasNoMatches(phoneUtil.findNumbers(text, region)));
427 * Strings with number-like things that shouldn't be found under any level.
429 private static final NumberTest[] IMPOSSIBLE_CASES = {
430 new NumberTest("12345", RegionCode.US),
431 new NumberTest("23456789", RegionCode.US),
432 new NumberTest("234567890112", RegionCode.US),
433 new NumberTest("650+253+1234", RegionCode.US),
434 new NumberTest("3/10/1984", RegionCode.CA),
435 new NumberTest("03/27/2011", RegionCode.US),
436 new NumberTest("31/8/2011", RegionCode.US),
437 new NumberTest("1/12/2011", RegionCode.US),
438 new NumberTest("10/12/82", RegionCode.DE),
439 new NumberTest("650x2531234", RegionCode.US),
440 new NumberTest("2012-01-02 08:00", RegionCode.US),
441 new NumberTest("2012/01/02 08:00", RegionCode.US),
442 new NumberTest("20120102 08:00", RegionCode.US),
446 * Strings with number-like things that should only be found under "possible".
448 private static final NumberTest[] POSSIBLE_ONLY_CASES = {
449 // US numbers cannot start with 7 in the test metadata to be valid.
450 new NumberTest("7121115678", RegionCode.US),
451 // 'X' should not be found in numbers at leniencies stricter than POSSIBLE, unless it represents
452 // a carrier code or extension.
453 new NumberTest("1650 x 253 - 1234", RegionCode.US),
454 new NumberTest("650 x 253 - 1234", RegionCode.US),
455 new NumberTest("6502531x234", RegionCode.US),
456 new NumberTest("(20) 3346 1234", RegionCode.GB), // Non-optional NP omitted
460 * Strings with number-like things that should only be found up to and including the "valid"
463 private static final NumberTest[] VALID_CASES = {
464 new NumberTest("65 02 53 00 00", RegionCode.US),
465 new NumberTest("6502 538365", RegionCode.US),
466 new NumberTest("650//253-1234", RegionCode.US), // 2 slashes are illegal at higher levels
467 new NumberTest("650/253/1234", RegionCode.US),
468 new NumberTest("9002309. 158", RegionCode.US),
469 new NumberTest("12 7/8 - 14 12/34 - 5", RegionCode.US),
470 new NumberTest("12.1 - 23.71 - 23.45", RegionCode.US),
471 new NumberTest("800 234 1 111x1111", RegionCode.US),
472 new NumberTest("1979-2011 100", RegionCode.US),
473 new NumberTest("+494949-4-94", RegionCode.DE), // National number in wrong format
474 new NumberTest("\uFF14\uFF11\uFF15\uFF16\uFF16\uFF16\uFF16-\uFF17\uFF17\uFF17", RegionCode.US),
475 new NumberTest("2012-0102 08", RegionCode.US), // Very strange formatting.
476 new NumberTest("2012-01-02 08", RegionCode.US),
477 // Breakdown assistance number with unexpected formatting.
478 new NumberTest("1800-1-0-10 22", RegionCode.AU),
479 new NumberTest("030-3-2 23 12 34", RegionCode.DE),
480 new NumberTest("03 0 -3 2 23 12 34", RegionCode.DE),
481 new NumberTest("(0)3 0 -3 2 23 12 34", RegionCode.DE),
482 new NumberTest("0 3 0 -3 2 23 12 34", RegionCode.DE),
486 * Strings with number-like things that should only be found up to and including the
487 * "strict_grouping" leniency level.
489 private static final NumberTest[] STRICT_GROUPING_CASES = {
490 new NumberTest("(415) 6667777", RegionCode.US),
491 new NumberTest("415-6667777", RegionCode.US),
492 // Should be found by strict grouping but not exact grouping, as the last two groups are
493 // formatted together as a block.
494 new NumberTest("0800-2491234", RegionCode.DE),
495 // Doesn't match any formatting in the test file, but almost matches an alternate format (the
496 // last two groups have been squashed together here).
497 new NumberTest("0900-1 123123", RegionCode.DE),
498 new NumberTest("(0)900-1 123123", RegionCode.DE),
499 new NumberTest("0 900-1 123123", RegionCode.DE),
500 // NDC also found as part of the country calling code; this shouldn't ruin the grouping
502 new NumberTest("+33 3 34 2312", RegionCode.FR),
506 * Strings with number-like things that should be found at all levels.
508 private static final NumberTest[] EXACT_GROUPING_CASES = {
509 new NumberTest("\uFF14\uFF11\uFF15\uFF16\uFF16\uFF16\uFF17\uFF17\uFF17\uFF17", RegionCode.US),
510 new NumberTest("\uFF14\uFF11\uFF15-\uFF16\uFF16\uFF16-\uFF17\uFF17\uFF17\uFF17", RegionCode.US),
511 new NumberTest("4156667777", RegionCode.US),
512 new NumberTest("4156667777 x 123", RegionCode.US),
513 new NumberTest("415-666-7777", RegionCode.US),
514 new NumberTest("415/666-7777", RegionCode.US),
515 new NumberTest("415-666-7777 ext. 503", RegionCode.US),
516 new NumberTest("1 415 666 7777 x 123", RegionCode.US),
517 new NumberTest("+1 415-666-7777", RegionCode.US),
518 new NumberTest("+494949 49", RegionCode.DE),
519 new NumberTest("+49-49-34", RegionCode.DE),
520 new NumberTest("+49-4931-49", RegionCode.DE),
521 new NumberTest("04931-49", RegionCode.DE), // With National Prefix
522 new NumberTest("+49-494949", RegionCode.DE), // One group with country code
523 new NumberTest("+49-494949 ext. 49", RegionCode.DE),
524 new NumberTest("+49494949 ext. 49", RegionCode.DE),
525 new NumberTest("0494949", RegionCode.DE),
526 new NumberTest("0494949 ext. 49", RegionCode.DE),
527 new NumberTest("01 (33) 3461 2234", RegionCode.MX), // Optional NP present
528 new NumberTest("(33) 3461 2234", RegionCode.MX), // Optional NP omitted
529 new NumberTest("1800-10-10 22", RegionCode.AU), // Breakdown assistance number.
530 // Doesn't match any formatting in the test file, but matches an alternate format exactly.
531 new NumberTest("0900-1 123 123", RegionCode.DE),
532 new NumberTest("(0)900-1 123 123", RegionCode.DE),
533 new NumberTest("0 900-1 123 123", RegionCode.DE),
534 new NumberTest("+33 3 34 23 12", RegionCode.FR),
537 public void testMatchesWithPossibleLeniency() throws Exception {
538 List<NumberTest> testCases = new ArrayList<NumberTest>();
539 testCases.addAll(Arrays.asList(STRICT_GROUPING_CASES));
540 testCases.addAll(Arrays.asList(EXACT_GROUPING_CASES));
541 testCases.addAll(Arrays.asList(VALID_CASES));
542 testCases.addAll(Arrays.asList(POSSIBLE_ONLY_CASES));
543 doTestNumberMatchesForLeniency(testCases, Leniency.POSSIBLE);
546 public void testNonMatchesWithPossibleLeniency() throws Exception {
547 List<NumberTest> testCases = new ArrayList<NumberTest>();
548 testCases.addAll(Arrays.asList(IMPOSSIBLE_CASES));
549 doTestNumberNonMatchesForLeniency(testCases, Leniency.POSSIBLE);
552 public void testMatchesWithValidLeniency() throws Exception {
553 List<NumberTest> testCases = new ArrayList<NumberTest>();
554 testCases.addAll(Arrays.asList(STRICT_GROUPING_CASES));
555 testCases.addAll(Arrays.asList(EXACT_GROUPING_CASES));
556 testCases.addAll(Arrays.asList(VALID_CASES));
557 doTestNumberMatchesForLeniency(testCases, Leniency.VALID);
560 public void testNonMatchesWithValidLeniency() throws Exception {
561 List<NumberTest> testCases = new ArrayList<NumberTest>();
562 testCases.addAll(Arrays.asList(IMPOSSIBLE_CASES));
563 testCases.addAll(Arrays.asList(POSSIBLE_ONLY_CASES));
564 doTestNumberNonMatchesForLeniency(testCases, Leniency.VALID);
567 public void testMatchesWithStrictGroupingLeniency() throws Exception {
568 List<NumberTest> testCases = new ArrayList<NumberTest>();
569 testCases.addAll(Arrays.asList(STRICT_GROUPING_CASES));
570 testCases.addAll(Arrays.asList(EXACT_GROUPING_CASES));
571 doTestNumberMatchesForLeniency(testCases, Leniency.STRICT_GROUPING);
574 public void testNonMatchesWithStrictGroupLeniency() throws Exception {
575 List<NumberTest> testCases = new ArrayList<NumberTest>();
576 testCases.addAll(Arrays.asList(IMPOSSIBLE_CASES));
577 testCases.addAll(Arrays.asList(POSSIBLE_ONLY_CASES));
578 testCases.addAll(Arrays.asList(VALID_CASES));
579 doTestNumberNonMatchesForLeniency(testCases, Leniency.STRICT_GROUPING);
582 public void testMatchesWithExactGroupingLeniency() throws Exception {
583 List<NumberTest> testCases = new ArrayList<NumberTest>();
584 testCases.addAll(Arrays.asList(EXACT_GROUPING_CASES));
585 doTestNumberMatchesForLeniency(testCases, Leniency.EXACT_GROUPING);
588 public void testNonMatchesExactGroupLeniency() throws Exception {
589 List<NumberTest> testCases = new ArrayList<NumberTest>();
590 testCases.addAll(Arrays.asList(IMPOSSIBLE_CASES));
591 testCases.addAll(Arrays.asList(POSSIBLE_ONLY_CASES));
592 testCases.addAll(Arrays.asList(VALID_CASES));
593 testCases.addAll(Arrays.asList(STRICT_GROUPING_CASES));
594 doTestNumberNonMatchesForLeniency(testCases, Leniency.EXACT_GROUPING);
597 private void doTestNumberMatchesForLeniency(List<NumberTest> testCases, Leniency leniency) {
598 int noMatchFoundCount = 0;
599 int wrongMatchFoundCount = 0;
600 for (NumberTest test : testCases) {
601 Iterator<PhoneNumberMatch> iterator =
602 findNumbersForLeniency(test.rawString, test.region, leniency);
603 PhoneNumberMatch match = iterator.hasNext() ? iterator.next() : null;
606 System.err.println("No match found in " + test.toString() + " for leniency: " + leniency);
608 if (!test.rawString.equals(match.rawString())) {
609 wrongMatchFoundCount++;
610 System.err.println("Found wrong match in test " + test.toString() +
611 ". Found " + match.rawString());
615 assertEquals(0, noMatchFoundCount);
616 assertEquals(0, wrongMatchFoundCount);
619 private void doTestNumberNonMatchesForLeniency(List<NumberTest> testCases, Leniency leniency) {
620 int matchFoundCount = 0;
621 for (NumberTest test : testCases) {
622 Iterator<PhoneNumberMatch> iterator =
623 findNumbersForLeniency(test.rawString, test.region, leniency);
624 PhoneNumberMatch match = iterator.hasNext() ? iterator.next() : null;
627 System.err.println("Match found in " + test.toString() + " for leniency: " + leniency);
630 assertEquals(0, matchFoundCount);
634 * Helper method which tests the contexts provided and ensures that:
635 * -- if isValid is true, they all find a test number inserted in the middle when leniency of
636 * matching is set to VALID; else no test number should be extracted at that leniency level
637 * -- if isPossible is true, they all find a test number inserted in the middle when leniency of
638 * matching is set to POSSIBLE; else no test number should be extracted at that leniency level
640 private void findMatchesInContexts(List<NumberContext> contexts, boolean isValid,
641 boolean isPossible, String region, String number) {
643 doTestInContext(number, region, contexts, Leniency.VALID);
645 for (NumberContext context : contexts) {
646 String text = context.leadingText + number + context.trailingText;
647 assertTrue("Should not have found a number in " + text,
648 hasNoMatches(phoneUtil.findNumbers(text, region)));
652 doTestInContext(number, region, contexts, Leniency.POSSIBLE);
654 for (NumberContext context : contexts) {
655 String text = context.leadingText + number + context.trailingText;
656 assertTrue("Should not have found a number in " + text,
657 hasNoMatches(phoneUtil.findNumbers(text, region, Leniency.POSSIBLE,
664 * Variant of findMatchesInContexts that uses a default number and region.
666 private void findMatchesInContexts(List<NumberContext> contexts, boolean isValid,
667 boolean isPossible) {
668 String region = RegionCode.US;
669 String number = "415-666-7777";
671 findMatchesInContexts(contexts, isValid, isPossible, region, number);
674 public void testNonMatchingBracketsAreInvalid() throws Exception {
675 // The digits up to the ", " form a valid US number, but it shouldn't be matched as one since
676 // there was a non-matching bracket present.
677 assertTrue(hasNoMatches(phoneUtil.findNumbers(
678 "80.585 [79.964, 81.191]", RegionCode.US)));
680 // The trailing "]" is thrown away before parsing, so the resultant number, while a valid US
681 // number, does not have matching brackets.
682 assertTrue(hasNoMatches(phoneUtil.findNumbers(
683 "80.585 [79.964]", RegionCode.US)));
685 assertTrue(hasNoMatches(phoneUtil.findNumbers(
686 "80.585 ((79.964)", RegionCode.US)));
688 // This case has too many sets of brackets to be valid.
689 assertTrue(hasNoMatches(phoneUtil.findNumbers(
690 "(80).(585) (79).(9)64", RegionCode.US)));
693 public void testNoMatchIfRegionIsNull() throws Exception {
694 // Fail on non-international prefix if region code is null.
695 assertTrue(hasNoMatches(phoneUtil.findNumbers(
696 "Random text body - number is 0331 6005, see you there", null)));
699 public void testNoMatchInEmptyString() throws Exception {
700 assertTrue(hasNoMatches(phoneUtil.findNumbers("", RegionCode.US)));
701 assertTrue(hasNoMatches(phoneUtil.findNumbers(" ", RegionCode.US)));
704 public void testNoMatchIfNoNumber() throws Exception {
705 assertTrue(hasNoMatches(phoneUtil.findNumbers(
706 "Random text body - number is foobar, see you there", RegionCode.US)));
709 public void testSequences() throws Exception {
710 // Test multiple occurrences.
711 String text = "Call 033316005 or 032316005!";
712 String region = RegionCode.NZ;
714 PhoneNumber number1 = new PhoneNumber();
715 number1.setCountryCode(phoneUtil.getCountryCodeForRegion(region));
716 number1.setNationalNumber(33316005);
717 PhoneNumberMatch match1 = new PhoneNumberMatch(5, "033316005", number1);
719 PhoneNumber number2 = new PhoneNumber();
720 number2.setCountryCode(phoneUtil.getCountryCodeForRegion(region));
721 number2.setNationalNumber(32316005);
722 PhoneNumberMatch match2 = new PhoneNumberMatch(19, "032316005", number2);
724 Iterator<PhoneNumberMatch> matches =
725 phoneUtil.findNumbers(text, region, Leniency.POSSIBLE, Long.MAX_VALUE).iterator();
727 assertEquals(match1, matches.next());
728 assertEquals(match2, matches.next());
731 public void testNullInput() throws Exception {
732 assertTrue(hasNoMatches(phoneUtil.findNumbers(null, RegionCode.US)));
733 assertTrue(hasNoMatches(phoneUtil.findNumbers(null, null)));
736 public void testMaxMatches() throws Exception {
737 // Set up text with 100 valid phone numbers.
738 StringBuilder numbers = new StringBuilder();
739 for (int i = 0; i < 100; i++) {
740 numbers.append("My info: 415-666-7777,");
743 // Matches all 100. Max only applies to failed cases.
744 List<PhoneNumber> expected = new ArrayList<PhoneNumber>(100);
745 PhoneNumber number = phoneUtil.parse("+14156667777", null);
746 for (int i = 0; i < 100; i++) {
747 expected.add(number);
750 Iterable<PhoneNumberMatch> iterable =
751 phoneUtil.findNumbers(numbers.toString(), RegionCode.US, Leniency.VALID, 10);
752 List<PhoneNumber> actual = new ArrayList<PhoneNumber>(100);
753 for (PhoneNumberMatch match : iterable) {
754 actual.add(match.number());
756 assertEquals(expected, actual);
759 public void testMaxMatchesInvalid() throws Exception {
760 // Set up text with 10 invalid phone numbers followed by 100 valid.
761 StringBuilder numbers = new StringBuilder();
762 for (int i = 0; i < 10; i++) {
763 numbers.append("My address 949-8945-0");
765 for (int i = 0; i < 100; i++) {
766 numbers.append("My info: 415-666-7777,");
769 Iterable<PhoneNumberMatch> iterable =
770 phoneUtil.findNumbers(numbers.toString(), RegionCode.US, Leniency.VALID, 10);
771 assertFalse(iterable.iterator().hasNext());
774 public void testMaxMatchesMixed() throws Exception {
775 // Set up text with 100 valid numbers inside an invalid number.
776 StringBuilder numbers = new StringBuilder();
777 for (int i = 0; i < 100; i++) {
778 numbers.append("My info: 415-666-7777 123 fake street");
781 // Only matches the first 10 despite there being 100 numbers due to max matches.
782 List<PhoneNumber> expected = new ArrayList<PhoneNumber>(100);
783 PhoneNumber number = phoneUtil.parse("+14156667777", null);
784 for (int i = 0; i < 10; i++) {
785 expected.add(number);
788 Iterable<PhoneNumberMatch> iterable =
789 phoneUtil.findNumbers(numbers.toString(), RegionCode.US, Leniency.VALID, 10);
790 List<PhoneNumber> actual = new ArrayList<PhoneNumber>(100);
791 for (PhoneNumberMatch match : iterable) {
792 actual.add(match.number());
794 assertEquals(expected, actual);
797 public void testNonPlusPrefixedNumbersNotFoundForInvalidRegion() throws Exception {
798 // Does not start with a "+", we won't match it.
799 Iterable<PhoneNumberMatch> iterable = phoneUtil.findNumbers("1 456 764 156", RegionCode.ZZ);
800 Iterator<PhoneNumberMatch> iterator = iterable.iterator();
802 assertFalse(iterator.hasNext());
805 fail("Violation of the Iterator contract.");
806 } catch (NoSuchElementException e) { /* Success */ }
807 assertFalse(iterator.hasNext());
810 public void testEmptyIteration() throws Exception {
811 Iterable<PhoneNumberMatch> iterable = phoneUtil.findNumbers("", RegionCode.ZZ);
812 Iterator<PhoneNumberMatch> iterator = iterable.iterator();
814 assertFalse(iterator.hasNext());
815 assertFalse(iterator.hasNext());
818 fail("Violation of the Iterator contract.");
819 } catch (NoSuchElementException e) { /* Success */ }
820 assertFalse(iterator.hasNext());
823 public void testSingleIteration() throws Exception {
824 Iterable<PhoneNumberMatch> iterable = phoneUtil.findNumbers("+14156667777", RegionCode.ZZ);
826 // With hasNext() -> next().
827 Iterator<PhoneNumberMatch> iterator = iterable.iterator();
828 // Double hasNext() to ensure it does not advance.
829 assertTrue(iterator.hasNext());
830 assertTrue(iterator.hasNext());
831 assertNotNull(iterator.next());
832 assertFalse(iterator.hasNext());
835 fail("Violation of the Iterator contract.");
836 } catch (NoSuchElementException e) { /* Success */ }
837 assertFalse(iterator.hasNext());
840 iterator = iterable.iterator();
841 assertNotNull(iterator.next());
844 fail("Violation of the Iterator contract.");
845 } catch (NoSuchElementException e) { /* Success */ }
848 public void testDoubleIteration() throws Exception {
849 Iterable<PhoneNumberMatch> iterable =
850 phoneUtil.findNumbers("+14156667777 foobar +14156667777 ", RegionCode.ZZ);
852 // With hasNext() -> next().
853 Iterator<PhoneNumberMatch> iterator = iterable.iterator();
854 // Double hasNext() to ensure it does not advance.
855 assertTrue(iterator.hasNext());
856 assertTrue(iterator.hasNext());
857 assertNotNull(iterator.next());
858 assertTrue(iterator.hasNext());
859 assertTrue(iterator.hasNext());
860 assertNotNull(iterator.next());
861 assertFalse(iterator.hasNext());
864 fail("Violation of the Iterator contract.");
865 } catch (NoSuchElementException e) { /* Success */ }
866 assertFalse(iterator.hasNext());
869 iterator = iterable.iterator();
870 assertNotNull(iterator.next());
871 assertNotNull(iterator.next());
874 fail("Violation of the Iterator contract.");
875 } catch (NoSuchElementException e) { /* Success */ }
879 * Ensures that {@link Iterator#remove()} is not supported and that calling it does not
880 * change iteration behavior.
882 public void testRemovalNotSupported() throws Exception {
883 Iterable<PhoneNumberMatch> iterable = phoneUtil.findNumbers("+14156667777", RegionCode.ZZ);
885 Iterator<PhoneNumberMatch> iterator = iterable.iterator();
888 fail("Iterator must not support remove.");
889 } catch (UnsupportedOperationException e) { /* success */ }
891 assertTrue(iterator.hasNext());
895 fail("Iterator must not support remove.");
896 } catch (UnsupportedOperationException e) { /* success */ }
898 assertNotNull(iterator.next());
902 fail("Iterator must not support remove.");
903 } catch (UnsupportedOperationException e) { /* success */ }
905 assertFalse(iterator.hasNext());
909 * Asserts that the expected match is non-null, and that the raw string and expected
910 * proto buffer are set appropriately.
912 private void assertMatchProperties(
913 PhoneNumberMatch match, String text, String number, String region) throws Exception {
914 PhoneNumber expectedResult = phoneUtil.parse(number, region);
915 assertNotNull("Did not find a number in '" + text + "'; expected " + number, match);
916 assertEquals(expectedResult, match.number());
917 assertEquals(number, match.rawString());
921 * Asserts that another number can be found in {@code text} starting at {@code index}, and that
922 * its corresponding range is {@code [start, end)}.
924 private void assertEqualRange(CharSequence text, int index, int start, int end) {
925 CharSequence sub = text.subSequence(index, text.length());
926 Iterator<PhoneNumberMatch> matches =
927 phoneUtil.findNumbers(sub, RegionCode.NZ, Leniency.POSSIBLE, Long.MAX_VALUE).iterator();
928 assertTrue(matches.hasNext());
929 PhoneNumberMatch match = matches.next();
930 assertEquals(start - index, match.start());
931 assertEquals(end - index, match.end());
932 assertEquals(sub.subSequence(match.start(), match.end()).toString(), match.rawString());
936 * Tests numbers found by {@link PhoneNumberUtil#findNumbers(CharSequence, String)} in various
939 * @param number the number to test and the corresponding region code to use
941 private void doTestFindInContext(String number, String defaultCountry) throws Exception {
942 findPossibleInContext(number, defaultCountry);
944 PhoneNumber parsed = phoneUtil.parse(number, defaultCountry);
945 if (phoneUtil.isValidNumber(parsed)) {
946 findValidInContext(number, defaultCountry);
951 * Tests valid numbers in contexts that should pass for {@link Leniency#POSSIBLE}.
953 private void findPossibleInContext(String number, String defaultCountry) {
954 ArrayList<NumberContext> contextPairs = new ArrayList<NumberContext>();
955 contextPairs.add(new NumberContext("", "")); // no context
956 contextPairs.add(new NumberContext(" ", "\t")); // whitespace only
957 contextPairs.add(new NumberContext("Hello ", "")); // no context at end
958 contextPairs.add(new NumberContext("", " to call me!")); // no context at start
959 contextPairs.add(new NumberContext("Hi there, call ", " to reach me!")); // no context at start
960 contextPairs.add(new NumberContext("Hi there, call ", ", or don't")); // with commas
961 // Three examples without whitespace around the number.
962 contextPairs.add(new NumberContext("Hi call", ""));
963 contextPairs.add(new NumberContext("", "forme"));
964 contextPairs.add(new NumberContext("Hi call", "forme"));
965 // With other small numbers.
966 contextPairs.add(new NumberContext("It's cheap! Call ", " before 6:30"));
967 // With a second number later.
968 contextPairs.add(new NumberContext("Call ", " or +1800-123-4567!"));
969 contextPairs.add(new NumberContext("Call me on June 2 at", "")); // with a Month-Day date
970 // With publication pages.
971 contextPairs.add(new NumberContext(
972 "As quoted by Alfonso 12-15 (2009), you may call me at ", ""));
973 contextPairs.add(new NumberContext(
974 "As quoted by Alfonso et al. 12-15 (2009), you may call me at ", ""));
975 // With dates, written in the American style.
976 contextPairs.add(new NumberContext(
977 "As I said on 03/10/2011, you may call me at ", ""));
978 // With trailing numbers after a comma. The 45 should not be considered an extension.
979 contextPairs.add(new NumberContext("", ", 45 days a year"));
980 // With a postfix stripped off as it looks like the start of another number.
981 contextPairs.add(new NumberContext("Call ", "/x12 more"));
983 doTestInContext(number, defaultCountry, contextPairs, Leniency.POSSIBLE);
987 * Tests valid numbers in contexts that fail for {@link Leniency#POSSIBLE} but are valid for
988 * {@link Leniency#VALID}.
990 private void findValidInContext(String number, String defaultCountry) {
991 ArrayList<NumberContext> contextPairs = new ArrayList<NumberContext>();
992 // With other small numbers.
993 contextPairs.add(new NumberContext("It's only 9.99! Call ", " to buy"));
994 // With a number Day.Month.Year date.
995 contextPairs.add(new NumberContext("Call me on 21.6.1984 at ", ""));
996 // With a number Month/Day date.
997 contextPairs.add(new NumberContext("Call me on 06/21 at ", ""));
998 // With a number Day.Month date.
999 contextPairs.add(new NumberContext("Call me on 21.6. at ", ""));
1000 // With a number Month/Day/Year date.
1001 contextPairs.add(new NumberContext("Call me on 06/21/84 at ", ""));
1003 doTestInContext(number, defaultCountry, contextPairs, Leniency.VALID);
1006 private void doTestInContext(String number, String defaultCountry,
1007 List<NumberContext> contextPairs, Leniency leniency) {
1008 for (NumberContext context : contextPairs) {
1009 String prefix = context.leadingText;
1010 String text = prefix + number + context.trailingText;
1012 int start = prefix.length();
1013 int end = start + number.length();
1014 Iterator<PhoneNumberMatch> iterator =
1015 phoneUtil.findNumbers(text, defaultCountry, leniency, Long.MAX_VALUE).iterator();
1017 PhoneNumberMatch match = iterator.hasNext() ? iterator.next() : null;
1018 assertNotNull("Did not find a number in '" + text + "'; expected '" + number + "'", match);
1020 CharSequence extracted = text.subSequence(match.start(), match.end());
1021 assertTrue("Unexpected phone region in '" + text + "'; extracted '" + extracted + "'",
1022 start == match.start() && end == match.end());
1023 assertTrue(number.contentEquals(extracted));
1024 assertTrue(match.rawString().contentEquals(extracted));
1026 ensureTermination(text, defaultCountry, leniency);
1031 * Exhaustively searches for phone numbers from each index within {@code text} to test that
1032 * finding matches always terminates.
1034 private void ensureTermination(String text, String defaultCountry, Leniency leniency) {
1035 for (int index = 0; index <= text.length(); index++) {
1036 String sub = text.substring(index);
1037 StringBuilder matches = new StringBuilder();
1038 // Iterates over all matches.
1039 for (PhoneNumberMatch match :
1040 phoneUtil.findNumbers(sub, defaultCountry, leniency, Long.MAX_VALUE)) {
1041 matches.append(", ").append(match.toString());
1046 private Iterator<PhoneNumberMatch> findNumbersForLeniency(
1047 String text, String defaultCountry, Leniency leniency) {
1048 return phoneUtil.findNumbers(text, defaultCountry, leniency, Long.MAX_VALUE).iterator();
1051 private boolean hasNoMatches(Iterable<PhoneNumberMatch> iterable) {
1052 return !iterable.iterator().hasNext();
1056 * Small class that holds the context of the number we are testing against. The test will
1057 * insert the phone number to be found between leadingText and trailingText.
1059 private static class NumberContext {
1060 final String leadingText;
1061 final String trailingText;
1063 NumberContext(String leadingText, String trailingText) {
1064 this.leadingText = leadingText;
1065 this.trailingText = trailingText;
1070 * Small class that holds the number we want to test and the region for which it should be valid.
1072 private static class NumberTest {
1073 final String rawString;
1074 final String region;
1076 NumberTest(String rawString, String regionCode) {
1077 this.rawString = rawString;
1078 this.region = regionCode;
1082 public String toString() {
1083 return rawString + " (" + region.toString() + ")";