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