blob: cc77c94de53bcdfbe270d96b8c257599299118a3 [file] [log] [blame]
Elliott Hughesd40e63e2011-02-17 16:20:07 -08001
2import java.io.*;
3import 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
32public 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];
Tom Giordanoaf936f12011-12-22 00:44:14 +110058 int length = 0;
Elliott Hughesd40e63e2011-02-17 16:20:07 -080059 while (true) {
60 int nbytes = in.read(buf);
61 if (nbytes == -1) {
62 break;
63 }
Tom Giordanoaf936f12011-12-22 00:44:14 +110064 length += nbytes;
Elliott Hughesd40e63e2011-02-17 16:20:07 -080065 out.write(buf, 0, nbytes);
66
67 byte[] nret = new byte[ret.length + nbytes];
68 System.arraycopy(ret, 0, nret, 0, ret.length);
69 System.arraycopy(buf, 0, nret, ret.length, nbytes);
70 ret = nret;
71 }
Tom Giordanoaf936f12011-12-22 00:44:14 +110072 if (length%4 != 0)
73 out.write(new byte[] {00,00,00,00}, 0, 4 - length % 4);
Elliott Hughesd40e63e2011-02-17 16:20:07 -080074 out.flush();
75 return ret;
76 }
77
78 // Write a 32-bit integer in network byte order
79 private void writeInt(OutputStream os, int x) throws IOException {
80 os.write((x >> 24) & 0xff);
81 os.write((x >> 16) & 0xff);
82 os.write((x >> 8) & 0xff);
83 os.write( x & 0xff);
84 }
85
86 public ZoneCompactor(String setupFilename, String dirName)
87 throws Exception {
88 File zoneInfoFile = new File("zoneinfo.dat");
89 zoneInfoFile.delete();
90 OutputStream zoneInfo = new FileOutputStream(zoneInfoFile);
91
92 BufferedReader rdr = new BufferedReader(new FileReader(setupFilename));
93
94 String s;
95 while ((s = rdr.readLine()) != null) {
96 s = s.trim();
97 if (s.startsWith("Link")) {
98 StringTokenizer st = new StringTokenizer(s);
99 st.nextToken();
100 String to = st.nextToken();
101 String from = st.nextToken();
102 links.put(from, to);
103 } else {
104 String link = links.get(s);
105 if (link == null) {
106 File f = new File(dirName, s);
107 long length = f.length();
108 starts.put(s, new Integer(start));
109 lengths.put(s, new Integer((int)length));
110
111 start += length;
Tom Giordanoaf936f12011-12-22 00:44:14 +1100112 if (start % 4 != 0)
113 start += 4 - start % 4;
114
Elliott Hughesd40e63e2011-02-17 16:20:07 -0800115 byte[] data = copyFile(f, zoneInfo);
116
117 TimeZone tz = ZoneInfo.make(s, data);
118 int gmtOffset = tz.getRawOffset();
119 offsets.put(s, new Integer(gmtOffset));
120 }
121 }
122 }
123 zoneInfo.close();
124
125 // Fill in fields for links
126 Iterator<String> iter = links.keySet().iterator();
127 while (iter.hasNext()) {
128 String from = iter.next();
129 String to = links.get(from);
130
131 starts.put(from, starts.get(to));
132 lengths.put(from, lengths.get(to));
133 offsets.put(from, offsets.get(to));
134 }
135
136 File idxFile = new File("zoneinfo.idx");
137 idxFile.delete();
138 FileOutputStream idx = new FileOutputStream(idxFile);
139
140 ArrayList<String> l = new ArrayList<String>();
141 l.addAll(starts.keySet());
142 Collections.sort(l);
143 Iterator<String> ziter = l.iterator();
144 while (ziter.hasNext()) {
145 String zname = ziter.next();
146 if (zname.length() >= MAXNAME) {
147 System.err.println("Error - zone filename exceeds " +
148 (MAXNAME - 1) + " characters!");
149 }
150
151 byte[] znameBuf = new byte[MAXNAME];
152 for (int i = 0; i < zname.length(); i++) {
153 znameBuf[i] = (byte)zname.charAt(i);
154 }
155 idx.write(znameBuf);
156 writeInt(idx, starts.get(zname).intValue());
157 writeInt(idx, lengths.get(zname).intValue());
158 writeInt(idx, offsets.get(zname).intValue());
159 }
160 idx.close();
161
162 // System.out.println("maxLength = " + maxLength);
163 }
164
165 public static void main(String[] args) throws Exception {
166 if (args.length != 2) {
167 System.err.println("usage: java ZoneCompactor <setup> <data dir>");
168 System.exit(0);
169 }
170 new ZoneCompactor(args[0], args[1]);
171 }
172
173}