1d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 2d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughesimport java.io.*; 3d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughesimport java.util.*; 4d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 5d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// usage: java ZoneCompiler <setup file> <top-level directory> 6d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 7d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// Compile a set of tzfile-formatted files into a single file plus 8d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// an index file. 9d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 10d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// The compilation is controlled by a setup file, which is provided as a 11d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// command-line argument. The setup file has the form: 12d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 13d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// Link <toName> <fromName> 14d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// ... 15d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// <zone filename> 16d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// ... 17d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 18d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// Note that the links must be declared prior to the zone names. A 19d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// zone name is a filename relative to the source directory such as 20d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 'GMT', 'Africa/Dakar', or 'America/Argentina/Jujuy'. 21d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 22d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// Use the 'zic' command-line tool to convert from flat files 23d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// (e.g., 'africa', 'northamerica') into a suitable source directory 24d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// hierarchy for this tool (e.g., 'data/Africa/Abidjan'). 25d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// 26d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// Example: 27d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// zic -d data tz2007h 28d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// javac ZoneCompactor.java 29d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// java ZoneCompactor setup data 30d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes// <produces zoneinfo.dat and zoneinfo.idx> 31d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 32d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughespublic class ZoneCompactor { 33d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 34d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // Zone name synonyms 35d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Map<String,String> links = new HashMap<String,String>(); 36d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 37d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // File starting bytes by zone name 38d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Map<String,Integer> starts = new HashMap<String,Integer>(); 39d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 40d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // File lengths by zone name 41d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Map<String,Integer> lengths = new HashMap<String,Integer>(); 42d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 43d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // Raw GMT offsets by zone name 44d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Map<String,Integer> offsets = new HashMap<String,Integer>(); 45d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes int start = 0; 46d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 47d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // Maximum number of characters in a zone name, including '\0' terminator 48d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes private static final int MAXNAME = 40; 49d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 50d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // Concatenate the contents of 'inFile' onto 'out' 51d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // and return the contents as a byte array. 52d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes private static byte[] copyFile(File inFile, OutputStream out) 53d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes throws Exception { 54d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes byte[] ret = new byte[0]; 55d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 56d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes InputStream in = new FileInputStream(inFile); 57d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes byte[] buf = new byte[8192]; 58d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes while (true) { 59d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes int nbytes = in.read(buf); 60d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes if (nbytes == -1) { 61d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes break; 62d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 63d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes out.write(buf, 0, nbytes); 64d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 65d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes byte[] nret = new byte[ret.length + nbytes]; 66d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes System.arraycopy(ret, 0, nret, 0, ret.length); 67d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes System.arraycopy(buf, 0, nret, ret.length, nbytes); 68d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes ret = nret; 69d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 70d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes out.flush(); 71d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes return ret; 72d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 73d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 74d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // Write a 32-bit integer in network byte order 75d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes private void writeInt(OutputStream os, int x) throws IOException { 76d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes os.write((x >> 24) & 0xff); 77d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes os.write((x >> 16) & 0xff); 78d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes os.write((x >> 8) & 0xff); 79d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes os.write( x & 0xff); 80d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 81d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 82d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes public ZoneCompactor(String setupFilename, String dirName) 83d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes throws Exception { 84d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes File zoneInfoFile = new File("zoneinfo.dat"); 85d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes zoneInfoFile.delete(); 86d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes OutputStream zoneInfo = new FileOutputStream(zoneInfoFile); 87d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 88d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes BufferedReader rdr = new BufferedReader(new FileReader(setupFilename)); 89d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 90d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String s; 91d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes while ((s = rdr.readLine()) != null) { 92d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes s = s.trim(); 93d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes if (s.startsWith("Link")) { 94d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes StringTokenizer st = new StringTokenizer(s); 95d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes st.nextToken(); 96d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String to = st.nextToken(); 97d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String from = st.nextToken(); 98d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes links.put(from, to); 99d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } else { 100d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String link = links.get(s); 101d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes if (link == null) { 102d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes File f = new File(dirName, s); 103d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes long length = f.length(); 104d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes starts.put(s, new Integer(start)); 105d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes lengths.put(s, new Integer((int)length)); 106d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 107d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes start += length; 108d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes byte[] data = copyFile(f, zoneInfo); 109d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 110d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes TimeZone tz = ZoneInfo.make(s, data); 111d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes int gmtOffset = tz.getRawOffset(); 112d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes offsets.put(s, new Integer(gmtOffset)); 113d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 114d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 115d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 116d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes zoneInfo.close(); 117d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 118d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // Fill in fields for links 119d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Iterator<String> iter = links.keySet().iterator(); 120d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes while (iter.hasNext()) { 121d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String from = iter.next(); 122d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String to = links.get(from); 123d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 124d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes starts.put(from, starts.get(to)); 125d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes lengths.put(from, lengths.get(to)); 126d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes offsets.put(from, offsets.get(to)); 127d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 128d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 129d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes File idxFile = new File("zoneinfo.idx"); 130d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes idxFile.delete(); 131d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes FileOutputStream idx = new FileOutputStream(idxFile); 132d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 133d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes ArrayList<String> l = new ArrayList<String>(); 134d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes l.addAll(starts.keySet()); 135d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Collections.sort(l); 136d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes Iterator<String> ziter = l.iterator(); 137d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes while (ziter.hasNext()) { 138d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes String zname = ziter.next(); 139d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes if (zname.length() >= MAXNAME) { 140d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes System.err.println("Error - zone filename exceeds " + 141d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes (MAXNAME - 1) + " characters!"); 142d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 143d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 144d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes byte[] znameBuf = new byte[MAXNAME]; 145d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes for (int i = 0; i < zname.length(); i++) { 146d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes znameBuf[i] = (byte)zname.charAt(i); 147d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 148d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes idx.write(znameBuf); 149d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes writeInt(idx, starts.get(zname).intValue()); 150d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes writeInt(idx, lengths.get(zname).intValue()); 151d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes writeInt(idx, offsets.get(zname).intValue()); 152d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 153d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes idx.close(); 154d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 155d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes // System.out.println("maxLength = " + maxLength); 156d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 157d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 158d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes public static void main(String[] args) throws Exception { 159d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes if (args.length != 2) { 160d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes System.err.println("usage: java ZoneCompactor <setup> <data dir>"); 161d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes System.exit(0); 162d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 163d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes new ZoneCompactor(args[0], args[1]); 164d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes } 165d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes 166d40e63ee47e4a7f072a9d9a20e09c26f0090b02cElliott Hughes} 167