| /* |
| * Copyright (c) 2000, 2013, 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; |
| } |
| } |