1 /* 2 * Copyright (C) 2012 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.buildtools; 18 19 import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; 20 21 import java.io.BufferedInputStream; 22 import java.io.BufferedReader; 23 import java.io.File; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.io.ObjectOutputStream; 30 import java.io.OutputStream; 31 import java.nio.charset.Charset; 32 import java.util.SortedMap; 33 import java.util.TreeMap; 34 import java.util.logging.Level; 35 import java.util.logging.Logger; 36 37 /** 38 * A utility that generates the binary serialization of the prefix/time zones mappings from a 39 * human-readable text file. 40 */ 41 public class GenerateTimeZonesMapData { 42 private final File inputTextFile; 43 private static final String MAPPING_DATA_FILE_NAME = "map_data"; 44 // The IO Handler used to output the generated binary file. 45 private final AbstractPhonePrefixDataIOHandler ioHandler; 46 47 private static final Logger logger = Logger.getLogger(GenerateTimeZonesMapData.class.getName()); 48 GenerateTimeZonesMapData(File inputTextFile, AbstractPhonePrefixDataIOHandler ioHandler)49 public GenerateTimeZonesMapData(File inputTextFile, AbstractPhonePrefixDataIOHandler ioHandler) 50 throws IOException { 51 this.inputTextFile = inputTextFile; 52 if (!inputTextFile.isFile()) { 53 throw new IOException("The provided input text file does not exist."); 54 } 55 this.ioHandler = ioHandler; 56 } 57 58 /** 59 * Reads phone prefix data from the provided input stream and returns a SortedMap with the 60 * prefix to time zones mappings. 61 */ 62 // @VisibleForTesting parseTextFile(InputStream input)63 static SortedMap<Integer, String> parseTextFile(InputStream input) 64 throws IOException, RuntimeException { 65 final SortedMap<Integer, String> timeZoneMap = new TreeMap<Integer, String>(); 66 BufferedReader bufferedReader = 67 new BufferedReader(new InputStreamReader( 68 new BufferedInputStream(input), Charset.forName("UTF-8"))); 69 int lineNumber = 1; 70 71 for (String line; (line = bufferedReader.readLine()) != null; lineNumber++) { 72 line = line.trim(); 73 if (line.length() == 0 || line.startsWith("#")) { 74 continue; 75 } 76 int indexOfPipe = line.indexOf('|'); 77 if (indexOfPipe == -1) { 78 throw new RuntimeException(String.format("line %d: malformatted data, expected '|'", 79 lineNumber)); 80 } 81 Integer prefix = Integer.parseInt(line.substring(0, indexOfPipe)); 82 String timezones = line.substring(indexOfPipe + 1); 83 if (timezones.isEmpty()) { 84 throw new RuntimeException(String.format("line %d: missing time zones", lineNumber)); 85 } 86 if (timeZoneMap.put(prefix, timezones) != null) { 87 throw new RuntimeException(String.format("duplicated prefix %d", prefix)); 88 } 89 } 90 return timeZoneMap; 91 } 92 93 /** 94 * Writes the provided phone prefix/time zones map to the provided output stream. 95 * 96 * @throws IOException 97 */ 98 // @VisibleForTesting writeToBinaryFile(SortedMap<Integer, String> sortedMap, OutputStream output)99 static void writeToBinaryFile(SortedMap<Integer, String> sortedMap, OutputStream output) 100 throws IOException { 101 // Build the corresponding PrefixTimeZonesMap and serialize it to the binary format. 102 PrefixTimeZonesMap prefixTimeZonesMap = new PrefixTimeZonesMap(); 103 prefixTimeZonesMap.readPrefixTimeZonesMap(sortedMap); 104 ObjectOutputStream objectOutputStream = new ObjectOutputStream(output); 105 prefixTimeZonesMap.writeExternal(objectOutputStream); 106 objectOutputStream.flush(); 107 } 108 109 /** 110 * Runs the prefix to time zones map data generator. 111 * 112 * @throws IOException 113 */ run()114 public void run() throws IOException { 115 FileInputStream fileInputStream = null; 116 FileOutputStream fileOutputStream = null; 117 try { 118 fileInputStream = new FileInputStream(inputTextFile); 119 SortedMap<Integer, String> mappings = parseTextFile(fileInputStream); 120 File outputBinaryFile = ioHandler.createFile(MAPPING_DATA_FILE_NAME); 121 try { 122 fileOutputStream = new FileOutputStream(outputBinaryFile); 123 writeToBinaryFile(mappings, fileOutputStream); 124 ioHandler.addFileToOutput(outputBinaryFile); 125 } finally { 126 ioHandler.closeFile(fileOutputStream); 127 } 128 } catch (RuntimeException e) { 129 logger.log(Level.SEVERE, 130 "Error processing file " + inputTextFile.getAbsolutePath()); 131 throw e; 132 } catch (IOException e) { 133 logger.log(Level.SEVERE, e.getMessage()); 134 } finally { 135 ioHandler.closeFile(fileInputStream); 136 ioHandler.closeFile(fileOutputStream); 137 ioHandler.close(); 138 } 139 logger.log(Level.INFO, "Time zone data successfully generated."); 140 } 141 } 142