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