--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/util/calendar/zi/ZoneInfoFile.java Tue Feb 12 09:25:43 2013 -0800
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (c) 2000, 2012, 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 "Class-path" 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.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.ref.SoftReference;
+import java.nio.file.FileSystems;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import sun.util.calendar.*;
+
+/**
+ * <code>ZoneInfoFile</code> reads Zone information files in the
+ * <java.home>/lib/zi directory and provides time zone
+ * information in the form of a {@link ZoneInfo} object. Also, it
+ * reads the ZoneInfoMappings file to obtain time zone IDs information
+ * that is used by the {@link ZoneInfo} class. The directory layout
+ * and data file formats are as follows.
+ *
+ * <p><strong>Directory layout</strong><p>
+ *
+ * All zone data files and ZoneInfoMappings are put under the
+ * <java.home>/lib/zi directory. A path name for a given time
+ * zone ID is a concatenation of <java.home>/lib/zi/ and the
+ * time zone ID. (The file separator is replaced with the platform
+ * dependent value. e.g., '\' for Win32.) An example layout will look
+ * like as follows.
+ * <blockquote>
+ * <pre>
+ * <java.home>/lib/zi/Africa/Addis_Ababa
+ * /Africa/Dakar
+ * /America/Los_Angeles
+ * /Asia/Singapore
+ * /EET
+ * /Europe/Oslo
+ * /GMT
+ * /Pacific/Galapagos
+ * ...
+ * /ZoneInfoMappings
+ * </pre>
+ * </blockquote>
+ *
+ * A zone data file has specific information of each zone.
+ * <code>ZoneInfoMappings</code> has global information of zone IDs so
+ * that the information can be obtained without instantiating all time
+ * zones.
+ *
+ * <p><strong>File format</strong><p>
+ *
+ * Two binary-file formats based on a simple Tag-Length-Value format are used
+ * to describe TimeZone information. The generic format of a data file is:
+ * <blockquote>
+ * <pre>
+ * DataFile {
+ * u1 magic[7];
+ * u1 version;
+ * data_item data[];
+ * }
+ * </pre>
+ * </blockquote>
+ * where <code>magic</code> is a magic number identifying a file
+ * format, <code>version</code> is the format version number, and
+ * <code>data</code> is one or more <code>data_item</code>s. The
+ * <code>data_item</code> structure is:
+ * <blockquote>
+ * <pre>
+ * data_item {
+ * u1 tag;
+ * u2 length;
+ * u1 value[length];
+ * }
+ * </pre>
+ * </blockquote>
+ * where <code>tag</code> indicates the data type of the item,
+ * <code>length</code> is a byte count of the following
+ * <code>value</code> that is the content of item data.
+ * <p>
+ * All data is stored in the big-endian order. There is no boundary
+ * alignment between date items.
+ *
+ * <p><strong>1. ZoneInfo data file</strong><p>
+ *
+ * Each ZoneInfo data file consists of the following members.
+ * <br>
+ * <blockquote>
+ * <pre>
+ * ZoneInfoDataFile {
+ * u1 magic[7];
+ * u1 version;
+ * SET OF<sup>1</sup> {
+ * transition transitions<sup>2</sup>;
+ * offset_table offsets<sup>2</sup>;
+ * simpletimezone stzparams<sup>2</sup>;
+ * raw_offset rawoffset;
+ * dstsaving dst;
+ * checksum crc32;
+ * gmtoffsetwillchange gmtflag<sup>2</sup>;
+ * }
+ * }
+ * 1: an unordered collection of zero or one occurrences of each item
+ * 2: optional item
+ * </pre>
+ * </blockquote>
+ * <code>magic</code> is a byte-string constant identifying the
+ * ZoneInfo data file. This field must be <code>"javazi\0"</code>
+ * defined as {@link #JAVAZI_LABEL}.
+ * <p>
+ * <code>version</code> is the version number of the file format. This
+ * will be used for compatibility check. This field must be
+ * <code>0x01</code> in this version.
+ * <p>
+ * <code>transition</code>, <code>offset_table</code> and
+ * <code>simpletimezone</code> have information of time transition
+ * from the past to the future. Therefore, these structures don't
+ * exist if the zone didn't change zone names and haven't applied DST in
+ * the past, and haven't planned to apply it. (e.g. Asia/Tokyo zone)
+ * <p>
+ * <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code>
+ * exist in every zoneinfo file. They are used by TimeZone.class indirectly.
+ *
+ * <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a>
+ * <blockquote>
+ * <pre>
+ * transition {
+ * u1 tag; // 0x04 : constant
+ * u2 length; // byte length of whole values
+ * s8 value[length/8]; // transitions in `long'
+ * }
+ * </pre>
+ * </blockquote>
+ * See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value.
+ *
+ * <p><strong>1.2 <code>offset_table</code> structure</strong><p>
+ * <blockquote>
+ * <pre>
+ * offset_table {
+ * u1 tag; // 0x05 : constant
+ * u2 length; // byte length of whole values
+ * s4 value[length/4]; // offset values in `int'
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <p><strong>1.3 <code>simpletimezone</code> structure</strong><p>
+ * See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams}
+ * about the value.
+ * <blockquote>
+ * <pre>
+ * simpletimezone {
+ * u1 tag; // 0x06 : constant
+ * u2 length; // byte length of whole values
+ * s4 value[length/4]; // SimpleTimeZone parameters
+ * }
+ * </pre>
+ * </blockquote>
+ * See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value.
+ *
+ * <p><strong>1.4 <code>raw_offset</code> structure</strong><p>
+ * <blockquote>
+ * <pre>
+ * raw_offset {
+ * u1 tag; // 0x01 : constant
+ * u2 length; // must be 4.
+ * s4 value; // raw GMT offset [millisecond]
+ * }
+ * </pre>
+ * </blockquote>
+ * See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value.
+ *
+ * <p><strong>1.5 <code>dstsaving</code> structure</strong><p>
+ * Value has dstSaving in seconds.
+ * <blockquote>
+ * <pre>
+ * dstsaving {
+ * u1 tag; // 0x02 : constant
+ * u2 length; // must be 2.
+ * s2 value; // DST save value [second]
+ * }
+ * </pre>
+ * </blockquote>
+ * See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value.
+ *
+ * <p><strong>1.6 <code>checksum</code> structure</strong><p>
+ * <blockquote>
+ * <pre>
+ * checksum {
+ * u1 tag; // 0x03 : constant
+ * u2 length; // must be 4.
+ * s4 value; // CRC32 value of transitions
+ * }
+ * </pre>
+ * </blockquote>
+ * See {@link ZoneInfo#checksum ZoneInfo.checksum}.
+ *
+ * <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p>
+ * This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}.
+ * If this record is not present in a zoneinfo file, 0 is assumed for
+ * the value.
+ * <blockquote>
+ * <pre>
+ * gmtoffsetwillchange {
+ * u1 tag; // 0x07 : constant
+ * u2 length; // must be 1.
+ * u1 value; // 1: if the GMT raw offset will change
+ * // in the future, 0, otherwise.
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ *
+ * <p><strong>2. ZoneInfoMappings file</strong><p>
+ *
+ * The ZoneInfoMappings file consists of the following members.
+ * <br>
+ * <blockquote>
+ * <pre>
+ * ZoneInfoMappings {
+ * u1 magic[7];
+ * u1 version;
+ * SET OF {
+ * versionName version;
+ * zone_id_table zoneIDs;
+ * raw_offset_table rawoffsets;
+ * raw_offset_index_table rawoffsetindices;
+ * alias_table aliases;
+ * excluded_list excludedList;
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <code>magic</code> is a byte-string constant which has the file type.
+ * This field must be <code>"javazm\0"</code> defined as {@link #JAVAZM_LABEL}.
+ * <p>
+ * <code>version</code> is the version number of this file
+ * format. This will be used for compatibility check. This field must
+ * be <code>0x01</code> in this version.
+ * <p>
+ * <code>versionName</code> shows which version of Olson's data has been used
+ * to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br>
+ * This field is for trouble-shooting and isn't usually used in runtime.
+ * <p>
+ * <code>zone_id_table</code>, <code>raw_offset_index_table</code> and
+ * <code>alias_table</code> are general information of supported
+ * zones.
+ *
+ * <p><strong>2.1 <code>zone_id_table</code> structure</strong><p>
+ * The list of zone IDs included in the zi database. The list does
+ * <em>not</em> include zone IDs, if any, listed in excludedList.
+ * <br>
+ * <blockquote>
+ * <pre>
+ * zone_id_table {
+ * u1 tag; // 0x40 : constant
+ * u2 length; // byte length of whole values
+ * u2 zone_id_count;
+ * zone_id value[zone_id_count];
+ * }
+ *
+ * zone_id {
+ * u1 byte_length; // byte length of id
+ * u1 id[byte_length]; // zone name string
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p>
+ * <br>
+ * <blockquote>
+ * <pre>
+ * raw_offset_table {
+ * u1 tag; // 0x41 : constant
+ * u2 length; // byte length of whole values
+ * s4 value[length/4]; // raw GMT offset in milliseconds
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p>
+ * <br>
+ * <blockquote>
+ * <pre>
+ * raw_offset_index_table {
+ * u1 tag; // 0x42 : constant
+ * u2 length; // byte length of whole values
+ * u1 value[length];
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <p><strong>2.4 <code>alias_table</code> structure</strong><p>
+ * <br>
+ * <blockquote>
+ * <pre>
+ * alias_table {
+ * u1 tag; // 0x43 : constant
+ * u2 length; // byte length of whole values
+ * u2 nentries; // number of id-pairs
+ * id_pair value[nentries];
+ * }
+ *
+ * id_pair {
+ * zone_id aliasname;
+ * zone_id ID;
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <p><strong>2.5 <code>versionName</code> structure</strong><p>
+ * <br>
+ * <blockquote>
+ * <pre>
+ * versionName {
+ * u1 tag; // 0x44 : constant
+ * u2 length; // byte length of whole values
+ * u1 value[length];
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * <p><strong>2.6 <code>excludeList</code> structure</strong><p>
+ * The list of zone IDs whose zones will change their GMT offsets
+ * (a.k.a. raw offsets) some time in the future. Those IDs must be
+ * added to the list of zone IDs for getAvailableIDs(). Also they must
+ * be examined for getAvailableIDs(int) to determine the
+ * <em>current</em> GMT offsets.
+ * <br>
+ * <blockquote>
+ * <pre>
+ * excluded_list {
+ * u1 tag; // 0x45 : constant
+ * u2 length; // byte length of whole values
+ * u2 nentries; // number of zone_ids
+ * zone_id value[nentries]; // excluded zone IDs
+ * }
+ * </pre>
+ * </blockquote>
+ *
+ * @since 1.4
+ */
+
+public class ZoneInfoFile {
+
+ /**
+ * The magic number for the ZoneInfo data file format.
+ */
+ public static final byte[] JAVAZI_LABEL = {
+ (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'i', (byte)'\0'
+ };
+ private static final int JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;
+
+ /**
+ * The ZoneInfo data file format version number. Must increase
+ * one when any incompatible change has been made.
+ */
+ public static final byte JAVAZI_VERSION = 0x01;
+
+ /**
+ * Raw offset data item tag.
+ */
+ public static final byte TAG_RawOffset = 1;
+
+ /**
+ * Known last Daylight Saving Time save value data item tag.
+ */
+ public static final byte TAG_LastDSTSaving = 2;
+
+ /**
+ * Checksum data item tag.
+ */
+ public static final byte TAG_CRC32 = 3;
+
+ /**
+ * Transition data item tag.
+ */
+ public static final byte TAG_Transition = 4;
+
+ /**
+ * Offset table data item tag.
+ */
+ public static final byte TAG_Offset = 5;
+
+ /**
+ * SimpleTimeZone parameters data item tag.
+ */
+ public static final byte TAG_SimpleTimeZone = 6;
+
+ /**
+ * Raw GMT offset will change in the future.
+ */
+ public static final byte TAG_GMTOffsetWillChange = 7;
+
+
+ /**
+ * The ZoneInfoMappings file name.
+ */
+ public static final String JAVAZM_FILE_NAME = "ZoneInfoMappings";
+
+ /**
+ * The magic number for the ZoneInfoMappings file format.
+ */
+ public static final byte[] JAVAZM_LABEL = {
+ (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'m', (byte)'\0'
+ };
+ private static final int JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;
+
+ /**
+ * The ZoneInfoMappings file format version number. Must increase
+ * one when any incompatible change has been made.
+ */
+ public static final byte JAVAZM_VERSION = 0x01;
+
+ /**
+ * Time zone IDs data item tag.
+ */
+ public static final byte TAG_ZoneIDs = 64;
+
+ /**
+ * Raw GMT offsets table data item tag.
+ */
+ public static final byte TAG_RawOffsets = 65;
+
+ /**
+ * Indices to the raw GMT offset table data item tag.
+ */
+ public static final byte TAG_RawOffsetIndices = 66;
+
+ /**
+ * Time zone aliases table data item tag.
+ */
+ public static final byte TAG_ZoneAliases = 67;
+
+ /**
+ * Olson's public zone information version tag.
+ */
+ public static final byte TAG_TZDataVersion = 68;
+
+ /**
+ * Excluded zones item tag. (Added in Mustang)
+ */
+ public static final byte TAG_ExcludedZones = 69;
+
+ private static Map<String, ZoneInfoOld> zoneInfoObjects = null;
+
+ private static final ZoneInfoOld GMT = new ZoneInfoOld("GMT", 0);
+
+ static String ziDir;
+
+ /**
+ * Converts the given time zone ID to a platform dependent path
+ * name. For example, "America/Los_Angeles" is converted to
+ * "America\Los_Angeles" on Win32.
+ * @return a modified ID replacing '/' with {@link
+ * java.io.File#separatorChar File.separatorChar} if needed.
+ */
+ public static String getFileName(String ID) {
+ if (File.separatorChar == '/') {
+ return ID;
+ }
+ return ID.replace('/', File.separatorChar);
+ }
+
+ /**
+ * Gets a ZoneInfo with the given GMT offset. The object
+ * has its ID in the format of GMT{+|-}hh:mm.
+ *
+ * @param originalId the given custom id (before normalized such as "GMT+9")
+ * @param gmtOffset GMT offset <em>in milliseconds</em>
+ * @return a ZoneInfo constructed with the given GMT offset
+ */
+ public static ZoneInfoOld getCustomTimeZone(String originalId, int gmtOffset) {
+ String id = toCustomID(gmtOffset);
+
+ ZoneInfoOld zi = getFromCache(id);
+ if (zi == null) {
+ zi = new ZoneInfoOld(id, gmtOffset);
+ zi = addToCache(id, zi);
+ if (!id.equals(originalId)) {
+ zi = addToCache(originalId, zi);
+ }
+ }
+ return (ZoneInfoOld) zi.clone();
+ }
+
+ public static String toCustomID(int gmtOffset) {
+ char sign;
+ int offset = gmtOffset / 60000;
+
+ if (offset >= 0) {
+ sign = '+';
+ } else {
+ sign = '-';
+ offset = -offset;
+ }
+ int hh = offset / 60;
+ int mm = offset % 60;
+
+ char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' };
+ if (hh >= 10) {
+ buf[4] += hh / 10;
+ }
+ buf[5] += hh % 10;
+ if (mm != 0) {
+ buf[7] += mm / 10;
+ buf[8] += mm % 10;
+ }
+ return new String(buf);
+ }
+
+ /**
+ * @return a ZoneInfo instance created for the specified id, or
+ * null if there is no time zone data file found for the specified
+ * id.
+ */
+ public static ZoneInfoOld getZoneInfoOld(String id) {
+ //treat GMT zone as special
+ if ("GMT".equals(id))
+ return (ZoneInfoOld) GMT.clone();
+ ZoneInfoOld zi = getFromCache(id);
+ if (zi == null) {
+ Map<String, String> aliases = ZoneInfoOld.getCachedAliasTable();
+ if (aliases != null && aliases.get(id) != null) {
+ return null;
+ }
+ zi = createZoneInfoOld(id);
+ if (zi == null) {
+ return null;
+ }
+ zi = addToCache(id, zi);
+ }
+ return (ZoneInfoOld) zi.clone();
+ }
+
+ synchronized static ZoneInfoOld getFromCache(String id) {
+ if (zoneInfoObjects == null) {
+ return null;
+ }
+ return zoneInfoObjects.get(id);
+ }
+
+ synchronized static ZoneInfoOld addToCache(String id, ZoneInfoOld zi) {
+ if (zoneInfoObjects == null) {
+ zoneInfoObjects = new HashMap<>();
+ } else {
+ ZoneInfoOld zone = zoneInfoObjects.get(id);
+ if (zone != null) {
+ return zone;
+ }
+ }
+ zoneInfoObjects.put(id, zi);
+ return zi;
+ }
+
+ private static ZoneInfoOld createZoneInfoOld(String id) {
+ byte[] buf = readZoneInfoFile(getFileName(id));
+ if (buf == null) {
+ return null;
+ }
+
+ int index = 0;
+ int filesize = buf.length;
+ int rawOffset = 0;
+ int dstSavings = 0;
+ int checksum = 0;
+ boolean willGMTOffsetChange = false;
+ long[] transitions = null;
+ int[] offsets = null;
+ int[] simpleTimeZoneParams = null;
+
+ try {
+ for (index = 0; index < JAVAZI_LABEL.length; index++) {
+ if (buf[index] != JAVAZI_LABEL[index]) {
+ System.err.println("ZoneInfoOld: wrong magic number: " + id);
+ return null;
+ }
+ }
+ if (buf[index++] > JAVAZI_VERSION) {
+ System.err.println("ZoneInfo: incompatible version ("
+ + buf[index - 1] + "): " + id);
+ return null;
+ }
+
+ while (index < filesize) {
+ byte tag = buf[index++];
+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
+
+ if (filesize < index+len) {
+ break;
+ }
+
+ switch (tag) {
+ case TAG_CRC32:
+ {
+ int val = buf[index++] & 0xff;
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ checksum = val;
+ }
+ break;
+
+ case TAG_LastDSTSaving:
+ {
+ short val = (short)(buf[index++] & 0xff);
+ val = (short)((val << 8) + (buf[index++] & 0xff));
+ dstSavings = val * 1000;
+ }
+ break;
+
+ case TAG_RawOffset:
+ {
+ int val = buf[index++] & 0xff;
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ rawOffset = val;
+ }
+ break;
+
+ case TAG_Transition:
+ {
+ int n = len / 8;
+ transitions = new long[n];
+ for (int i = 0; i < n; i ++) {
+ long val = buf[index++] & 0xff;
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ transitions[i] = val;
+ }
+ }
+ break;
+
+ case TAG_Offset:
+ {
+ int n = len / 4;
+ offsets = new int[n];
+ for (int i = 0; i < n; i ++) {
+ int val = buf[index++] & 0xff;
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ offsets[i] = val;
+ }
+ }
+ break;
+
+ case TAG_SimpleTimeZone:
+ {
+ if (len != 32 && len != 40) {
+ System.err.println("ZoneInfo: wrong SimpleTimeZone parameter size");
+ return null;
+ }
+ int n = len / 4;
+ simpleTimeZoneParams = new int[n];
+ for (int i = 0; i < n; i++) {
+ int val = buf[index++] & 0xff;
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ simpleTimeZoneParams[i] = val;
+ }
+ }
+ break;
+
+ case TAG_GMTOffsetWillChange:
+ {
+ if (len != 1) {
+ System.err.println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange");
+ }
+ willGMTOffsetChange = buf[index++] == 1;
+ }
+ break;
+
+ default:
+ System.err.println("ZoneInfo: unknown tag < " + tag + ">. ignored.");
+ index += len;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("ZoneInfo: corrupted zoneinfo file: " + id);
+ return null;
+ }
+
+ if (index != filesize) {
+ System.err.println("ZoneInfo: wrong file size: " + id);
+ return null;
+ }
+
+ return new ZoneInfoOld(id, rawOffset, dstSavings, checksum,
+ transitions, offsets, simpleTimeZoneParams,
+ willGMTOffsetChange);
+ }
+
+ private volatile static SoftReference<List<String>> zoneIDs = null;
+
+ static List<String> getZoneIDs() {
+ List<String> ids = null;
+ SoftReference<List<String>> cache = zoneIDs;
+ if (cache != null) {
+ ids = cache.get();
+ if (ids != null) {
+ return ids;
+ }
+ }
+ byte[] buf = null;
+ buf = getZoneInfoOldMappings();
+ int index = JAVAZM_LABEL_LENGTH + 1;
+ int filesize = buf.length;
+ try {
+ loop:
+ while (index < filesize) {
+ byte tag = buf[index++];
+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
+
+ switch (tag) {
+ case TAG_ZoneIDs:
+ {
+ int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
+ ids = new ArrayList<>(n);
+
+ for (int i = 0; i < n; i++) {
+ byte m = buf[index++];
+ ids.add(new String(buf, index, m, "UTF-8"));
+ index += m;
+ }
+ }
+ break loop;
+
+ default:
+ index += len;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
+ }
+
+ zoneIDs = new SoftReference<>(ids);
+ return ids;
+ }
+
+ /**
+ * @return an alias table in HashMap where a key is an alias ID
+ * (e.g., "PST") and its value is a real time zone ID (e.g.,
+ * "America/Los_Angeles").
+ */
+ static Map<String, String> getZoneAliases() {
+ byte[] buf = getZoneInfoOldMappings();
+ int index = JAVAZM_LABEL_LENGTH + 1;
+ int filesize = buf.length;
+ Map<String, String> aliases = null;
+
+ try {
+ loop:
+ while (index < filesize) {
+ byte tag = buf[index++];
+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
+
+ switch (tag) {
+ case TAG_ZoneAliases:
+ {
+ int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
+ aliases = new HashMap<>(n);
+ for (int i = 0; i < n; i++) {
+ byte m = buf[index++];
+ String name = new String(buf, index, m, "UTF-8");
+ index += m;
+ m = buf[index++];
+ String realName = new String(buf, index, m, "UTF-8");
+ index += m;
+ aliases.put(name, realName);
+ }
+ }
+ break loop;
+
+ default:
+ index += len;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
+ return null;
+ }
+ return aliases;
+ }
+
+ private volatile static SoftReference<List<String>> excludedIDs = null;
+ private volatile static boolean hasNoExcludeList = false;
+
+ /**
+ * @return a List of zone IDs for zones that will change their GMT
+ * offsets in some future time.
+ *
+ * @since 1.6
+ */
+ static List<String> getExcludedZones() {
+ if (hasNoExcludeList) {
+ return null;
+ }
+
+ List<String> excludeList = null;
+
+ SoftReference<List<String>> cache = excludedIDs;
+ if (cache != null) {
+ excludeList = cache.get();
+ if (excludeList != null) {
+ return excludeList;
+ }
+ }
+
+ byte[] buf = getZoneInfoOldMappings();
+ int index = JAVAZM_LABEL_LENGTH + 1;
+ int filesize = buf.length;
+
+ try {
+ loop:
+ while (index < filesize) {
+ byte tag = buf[index++];
+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
+
+ switch (tag) {
+ case TAG_ExcludedZones:
+ {
+ int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
+ excludeList = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ byte m = buf[index++];
+ String name = new String(buf, index, m, "UTF-8");
+ index += m;
+ excludeList.add(name);
+ }
+ }
+ break loop;
+
+ default:
+ index += len;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
+ return null;
+ }
+
+ if (excludeList != null) {
+ excludedIDs = new SoftReference<>(excludeList);
+ } else {
+ hasNoExcludeList = true;
+ }
+ return excludeList;
+ }
+
+ private volatile static SoftReference<byte[]> rawOffsetIndices = null;
+
+ static byte[] getRawOffsetIndices() {
+ byte[] indices = null;
+
+ SoftReference<byte[]> cache = rawOffsetIndices;
+ if (cache != null) {
+ indices = cache.get();
+ if (indices != null) {
+ return indices;
+ }
+ }
+
+ byte[] buf = getZoneInfoOldMappings();
+ int index = JAVAZM_LABEL_LENGTH + 1;
+ int filesize = buf.length;
+
+ try {
+ loop:
+ while (index < filesize) {
+ byte tag = buf[index++];
+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
+
+ switch (tag) {
+ case TAG_RawOffsetIndices:
+ {
+ indices = new byte[len];
+ for (int i = 0; i < len; i++) {
+ indices[i] = buf[index++];
+ }
+ }
+ break loop;
+
+ default:
+ index += len;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
+ }
+
+ rawOffsetIndices = new SoftReference<>(indices);
+ return indices;
+ }
+
+ private volatile static SoftReference<int[]> rawOffsets = null;
+
+ static int[] getRawOffsets() {
+ int[] offsets = null;
+
+ SoftReference<int[]> cache = rawOffsets;
+ if (cache != null) {
+ offsets = cache.get();
+ if (offsets != null) {
+ return offsets;
+ }
+ }
+
+ byte[] buf = getZoneInfoOldMappings();
+ int index = JAVAZM_LABEL_LENGTH + 1;
+ int filesize = buf.length;
+
+ try {
+ loop:
+ while (index < filesize) {
+ byte tag = buf[index++];
+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
+
+ switch (tag) {
+ case TAG_RawOffsets:
+ {
+ int n = len/4;
+ offsets = new int[n];
+ for (int i = 0; i < n; i++) {
+ int val = buf[index++] & 0xff;
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ val = (val << 8) + (buf[index++] & 0xff);
+ offsets[i] = val;
+ }
+ }
+ break loop;
+
+ default:
+ index += len;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
+ }
+
+ rawOffsets = new SoftReference<>(offsets);
+ return offsets;
+ }
+
+ private volatile static SoftReference<byte[]> zoneInfoMappings = null;
+
+ private static byte[] getZoneInfoOldMappings() {
+ byte[] data;
+ SoftReference<byte[]> cache = zoneInfoMappings;
+ if (cache != null) {
+ data = cache.get();
+ if (data != null) {
+ return data;
+ }
+ }
+ data = readZoneInfoFile(JAVAZM_FILE_NAME);
+ if (data == null) {
+ throw new RuntimeException("ZoneInfoOldMapping " +
+ JAVAZM_FILE_NAME + " either doesn't exist or doesn't have data");
+ }
+
+ int index;
+ for (index = 0; index < JAVAZM_LABEL.length; index++) {
+ if (data[index] != JAVAZM_LABEL[index]) {
+ System.err.println("ZoneInfoOld: wrong magic number: " + JAVAZM_FILE_NAME);
+ return null;
+ }
+ }
+ if (data[index++] > JAVAZM_VERSION) {
+ System.err.println("ZoneInfoOld: incompatible version ("
+ + data[index - 1] + "): " + JAVAZM_FILE_NAME);
+ return null;
+ }
+
+ zoneInfoMappings = new SoftReference<>(data);
+ return data;
+ }
+
+ /**
+ * Reads the specified file under <java.home>/lib/zi into a buffer.
+ * @return the buffer, or null if any I/O error occurred.
+ */
+ private static byte[] readZoneInfoFile(final String fileName) {
+ if (fileName.indexOf("..") >= 0) {
+ return null;
+ }
+ byte[] buffer = null;
+ File file = new File(ziDir, fileName);
+ try {
+ int filesize = (int)file.length();
+ if (filesize > 0) {
+ FileInputStream fis = new FileInputStream(file);
+ buffer = new byte[filesize];
+ try {
+ if (fis.read(buffer) != filesize) {
+ throw new IOException("read error on " + fileName);
+ }
+ } finally {
+ fis.close();
+ }
+ }
+ } catch (Exception ex) {
+ if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) {
+ System.err.println("ZoneInfoOld: " + ex.getMessage());
+ }
+ }
+ return buffer;
+ }
+
+ private ZoneInfoFile() {
+ }
+}