Gen.java revision 9330:8b1f1c2a400f
1/*
2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26import  java.io.IOException;
27import  java.io.File;
28import  java.io.FileOutputStream;
29import  java.io.DataOutputStream;
30import  java.io.RandomAccessFile;
31import  java.util.List;
32import  java.util.Map;
33import  java.util.Set;
34
35/**
36 * <code>Gen</code> is one of back-end classes of javazic, and generates
37 * ZoneInfoMappings and zone-specific file for each zone.
38 */
39class Gen extends BackEnd {
40
41    /**
42     * Generates datafile in binary TLV format for each time zone.
43     * Regarding contents of output files, see {@link ZoneInfoFile}.
44     *
45     * @param Timezone
46     * @return 0 if no errors, or 1 if error occurred.
47     */
48    int processZoneinfo(Timezone tz) {
49        try {
50            int size;
51            String outputDir = Main.getOutputDir();
52            String zonefile = ZoneInfoFile.getFileName(tz.getName());
53
54            /* If outputDir doesn't end with file-separator, adds it. */
55            if (!outputDir.endsWith(File.separator)) {
56                outputDir += File.separatorChar;
57            }
58
59            /* If zonefile includes file-separator, it's treated as part of
60             * pathname. And make directory if necessary.
61             */
62            int index = zonefile.lastIndexOf(File.separatorChar);
63            if (index != -1) {
64                outputDir += zonefile.substring(0, index+1);
65            }
66            File outD = new File(outputDir);
67            outD.mkdirs();
68
69            FileOutputStream fos =
70                new FileOutputStream(outputDir + zonefile.substring(index+1));
71            DataOutputStream dos = new DataOutputStream(fos);
72
73            /* Output Label */
74            dos.write(ZoneInfoFile.JAVAZI_LABEL, 0,
75                      ZoneInfoFile.JAVAZI_LABEL.length);
76
77            /* Output Version of ZoneInfoFile */
78            dos.writeByte(ZoneInfoFile.JAVAZI_VERSION);
79
80            List<Long> transitions = tz.getTransitions();
81            if (transitions != null) {
82                List<Integer> dstOffsets = tz.getDstOffsets();
83                List<Integer> offsets = tz.getOffsets();
84
85                if ((dstOffsets == null && offsets != null) ||
86                    (dstOffsets != null && offsets == null)) {
87                    Main.panic("Data not exist. (dstOffsets or offsets)");
88                    return 1;
89                }
90
91                /* Output Transition records */
92                dos.writeByte(ZoneInfoFile.TAG_Transition);
93                size = transitions.size();
94                dos.writeShort((size * 8) & 0xFFFF);
95                int dstoffset;
96                for (int i = 0; i < size; i++) {
97                    /* if DST offset is 0, this means DST isn't used.
98                     * (NOT: offset's index is 0.)
99                     */
100                    if ((dstoffset = dstOffsets.get(i).intValue()) == -1) {
101                        dstoffset = 0;
102                    }
103
104                    dos.writeLong((transitions.get(i).longValue() << 12)
105                                  | (dstoffset << 4)
106                                  | offsets.get(i).intValue());
107
108                }
109
110                /* Output data for GMTOffset */
111                List<Integer> gmtoffset = tz.getGmtOffsets();
112                dos.writeByte(ZoneInfoFile.TAG_Offset);
113                size = gmtoffset.size();
114                dos.writeShort((size * 4) & 0xFFFF);
115                for (int i = 0; i < size; i++) {
116                    dos.writeInt(gmtoffset.get(i));
117                }
118            }
119
120            /* Output data for SimpleTimeZone */
121            List<RuleRec> stz = tz.getLastRules();
122            if (stz != null) {
123                RuleRec[] rr = new RuleRec[2];
124                boolean wall = true;
125
126                rr[0] = stz.get(0);
127                rr[1] = stz.get(1);
128
129                dos.writeByte(ZoneInfoFile.TAG_SimpleTimeZone);
130                wall = rr[0].getTime().isWall() && rr[1].getTime().isWall();
131                if (wall) {
132                    dos.writeShort(32);
133                } else {
134                    dos.writeShort(40);
135                }
136
137                for (int i = 0; i < 2; i++) {
138                    dos.writeInt(rr[i].getMonthNum() - 1); // 0-based month number
139                    dos.writeInt(rr[i].getDay().getDayForSimpleTimeZone());
140                    dos.writeInt(rr[i].getDay().getDayOfWeekForSimpleTimeZoneInt());
141                    dos.writeInt((int)rr[i].getTime().getTime());
142                    if (!wall) {
143                        dos.writeInt((rr[i].getTime().getType() & 0xFF) - 1);
144                    }
145                }
146            }
147
148            /* Output RawOffset */
149            dos.writeByte(ZoneInfoFile.TAG_RawOffset);
150            dos.writeShort(4);
151            dos.writeInt(tz.getRawOffset());
152
153            /* Output willGMTOffsetChange flag */
154            if (tz.willGMTOffsetChange()) {
155                dos.writeByte(ZoneInfoFile.TAG_GMTOffsetWillChange);
156                dos.writeShort(1);
157                dos.writeByte(1);
158            }
159
160            /* Output LastDSTSaving */
161            dos.writeByte(ZoneInfoFile.TAG_LastDSTSaving);
162            dos.writeShort(2);
163            dos.writeShort(tz.getLastDSTSaving()/1000);
164
165            /* Output checksum */
166            dos.writeByte(ZoneInfoFile.TAG_CRC32);
167            dos.writeShort(4);
168            dos.writeInt(tz.getCRC32());
169
170            fos.close();
171            dos.close();
172        } catch(IOException e) {
173            Main.panic("IO error: "+e.getMessage());
174            return 1;
175        }
176
177        return 0;
178    }
179
180    /**
181     * Generates ZoneInfoMappings in binary TLV format for each zone.
182     * Regarding contents of output files, see {@link ZoneInfoFile}.
183     *
184     * @param Mappings
185     * @return 0 if no errors, or 1 if error occurred.
186     */
187    int generateSrc(Mappings map) {
188        try {
189            int index;
190            int block_size;
191            int roi_size;
192            long fp;
193            String outputDir = Main.getOutputDir();
194
195            /* If outputDir doesn't end with file-separator, adds it. */
196            if (!outputDir.endsWith(File.separator)) {
197                outputDir += File.separatorChar;
198            }
199
200            File outD = new File(outputDir);
201            outD.mkdirs();
202
203            /* Open ZoneInfoMapping file to write. */
204            RandomAccessFile raf =
205                new RandomAccessFile(outputDir + ZoneInfoFile.JAVAZM_FILE_NAME, "rw");
206
207            /* Whether rawOffsetIndex list exists or not. */
208            List<Integer> roi = map.getRawOffsetsIndex();
209            if (roi == null) {
210                Main.panic("Data not exist. (rawOffsetsIndex)");
211                return 1;
212            }
213            roi_size = roi.size();
214
215            /* Whether rawOffsetIndexTable list exists or not. */
216            List<Set<String>> roit = map.getRawOffsetsIndexTable();
217            if (roit == null || roit.size() != roi_size) {
218                Main.panic("Data not exist. (rawOffsetsIndexTable) Otherwise, Invalid size");
219                return 1;
220            }
221
222            /* Output Label */
223            raf.write(ZoneInfoFile.JAVAZM_LABEL, 0,
224                      ZoneInfoFile.JAVAZM_LABEL.length);
225
226            /* Output Version */
227            raf.writeByte(ZoneInfoFile.JAVAZM_VERSION);
228
229            index = ZoneInfoFile.JAVAZM_LABEL.length + 2;
230
231            /* Output Version of Olson's tzdata */
232            byte[] b = Main.getVersionName().getBytes("UTF-8");
233            raf.writeByte(ZoneInfoFile.TAG_TZDataVersion);
234            raf.writeShort((b.length+1) & 0xFFFF);
235            raf.write(b);
236            raf.writeByte(0x00);
237            index += b.length + 4;
238
239            /* Output ID list. */
240            raf.writeByte(ZoneInfoFile.TAG_ZoneIDs);
241            block_size = 2;
242            raf.writeShort(block_size & 0xFFFF);
243            short nID = 0;
244            raf.writeShort(nID & 0xFFFF);
245            for (int i = 0; i < roi_size; i++) {
246                for (String key : roit.get(i)) {
247                    byte size = (byte)key.getBytes("UTF-8").length;
248                    raf.writeByte(size & 0xFF);
249                    raf.write(key.getBytes("UTF-8"), 0, size);
250                    block_size += 1 + size;
251                    nID++;
252                }
253            }
254            fp = raf.getFilePointer();
255            raf.seek(index);
256            raf.writeShort((block_size) & 0xFFFF);
257            raf.writeShort(nID & 0xFFFF);
258            raf.seek(fp);
259
260            /* Output sorted rawOffset list. */
261            raf.writeByte(ZoneInfoFile.TAG_RawOffsets);
262            index += 3 + block_size;
263            block_size = roi_size * 4;
264            raf.writeShort(block_size & 0xFFFF);
265            for (int i = 0; i < roi_size; i++) {
266                raf.writeInt(Integer.parseInt(roi.get(i).toString()));
267            }
268
269            /* Output sorted rawOffsetIndex list. */
270            raf.writeByte(ZoneInfoFile.TAG_RawOffsetIndices);
271            index += 3 + block_size;
272            block_size = 0;
273            raf.writeShort(block_size & 0xFFFF);
274            int num;
275            for (int i = 0; i < roi_size; i++) {
276                num = roit.get(i).size();
277                block_size += num;
278                for (int j = 0; j < num; j++) {
279                    raf.writeByte(i);
280                }
281            }
282            fp = raf.getFilePointer();
283            raf.seek(index);
284            raf.writeShort((block_size) & 0xFFFF);
285            raf.seek(fp);
286
287            /* Whether alias list exists or not. */
288            Map<String,String> a = map.getAliases();
289            if (a == null) {
290                Main.panic("Data not exist. (aliases)");
291                return 0;
292            }
293
294            /* Output ID list. */
295            raf.writeByte(ZoneInfoFile.TAG_ZoneAliases);
296            index += 3 + block_size;
297            block_size = 2;
298            raf.writeShort(block_size & 0xFFFF);
299            raf.writeShort(a.size() & 0xFFFF);
300            for (String key : a.keySet()) {
301                String alias = a.get(key);
302                byte key_size = (byte)key.length();
303                byte alias_size = (byte)alias.length();
304                raf.writeByte(key_size & 0xFF);
305                raf.write(key.getBytes("UTF-8"), 0, key_size);
306                raf.writeByte(alias_size & 0xFF);
307                raf.write(alias.getBytes("UTF-8"), 0, alias_size);
308                block_size += 2 + key_size + alias_size;
309            }
310            fp = raf.getFilePointer();
311            raf.seek(index);
312            raf.writeShort((block_size) & 0xFFFF);
313            raf.seek(fp);
314
315            /* Output the exclude list if it exists. */
316            List<String> excludedZones = map.getExcludeList();
317            if (excludedZones != null) {
318                raf.writeByte(ZoneInfoFile.TAG_ExcludedZones);
319                index += 3 + block_size;
320                block_size = 2;
321                raf.writeShort(block_size & 0xFFFF);  // place holder
322                raf.writeShort(excludedZones.size()); // the number of excluded zones
323                for (String name : excludedZones) {
324                    byte size = (byte) name.length();
325                    raf.writeByte(size);                 // byte length
326                    raf.write(name.getBytes("UTF-8"), 0, size); // zone name
327                    block_size += 1 + size;
328                }
329                fp = raf.getFilePointer();
330                raf.seek(index);
331                raf.writeShort(block_size & 0xFFFF);
332                raf.seek(fp);
333            }
334
335            /* Close ZoneInfoMapping file. */
336            raf.close();
337        } catch(IOException e) {
338            Main.panic("IO error: "+e.getMessage());
339            return 1;
340        }
341
342        return 0;
343    }
344}
345