001: /*
002: * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.javazic;
027:
028: import java.io.IOException;
029: import java.io.File;
030: import java.io.FileOutputStream;
031: import java.io.DataOutputStream;
032: import java.io.RandomAccessFile;
033: import java.util.ArrayList;
034: import java.util.Iterator;
035: import java.util.LinkedList;
036: import java.util.List;
037: import java.util.Map;
038: import java.util.Set;
039: import java.util.TreeMap;
040: import java.util.TreeSet;
041: import sun.util.calendar.ZoneInfoFile;
042:
043: /**
044: * <code>Gen</code> is one of back-end classes of javazic, and generates
045: * ZoneInfoMappings and zone-specific file for each zone.
046: */
047: class Gen extends BackEnd {
048:
049: /**
050: * Generates datafile in binary TLV format for each time zone.
051: * Regarding contents of output files, see {@link ZoneInfoFile}.
052: *
053: * @param Timezone
054: * @return 0 if no errors, or 1 if error occurred.
055: */
056: int processZoneinfo(Timezone tz) {
057: try {
058: int size;
059: String outputDir = Main.getOutputDir();
060: String zonefile = ZoneInfoFile.getFileName(tz.getName());
061:
062: /* If outputDir doesn't end with file-separator, adds it. */
063: if (!outputDir.endsWith(File.separator)) {
064: outputDir += File.separatorChar;
065: }
066:
067: /* If zonefile includes file-separator, it's treated as part of
068: * pathname. And make directory if necessary.
069: */
070: int index = zonefile.lastIndexOf(File.separatorChar);
071: if (index != -1) {
072: outputDir += zonefile.substring(0, index + 1);
073: }
074: File outD = new File(outputDir);
075: outD.mkdirs();
076:
077: FileOutputStream fos = new FileOutputStream(outputDir
078: + zonefile.substring(index + 1));
079: DataOutputStream dos = new DataOutputStream(fos);
080:
081: /* Output Label */
082: dos.write(ZoneInfoFile.JAVAZI_LABEL, 0,
083: ZoneInfoFile.JAVAZI_LABEL.length);
084:
085: /* Output Version of ZoneInfoFile */
086: dos.writeByte(ZoneInfoFile.JAVAZI_VERSION);
087:
088: List<Long> transitions = tz.getTransitions();
089: if (transitions != null) {
090: List<Integer> dstOffsets = tz.getDstOffsets();
091: List<Integer> offsets = tz.getOffsets();
092:
093: if ((dstOffsets == null && offsets != null)
094: || (dstOffsets != null && offsets == null)) {
095: Main
096: .panic("Data not exist. (dstOffsets or offsets)");
097: return 1;
098: }
099:
100: /* Output Transition records */
101: dos.writeByte(ZoneInfoFile.TAG_Transition);
102: size = transitions.size();
103: dos.writeShort((size * 8) & 0xFFFF);
104: int dstoffset;
105: for (int i = 0; i < size; i++) {
106: /* if DST offset is 0, this means DST isn't used.
107: * (NOT: offset's index is 0.)
108: */
109: if ((dstoffset = ((Integer) dstOffsets.get(i))
110: .intValue()) == -1) {
111: dstoffset = 0;
112: }
113:
114: dos.writeLong((((Long) transitions.get(i))
115: .longValue() << 12)
116: | (dstoffset << 4)
117: | ((Integer) offsets.get(i)).intValue());
118:
119: }
120:
121: /* Output data for GMTOffset */
122: List<Integer> gmtoffset = tz.getGmtOffsets();
123: dos.writeByte(ZoneInfoFile.TAG_Offset);
124: size = gmtoffset.size();
125: dos.writeShort((size * 4) & 0xFFFF);
126: for (int i = 0; i < size; i++) {
127: dos.writeInt(gmtoffset.get(i));
128: }
129: }
130:
131: /* Output data for SimpleTimeZone */
132: List<RuleRec> stz = tz.getLastRules();
133: if (stz != null) {
134: RuleRec[] rr = new RuleRec[2];
135: boolean wall = true;
136:
137: rr[0] = stz.get(0);
138: rr[1] = stz.get(1);
139:
140: dos.writeByte(ZoneInfoFile.TAG_SimpleTimeZone);
141: wall = rr[0].getTime().isWall()
142: && rr[1].getTime().isWall();
143: if (wall) {
144: dos.writeShort(32);
145: } else {
146: dos.writeShort(40);
147: }
148:
149: for (int i = 0; i < 2; i++) {
150: dos.writeInt(rr[i].getMonthNum() - 1); // 0-based month number
151: dos.writeInt(rr[i].getDay()
152: .getDayForSimpleTimeZone());
153: dos.writeInt(rr[i].getDay()
154: .getDayOfWeekForSimpleTimeZoneInt());
155: dos.writeInt((int) rr[i].getTime().getTime());
156: if (!wall) {
157: dos
158: .writeInt((rr[i].getTime().getType() & 0xFF) - 1);
159: }
160: }
161: }
162:
163: /* Output RawOffset */
164: dos.writeByte(ZoneInfoFile.TAG_RawOffset);
165: dos.writeShort(4);
166: dos.writeInt(tz.getRawOffset());
167:
168: /* Output willGMTOffsetChange flag */
169: if (tz.willGMTOffsetChange()) {
170: dos.writeByte(ZoneInfoFile.TAG_GMTOffsetWillChange);
171: dos.writeShort(1);
172: dos.writeByte(1);
173: }
174:
175: /* Output LastDSTSaving */
176: dos.writeByte(ZoneInfoFile.TAG_LastDSTSaving);
177: dos.writeShort(2);
178: dos.writeShort(tz.getLastDSTSaving() / 1000);
179:
180: /* Output checksum */
181: dos.writeByte(ZoneInfoFile.TAG_CRC32);
182: dos.writeShort(4);
183: dos.writeInt(tz.getCRC32());
184:
185: fos.close();
186: dos.close();
187: } catch (IOException e) {
188: Main.panic("IO error: " + e.getMessage());
189: return 1;
190: }
191:
192: return 0;
193: }
194:
195: /**
196: * Generates ZoneInfoMappings in binary TLV format for each zone.
197: * Regarding contents of output files, see {@link ZoneInfoFile}.
198: *
199: * @param Mappings
200: * @return 0 if no errors, or 1 if error occurred.
201: */
202: int generateSrc(Mappings map) {
203: try {
204: int index;
205: int block_size;
206: int roi_size;
207: long fp;
208: String outputDir = Main.getOutputDir();
209:
210: /* If outputDir doesn't end with file-separator, adds it. */
211: if (!outputDir.endsWith(File.separator)) {
212: outputDir += File.separatorChar;
213: }
214:
215: File outD = new File(outputDir);
216: outD.mkdirs();
217:
218: /* Open ZoneInfoMapping file to write. */
219: RandomAccessFile raf = new RandomAccessFile(outputDir
220: + ZoneInfoFile.JAVAZM_FILE_NAME, "rw");
221:
222: /* Whether rawOffsetIndex list exists or not. */
223: List<Integer> roi = map.getRawOffsetsIndex();
224: if (roi == null) {
225: Main.panic("Data not exist. (rawOffsetsIndex)");
226: return 1;
227: }
228: roi_size = roi.size();
229:
230: /* Whether rawOffsetIndexTable list exists or not. */
231: List<Set<String>> roit = map.getRawOffsetsIndexTable();
232: if (roit == null || roit.size() != roi_size) {
233: Main
234: .panic("Data not exist. (rawOffsetsIndexTable) Otherwise, Invalid size");
235: return 1;
236: }
237:
238: /* Output Label */
239: raf.write(ZoneInfoFile.JAVAZM_LABEL, 0,
240: ZoneInfoFile.JAVAZM_LABEL.length);
241:
242: /* Output Version */
243: raf.writeByte(ZoneInfoFile.JAVAZM_VERSION);
244:
245: index = ZoneInfoFile.JAVAZM_LABEL.length + 2;
246:
247: /* Output Version of Olson's tzdata */
248: byte[] b = Main.getVersionName().getBytes("UTF-8");
249: raf.writeByte(ZoneInfoFile.TAG_TZDataVersion);
250: raf.writeShort((b.length + 1) & 0xFFFF);
251: raf.write(b);
252: raf.writeByte(0x00);
253: index += b.length + 4;
254:
255: /* Output ID list. */
256: raf.writeByte(ZoneInfoFile.TAG_ZoneIDs);
257: block_size = 2;
258: raf.writeShort(block_size & 0xFFFF);
259: short nID = 0;
260: raf.writeShort(nID & 0xFFFF);
261: for (int i = 0; i < roi_size; i++) {
262: for (String key : roit.get(i)) {
263: byte size = (byte) key.getBytes("UTF-8").length;
264: raf.writeByte(size & 0xFF);
265: raf.write(key.getBytes("UTF-8"), 0, size);
266: block_size += 1 + size;
267: nID++;
268: }
269: }
270: fp = raf.getFilePointer();
271: raf.seek(index);
272: raf.writeShort((block_size) & 0xFFFF);
273: raf.writeShort(nID & 0xFFFF);
274: raf.seek(fp);
275:
276: /* Output sorted rawOffset list. */
277: raf.writeByte(ZoneInfoFile.TAG_RawOffsets);
278: index += 3 + block_size;
279: block_size = roi_size * 4;
280: raf.writeShort(block_size & 0xFFFF);
281: for (int i = 0; i < roi_size; i++) {
282: raf.writeInt(Integer.parseInt(roi.get(i).toString()));
283: }
284:
285: /* Output sorted rawOffsetIndex list. */
286: raf.writeByte(ZoneInfoFile.TAG_RawOffsetIndices);
287: index += 3 + block_size;
288: block_size = 0;
289: raf.writeShort(block_size & 0xFFFF);
290: int num;
291: for (int i = 0; i < roi_size; i++) {
292: num = roit.get(i).size();
293: block_size += num;
294: for (int j = 0; j < num; j++) {
295: raf.writeByte(i);
296: }
297: }
298: fp = raf.getFilePointer();
299: raf.seek(index);
300: raf.writeShort((block_size) & 0xFFFF);
301: raf.seek(fp);
302:
303: /* Whether alias list exists or not. */
304: Map<String, String> a = map.getAliases();
305: if (a == null) {
306: Main.panic("Data not exist. (aliases)");
307: return 0;
308: }
309:
310: /* Output ID list. */
311: raf.writeByte(ZoneInfoFile.TAG_ZoneAliases);
312: index += 3 + block_size;
313: block_size = 2;
314: raf.writeShort(block_size & 0xFFFF);
315: raf.writeShort(a.size() & 0xFFFF);
316: for (String key : a.keySet()) {
317: String alias = a.get(key);
318: byte key_size = (byte) key.length();
319: byte alias_size = (byte) alias.length();
320: raf.writeByte(key_size & 0xFF);
321: raf.write(key.getBytes("UTF-8"), 0, key_size);
322: raf.writeByte(alias_size & 0xFF);
323: raf.write(alias.getBytes("UTF-8"), 0, alias_size);
324: block_size += 2 + key_size + alias_size;
325: }
326: fp = raf.getFilePointer();
327: raf.seek(index);
328: raf.writeShort((block_size) & 0xFFFF);
329: raf.seek(fp);
330:
331: /* Output the exclude list if it exists. */
332: List<String> excludedZones = map.getExcludeList();
333: if (excludedZones != null) {
334: raf.writeByte(ZoneInfoFile.TAG_ExcludedZones);
335: index += 3 + block_size;
336: block_size = 2;
337: raf.writeShort(block_size & 0xFFFF); // place holder
338: raf.writeShort(excludedZones.size()); // the number of excluded zones
339: for (String name : excludedZones) {
340: byte size = (byte) name.length();
341: raf.writeByte(size); // byte length
342: raf.write(name.getBytes("UTF-8"), 0, size); // zone name
343: block_size += 1 + size;
344: }
345: fp = raf.getFilePointer();
346: raf.seek(index);
347: raf.writeShort(block_size & 0xFFFF);
348: raf.seek(fp);
349: }
350:
351: /* Close ZoneInfoMapping file. */
352: raf.close();
353: } catch (IOException e) {
354: Main.panic("IO error: " + e.getMessage());
355: return 1;
356: }
357:
358: return 0;
359: }
360: }
|