• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (C) 2011 The Libphonenumber Authors
3  *
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
7  *
8  *  http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.google.i18n.phonenumbers;
18 
19 import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
20 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
21 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
22 import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.util.Arrays;
26 import java.util.regex.PatternSyntaxException;
27 import javax.xml.parsers.DocumentBuilder;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.parsers.ParserConfigurationException;
30 import junit.framework.TestCase;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.xml.sax.InputSource;
34 import org.xml.sax.SAXException;
35 
36 /**
37  * Unit tests for BuildMetadataFromXml.java
38  *
39  * @author Philippe Liard
40  */
41 public class BuildMetadataFromXmlTest extends TestCase {
42 
43   // Helper method that outputs a DOM element from a XML string.
parseXmlString(String xmlString)44   private static Element parseXmlString(String xmlString)
45       throws ParserConfigurationException, SAXException, IOException {
46     DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
47     DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
48     InputSource inputSource = new InputSource();
49     inputSource.setCharacterStream(new StringReader(xmlString));
50     return documentBuilder.parse(inputSource).getDocumentElement();
51   }
52 
53   // Tests validateRE().
testValidateRERemovesWhiteSpaces()54   public void testValidateRERemovesWhiteSpaces() {
55     String input = " hello world ";
56     // Should remove all the white spaces contained in the provided string.
57     assertEquals("helloworld", BuildMetadataFromXml.validateRE(input, true));
58     // Make sure it only happens when the last parameter is set to true.
59     assertEquals(" hello world ", BuildMetadataFromXml.validateRE(input, false));
60   }
61 
testValidateREThrowsException()62   public void testValidateREThrowsException() {
63     String invalidPattern = "[";
64     // Should throw an exception when an invalid pattern is provided independently of the last
65     // parameter (remove white spaces).
66     try {
67       BuildMetadataFromXml.validateRE(invalidPattern, false);
68       fail();
69     } catch (PatternSyntaxException e) {
70       // Test passed.
71     }
72     try {
73       BuildMetadataFromXml.validateRE(invalidPattern, true);
74       fail();
75     } catch (PatternSyntaxException e) {
76       // Test passed.
77     }
78     // We don't allow | to be followed by ) because it introduces bugs, since we typically use it at
79     // the end of each line and when a line is deleted, if the pipe from the previous line is not
80     // removed, we end up erroneously accepting an empty group as well.
81     String patternWithPipeFollowedByClosingParentheses = "|)";
82     try {
83       BuildMetadataFromXml.validateRE(patternWithPipeFollowedByClosingParentheses, true);
84       fail();
85     } catch (PatternSyntaxException e) {
86       // Test passed.
87     }
88     String patternWithPipeFollowedByNewLineAndClosingParentheses = "|\n)";
89     try {
90       BuildMetadataFromXml.validateRE(patternWithPipeFollowedByNewLineAndClosingParentheses, true);
91       fail();
92     } catch (PatternSyntaxException e) {
93       // Test passed.
94     }
95   }
96 
testValidateRE()97   public void testValidateRE() {
98     String validPattern = "[a-zA-Z]d{1,9}";
99     // The provided pattern should be left unchanged.
100     assertEquals(validPattern, BuildMetadataFromXml.validateRE(validPattern, false));
101   }
102 
103   // Tests getNationalPrefix().
testGetNationalPrefix()104   public void testGetNationalPrefix()
105       throws ParserConfigurationException, SAXException, IOException {
106     String xmlInput = "<territory nationalPrefix='00'/>";
107     Element territoryElement = parseXmlString(xmlInput);
108     assertEquals("00", BuildMetadataFromXml.getNationalPrefix(territoryElement));
109   }
110 
111   // Tests loadTerritoryTagMetadata().
testLoadTerritoryTagMetadata()112   public void testLoadTerritoryTagMetadata()
113       throws ParserConfigurationException, SAXException, IOException {
114     String xmlInput = "<territory"
115         + "  countryCode='33' leadingDigits='2' internationalPrefix='00'"
116         + "  preferredInternationalPrefix='00~11' nationalPrefixForParsing='0'"
117         + "  nationalPrefixTransformRule='9$1'"  // nationalPrefix manually injected.
118         + "  preferredExtnPrefix=' x' mainCountryForCode='true'"
119         + "  leadingZeroPossible='true' mobileNumberPortableRegion='true'>"
120         + "</territory>";
121     Element territoryElement = parseXmlString(xmlInput);
122     PhoneMetadata.Builder phoneMetadata =
123         BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "0");
124     assertEquals(33, phoneMetadata.getCountryCode());
125     assertEquals("2", phoneMetadata.getLeadingDigits());
126     assertEquals("00", phoneMetadata.getInternationalPrefix());
127     assertEquals("00~11", phoneMetadata.getPreferredInternationalPrefix());
128     assertEquals("0", phoneMetadata.getNationalPrefixForParsing());
129     assertEquals("9$1", phoneMetadata.getNationalPrefixTransformRule());
130     assertEquals("0", phoneMetadata.getNationalPrefix());
131     assertEquals(" x", phoneMetadata.getPreferredExtnPrefix());
132     assertTrue(phoneMetadata.getMainCountryForCode());
133     assertTrue(phoneMetadata.isMobileNumberPortableRegion());
134   }
135 
testLoadTerritoryTagMetadataSetsBooleanFieldsToFalseByDefault()136   public void testLoadTerritoryTagMetadataSetsBooleanFieldsToFalseByDefault()
137       throws ParserConfigurationException, SAXException, IOException {
138     String xmlInput = "<territory countryCode='33'/>";
139     Element territoryElement = parseXmlString(xmlInput);
140     PhoneMetadata.Builder phoneMetadata =
141         BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "");
142     assertFalse(phoneMetadata.getMainCountryForCode());
143     assertFalse(phoneMetadata.isMobileNumberPortableRegion());
144   }
145 
testLoadTerritoryTagMetadataSetsNationalPrefixForParsingByDefault()146   public void testLoadTerritoryTagMetadataSetsNationalPrefixForParsingByDefault()
147       throws ParserConfigurationException, SAXException, IOException {
148     String xmlInput = "<territory countryCode='33'/>";
149     Element territoryElement = parseXmlString(xmlInput);
150     PhoneMetadata.Builder phoneMetadata =
151         BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "00");
152     // When unspecified, nationalPrefixForParsing defaults to nationalPrefix.
153     assertEquals("00", phoneMetadata.getNationalPrefix());
154     assertEquals(phoneMetadata.getNationalPrefix(), phoneMetadata.getNationalPrefixForParsing());
155   }
156 
testLoadTerritoryTagMetadataWithRequiredAttributesOnly()157   public void testLoadTerritoryTagMetadataWithRequiredAttributesOnly()
158       throws ParserConfigurationException, SAXException, IOException {
159     String xmlInput = "<territory countryCode='33' internationalPrefix='00'/>";
160     Element territoryElement = parseXmlString(xmlInput);
161     // Should not throw any exception.
162     BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "");
163   }
164 
165   // Tests loadInternationalFormat().
testLoadInternationalFormat()166   public void testLoadInternationalFormat()
167       throws ParserConfigurationException, SAXException, IOException {
168     String intlFormat = "$1 $2";
169     String xmlInput = "<numberFormat><intlFormat>" + intlFormat + "</intlFormat></numberFormat>";
170     Element numberFormatElement = parseXmlString(xmlInput);
171     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
172     NumberFormat nationalFormat = NumberFormat.newBuilder().build();
173 
174     assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
175                                                             nationalFormat));
176     assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat());
177   }
178 
testLoadInternationalFormatWithBothNationalAndIntlFormatsDefined()179   public void testLoadInternationalFormatWithBothNationalAndIntlFormatsDefined()
180       throws ParserConfigurationException, SAXException, IOException {
181     String intlFormat = "$1 $2";
182     String xmlInput = "<numberFormat><intlFormat>" + intlFormat + "</intlFormat></numberFormat>";
183     Element numberFormatElement = parseXmlString(xmlInput);
184     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
185     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
186     nationalFormat.setFormat("$1");
187 
188     assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
189                                                             nationalFormat.build()));
190     assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat());
191   }
192 
testLoadInternationalFormatExpectsOnlyOnePattern()193   public void testLoadInternationalFormatExpectsOnlyOnePattern()
194       throws ParserConfigurationException, SAXException, IOException {
195     String xmlInput = "<numberFormat><intlFormat/><intlFormat/></numberFormat>";
196     Element numberFormatElement = parseXmlString(xmlInput);
197     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
198 
199     // Should throw an exception as multiple intlFormats are provided.
200     try {
201       BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
202                                                    NumberFormat.newBuilder().build());
203       fail();
204     } catch (RuntimeException e) {
205       // Test passed.
206     }
207   }
208 
testLoadInternationalFormatUsesNationalFormatByDefault()209   public void testLoadInternationalFormatUsesNationalFormatByDefault()
210       throws ParserConfigurationException, SAXException, IOException {
211     String xmlInput = "<numberFormat></numberFormat>";
212     Element numberFormatElement = parseXmlString(xmlInput);
213     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
214     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
215     String nationalPattern = "$1 $2 $3";
216     nationalFormat.setFormat(nationalPattern);
217 
218     assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
219                                                              nationalFormat.build()));
220     assertEquals(nationalPattern, metadata.getIntlNumberFormat(0).getFormat());
221   }
222 
testLoadInternationalFormatCopiesNationalFormatData()223   public void testLoadInternationalFormatCopiesNationalFormatData()
224       throws ParserConfigurationException, SAXException, IOException {
225     String xmlInput = "<numberFormat></numberFormat>";
226     Element numberFormatElement = parseXmlString(xmlInput);
227     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
228     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
229     nationalFormat.setFormat("$1-$2");
230     nationalFormat.setNationalPrefixOptionalWhenFormatting(true);
231 
232     assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
233                                                              nationalFormat.build()));
234     assertTrue(metadata.getIntlNumberFormat(0).getNationalPrefixOptionalWhenFormatting());
235   }
236 
testLoadNationalFormat()237   public void testLoadNationalFormat()
238       throws ParserConfigurationException, SAXException, IOException {
239     String nationalFormat = "$1 $2";
240     String xmlInput = String.format("<numberFormat><format>%s</format></numberFormat>",
241                                     nationalFormat);
242     Element numberFormatElement = parseXmlString(xmlInput);
243     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
244     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
245     BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
246     assertEquals(nationalFormat, numberFormat.getFormat());
247   }
248 
testLoadNationalFormatRequiresFormat()249   public void testLoadNationalFormatRequiresFormat()
250       throws ParserConfigurationException, SAXException, IOException {
251     String xmlInput = "<numberFormat></numberFormat>";
252     Element numberFormatElement = parseXmlString(xmlInput);
253     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
254     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
255 
256     try {
257       BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
258       fail();
259     } catch (RuntimeException e) {
260       // Test passed.
261     }
262   }
263 
testLoadNationalFormatExpectsExactlyOneFormat()264   public void testLoadNationalFormatExpectsExactlyOneFormat()
265       throws ParserConfigurationException, SAXException, IOException {
266     String xmlInput = "<numberFormat><format/><format/></numberFormat>";
267     Element numberFormatElement = parseXmlString(xmlInput);
268     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
269     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
270 
271     try {
272       BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
273       fail();
274     } catch (RuntimeException e) {
275       // Test passed.
276     }
277   }
278 
279   // Tests loadAvailableFormats().
testLoadAvailableFormats()280   public void testLoadAvailableFormats()
281       throws ParserConfigurationException, SAXException, IOException {
282     String xmlInput = "<territory>"
283         + "  <availableFormats>"
284         + "    <numberFormat nationalPrefixFormattingRule='($FG)'"
285         + "                  carrierCodeFormattingRule='$NP $CC ($FG)'>"
286         + "      <format>$1 $2 $3</format>"
287         + "    </numberFormat>"
288         + "  </availableFormats>"
289         + "</territory>";
290     Element element = parseXmlString(xmlInput);
291     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
292     BuildMetadataFromXml.loadAvailableFormats(
293         metadata, element, "0", "", false /* NP not optional */);
294     assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
295     assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule());
296     assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
297   }
298 
testLoadAvailableFormatsPropagatesCarrierCodeFormattingRule()299   public void testLoadAvailableFormatsPropagatesCarrierCodeFormattingRule()
300       throws ParserConfigurationException, SAXException, IOException {
301     String xmlInput =
302         "<territory carrierCodeFormattingRule='$NP $CC ($FG)'>"
303         + "  <availableFormats>"
304         + "    <numberFormat nationalPrefixFormattingRule='($FG)'>"
305         + "      <format>$1 $2 $3</format>"
306         + "    </numberFormat>"
307         + "  </availableFormats>"
308         + "</territory>";
309     Element element = parseXmlString(xmlInput);
310     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
311     BuildMetadataFromXml.loadAvailableFormats(
312         metadata, element, "0", "", false /* NP not optional */);
313     assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
314     assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule());
315     assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
316   }
317 
testLoadAvailableFormatsSetsProvidedNationalPrefixFormattingRule()318   public void testLoadAvailableFormatsSetsProvidedNationalPrefixFormattingRule()
319       throws ParserConfigurationException, SAXException, IOException {
320     String xmlInput = "<territory>"
321         + "  <availableFormats>"
322         + "    <numberFormat><format>$1 $2 $3</format></numberFormat>"
323         + "  </availableFormats>"
324         + "</territory>";
325     Element element = parseXmlString(xmlInput);
326     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
327     BuildMetadataFromXml.loadAvailableFormats(
328         metadata, element, "", "($1)", false /* NP not optional */);
329     assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
330   }
331 
testLoadAvailableFormatsClearsIntlFormat()332   public void testLoadAvailableFormatsClearsIntlFormat()
333       throws ParserConfigurationException, SAXException, IOException {
334     String xmlInput = "<territory>"
335         + "  <availableFormats>"
336         + "    <numberFormat><format>$1 $2 $3</format></numberFormat>"
337         + "  </availableFormats>"
338         + "</territory>";
339     Element element = parseXmlString(xmlInput);
340     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
341     BuildMetadataFromXml.loadAvailableFormats(
342         metadata, element, "0", "($1)", false /* NP not optional */);
343     assertEquals(0, metadata.getIntlNumberFormatCount());
344   }
345 
testLoadAvailableFormatsHandlesMultipleNumberFormats()346   public void testLoadAvailableFormatsHandlesMultipleNumberFormats()
347       throws ParserConfigurationException, SAXException, IOException {
348     String xmlInput = "<territory>"
349         + "  <availableFormats>"
350         + "    <numberFormat><format>$1 $2 $3</format></numberFormat>"
351         + "    <numberFormat><format>$1-$2</format></numberFormat>"
352         + "  </availableFormats>"
353         + "</territory>";
354     Element element = parseXmlString(xmlInput);
355     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
356     BuildMetadataFromXml.loadAvailableFormats(
357         metadata, element, "0", "($1)", false /* NP not optional */);
358     assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
359     assertEquals("$1-$2", metadata.getNumberFormat(1).getFormat());
360   }
361 
testLoadInternationalFormatDoesNotSetIntlFormatWhenNA()362   public void testLoadInternationalFormatDoesNotSetIntlFormatWhenNA()
363       throws ParserConfigurationException, SAXException, IOException {
364     String xmlInput = "<numberFormat><intlFormat>NA</intlFormat></numberFormat>";
365     Element numberFormatElement = parseXmlString(xmlInput);
366     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
367     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
368     nationalFormat.setFormat("$1 $2");
369 
370     BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
371                                                  nationalFormat.build());
372     assertEquals(0, metadata.getIntlNumberFormatCount());
373   }
374 
375   // Tests setLeadingDigitsPatterns().
testSetLeadingDigitsPatterns()376   public void testSetLeadingDigitsPatterns()
377       throws ParserConfigurationException, SAXException, IOException {
378     String xmlInput = "<numberFormat>"
379         + "<leadingDigits>1</leadingDigits><leadingDigits>2</leadingDigits>"
380         + "</numberFormat>";
381     Element numberFormatElement = parseXmlString(xmlInput);
382     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
383     BuildMetadataFromXml.setLeadingDigitsPatterns(numberFormatElement, numberFormat);
384 
385     assertEquals("1", numberFormat.getLeadingDigitsPattern(0));
386     assertEquals("2", numberFormat.getLeadingDigitsPattern(1));
387   }
388 
389   // Tests setLeadingDigitsPatterns() in the case of international and national formatting rules
390   // being present but not both defined for this numberFormat - we don't want to add them twice.
testSetLeadingDigitsPatternsNotAddedTwiceWhenInternationalFormatsPresent()391   public void testSetLeadingDigitsPatternsNotAddedTwiceWhenInternationalFormatsPresent()
392       throws ParserConfigurationException, SAXException, IOException {
393     String xmlInput = "<availableFormats>"
394         + "  <numberFormat pattern=\"(1)(\\d{3})\">"
395         + "    <leadingDigits>1</leadingDigits>"
396         + "    <format>$1</format>"
397         + "  </numberFormat>"
398         + "  <numberFormat pattern=\"(2)(\\d{3})\">"
399         + "    <leadingDigits>2</leadingDigits>"
400         + "    <format>$1</format>"
401         + "    <intlFormat>9-$1</intlFormat>"
402         + "  </numberFormat>"
403         + "</availableFormats>";
404     Element element = parseXmlString(xmlInput);
405     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
406     BuildMetadataFromXml.loadAvailableFormats(
407         metadata, element, "0", "", false /* NP not optional */);
408     assertEquals(1, metadata.getNumberFormat(0).leadingDigitsPatternSize());
409     assertEquals(1, metadata.getNumberFormat(1).leadingDigitsPatternSize());
410     // When we merge the national format rules into the international format rules, we shouldn't add
411     // the leading digit patterns multiple times.
412     assertEquals(1, metadata.getIntlNumberFormat(0).leadingDigitsPatternSize());
413     assertEquals(1, metadata.getIntlNumberFormat(1).leadingDigitsPatternSize());
414   }
415 
416   // Tests getNationalPrefixFormattingRuleFromElement().
testGetNationalPrefixFormattingRuleFromElement()417   public void testGetNationalPrefixFormattingRuleFromElement()
418       throws ParserConfigurationException, SAXException, IOException {
419     String xmlInput = "<territory nationalPrefixFormattingRule='$NP$FG'/>";
420     Element element = parseXmlString(xmlInput);
421     assertEquals("0$1",
422                  BuildMetadataFromXml.getNationalPrefixFormattingRuleFromElement(element, "0"));
423   }
424 
425   // Tests getDomesticCarrierCodeFormattingRuleFromElement().
testGetDomesticCarrierCodeFormattingRuleFromElement()426   public void testGetDomesticCarrierCodeFormattingRuleFromElement()
427       throws ParserConfigurationException, SAXException, IOException {
428     String xmlInput = "<territory carrierCodeFormattingRule='$NP$CC $FG'/>";
429     Element element = parseXmlString(xmlInput);
430     assertEquals("0$CC $1",
431                  BuildMetadataFromXml.getDomesticCarrierCodeFormattingRuleFromElement(element,
432                                                                                       "0"));
433   }
434 
435   // Tests processPhoneNumberDescElement().
testProcessPhoneNumberDescElementWithInvalidInput()436   public void testProcessPhoneNumberDescElementWithInvalidInput()
437       throws ParserConfigurationException, SAXException, IOException {
438     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
439     Element territoryElement = parseXmlString("<territory/>");
440     PhoneNumberDesc.Builder phoneNumberDesc;
441 
442     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
443         generalDesc, territoryElement, "invalidType");
444     assertFalse(phoneNumberDesc.hasNationalNumberPattern());
445   }
446 
testProcessPhoneNumberDescElementOverridesGeneralDesc()447   public void testProcessPhoneNumberDescElementOverridesGeneralDesc()
448       throws ParserConfigurationException, SAXException, IOException {
449     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
450     generalDesc.setNationalNumberPattern("\\d{8}");
451     String xmlInput = "<territory><fixedLine>"
452         + "  <nationalNumberPattern>\\d{6}</nationalNumberPattern>"
453         + "</fixedLine></territory>";
454     Element territoryElement = parseXmlString(xmlInput);
455     PhoneNumberDesc.Builder phoneNumberDesc;
456 
457     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
458         generalDesc, territoryElement, "fixedLine");
459     assertEquals("\\d{6}", phoneNumberDesc.getNationalNumberPattern());
460   }
461 
testBuildPhoneMetadataCollection_liteBuild()462   public void testBuildPhoneMetadataCollection_liteBuild() throws Exception {
463     String xmlInput =
464         "<phoneNumberMetadata>"
465         + "  <territories>"
466         + "    <territory id=\"AM\" countryCode=\"374\" internationalPrefix=\"00\">"
467         + "      <generalDesc>"
468         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
469         + "      </generalDesc>"
470         + "      <fixedLine>"
471         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
472         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
473         + "        <exampleNumber>10123456</exampleNumber>"
474         + "      </fixedLine>"
475         + "      <mobile>"
476         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
477         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
478         + "        <exampleNumber>10123456</exampleNumber>"
479         + "      </mobile>"
480         + "    </territory>"
481         + "  </territories>"
482         + "</phoneNumberMetadata>";
483     Document document = parseXmlString(xmlInput).getOwnerDocument();
484 
485     PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection(
486         document,
487         true,  // liteBuild
488         false,  // specialBuild
489         false,  // isShortNumberMetadata
490         false);  // isAlternateFormatsMetadata
491 
492     assertTrue(metadataCollection.getMetadataCount() == 1);
493     PhoneMetadata metadata = metadataCollection.getMetadataList().get(0);
494     assertTrue(metadata.hasGeneralDesc());
495     assertFalse(metadata.getGeneralDesc().hasExampleNumber());
496     // Some Phonemetadata.java implementations may have custom logic, so we ensure this
497     // implementation is doing the right thing by checking the value of the example number even when
498     // hasExampleNumber is false.
499     assertEquals("", metadata.getGeneralDesc().getExampleNumber());
500     assertTrue(metadata.hasFixedLine());
501     assertFalse(metadata.getFixedLine().hasExampleNumber());
502     assertEquals("", metadata.getFixedLine().getExampleNumber());
503     assertTrue(metadata.hasMobile());
504     assertFalse(metadata.getMobile().hasExampleNumber());
505     assertEquals("", metadata.getMobile().getExampleNumber());
506   }
507 
testBuildPhoneMetadataCollection_specialBuild()508   public void testBuildPhoneMetadataCollection_specialBuild() throws Exception {
509     String xmlInput =
510         "<phoneNumberMetadata>"
511         + "  <territories>"
512         + "    <territory id=\"AM\" countryCode=\"374\" internationalPrefix=\"00\">"
513         + "      <generalDesc>"
514         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
515         + "      </generalDesc>"
516         + "      <fixedLine>"
517         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
518         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
519         + "        <exampleNumber>10123456</exampleNumber>"
520         + "      </fixedLine>"
521         + "      <mobile>"
522         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
523         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
524         + "        <exampleNumber>10123456</exampleNumber>"
525         + "      </mobile>"
526         + "    </territory>"
527         + "  </territories>"
528         + "</phoneNumberMetadata>";
529     Document document = parseXmlString(xmlInput).getOwnerDocument();
530 
531     PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection(
532         document,
533         false,  // liteBuild
534         true,  // specialBuild
535         false,  // isShortNumberMetadata
536         false);  // isAlternateFormatsMetadata
537 
538     assertTrue(metadataCollection.getMetadataCount() == 1);
539     PhoneMetadata metadata = metadataCollection.getMetadataList().get(0);
540     assertTrue(metadata.hasGeneralDesc());
541     assertFalse(metadata.getGeneralDesc().hasExampleNumber());
542     // Some Phonemetadata.java implementations may have custom logic, so we ensure this
543     // implementation is doing the right thing by checking the value of the example number even when
544     // hasExampleNumber is false.
545     assertEquals("", metadata.getGeneralDesc().getExampleNumber());
546     // TODO: Consider clearing fixed-line if empty after being filtered.
547     assertTrue(metadata.hasFixedLine());
548     assertFalse(metadata.getFixedLine().hasExampleNumber());
549     assertEquals("", metadata.getFixedLine().getExampleNumber());
550     assertTrue(metadata.hasMobile());
551     assertTrue(metadata.getMobile().hasExampleNumber());
552     assertEquals("10123456", metadata.getMobile().getExampleNumber());
553   }
554 
testBuildPhoneMetadataCollection_fullBuild()555   public void testBuildPhoneMetadataCollection_fullBuild() throws Exception {
556     String xmlInput =
557         "<phoneNumberMetadata>"
558         + "  <territories>"
559         + "    <territory id=\"AM\" countryCode=\"374\" internationalPrefix=\"00\">"
560         + "      <generalDesc>"
561         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
562         + "      </generalDesc>"
563         + "      <fixedLine>"
564         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
565         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
566         + "        <exampleNumber>10123456</exampleNumber>"
567         + "      </fixedLine>"
568         + "      <mobile>"
569         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
570         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
571         + "        <exampleNumber>10123456</exampleNumber>"
572         + "      </mobile>"
573         + "    </territory>"
574         + "  </territories>"
575         + "</phoneNumberMetadata>";
576     Document document = parseXmlString(xmlInput).getOwnerDocument();
577 
578     PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection(
579         document,
580         false,  // liteBuild
581         false,  // specialBuild
582         false,  // isShortNumberMetadata
583         false);  // isAlternateFormatsMetadata
584 
585     assertTrue(metadataCollection.getMetadataCount() == 1);
586     PhoneMetadata metadata = metadataCollection.getMetadataList().get(0);
587     assertTrue(metadata.hasGeneralDesc());
588     assertFalse(metadata.getGeneralDesc().hasExampleNumber());
589     // Some Phonemetadata.java implementations may have custom logic, so we ensure this
590     // implementation is doing the right thing by checking the value of the example number even when
591     // hasExampleNumber is false.
592     assertEquals("", metadata.getGeneralDesc().getExampleNumber());
593     assertTrue(metadata.hasFixedLine());
594     assertTrue(metadata.getFixedLine().hasExampleNumber());
595     assertEquals("10123456", metadata.getFixedLine().getExampleNumber());
596     assertTrue(metadata.hasMobile());
597     assertTrue(metadata.getMobile().hasExampleNumber());
598     assertEquals("10123456", metadata.getMobile().getExampleNumber());
599   }
600 
testProcessPhoneNumberDescOutputsExampleNumberByDefault()601   public void testProcessPhoneNumberDescOutputsExampleNumberByDefault()
602       throws ParserConfigurationException, SAXException, IOException {
603     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
604     String xmlInput = "<territory><fixedLine>"
605         + "  <exampleNumber>01 01 01 01</exampleNumber>"
606         + "</fixedLine></territory>";
607     Element territoryElement = parseXmlString(xmlInput);
608     PhoneNumberDesc.Builder phoneNumberDesc;
609 
610     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
611         generalDesc, territoryElement, "fixedLine");
612     assertEquals("01 01 01 01", phoneNumberDesc.getExampleNumber());
613   }
614 
testProcessPhoneNumberDescRemovesWhiteSpacesInPatterns()615   public void testProcessPhoneNumberDescRemovesWhiteSpacesInPatterns()
616       throws ParserConfigurationException, SAXException, IOException {
617     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
618     String xmlInput = "<territory><fixedLine>"
619         + "  <nationalNumberPattern>\t \\d { 6 } </nationalNumberPattern>"
620         + "</fixedLine></territory>";
621     Element countryElement = parseXmlString(xmlInput);
622     PhoneNumberDesc.Builder phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
623         generalDesc, countryElement, "fixedLine");
624     assertEquals("\\d{6}", phoneNumberDesc.getNationalNumberPattern());
625   }
626 
627   // Tests setRelevantDescPatterns().
testSetRelevantDescPatternsSetsSameMobileAndFixedLinePattern()628   public void testSetRelevantDescPatternsSetsSameMobileAndFixedLinePattern()
629       throws ParserConfigurationException, SAXException, IOException {
630     String xmlInput = "<territory countryCode=\"33\">"
631         + "  <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>"
632         + "  <mobile><nationalNumberPattern>\\d{6}</nationalNumberPattern></mobile>"
633         + "</territory>";
634     Element territoryElement = parseXmlString(xmlInput);
635     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
636     // Should set sameMobileAndFixedPattern to true.
637     BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
638         false /* isShortNumberMetadata */);
639     assertTrue(metadata.getSameMobileAndFixedLinePattern());
640   }
641 
testSetRelevantDescPatternsSetsAllDescriptionsForRegularLengthNumbers()642   public void testSetRelevantDescPatternsSetsAllDescriptionsForRegularLengthNumbers()
643       throws ParserConfigurationException, SAXException, IOException {
644     String xmlInput = "<territory countryCode=\"33\">"
645         + "  <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>"
646         + "  <mobile><nationalNumberPattern>\\d{2}</nationalNumberPattern></mobile>"
647         + "  <pager><nationalNumberPattern>\\d{3}</nationalNumberPattern></pager>"
648         + "  <tollFree><nationalNumberPattern>\\d{4}</nationalNumberPattern></tollFree>"
649         + "  <premiumRate><nationalNumberPattern>\\d{5}</nationalNumberPattern></premiumRate>"
650         + "  <sharedCost><nationalNumberPattern>\\d{6}</nationalNumberPattern></sharedCost>"
651         + "  <personalNumber><nationalNumberPattern>\\d{7}</nationalNumberPattern></personalNumber>"
652         + "  <voip><nationalNumberPattern>\\d{8}</nationalNumberPattern></voip>"
653         + "  <uan><nationalNumberPattern>\\d{9}</nationalNumberPattern></uan>"
654         + "</territory>";
655     Element territoryElement = parseXmlString(xmlInput);
656     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
657     BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
658         false /* isShortNumberMetadata */);
659     assertEquals("\\d{1}", metadata.getFixedLine().getNationalNumberPattern());
660     assertEquals("\\d{2}", metadata.getMobile().getNationalNumberPattern());
661     assertEquals("\\d{3}", metadata.getPager().getNationalNumberPattern());
662     assertEquals("\\d{4}", metadata.getTollFree().getNationalNumberPattern());
663     assertEquals("\\d{5}", metadata.getPremiumRate().getNationalNumberPattern());
664     assertEquals("\\d{6}", metadata.getSharedCost().getNationalNumberPattern());
665     assertEquals("\\d{7}", metadata.getPersonalNumber().getNationalNumberPattern());
666     assertEquals("\\d{8}", metadata.getVoip().getNationalNumberPattern());
667     assertEquals("\\d{9}", metadata.getUan().getNationalNumberPattern());
668   }
669 
testSetRelevantDescPatternsSetsAllDescriptionsForShortNumbers()670   public void testSetRelevantDescPatternsSetsAllDescriptionsForShortNumbers()
671       throws ParserConfigurationException, SAXException, IOException {
672     String xmlInput = "<territory ID=\"FR\">"
673         + "  <tollFree><nationalNumberPattern>\\d{1}</nationalNumberPattern></tollFree>"
674         + "  <standardRate><nationalNumberPattern>\\d{2}</nationalNumberPattern></standardRate>"
675         + "  <premiumRate><nationalNumberPattern>\\d{3}</nationalNumberPattern></premiumRate>"
676         + "  <shortCode><nationalNumberPattern>\\d{4}</nationalNumberPattern></shortCode>"
677         + "  <carrierSpecific>"
678         + "    <nationalNumberPattern>\\d{5}</nationalNumberPattern>"
679         + "  </carrierSpecific>"
680         + "  <smsServices>"
681         + "    <nationalNumberPattern>\\d{6}</nationalNumberPattern>"
682         + "  </smsServices>"
683         + "</territory>";
684     Element territoryElement = parseXmlString(xmlInput);
685     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
686     BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
687         true /* isShortNumberMetadata */);
688     assertEquals("\\d{1}", metadata.getTollFree().getNationalNumberPattern());
689     assertEquals("\\d{2}", metadata.getStandardRate().getNationalNumberPattern());
690     assertEquals("\\d{3}", metadata.getPremiumRate().getNationalNumberPattern());
691     assertEquals("\\d{4}", metadata.getShortCode().getNationalNumberPattern());
692     assertEquals("\\d{5}", metadata.getCarrierSpecific().getNationalNumberPattern());
693     assertEquals("\\d{6}", metadata.getSmsServices().getNationalNumberPattern());
694   }
695 
testSetRelevantDescPatternsThrowsErrorIfTypePresentMultipleTimes()696   public void testSetRelevantDescPatternsThrowsErrorIfTypePresentMultipleTimes()
697       throws ParserConfigurationException, SAXException, IOException {
698     String xmlInput = "<territory countryCode=\"33\">"
699         + "  <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>"
700         + "  <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>"
701         + "</territory>";
702     Element territoryElement = parseXmlString(xmlInput);
703     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
704     try {
705       BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
706           false /* isShortNumberMetadata */);
707       fail("Fixed-line info present twice for France: we should fail.");
708     } catch (RuntimeException expected) {
709       assertEquals("Multiple elements with type fixedLine found.", expected.getMessage());
710     }
711   }
712 
testAlternateFormatsOmitsDescPatterns()713   public void testAlternateFormatsOmitsDescPatterns()
714       throws ParserConfigurationException, SAXException, IOException {
715     String xmlInput = "<territory countryCode=\"33\">"
716         + "  <availableFormats>"
717         + "    <numberFormat pattern=\"(1)(\\d{3})\">"
718         + "      <leadingDigits>1</leadingDigits>"
719         + "      <format>$1</format>"
720         + "    </numberFormat>"
721         + "  </availableFormats>"
722         + "  <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>"
723         + "  <shortCode><nationalNumberPattern>\\d{2}</nationalNumberPattern></shortCode>"
724         + "</territory>";
725     Element territoryElement = parseXmlString(xmlInput);
726     PhoneMetadata metadata = BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement,
727         false /* isShortNumberMetadata */, true /* isAlternateFormatsMetadata */).build();
728     assertEquals("(1)(\\d{3})", metadata.getNumberFormat(0).getPattern());
729     assertEquals("1", metadata.getNumberFormat(0).getLeadingDigitsPattern(0));
730     assertEquals("$1", metadata.getNumberFormat(0).getFormat());
731     assertFalse(metadata.hasFixedLine());
732     assertNull(metadata.getFixedLine());
733     assertFalse(metadata.hasShortCode());
734     assertNull(metadata.getShortCode());
735   }
736 
testNationalPrefixRulesSetCorrectly()737   public void testNationalPrefixRulesSetCorrectly()
738       throws ParserConfigurationException, SAXException, IOException {
739     String xmlInput = "<territory countryCode=\"33\" nationalPrefix=\"0\""
740         + " nationalPrefixFormattingRule=\"$NP$FG\">"
741         + "  <availableFormats>"
742         + "    <numberFormat pattern=\"(1)(\\d{3})\" nationalPrefixOptionalWhenFormatting=\"true\">"
743         + "      <leadingDigits>1</leadingDigits>"
744         + "      <format>$1</format>"
745         + "    </numberFormat>"
746         + "    <numberFormat pattern=\"(\\d{3})\" nationalPrefixOptionalWhenFormatting=\"false\">"
747         + "      <leadingDigits>2</leadingDigits>"
748         + "      <format>$1</format>"
749         + "    </numberFormat>"
750         + "  </availableFormats>"
751         + "  <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>"
752         + "</territory>";
753     Element territoryElement = parseXmlString(xmlInput);
754     PhoneMetadata metadata = BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement,
755         false /* isShortNumberMetadata */, true /* isAlternateFormatsMetadata */).build();
756     assertTrue(metadata.getNumberFormat(0).getNationalPrefixOptionalWhenFormatting());
757     // This is inherited from the territory, with $NP replaced by the actual national prefix, and
758     // $FG replaced with $1.
759     assertEquals("0$1", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
760     // Here it is explicitly set to false.
761     assertFalse(metadata.getNumberFormat(1).getNationalPrefixOptionalWhenFormatting());
762   }
763 
testProcessPhoneNumberDescElement_PossibleLengthsSetCorrectly()764   public void testProcessPhoneNumberDescElement_PossibleLengthsSetCorrectly() throws Exception {
765     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
766     // The number lengths set for the general description must be a super-set of those in the
767     // element being parsed.
768     generalDesc.addPossibleLength(4);
769     generalDesc.addPossibleLength(6);
770     generalDesc.addPossibleLength(7);
771     generalDesc.addPossibleLength(13);
772     Element territoryElement = parseXmlString("<territory>"
773         + "<fixedLine>"
774         // Sorting will be done when parsing.
775         + "  <possibleLengths national=\"13,4\" localOnly=\"6\"/>"
776         + "</fixedLine>"
777         + "</territory>");
778 
779     PhoneNumberDesc.Builder fixedLine;
780     PhoneNumberDesc.Builder mobile;
781 
782     fixedLine = BuildMetadataFromXml.processPhoneNumberDescElement(
783         generalDesc, territoryElement, "fixedLine");
784     mobile = BuildMetadataFromXml.processPhoneNumberDescElement(
785         generalDesc, territoryElement, "mobile");
786 
787     assertEquals(2, fixedLine.getPossibleLengthCount());
788     assertEquals(4, fixedLine.getPossibleLength(0));
789     assertEquals(13, fixedLine.getPossibleLength(1));
790     assertEquals(1, fixedLine.getPossibleLengthLocalOnlyCount());
791 
792     // We use [-1] to denote that there are no possible lengths; we don't leave it empty, since for
793     // compression reasons, we use the empty list to mean that the generalDesc possible lengths
794     // apply.
795     assertEquals(1, mobile.getPossibleLengthCount());
796     assertEquals(-1, mobile.getPossibleLength(0));
797     assertEquals(0, mobile.getPossibleLengthLocalOnlyCount());
798   }
799 
testSetPossibleLengthsGeneralDesc_BuiltFromChildElements()800   public void testSetPossibleLengthsGeneralDesc_BuiltFromChildElements() throws Exception {
801     Element territoryElement = parseXmlString("<territory>"
802         + "<fixedLine>"
803         + "  <possibleLengths national=\"13\" localOnly=\"6\"/>"
804         + "</fixedLine>"
805         + "<mobile>"
806         + "  <possibleLengths national=\"15\" localOnly=\"7,13\"/>"
807         + "</mobile>"
808         + "<tollFree>"
809         + "  <possibleLengths national=\"15\"/>"
810         + "</tollFree>"
811         + "</territory>");
812     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
813     BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
814         generalDesc, "someId", territoryElement, false /* not short-number metadata */);
815 
816     assertEquals(2, generalDesc.getPossibleLengthCount());
817     assertEquals(13, generalDesc.getPossibleLength(0));
818     // 15 is present twice in the input in different sections, but only once in the output.
819     assertEquals(15, generalDesc.getPossibleLength(1));
820     assertEquals(2, generalDesc.getPossibleLengthLocalOnlyCount());
821     assertEquals(6, generalDesc.getPossibleLengthLocalOnly(0));
822     assertEquals(7, generalDesc.getPossibleLengthLocalOnly(1));
823     // 13 is skipped as a "local only" length, since it is also present as a normal length.
824   }
825 
testSetPossibleLengthsGeneralDesc_IgnoresNoIntlDialling()826   public void testSetPossibleLengthsGeneralDesc_IgnoresNoIntlDialling() throws Exception {
827     Element territoryElement = parseXmlString("<territory>"
828         + "<fixedLine>"
829         + "  <possibleLengths national=\"13\"/>"
830         + "</fixedLine>"
831         + "<noInternationalDialling>"
832         + "  <possibleLengths national=\"15\"/>"
833         + "</noInternationalDialling>"
834         + "</territory>");
835     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
836     BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
837         generalDesc, "someId", territoryElement, false /* not short-number metadata */);
838 
839     assertEquals(1, generalDesc.getPossibleLengthCount());
840     assertEquals(13, generalDesc.getPossibleLength(0));
841     // 15 is skipped because noInternationalDialling should not contribute to the general lengths;
842     // it isn't a particular "type" of number per se, it is a property that different types may
843     // have.
844   }
845 
testSetPossibleLengthsGeneralDesc_ShortNumberMetadata()846   public void testSetPossibleLengthsGeneralDesc_ShortNumberMetadata() throws Exception {
847     Element territoryElement = parseXmlString("<territory>"
848         + "<shortCode>"
849         + "  <possibleLengths national=\"6,13\"/>"
850         + "</shortCode>"
851         + "<carrierSpecific>"
852         + "  <possibleLengths national=\"7,13,15\"/>"
853         + "</carrierSpecific>"
854         + "<tollFree>"
855         + "  <possibleLengths national=\"15\"/>"
856         + "</tollFree>"
857         + "<smsServices>"
858         + "  <possibleLengths national=\"5\"/>"
859         + "</smsServices>"
860         + "</territory>");
861     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
862     BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
863         generalDesc, "someId", territoryElement, true /* short-number metadata */);
864 
865     // All elements other than shortCode are ignored when creating the general desc.
866     assertEquals(2, generalDesc.getPossibleLengthCount());
867     assertEquals(6, generalDesc.getPossibleLength(0));
868     assertEquals(13, generalDesc.getPossibleLength(1));
869   }
870 
testSetPossibleLengthsGeneralDesc_ShortNumberMetadataErrorsOnLocalLengths()871   public void testSetPossibleLengthsGeneralDesc_ShortNumberMetadataErrorsOnLocalLengths()
872       throws Exception {
873     Element territoryElement = parseXmlString("<territory>"
874         + "<shortCode>"
875         + "  <possibleLengths national=\"13\" localOnly=\"6\"/>"
876         + "</shortCode>"
877         + "</territory>");
878 
879     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
880     try {
881       BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
882           generalDesc, "someId", territoryElement, true /* short-number metadata */);
883       fail();
884     } catch (RuntimeException expected) {
885       // This should be an error, localOnly is not permitted in short-code metadata.
886       assertEquals("Found local-only lengths in short-number metadata", expected.getMessage());
887     }
888   }
889 
testProcessPhoneNumberDescElement_ErrorDuplicates()890   public void testProcessPhoneNumberDescElement_ErrorDuplicates() throws Exception {
891     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
892     generalDesc.addPossibleLength(6);
893 
894     Element territoryElement = parseXmlString("<territory>"
895         + "<mobile>"
896         + "  <possibleLengths national=\"6,6\"/>"
897         + "</mobile>"
898         + "</territory>");
899 
900     try {
901       BuildMetadataFromXml.processPhoneNumberDescElement(generalDesc, territoryElement, "mobile");
902       fail("Invalid data seen: expected failure.");
903     } catch (RuntimeException expected) {
904       // This should be an error, 6 is seen twice.
905       assertEquals("Duplicate length element found (6) in possibleLength string 6,6",
906           expected.getMessage());
907     }
908   }
909 
testProcessPhoneNumberDescElement_ErrorDuplicatesOneLocal()910   public void testProcessPhoneNumberDescElement_ErrorDuplicatesOneLocal() throws Exception {
911     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
912     generalDesc.addPossibleLength(6);
913 
914     Element territoryElement = parseXmlString("<territory>"
915         + "<mobile>"
916         + "  <possibleLengths national=\"6\" localOnly=\"6\"/>"
917         + "</mobile>"
918         + "</territory>");
919 
920     try {
921       BuildMetadataFromXml.processPhoneNumberDescElement(generalDesc, territoryElement, "mobile");
922       fail("Invalid data seen: expected failure.");
923     } catch (RuntimeException expected) {
924       // This should be an error, 6 is seen twice.
925       assertEquals("Possible length(s) found specified as a normal and local-only length: [6]",
926           expected.getMessage());
927     }
928   }
929 
testProcessPhoneNumberDescElement_ErrorUncoveredLengths()930   public void testProcessPhoneNumberDescElement_ErrorUncoveredLengths() throws Exception {
931     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
932     generalDesc.addPossibleLength(4);
933     Element territoryElement = parseXmlString("<territory>"
934         + "<noInternationalDialling>"
935         // Sorting will be done when parsing.
936         + "  <possibleLengths national=\"6,7,4\"/>"
937         + "</noInternationalDialling>"
938         + "</territory>");
939     try {
940       BuildMetadataFromXml.processPhoneNumberDescElement(
941           generalDesc, territoryElement, "noInternationalDialling");
942       fail("Lengths present not covered by the general desc: should fail.");
943     } catch (RuntimeException expected) {
944       // Lengths were present that the general description didn't know about.
945       assertTrue(expected.getMessage().contains("Out-of-range possible length"));
946     }
947   }
948 
testProcessPhoneNumberDescElement_SameAsParent()949   public void testProcessPhoneNumberDescElement_SameAsParent() throws Exception {
950     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
951     // The number lengths set for the general description must be a super-set of those in the
952     // element being parsed.
953     generalDesc.addPossibleLength(4);
954     generalDesc.addPossibleLength(6);
955     generalDesc.addPossibleLength(7);
956     generalDesc.addPossibleLengthLocalOnly(2);
957     Element territoryElement = parseXmlString("<territory>"
958         + "<fixedLine>"
959         // Sorting will be done when parsing.
960         + "  <possibleLengths national=\"6,7,4\" localOnly=\"2\"/>"
961         + "</fixedLine>"
962         + "</territory>");
963 
964     PhoneNumberDesc.Builder phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
965         generalDesc, territoryElement, "fixedLine");
966     // No possible lengths should be present, because they match the general description.
967     assertEquals(0, phoneNumberDesc.getPossibleLengthCount());
968     // Local-only lengths should be present for child elements such as fixed-line.
969     assertEquals(1, phoneNumberDesc.getPossibleLengthLocalOnlyCount());
970   }
971 
testProcessPhoneNumberDescElement_InvalidNumber()972   public void testProcessPhoneNumberDescElement_InvalidNumber() throws Exception {
973     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
974     generalDesc.addPossibleLength(4);
975     Element territoryElement = parseXmlString("<territory>"
976         + "<fixedLine>"
977         + "  <possibleLengths national=\"4d\"/>"
978         + "</fixedLine>"
979         + "</territory>");
980 
981     try {
982       BuildMetadataFromXml.processPhoneNumberDescElement(
983           generalDesc, territoryElement, "fixedLine");
984       fail("4d is not a number.");
985     } catch (NumberFormatException expected) {
986       assertEquals("For input string: \"4d\"", expected.getMessage());
987     }
988   }
989 
testLoadCountryMetadata_GeneralDescHasNumberLengthsSet()990   public void testLoadCountryMetadata_GeneralDescHasNumberLengthsSet() throws Exception {
991     Element territoryElement = parseXmlString("<territory>"
992         + "<generalDesc>"
993         // This shouldn't be set, the possible lengths should be derived for generalDesc.
994         + "  <possibleLengths national=\"4\"/>"
995         + "</generalDesc>"
996         + "<fixedLine>"
997         + "  <possibleLengths national=\"4\"/>"
998         + "</fixedLine>"
999         + "</territory>");
1000 
1001     try {
1002       BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement,
1003           false /* isShortNumberMetadata */, false /* isAlternateFormatsMetadata */);
1004       fail("Possible lengths explicitly set for generalDesc and should not be: we should fail.");
1005     } catch (RuntimeException expected) {
1006       assertEquals("Found possible lengths specified at general desc: this should be derived"
1007           + " from child elements. Affected country: FR", expected.getMessage());
1008     }
1009   }
1010 
testProcessPhoneNumberDescElement_ErrorEmptyPossibleLengthStringAttribute()1011   public void testProcessPhoneNumberDescElement_ErrorEmptyPossibleLengthStringAttribute()
1012       throws Exception {
1013     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1014     generalDesc.addPossibleLength(4);
1015     Element territoryElement = parseXmlString("<territory>"
1016         + "<fixedLine>"
1017         + "  <possibleLengths national=\"\"/>"
1018         + "</fixedLine>"
1019         + "</territory>");
1020     try {
1021       BuildMetadataFromXml.processPhoneNumberDescElement(
1022           generalDesc, territoryElement, "fixedLine");
1023       fail("Empty possible length string.");
1024     } catch (RuntimeException expected) {
1025       assertEquals("Empty possibleLength string found.", expected.getMessage());
1026     }
1027   }
1028 
testProcessPhoneNumberDescElement_ErrorRangeSpecifiedWithComma()1029   public void testProcessPhoneNumberDescElement_ErrorRangeSpecifiedWithComma()
1030       throws Exception {
1031     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1032     generalDesc.addPossibleLength(4);
1033     Element territoryElement = parseXmlString("<territory>"
1034         + "<fixedLine>"
1035         + "  <possibleLengths national=\"[4,7]\"/>"
1036         + "</fixedLine>"
1037         + "</territory>");
1038     try {
1039       BuildMetadataFromXml.processPhoneNumberDescElement(
1040           generalDesc, territoryElement, "fixedLine");
1041       fail("Ranges shouldn't use a comma.");
1042     } catch (RuntimeException expected) {
1043       assertEquals("Missing end of range character in possible length string [4,7].",
1044           expected.getMessage());
1045     }
1046   }
1047 
testProcessPhoneNumberDescElement_ErrorIncompleteRange()1048   public void testProcessPhoneNumberDescElement_ErrorIncompleteRange() throws Exception {
1049     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1050     generalDesc.addPossibleLength(4);
1051     Element territoryElement = parseXmlString("<territory>"
1052         + "<fixedLine>"
1053         + "  <possibleLengths national=\"[4-\"/>"
1054         + "</fixedLine>"
1055         + "</territory>");
1056 
1057     try {
1058       BuildMetadataFromXml.processPhoneNumberDescElement(
1059           generalDesc, territoryElement, "fixedLine");
1060       fail("Should fail: range incomplete.");
1061     } catch (RuntimeException expected) {
1062       assertEquals("Missing end of range character in possible length string [4-.",
1063           expected.getMessage());
1064     }
1065   }
1066 
testProcessPhoneNumberDescElement_ErrorNoDashInRange()1067   public void testProcessPhoneNumberDescElement_ErrorNoDashInRange() throws Exception {
1068     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1069     generalDesc.addPossibleLength(4);
1070     Element territoryElement = parseXmlString("<territory>"
1071         + "<fixedLine>"
1072         + "  <possibleLengths national=\"[4:10]\"/>"
1073         + "</fixedLine>"
1074         + "</territory>");
1075 
1076     try {
1077       BuildMetadataFromXml.processPhoneNumberDescElement(
1078           generalDesc, territoryElement, "fixedLine");
1079       fail("Should fail: range incomplete.");
1080     } catch (RuntimeException expected) {
1081       assertEquals("Ranges must have exactly one - character: missing for [4:10].",
1082           expected.getMessage());
1083     }
1084   }
1085 
testProcessPhoneNumberDescElement_ErrorRangeIsNotFromMinToMax()1086   public void testProcessPhoneNumberDescElement_ErrorRangeIsNotFromMinToMax() throws Exception {
1087     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1088     generalDesc.addPossibleLength(4);
1089     Element territoryElement = parseXmlString("<territory>"
1090         + "<fixedLine>"
1091         + "  <possibleLengths national=\"[10-10]\"/>"
1092         + "</fixedLine>"
1093         + "</territory>");
1094 
1095     try {
1096       BuildMetadataFromXml.processPhoneNumberDescElement(
1097           generalDesc, territoryElement, "fixedLine");
1098       fail("Should fail: range even.");
1099     } catch (RuntimeException expected) {
1100       assertEquals("The first number in a range should be two or more digits lower than the second."
1101           + " Culprit possibleLength string: [10-10]", expected.getMessage());
1102     }
1103   }
1104 
testGetMetadataFilter()1105   public void testGetMetadataFilter() {
1106     assertEquals(BuildMetadataFromXml.getMetadataFilter(false, false),
1107         MetadataFilter.emptyFilter());
1108     assertEquals(BuildMetadataFromXml.getMetadataFilter(true, false),
1109         MetadataFilter.forLiteBuild());
1110     assertEquals(BuildMetadataFromXml.getMetadataFilter(false, true),
1111         MetadataFilter.forSpecialBuild());
1112     try {
1113       BuildMetadataFromXml.getMetadataFilter(true, true);
1114       fail("getMetadataFilter should fail when liteBuild and specialBuild are both set");
1115     } catch (RuntimeException e) {
1116       // Test passed.
1117     }
1118   }
1119 }
1120