blob: 9e348a369b797bb40f63094eec11a8058ab06aff [file] [log] [blame]
/*
* 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;
}
}