@ -16,6 +16,8 @@
package com.google.i18n.phonenumbers ;
import com.google.auto.value.AutoValue ;
import com.google.common.collect.ImmutableSortedSet ;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata ;
import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection ;
@ -26,6 +28,7 @@ import java.io.FileWriter;
import java.io.IOException ;
import java.io.ObjectOutputStream ;
import java.io.Writer ;
import java.util.Collection ;
import java.util.Formatter ;
import java.util.List ;
import java.util.Map ;
@ -34,6 +37,7 @@ import java.util.SortedSet;
import java.util.TreeSet ;
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
import javax.annotation.Nullable ;
/ * *
* Tool to convert phone number metadata from the XML format to protocol buffer format .
@ -56,6 +60,7 @@ public class BuildMetadataProtoFromXml extends Command {
private static final String COPYRIGHT = "copyright" ;
private static final String SINGLE_FILE = "single-file" ;
private static final String LITE_BUILD = "lite-build" ;
private static final String BUILD_REGIONCODE = "build-regioncode" ;
/ / Only supported for clients who have consulted with the libphonenumber team , and the behavior is
/ / subject to change without notice .
private static final String SPECIAL_BUILD = "special-build" ;
@ -81,6 +86,8 @@ public class BuildMetadataProtoFromXml extends Command {
" [--" + LITE_BUILD + "=<true|false>] Optional (default: false). In a lite build,\n" +
" certain metadata will be omitted. At this\n" +
" moment, example numbers information is omitted.\n" +
" [--" + BUILD_REGIONCODE + "=<true|false>] Optional (default: false). Generate\n" +
" RegionCode class with constants for all region codes.\n" +
"\n" +
"Example command line invocation:\n" +
CLASS_NAME + " \\\n" +
@ -90,12 +97,32 @@ public class BuildMetadataProtoFromXml extends Command {
" --" + MAPPING_CLASS + "=CountryCodeToRegionCodeMap \\\n" +
" --" + COPYRIGHT + "=2010 \\\n" +
" --" + SINGLE_FILE + "=false \\\n" +
" --" + LITE_BUILD + "=false\n" ;
" --" + LITE_BUILD + "=false\n" +
" --" + BUILD_REGIONCODE + "=true\n" ;
private static final String GENERATION_COMMENT =
"/* This file is automatically generated by {@link " + CLASS_NAME + "}.\n" +
" * Please don't modify it directly.\n" +
" */\n\n" ;
private static final String MAP_COMMENT =
" // A mapping from a country code to the region codes which denote the\n" +
" // country/region represented by that country code. In the case of multiple\n" +
" // countries sharing a calling code, such as the NANPA countries, the one\n" +
" // indicated with \"isMainCountryForCode\" in the metadata should be first.\n" ;
private static final String COUNTRY_CODE_SET_COMMENT =
" // A set of all country codes for which data is available.\n" ;
private static final String REGION_CODE_SET_COMMENT =
" // A set of all region codes for which data is available.\n" ;
private static final double CAPACITY_FACTOR = 0 . 75 ;
private static final String CAPACITY_COMMENT =
" // The capacity is set to %d as there are %d different entries,\n" +
" // and this offers a load factor of roughly " + CAPACITY_FACTOR + ".\n" ;
private static final String REGION_CODE_CONSTS_JAVADOC =
"/**\n" +
" * Class containing string constants of region codes for easier testing.\n" +
" */\n" ;
@Override
public String getCommandName ( ) {
@ -115,6 +142,7 @@ public class BuildMetadataProtoFromXml extends Command {
boolean singleFile = false ;
boolean liteBuild = false ;
boolean specialBuild = false ;
boolean buildRegioncode = false ;
for ( int i = 1 ; i < getArgs ( ) . length ; i + + ) {
String key = null ;
@ -144,6 +172,9 @@ public class BuildMetadataProtoFromXml extends Command {
} else if ( SPECIAL_BUILD . equals ( key ) & &
( "true" . equalsIgnoreCase ( value ) | | "false" . equalsIgnoreCase ( value ) ) ) {
specialBuild = "true" . equalsIgnoreCase ( value ) ;
} else if ( BUILD_REGIONCODE . equals ( key ) & &
( "true" . equalsIgnoreCase ( value ) | | "false" . equalsIgnoreCase ( value ) ) ) {
buildRegioncode = "true" . equalsIgnoreCase ( value ) ;
} else {
System . err . println ( HELP_MESSAGE ) ;
System . err . println ( "Illegal command line parameter: " + getArgs ( ) [ i ] ) ;
@ -195,6 +226,16 @@ public class BuildMetadataProtoFromXml extends Command {
writeCountryCallingCodeMappingToJavaFile (
countryCodeToRegionCodeMap , outputDir , mappingClass , copyright ) ;
if ( buildRegioncode ) {
SortedSet < String > regionCodeSet = new TreeSet < String > ( ) ;
/ / Official code for the unknown region .
regionCodeSet . add ( "ZZ" ) ;
regionCodeSet . addAll ( BuildMetadataFromXml . buildRegionCodeList ( metadataCollection ) ) ;
System . out . println ( "Found " + regionCodeSet . size ( ) + " region codes" ) ;
writeRegionCodeConstantsToJavaFile ( regionCodeSet , outputDir , copyright ) ;
}
} catch ( Exception e ) {
e . printStackTrace ( ) ;
return false ;
@ -219,20 +260,6 @@ public class BuildMetadataProtoFromXml extends Command {
System . out . println ( "Deleted " + counter + " old files" ) ;
}
private static final String MAP_COMMENT =
" // A mapping from a country code to the region codes which denote the\n" +
" // country/region represented by that country code. In the case of multiple\n" +
" // countries sharing a calling code, such as the NANPA countries, the one\n" +
" // indicated with \"isMainCountryForCode\" in the metadata should be first.\n" ;
private static final String COUNTRY_CODE_SET_COMMENT =
" // A set of all country codes for which data is available.\n" ;
private static final String REGION_CODE_SET_COMMENT =
" // A set of all region codes for which data is available.\n" ;
private static final double CAPACITY_FACTOR = 0 . 75 ;
private static final String CAPACITY_COMMENT =
" // The capacity is set to %d as there are %d different entries,\n" +
" // and this offers a load factor of roughly " + CAPACITY_FACTOR + ".\n" ;
private static void writeCountryCallingCodeMappingToJavaFile (
Map < Integer , List < String > > countryCodeToRegionCodeMap ,
String outputDir , String mappingClass , String copyright ) throws IOException {
@ -247,7 +274,11 @@ public class BuildMetadataProtoFromXml extends Command {
}
boolean hasCountryCodes = countryCodeToRegionCodeMap . size ( ) > 1 ;
ClassWriter writer = new ClassWriter ( outputDir , mappingClass , copyright ) ;
ClassWriter . Builder writer = ClassWriter . builder ( )
. setOutputDir ( outputDir )
. setCopyright ( Integer . parseInt ( copyright ) )
. setModifiers ( "public" )
. setName ( mappingClass ) ;
int capacity = ( int ) ( countryCodeToRegionCodeMap . size ( ) / CAPACITY_FACTOR ) ;
if ( hasRegionCodes & & hasCountryCodes ) {
@ -260,10 +291,10 @@ public class BuildMetadataProtoFromXml extends Command {
writeRegionCodeSet ( writer , capacity , regionCodeList ) ;
}
writer . writeToFile ( ) ;
writer . build ( ) . writeToFile ( ) ;
}
private static void writeMap ( ClassWriter writer , int capacity ,
private static void writeMap ( ClassWriter . Builder writer , int capacity ,
Map < Integer , List < String > > countryCodeToRegionCodeMap ) {
writer . addToBody ( MAP_COMMENT ) ;
@ -297,7 +328,7 @@ public class BuildMetadataProtoFromXml extends Command {
writer . addToBody ( " }\n" ) ;
}
private static void writeRegionCodeSet ( ClassWriter writer , int capacity ,
private static void writeRegionCodeSet ( ClassWriter . Builder writer , int capacity ,
List < String > regionCodeList ) {
writer . addToBody ( REGION_CODE_SET_COMMENT ) ;
@ -318,7 +349,7 @@ public class BuildMetadataProtoFromXml extends Command {
writer . addToBody ( " }\n" ) ;
}
private static void writeCountryCodeSet ( ClassWriter writer , int capacity ,
private static void writeCountryCodeSet ( ClassWriter . Builder writer , int capacity ,
Set < Integer > countryCodeSet ) {
writer . addToBody ( COUNTRY_CODE_SET_COMMENT ) ;
@ -339,51 +370,103 @@ public class BuildMetadataProtoFromXml extends Command {
writer . addToBody ( " }\n" ) ;
}
private static final class ClassWriter {
private final String name ;
private final String copyright ;
private static void writeRegionCodeConstantsToJavaFile ( Collection < String > regionCodeList ,
String outputDir , String copyright ) throws IOException {
ClassWriter . Builder writer = ClassWriter . builder ( )
. setOutputDir ( outputDir )
. setName ( "RegionCode" )
. setModifiers ( "final" )
. setCopyright ( Integer . parseInt ( copyright ) ) ;
private final SortedSet < String > imports ;
private final StringBuffer body ;
private final Formatter formatter ;
private final Writer writer ;
writer . setJavadoc ( REGION_CODE_CONSTS_JAVADOC ) ;
for ( String regionCode : regionCodeList ) {
String variableName = regionCode . toUpperCase ( ) ;
if ( variableName . equals ( "001" ) ) {
writer . addToBody ( " // Region code for global networks (e.g. +800 numbers).\n" ) ;
variableName = "UN001" ;
} else if ( variableName . equals ( "ZZ" ) ) {
writer . addToBody ( " // Official code for the unknown region.\n" ) ;
}
writer . addToBody ( " static final String " + variableName + " = \"" + regionCode + "\";\n" ) ;
}
writer . build ( ) . writeToFile ( ) ;
}
ClassWriter ( String outputDir , String name , String copyright ) throws IOException {
this . name = name ;
this . copyright = copyright ;
imports = new TreeSet < String > ( ) ;
body = new StringBuffer ( ) ;
formatter = new Formatter ( body ) ;
writer = new BufferedWriter ( new FileWriter ( new File ( outputDir , name + ".java" ) ) ) ;
@AutoValue
abstract static class ClassWriter {
abstract String outputDir ( ) ;
abstract Integer copyright ( ) ;
abstract ImmutableSortedSet < String > imports ( ) ;
abstract String javadoc ( ) ;
abstract String modifiers ( ) ;
abstract String name ( ) ;
abstract String body ( ) ;
static Builder builder ( ) {
return new AutoValue_BuildMetadataProtoFromXml_ClassWriter . Builder ( )
. setModifiers ( "" ) ;
}
void addToImports ( String name ) {
imports . add ( name ) ;
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder setOutputDir ( String outputDir ) ;
abstract Builder setCopyright ( Integer copyright ) ;
abstract ImmutableSortedSet . Builder < String > importsBuilder ( ) ;
final Builder addToImports ( String name ) {
importsBuilder ( ) . add ( name ) ;
return this ;
}
void addToBody ( CharSequence text ) {
body . append ( text ) ;
}
abstract Builder setJavadoc ( String javadoc ) ;
abstract Builder setName ( String name ) ;
abstract Builder setModifiers ( String modifiers ) ;
abstract Builder setBody ( String body ) ;
private final StringBuilder bodyBuilder = new StringBuilder ( ) ;
final Builder addToBody ( String text ) {
bodyBuilder . append ( text ) ;
return this ;
}
final Builder formatToBody ( String format , Object . . . args ) {
Formatter formatter = new Formatter ( bodyBuilder ) ;
formatter . format ( format , args ) ;
return this ;
}
void formatToBody ( String format , Object . . . args ) {
formatter . format ( format , args ) ;
abstract ClassWriter autoBuild ( ) ;
final ClassWriter build ( ) {
setBody ( bodyBuilder . toString ( ) ) ;
return autoBuild ( ) ;
}
}
void writeToFile ( ) throws IOException {
CopyrightNotice . writeTo ( writer , Integer . valueOf ( copyright ) ) ;
Writer writer = new BufferedWriter ( new FileWriter ( new File ( outputDir ( ) , name ( ) + ".java" ) ) ) ;
CopyrightNotice . writeTo ( writer , copyright ( ) ) ;
writer . write ( GENERATION_COMMENT ) ;
writer . write ( "package " + PACKAGE_NAME + ";\n\n" ) ;
if ( ! imports . isEmpty ( ) ) {
for ( String item : imports ) {
if ( ! imports ( ) . isEmpty ( ) ) {
for ( String item : imports ( ) ) {
writer . write ( "import " + item + ";\n" ) ;
}
writer . write ( "\n" ) ;
}
writer . write ( "public class " + name + " {\n" ) ;
writer . write ( body . toString ( ) ) ;
writer . write ( javadoc ( ) ) ;
if ( ! modifiers ( ) . isEmpty ( ) ) {
writer . write ( modifiers ( ) + " " ) ;
}
writer . write ( "class " + name ( ) + " {\n" ) ;
writer . write ( body ( ) ) ;
writer . write ( "}\n" ) ;
writer . flush ( ) ;