/* * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.IOException; import java.io.File; import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.RandomAccessFile; import java.util.List; import java.util.Map; import java.util.Set; /** * <code>Gen</code> is one of back-end classes of javazic, and generates * ZoneInfoMappings and zone-specific file for each zone. */ class Gen extends BackEnd { /** * Generates datafile in binary TLV format for each time zone. * Regarding contents of output files, see {@link ZoneInfoFile}. * * @param Timezone * @return 0 if no errors, or 1 if error occurred. */ int processZoneinfo(Timezone tz) { try { int size; String outputDir = Main.getOutputDir(); String zonefile = ZoneInfoFile.getFileName(tz.getName()); /* If outputDir doesn't end with file-separator, adds it. */ if (!outputDir.endsWith(File.separator)) { outputDir += File.separatorChar; } /* If zonefile includes file-separator, it's treated as part of * pathname. And make directory if necessary. */ int index = zonefile.lastIndexOf(File.separatorChar); if (index != -1) { outputDir += zonefile.substring(0, index+1); } File outD = new File(outputDir); outD.mkdirs(); FileOutputStream fos = new FileOutputStream(outputDir + zonefile.substring(index+1)); DataOutputStream dos = new DataOutputStream(fos); /* Output Label */ dos.write(ZoneInfoFile.JAVAZI_LABEL, 0, ZoneInfoFile.JAVAZI_LABEL.length); /* Output Version of ZoneInfoFile */ dos.writeByte(ZoneInfoFile.JAVAZI_VERSION); List<Long> transitions = tz.getTransitions(); if (transitions != null) { List<Integer> dstOffsets = tz.getDstOffsets(); List<Integer> offsets = tz.getOffsets(); if ((dstOffsets == null && offsets != null) || (dstOffsets != null && offsets == null)) { Main.panic("Data not exist. (dstOffsets or offsets)"); return 1; } /* Output Transition records */ dos.writeByte(ZoneInfoFile.TAG_Transition); size = transitions.size(); dos.writeShort((size * 8) & 0xFFFF); int dstoffset; for (int i = 0; i < size; i++) { /* if DST offset is 0, this means DST isn't used. * (NOT: offset's index is 0.) */ if ((dstoffset = dstOffsets.get(i).intValue()) == -1) { dstoffset = 0; } dos.writeLong((transitions.get(i).longValue() << 12) | (dstoffset << 4) | offsets.get(i).intValue()); } /* Output data for GMTOffset */ List<Integer> gmtoffset = tz.getGmtOffsets(); dos.writeByte(ZoneInfoFile.TAG_Offset); size = gmtoffset.size(); dos.writeShort((size * 4) & 0xFFFF); for (int i = 0; i < size; i++) { dos.writeInt(gmtoffset.get(i)); } } /* Output data for SimpleTimeZone */ List<RuleRec> stz = tz.getLastRules(); if (stz != null) { RuleRec[] rr = new RuleRec[2]; boolean wall = true; rr[0] = stz.get(0); rr[1] = stz.get(1); dos.writeByte(ZoneInfoFile.TAG_SimpleTimeZone); wall = rr[0].getTime().isWall() && rr[1].getTime().isWall(); if (wall) { dos.writeShort(32); } else { dos.writeShort(40); } for (int i = 0; i < 2; i++) { dos.writeInt(rr[i].getMonthNum() - 1); // 0-based month number dos.writeInt(rr[i].getDay().getDayForSimpleTimeZone()); dos.writeInt(rr[i].getDay().getDayOfWeekForSimpleTimeZoneInt()); dos.writeInt((int)rr[i].getTime().getTime()); if (!wall) { dos.writeInt((rr[i].getTime().getType() & 0xFF) - 1); } } } /* Output RawOffset */ dos.writeByte(ZoneInfoFile.TAG_RawOffset); dos.writeShort(4); dos.writeInt(tz.getRawOffset()); /* Output willGMTOffsetChange flag */ if (tz.willGMTOffsetChange()) { dos.writeByte(ZoneInfoFile.TAG_GMTOffsetWillChange); dos.writeShort(1); dos.writeByte(1); } /* Output LastDSTSaving */ dos.writeByte(ZoneInfoFile.TAG_LastDSTSaving); dos.writeShort(2); dos.writeShort(tz.getLastDSTSaving()/1000); /* Output checksum */ dos.writeByte(ZoneInfoFile.TAG_CRC32); dos.writeShort(4); dos.writeInt(tz.getCRC32()); fos.close(); dos.close(); } catch(IOException e) { Main.panic("IO error: "+e.getMessage()); return 1; } return 0; } /** * Generates ZoneInfoMappings in binary TLV format for each zone. * Regarding contents of output files, see {@link ZoneInfoFile}. * * @param Mappings * @return 0 if no errors, or 1 if error occurred. */ int generateSrc(Mappings map) { try { int index; int block_size; int roi_size; long fp; String outputDir = Main.getOutputDir(); /* If outputDir doesn't end with file-separator, adds it. */ if (!outputDir.endsWith(File.separator)) { outputDir += File.separatorChar; } File outD = new File(outputDir); outD.mkdirs(); /* Open ZoneInfoMapping file to write. */ RandomAccessFile raf = new RandomAccessFile(outputDir + ZoneInfoFile.JAVAZM_FILE_NAME, "rw"); /* Whether rawOffsetIndex list exists or not. */ List<Integer> roi = map.getRawOffsetsIndex(); if (roi == null) { Main.panic("Data not exist. (rawOffsetsIndex)"); return 1; } roi_size = roi.size(); /* Whether rawOffsetIndexTable list exists or not. */ List<Set<String>> roit = map.getRawOffsetsIndexTable(); if (roit == null || roit.size() != roi_size) { Main.panic("Data not exist. (rawOffsetsIndexTable) Otherwise, Invalid size"); return 1; } /* Output Label */ raf.write(ZoneInfoFile.JAVAZM_LABEL, 0, ZoneInfoFile.JAVAZM_LABEL.length); /* Output Version */ raf.writeByte(ZoneInfoFile.JAVAZM_VERSION); index = ZoneInfoFile.JAVAZM_LABEL.length + 2; /* Output Version of Olson's tzdata */ byte[] b = Main.getVersionName().getBytes("UTF-8"); raf.writeByte(ZoneInfoFile.TAG_TZDataVersion); raf.writeShort((b.length+1) & 0xFFFF); raf.write(b); raf.writeByte(0x00); index += b.length + 4; /* Output ID list. */ raf.writeByte(ZoneInfoFile.TAG_ZoneIDs); block_size = 2; raf.writeShort(block_size & 0xFFFF); short nID = 0; raf.writeShort(nID & 0xFFFF); for (int i = 0; i < roi_size; i++) { for (String key : roit.get(i)) { byte size = (byte)key.getBytes("UTF-8").length; raf.writeByte(size & 0xFF); raf.write(key.getBytes("UTF-8"), 0, size); block_size += 1 + size; nID++; } } fp = raf.getFilePointer(); raf.seek(index); raf.writeShort((block_size) & 0xFFFF); raf.writeShort(nID & 0xFFFF); raf.seek(fp); /* Output sorted rawOffset list. */ raf.writeByte(ZoneInfoFile.TAG_RawOffsets); index += 3 + block_size; block_size = roi_size * 4; raf.writeShort(block_size & 0xFFFF); for (int i = 0; i < roi_size; i++) { raf.writeInt(Integer.parseInt(roi.get(i).toString())); } /* Output sorted rawOffsetIndex list. */ raf.writeByte(ZoneInfoFile.TAG_RawOffsetIndices); index += 3 + block_size; block_size = 0; raf.writeShort(block_size & 0xFFFF); int num; for (int i = 0; i < roi_size; i++) { num = roit.get(i).size(); block_size += num; for (int j = 0; j < num; j++) { raf.writeByte(i); } } fp = raf.getFilePointer(); raf.seek(index); raf.writeShort((block_size) & 0xFFFF); raf.seek(fp); /* Whether alias list exists or not. */ Map<String,String> a = map.getAliases(); if (a == null) { Main.panic("Data not exist. (aliases)"); return 0; } /* Output ID list. */ raf.writeByte(ZoneInfoFile.TAG_ZoneAliases); index += 3 + block_size; block_size = 2; raf.writeShort(block_size & 0xFFFF); raf.writeShort(a.size() & 0xFFFF); for (String key : a.keySet()) { String alias = a.get(key); byte key_size = (byte)key.length(); byte alias_size = (byte)alias.length(); raf.writeByte(key_size & 0xFF); raf.write(key.getBytes("UTF-8"), 0, key_size); raf.writeByte(alias_size & 0xFF); raf.write(alias.getBytes("UTF-8"), 0, alias_size); block_size += 2 + key_size + alias_size; } fp = raf.getFilePointer(); raf.seek(index); raf.writeShort((block_size) & 0xFFFF); raf.seek(fp); /* Output the exclude list if it exists. */ List<String> excludedZones = map.getExcludeList(); if (excludedZones != null) { raf.writeByte(ZoneInfoFile.TAG_ExcludedZones); index += 3 + block_size; block_size = 2; raf.writeShort(block_size & 0xFFFF); // place holder raf.writeShort(excludedZones.size()); // the number of excluded zones for (String name : excludedZones) { byte size = (byte) name.length(); raf.writeByte(size); // byte length raf.write(name.getBytes("UTF-8"), 0, size); // zone name block_size += 1 + size; } fp = raf.getFilePointer(); raf.seek(index); raf.writeShort(block_size & 0xFFFF); raf.seek(fp); } /* Close ZoneInfoMapping file. */ raf.close(); } catch(IOException e) { Main.panic("IO error: "+e.getMessage()); return 1; } return 0; } }