4141import sys
4242import os
4343import re
44+ import getopt
4445import datetime
4546from xml .etree import ElementTree as etree
4647
5657TOP_XPATH = "territories"
5758# XML element name for the territory element
5859TERRITORY_TAG = "territory"
60+ # Marker for unavailable entries
61+ DATA_NA = "NA"
5962
6063# Boilerplate text for generated Python files
6164METADATA_FILE_PROLOG = '"""Auto-generated file, do not edit by hand."""'
7174from %(module)s.phonemetadata import NumberFormat, PhoneNumberDesc, PhoneMetadata
7275'''
7376
77+ # Boilerplate header for individual country-code alternate number format data files
78+ _ALT_FORMAT_METADATA_PROLOG = '''"""Auto-generated file, do not edit by hand. %s metadata"""
79+ from %s.phonemetadata import NumberFormat
80+ '''
81+
7482# Copyright notice covering the XML metadata; include current year.
7583COPYRIGHT_NOTICE = """# Copyright (C) 2010-%s The Libphonenumber Authors
7684#
@@ -145,6 +153,30 @@ def _expand_formatting_rule(rule, national_prefix):
145153 return rule
146154
147155
156+ class XAlternateNumberFormat (UnicodeMixin ):
157+ """Parse alternate NumberFormat objects from XML element"""
158+ def __init__ (self , xtag ):
159+ if xtag is None :
160+ self .o = None
161+ else :
162+ self .o = NumberFormat ()
163+ self .o ._mutable = True
164+ self .o .pattern = xtag .attrib ['pattern' ] # REQUIRED attribute
165+ self .o .format = _get_unique_child_value (xtag , 'format' )
166+ if self .o .format is None :
167+ raise Exception ("No format pattern found" )
168+ else :
169+ # Replace '$1' etc with '\1' to match Python regexp group reference format
170+ self .o .format = re .sub ('\$' , ur'\\' , self .o .format )
171+ xleading_digits = xtag .findall ("leadingDigits" )
172+ for xleading_digit in xleading_digits :
173+ self .o .leading_digits_pattern .append (_dews_re (xleading_digit .text ))
174+ # Currently this assumes no intlFormat elements in the element
175+
176+ def __unicode__ (self ):
177+ return unicode (self .o )
178+
179+
148180class XNumberFormat (UnicodeMixin ):
149181 """Parsed NumberFormat objects from XML element"""
150182 def __init__ (self , owning_xterr , xtag , national_prefix ,
@@ -217,7 +249,7 @@ def __init__(self, owning_xterr, xtag, national_prefix,
217249 else :
218250 # Replace '$1' etc with '\1' to match Python regexp group reference format
219251 intl_format = re .sub ('\$' , u (r'\\' ), intl_format )
220- if intl_format != "NA" :
252+ if intl_format != DATA_NA :
221253 self .io .format = intl_format
222254 owning_xterr .has_explicit_intl_format = True
223255 if self .io .format is not None :
@@ -239,8 +271,8 @@ def __init__(self, xtag,
239271 self .o .example_number = None
240272 if xtag is None :
241273 if fill_na :
242- self .o .national_number_pattern = "NA"
243- self .o .possible_number_pattern = "NA"
274+ self .o .national_number_pattern = DATA_NA
275+ self .o .possible_number_pattern = DATA_NA
244276 return
245277 # Start with the template values
246278 if template is not None :
@@ -263,6 +295,26 @@ def __unicode__(self):
263295 return u (self .o )
264296
265297
298+ class XAlternateTerritory (UnicodeMixin ):
299+ """Parse alternate format metadata from XML element (territory)"""
300+ def __init__ (self , xterritory ):
301+ self .country_code = int (xterritory .attrib ['countryCode' ])
302+ # Look for available formats
303+ self .number_format = []
304+ formats = _get_unique_child (xterritory , "availableFormats" )
305+ if formats is not None :
306+ for xelt in formats .findall ("numberFormat" ):
307+ # Create an XNumberFormat object, which contains a NumberFormat object
308+ # or two, and which self-registers them with self.o
309+ self .number_format .append (XAlternateNumberFormat (xelt ).o )
310+ if len (self .number_format ) == 0 :
311+ raise Exception ("No number formats found in available formats" )
312+ # Currently this assumes no intlFormat elements in the file
313+
314+ def __unicode__ (self ):
315+ return unicode (self .number_format )
316+
317+
266318class XTerritory (UnicodeMixin ):
267319 """Parse PhoneMetadata from XML element (territory)"""
268320 def __init__ (self , xterritory ):
@@ -389,6 +441,23 @@ def __init__(self, filename):
389441 self .territory [id ] = terrobj
390442 else :
391443 raise Exception ("Unexpected element %s found" % xterritory .tag )
444+ self .alt_territory = None
445+
446+ def add_alternate_formats (self , filename ):
447+ """Add phone number alternate format metadata retrieved from XML"""
448+ with open (filename , "r" ) as infile :
449+ xtree = etree .parse (infile )
450+ self .alt_territory = {} # country_code to XAlternateTerritory
451+ xterritories = xtree .find (TOP_XPATH )
452+ for xterritory in xterritories :
453+ if xterritory .tag == TERRITORY_TAG :
454+ terrobj = XAlternateTerritory (xterritory )
455+ id = str (terrobj .country_code )
456+ if id in self .alt_territory :
457+ raise Exception ("Duplicate entry for %s" % id )
458+ self .alt_territory [id ] = terrobj
459+ else :
460+ raise Exception ("Unexpected element %s found" % xterritory .tag )
392461
393462 def __unicode__ (self ):
394463 return u ("\n " ).join ([u ("%s: %s" ) % (country_id , territory ) for country_id , territory in self .territory .items ()])
@@ -400,6 +469,13 @@ def emit_metadata_for_region_py(self, region, region_filename, module_prefix):
400469 prnt (_REGION_METADATA_PROLOG % {'region' : terrobj .identifier (), 'module' : module_prefix }, file = outfile )
401470 prnt ("PHONE_METADATA_%s = %s" % (terrobj .identifier (), terrobj ), file = outfile )
402471
472+ def emit_alt_formats_for_cc_py (self , cc , cc_filename , module_prefix ):
473+ """Emit Python code generating the alternate format metadata for the given country code"""
474+ terrobj = self .alt_territory [cc ]
475+ with open (cc_filename , "w" ) as outfile :
476+ print >> outfile , _ALT_FORMAT_METADATA_PROLOG % (cc , module_prefix )
477+ print >> outfile , "PHONE_ALT_FORMAT_%s = %s" % (cc , terrobj )
478+
403479 def emit_metadata_py (self , datadir , module_prefix ):
404480 """Emit Python code for the phone number metadata to the given file, and
405481 to a data/ subdirectory in the same directory as that file."""
@@ -413,12 +489,24 @@ def emit_metadata_py(self, datadir, module_prefix):
413489 filename = os .path .join (datadir , "region_%s.py" % country_id )
414490 self .emit_metadata_for_region_py (country_id , filename , module_prefix )
415491
492+ # Same for any per-country-code alternate format files
493+ if self .alt_territory is not None :
494+ for country_code in sorted (self .alt_territory .keys ()):
495+ filename = os .path .join (datadir , "alt_format_%s.py" % country_code )
496+ self .emit_alt_formats_for_cc_py (country_code , filename , module_prefix )
497+
416498 # Now build a module file that includes them all
417499 with open (modulefilename , "w" ) as outfile :
418500 prnt (METADATA_FILE_PROLOG , file = outfile )
419501 prnt (COPYRIGHT_NOTICE , file = outfile )
420502 for country_id in sorted (self .territory .keys ()):
421503 prnt ("from .region_%s import PHONE_METADATA_%s" % (country_id , country_id ), file = outfile )
504+ if self .alt_territory is not None :
505+ for country_code in sorted (self .alt_territory .keys ()):
506+ prnt ("from .alt_format_%s import PHONE_ALT_FORMAT_%s" % (country_code , country_code ), file = outfile )
507+ prnt ("_ALT_NUMBER_FORMATS = {%s}" %
508+ ", " .join (["%s: PHONE_ALT_FORMAT_%s" % (cc , cc ) for cc in sorted (self .alt_territory .keys ())]),
509+ file = outfile )
422510 # Emit the mapping from country code to region code
423511 prnt (_COUNTRY_CODE_TO_REGION_CODE_PROLOG , file = outfile )
424512 prnt ("_COUNTRY_CODE_TO_REGION_CODE = {" , file = outfile )
@@ -442,11 +530,30 @@ def emit_metadata_py(self, datadir, module_prefix):
442530
443531def _standalone (argv ):
444532 """Parse the given XML file and emit generated code."""
445- if len (argv ) != 3 :
533+ alternate = None
534+ try :
535+ opts , args = getopt .getopt (argv , "ha:" , ("help" , "alt=" ))
536+ except getopt .GetoptError :
537+ prnt (__doc__ , file = sys .stderr )
538+ sys .exit (1 )
539+ for opt , arg in opts :
540+ if opt in ("-h" , "--help" ):
541+ prnt (__doc__ , file = sys .stderr )
542+ sys .exit (1 )
543+ elif opt in ("-a" , "--alt" ):
544+ alternate = arg
545+ else :
546+ prnt ("Unknown option %s" % opt , file = sys .stderr )
547+ prnt (__doc__ , file = sys .stderr )
548+ sys .exit (1 )
549+
550+ if len (args ) != 3 :
446551 prnt (__doc__ , file = sys .stderr )
447552 sys .exit (1 )
448- pmd = XPhoneNumberMetadata (argv [0 ])
449- pmd .emit_metadata_py (argv [1 ], argv [2 ])
553+ pmd = XPhoneNumberMetadata (args [0 ])
554+ if alternate is not None :
555+ pmd .add_alternate_formats (alternate )
556+ pmd .emit_metadata_py (args [1 ], args [2 ])
450557
451558
452559if __name__ == "__main__" :
0 commit comments