2 * Copyright (C) 2010 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.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;
24 import java.io.BufferedWriter;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.util.Formatter;
28 import java.util.List;
32 * Tool to convert phone number metadata from the XML format to JSON format.
34 * @author Nikolaos Trogkanis
36 public class BuildMetadataJsonFromXml extends Command {
37 private static final String NAMESPACE = "i18n.phonenumbers.metadata";
39 private static final String HELP_MESSAGE =
41 "BuildMetadataJsonFromXml <inputFile> <outputFile> [<liteBuild>]\n" +
44 " inputFile The input file containing phone number metadata in XML format.\n" +
45 " outputFile The output file to contain phone number metadata in JSON format.\n" +
46 " liteBuild Whether to generate the lite-version of the metadata (default:\n" +
47 " false). When set to true certain metadata will be omitted.\n" +
48 " At this moment, example numbers information is omitted.\n" +
50 "Example command line invocation:\n" +
51 "BuildMetadataJsonFromXml PhoneNumberMetadata.xml metadatalite.js true\n";
53 private static final String FILE_OVERVIEW =
55 " * @fileoverview Generated metadata for file\n" +
57 " * @author Nikolaos Trogkanis\n" +
60 private static final String COUNTRY_CODE_TO_REGION_CODE_MAP_COMMENT =
62 " * A mapping from a country calling code to the region codes which denote the\n" +
63 " * region represented by that country calling code. In the case of multiple\n" +
64 " * countries sharing a calling code, such as the NANPA regions, the one\n" +
65 " * indicated with \"isMainCountryForCode\" in the metadata should be first.\n" +
66 " * @type {!Object.<number, Array.<string>>}\n" +
69 private static final String COUNTRY_TO_METADATA_COMMENT =
71 " * A mapping from a region code to the PhoneMetadata for that region.\n" +
72 " * @type {!Object.<string, Array>}\n" +
75 private static final int COPYRIGHT_YEAR = 2010;
78 public String getCommandName() {
79 return "BuildMetadataJsonFromXml";
83 public boolean start() {
84 String[] args = getArgs();
86 if (args.length != 3 && args.length != 4) {
87 System.err.println(HELP_MESSAGE);
90 String inputFile = args[1];
91 String outputFile = args[2];
92 boolean liteBuild = args.length > 3 && args[3].equals("true");
95 PhoneMetadataCollection metadataCollection =
96 BuildMetadataFromXml.buildPhoneMetadataCollection(inputFile, liteBuild);
97 Map<Integer, List<String>> countryCodeToRegionCodeMap =
98 BuildMetadataFromXml.buildCountryCodeToRegionCodeMap(metadataCollection);
100 BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));
102 CopyrightNotice.writeTo(writer, COPYRIGHT_YEAR, true);
103 Formatter formatter = new Formatter(writer);
104 formatter.format(FILE_OVERVIEW, inputFile);
106 writer.write("goog.provide('" + NAMESPACE + "');\n\n");
108 writer.write(COUNTRY_CODE_TO_REGION_CODE_MAP_COMMENT);
109 writer.write(NAMESPACE + ".countryCodeToRegionCodeMap = ");
110 writeCountryCodeToRegionCodeMap(countryCodeToRegionCodeMap, writer);
111 writer.write(";\n\n");
113 writer.write(COUNTRY_TO_METADATA_COMMENT);
114 writer.write(NAMESPACE + ".countryToMetadata = ");
115 writeCountryToMetadataMap(metadataCollection, writer);
121 } catch (Exception e) {
128 // Writes a PhoneMetadataCollection in JSON format.
129 private static void writeCountryToMetadataMap(PhoneMetadataCollection metadataCollection,
130 BufferedWriter writer) throws IOException {
132 boolean isFirstTimeInLoop = true;
133 for (PhoneMetadata metadata : metadataCollection.getMetadataList()) {
134 if (isFirstTimeInLoop) {
135 isFirstTimeInLoop = false;
139 String key = metadata.getId();
140 // For non-geographical country calling codes (e.g. +800), use the country calling codes
141 // instead of the region code as key in the map.
142 if (key.equals("001")) {
143 key = Integer.toString(metadata.getCountryCode());
145 JSArrayBuilder jsArrayBuilder = new JSArrayBuilder();
146 toJsArray(metadata, jsArrayBuilder);
150 writer.write(jsArrayBuilder.toString());
155 // Writes a Map<Integer, List<String>> in JSON format.
156 private static void writeCountryCodeToRegionCodeMap(
157 Map<Integer, List<String>> countryCodeToRegionCodeMap,
158 BufferedWriter writer) throws IOException {
160 boolean isFirstTimeInLoop = true;
161 for (Map.Entry<Integer, List<String>> entry : countryCodeToRegionCodeMap.entrySet()) {
162 if (isFirstTimeInLoop) {
163 isFirstTimeInLoop = false;
167 writer.write(Integer.toString(entry.getKey()));
169 JSArrayBuilder jsArrayBuilder = new JSArrayBuilder();
170 jsArrayBuilder.beginArray();
171 jsArrayBuilder.appendIterator(entry.getValue().iterator());
172 jsArrayBuilder.endArray();
173 writer.write(jsArrayBuilder.toString());
178 // Converts NumberFormat to JSArray.
179 private static void toJsArray(NumberFormat format, JSArrayBuilder jsArrayBuilder) {
180 jsArrayBuilder.beginArray();
183 jsArrayBuilder.append(null);
184 // required string pattern = 1;
185 jsArrayBuilder.append(format.getPattern());
186 // required string format = 2;
187 jsArrayBuilder.append(format.getFormat());
188 // repeated string leading_digits_pattern = 3;
189 int leadingDigitsPatternSize = format.leadingDigitsPatternSize();
190 if (leadingDigitsPatternSize > 0) {
191 jsArrayBuilder.beginArray();
192 for (int i = 0; i < leadingDigitsPatternSize; i++) {
193 jsArrayBuilder.append(format.getLeadingDigitsPattern(i));
195 jsArrayBuilder.endArray();
197 jsArrayBuilder.append(null);
199 // optional string national_prefix_formatting_rule = 4;
200 if (format.hasNationalPrefixFormattingRule()) {
201 jsArrayBuilder.append(format.getNationalPrefixFormattingRule());
203 jsArrayBuilder.append(null);
205 // optional string domestic_carrier_code_formatting_rule = 5;
206 if (format.hasDomesticCarrierCodeFormattingRule()) {
207 jsArrayBuilder.append(format.getDomesticCarrierCodeFormattingRule());
209 jsArrayBuilder.append(null);
211 // optional bool national_prefix_optional_when_formatting = 6;
212 if (format.hasNationalPrefixOptionalWhenFormatting()) {
213 jsArrayBuilder.append(format.isNationalPrefixOptionalWhenFormatting());
215 jsArrayBuilder.append(null);
218 jsArrayBuilder.endArray();
221 // Converts PhoneNumberDesc to JSArray.
222 private static void toJsArray(PhoneNumberDesc desc, JSArrayBuilder jsArrayBuilder) {
224 // Some descriptions are optional; in these cases we just append null and return if they are
226 jsArrayBuilder.append(null);
229 jsArrayBuilder.beginArray();
232 jsArrayBuilder.append(null);
234 jsArrayBuilder.append(null);
235 // optional string national_number_pattern = 2;
236 if (desc.hasNationalNumberPattern()) {
237 jsArrayBuilder.append(desc.getNationalNumberPattern());
239 jsArrayBuilder.append(null);
241 // optional string possible_number_pattern = 3;
242 if (desc.hasPossibleNumberPattern()) {
243 jsArrayBuilder.append(desc.getPossibleNumberPattern());
245 jsArrayBuilder.append(null);
248 jsArrayBuilder.append(null);
250 jsArrayBuilder.append(null);
251 // optional string example_number = 6;
252 if (desc.hasExampleNumber()) {
253 jsArrayBuilder.append(desc.getExampleNumber());
255 jsArrayBuilder.append(null);
258 jsArrayBuilder.endArray();
261 // Converts PhoneMetadata to JSArray.
262 private static void toJsArray(PhoneMetadata metadata, JSArrayBuilder jsArrayBuilder) {
263 jsArrayBuilder.beginArray();
266 jsArrayBuilder.append(null);
267 // optional PhoneNumberDesc general_desc = 1;
268 toJsArray(metadata.getGeneralDesc(), jsArrayBuilder);
269 // optional PhoneNumberDesc fixed_line = 2;
270 toJsArray(metadata.getFixedLine(), jsArrayBuilder);
271 // optional PhoneNumberDesc mobile = 3;
272 toJsArray(metadata.getMobile(), jsArrayBuilder);
273 // optional PhoneNumberDesc toll_free = 4;
274 toJsArray(metadata.getTollFree(), jsArrayBuilder);
275 // optional PhoneNumberDesc premium_rate = 5;
276 toJsArray(metadata.getPremiumRate(), jsArrayBuilder);
277 // optional PhoneNumberDesc shared_cost = 6;
278 toJsArray(metadata.getSharedCost(), jsArrayBuilder);
279 // optional PhoneNumberDesc personal_number = 7;
280 toJsArray(metadata.getPersonalNumber(), jsArrayBuilder);
281 // optional PhoneNumberDesc voip = 8;
282 toJsArray(metadata.getVoip(), jsArrayBuilder);
283 // required string id = 9;
284 jsArrayBuilder.append(metadata.getId());
285 // optional int32 country_code = 10;
286 if (metadata.hasCountryCode()) {
287 jsArrayBuilder.append(metadata.getCountryCode());
289 jsArrayBuilder.append(null);
291 // optional string international_prefix = 11;
292 if (metadata.hasInternationalPrefix()) {
293 jsArrayBuilder.append(metadata.getInternationalPrefix());
295 jsArrayBuilder.append(null);
298 // optional string national_prefix = 12;
299 if (metadata.hasNationalPrefix()) {
300 jsArrayBuilder.append(metadata.getNationalPrefix());
302 jsArrayBuilder.append(null);
304 // optional string preferred_extn_prefix = 13;
305 if (metadata.hasPreferredExtnPrefix()) {
306 jsArrayBuilder.append(metadata.getPreferredExtnPrefix());
308 jsArrayBuilder.append(null);
311 jsArrayBuilder.append(null);
312 // optional string national_prefix_for_parsing = 15;
313 if (metadata.hasNationalPrefixForParsing()) {
314 jsArrayBuilder.append(metadata.getNationalPrefixForParsing());
316 jsArrayBuilder.append(null);
318 // optional string national_prefix_transform_rule = 16;
319 if (metadata.hasNationalPrefixTransformRule()) {
320 jsArrayBuilder.append(metadata.getNationalPrefixTransformRule());
322 jsArrayBuilder.append(null);
324 // optional string preferred_international_prefix = 17;
325 if (metadata.hasPreferredInternationalPrefix()) {
326 jsArrayBuilder.append(metadata.getPreferredInternationalPrefix());
328 jsArrayBuilder.append(null);
330 // optional bool same_mobile_and_fixed_line_pattern = 18 [default=false];
331 if (metadata.isSameMobileAndFixedLinePattern()) {
332 jsArrayBuilder.append(1);
334 jsArrayBuilder.append(null);
336 // repeated NumberFormat number_format = 19;
337 int numberFormatSize = metadata.numberFormatSize();
338 if (numberFormatSize > 0) {
339 jsArrayBuilder.beginArray();
340 for (int i = 0; i < numberFormatSize; i++) {
341 toJsArray(metadata.getNumberFormat(i), jsArrayBuilder);
343 jsArrayBuilder.endArray();
345 jsArrayBuilder.append(null);
347 // repeated NumberFormat intl_number_format = 20;
348 int intlNumberFormatSize = metadata.intlNumberFormatSize();
349 if (intlNumberFormatSize > 0) {
350 jsArrayBuilder.beginArray();
351 for (int i = 0; i < intlNumberFormatSize; i++) {
352 toJsArray(metadata.getIntlNumberFormat(i), jsArrayBuilder);
354 jsArrayBuilder.endArray();
356 jsArrayBuilder.append(null);
358 // optional PhoneNumberDesc pager = 21;
359 toJsArray(metadata.getPager(), jsArrayBuilder);
360 // optional bool main_country_for_code = 22 [default=false];
361 if (metadata.isMainCountryForCode()) {
362 jsArrayBuilder.append(1);
364 jsArrayBuilder.append(null);
366 // optional string leading_digits = 23;
367 if (metadata.hasLeadingDigits()) {
368 jsArrayBuilder.append(metadata.getLeadingDigits());
370 jsArrayBuilder.append(null);
372 // optional PhoneNumberDesc no_international_dialling = 24;
373 toJsArray(metadata.getNoInternationalDialling(), jsArrayBuilder);
374 // optional PhoneNumberDesc uan = 25;
375 toJsArray(metadata.getUan(), jsArrayBuilder);
376 // optional bool leading_zero_possible = 26 [default=false];
377 if (metadata.isLeadingZeroPossible()) {
378 jsArrayBuilder.append(1);
380 jsArrayBuilder.append(null);
382 // optional PhoneNumberDesc emergency = 27;
383 toJsArray(metadata.getEmergency(), jsArrayBuilder);
384 // optional PhoneNumberDesc voicemail = 28;
385 toJsArray(metadata.getVoicemail(), jsArrayBuilder);
386 // Fields 29-31 are omitted due to space increase.
387 // optional PhoneNumberDesc short_code = 29;
388 // optional PhoneNumberDesc standard_rate = 30;
389 // optional PhoneNumberDesc carrier_specific = 31;
390 // optional bool mobile_number_portable_region = 32 [default=false];
391 // Omit since the JS API doesn't expose this data.
392 // Note: Need to add null for each of the above fields when a subsequent
393 // field is being populated.
395 jsArrayBuilder.endArray();