History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/util/calendar/ZoneInfoFile.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/util/calendar/ZoneInfoFile.java+1071−0
@@ -0,0 +1,1071 @@
1+/*
2+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.util.calendar;
27+
28+import java.io.File;
29+import java.io.FileInputStream;
30+import java.io.FileNotFoundException;
31+import java.io.IOException;
32+import java.lang.ref.SoftReference;
33+import java.nio.file.FileSystems;
34+import java.security.AccessController;
35+import java.security.PrivilegedAction;
36+import java.security.PrivilegedActionException;
37+import java.security.PrivilegedExceptionAction;
38+import java.util.ArrayList;
39+import java.util.HashMap;
40+import java.util.List;
41+import java.util.Map;
42+
43+/**
44+ * <code>ZoneInfoFile</code> reads Zone information files in the
45+ * &lt;java.home&gt;/lib/zi directory and provides time zone
46+ * information in the form of a {@link ZoneInfo} object. Also, it
47+ * reads the ZoneInfoMappings file to obtain time zone IDs information
48+ * that is used by the {@link ZoneInfo} class. The directory layout
49+ * and data file formats are as follows.
50+ *
51+ * <p><strong>Directory layout</strong><p>
52+ *
53+ * All zone data files and ZoneInfoMappings are put under the
54+ * &lt;java.home&gt;/lib/zi directory. A path name for a given time
55+ * zone ID is a concatenation of &lt;java.home&gt;/lib/zi/ and the
56+ * time zone ID. (The file separator is replaced with the platform
57+ * dependent value. e.g., '\' for Win32.) An example layout will look
58+ * like as follows.
59+ * <blockquote>
60+ * <pre>
61+ * &lt;java.home&gt;/lib/zi/Africa/Addis_Ababa
62+ * /Africa/Dakar
63+ * /America/Los_Angeles
64+ * /Asia/Singapore
65+ * /EET
66+ * /Europe/Oslo
67+ * /GMT
68+ * /Pacific/Galapagos
69+ * ...
70+ * /ZoneInfoMappings
71+ * </pre>
72+ * </blockquote>
73+ *
74+ * A zone data file has specific information of each zone.
75+ * <code>ZoneInfoMappings</code> has global information of zone IDs so
76+ * that the information can be obtained without instantiating all time
77+ * zones.
78+ *
79+ * <p><strong>File format</strong><p>
80+ *
81+ * Two binary-file formats based on a simple Tag-Length-Value format are used
82+ * to describe TimeZone information. The generic format of a data file is:
83+ * <blockquote>
84+ * <pre>
85+ * DataFile {
86+ * u1 magic[7];
87+ * u1 version;
88+ * data_item data[];
89+ * }
90+ * </pre>
91+ * </blockquote>
92+ * where <code>magic</code> is a magic number identifying a file
93+ * format, <code>version</code> is the format version number, and
94+ * <code>data</code> is one or more <code>data_item</code>s. The
95+ * <code>data_item</code> structure is:
96+ * <blockquote>
97+ * <pre>
98+ * data_item {
99+ * u1 tag;
100+ * u2 length;
101+ * u1 value[length];
102+ * }
103+ * </pre>
104+ * </blockquote>
105+ * where <code>tag</code> indicates the data type of the item,
106+ * <code>length</code> is a byte count of the following
107+ * <code>value</code> that is the content of item data.
108+ * <p>
109+ * All data is stored in the big-endian order. There is no boundary
110+ * alignment between date items.
111+ *
112+ * <p><strong>1. ZoneInfo data file</strong><p>
113+ *
114+ * Each ZoneInfo data file consists of the following members.
115+ * <br>
116+ * <blockquote>
117+ * <pre>
118+ * ZoneInfoDataFile {
119+ * u1 magic[7];
120+ * u1 version;
121+ * SET OF<sup>1</sup> {
122+ * transition transitions<sup>2</sup>;
123+ * offset_table offsets<sup>2</sup>;
124+ * simpletimezone stzparams<sup>2</sup>;
125+ * raw_offset rawoffset;
126+ * dstsaving dst;
127+ * checksum crc32;
128+ * gmtoffsetwillchange gmtflag<sup>2</sup>;
129+ * }
130+ * }
131+ * 1: an unordered collection of zero or one occurrences of each item
132+ * 2: optional item
133+ * </pre>
134+ * </blockquote>
135+ * <code>magic</code> is a byte-string constant identifying the
136+ * ZoneInfo data file. This field must be <code>"javazi&#92;0"</code>
137+ * defined as {@link #JAVAZI_LABEL}.
138+ * <p>
139+ * <code>version</code> is the version number of the file format. This
140+ * will be used for compatibility check. This field must be
141+ * <code>0x01</code> in this version.
142+ * <p>
143+ * <code>transition</code>, <code>offset_table</code> and
144+ * <code>simpletimezone</code> have information of time transition
145+ * from the past to the future. Therefore, these structures don't
146+ * exist if the zone didn't change zone names and haven't applied DST in
147+ * the past, and haven't planned to apply it. (e.g. Asia/Tokyo zone)
148+ * <p>
149+ * <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code>
150+ * exist in every zoneinfo file. They are used by TimeZone.class indirectly.
151+ *
152+ * <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a>
153+ * <blockquote>
154+ * <pre>
155+ * transition {
156+ * u1 tag; // 0x04 : constant
157+ * u2 length; // byte length of whole values
158+ * s8 value[length/8]; // transitions in `long'
159+ * }
160+ * </pre>
161+ * </blockquote>
162+ * See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value.
163+ *
164+ * <p><strong>1.2 <code>offset_table</code> structure</strong><p>
165+ * <blockquote>
166+ * <pre>
167+ * offset_table {
168+ * u1 tag; // 0x05 : constant
169+ * u2 length; // byte length of whole values
170+ * s4 value[length/4]; // offset values in `int'
171+ * }
172+ * </pre>
173+ * </blockquote>
174+ *
175+ * <p><strong>1.3 <code>simpletimezone</code> structure</strong><p>
176+ * See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams}
177+ * about the value.
178+ * <blockquote>
179+ * <pre>
180+ * simpletimezone {
181+ * u1 tag; // 0x06 : constant
182+ * u2 length; // byte length of whole values
183+ * s4 value[length/4]; // SimpleTimeZone parameters
184+ * }
185+ * </pre>
186+ * </blockquote>
187+ * See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value.
188+ *
189+ * <p><strong>1.4 <code>raw_offset</code> structure</strong><p>
190+ * <blockquote>
191+ * <pre>
192+ * raw_offset {
193+ * u1 tag; // 0x01 : constant
194+ * u2 length; // must be 4.
195+ * s4 value; // raw GMT offset [millisecond]
196+ * }
197+ * </pre>
198+ * </blockquote>
199+ * See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value.
200+ *
201+ * <p><strong>1.5 <code>dstsaving</code> structure</strong><p>
202+ * Value has dstSaving in seconds.
203+ * <blockquote>
204+ * <pre>
205+ * dstsaving {
206+ * u1 tag; // 0x02 : constant
207+ * u2 length; // must be 2.
208+ * s2 value; // DST save value [second]
209+ * }
210+ * </pre>
211+ * </blockquote>
212+ * See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value.
213+ *
214+ * <p><strong>1.6 <code>checksum</code> structure</strong><p>
215+ * <blockquote>
216+ * <pre>
217+ * checksum {
218+ * u1 tag; // 0x03 : constant
219+ * u2 length; // must be 4.
220+ * s4 value; // CRC32 value of transitions
221+ * }
222+ * </pre>
223+ * </blockquote>
224+ * See {@link ZoneInfo#checksum ZoneInfo.checksum}.
225+ *
226+ * <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p>
227+ * This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}.
228+ * If this record is not present in a zoneinfo file, 0 is assumed for
229+ * the value.
230+ * <blockquote>
231+ * <pre>
232+ * gmtoffsetwillchange {
233+ * u1 tag; // 0x07 : constant
234+ * u2 length; // must be 1.
235+ * u1 value; // 1: if the GMT raw offset will change
236+ * // in the future, 0, otherwise.
237+ * }
238+ * </pre>
239+ * </blockquote>
240+ *
241+ *
242+ * <p><strong>2. ZoneInfoMappings file</strong><p>
243+ *
244+ * The ZoneInfoMappings file consists of the following members.
245+ * <br>
246+ * <blockquote>
247+ * <pre>
248+ * ZoneInfoMappings {
249+ * u1 magic[7];
250+ * u1 version;
251+ * SET OF {
252+ * versionName version;
253+ * zone_id_table zoneIDs;
254+ * raw_offset_table rawoffsets;
255+ * raw_offset_index_table rawoffsetindices;
256+ * alias_table aliases;
257+ * excluded_list excludedList;
258+ * }
259+ * }
260+ * </pre>
261+ * </blockquote>
262+ *
263+ * <code>magic</code> is a byte-string constant which has the file type.
264+ * This field must be <code>"javazm&#92;0"</code> defined as {@link #JAVAZM_LABEL}.
265+ * <p>
266+ * <code>version</code> is the version number of this file
267+ * format. This will be used for compatibility check. This field must
268+ * be <code>0x01</code> in this version.
269+ * <p>
270+ * <code>versionName</code> shows which version of Olson's data has been used
271+ * to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br>
272+ * This field is for trouble-shooting and isn't usually used in runtime.
273+ * <p>
274+ * <code>zone_id_table</code>, <code>raw_offset_index_table</code> and
275+ * <code>alias_table</code> are general information of supported
276+ * zones.
277+ *
278+ * <p><strong>2.1 <code>zone_id_table</code> structure</strong><p>
279+ * The list of zone IDs included in the zi database. The list does
280+ * <em>not</em> include zone IDs, if any, listed in excludedList.
281+ * <br>
282+ * <blockquote>
283+ * <pre>
284+ * zone_id_table {
285+ * u1 tag; // 0x40 : constant
286+ * u2 length; // byte length of whole values
287+ * u2 zone_id_count;
288+ * zone_id value[zone_id_count];
289+ * }
290+ *
291+ * zone_id {
292+ * u1 byte_length; // byte length of id
293+ * u1 id[byte_length]; // zone name string
294+ * }
295+ * </pre>
296+ * </blockquote>
297+ *
298+ * <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p>
299+ * <br>
300+ * <blockquote>
301+ * <pre>
302+ * raw_offset_table {
303+ * u1 tag; // 0x41 : constant
304+ * u2 length; // byte length of whole values
305+ * s4 value[length/4]; // raw GMT offset in milliseconds
306+ * }
307+ * </pre>
308+ * </blockquote>
309+ *
310+ * <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p>
311+ * <br>
312+ * <blockquote>
313+ * <pre>
314+ * raw_offset_index_table {
315+ * u1 tag; // 0x42 : constant
316+ * u2 length; // byte length of whole values
317+ * u1 value[length];
318+ * }
319+ * </pre>
320+ * </blockquote>
321+ *
322+ * <p><strong>2.4 <code>alias_table</code> structure</strong><p>
323+ * <br>
324+ * <blockquote>
325+ * <pre>
326+ * alias_table {
327+ * u1 tag; // 0x43 : constant
328+ * u2 length; // byte length of whole values
329+ * u2 nentries; // number of id-pairs
330+ * id_pair value[nentries];
331+ * }
332+ *
333+ * id_pair {
334+ * zone_id aliasname;
335+ * zone_id ID;
336+ * }
337+ * </pre>
338+ * </blockquote>
339+ *
340+ * <p><strong>2.5 <code>versionName</code> structure</strong><p>
341+ * <br>
342+ * <blockquote>
343+ * <pre>
344+ * versionName {
345+ * u1 tag; // 0x44 : constant
346+ * u2 length; // byte length of whole values
347+ * u1 value[length];
348+ * }
349+ * </pre>
350+ * </blockquote>
351+ *
352+ * <p><strong>2.6 <code>excludeList</code> structure</strong><p>
353+ * The list of zone IDs whose zones will change their GMT offsets
354+ * (a.k.a. raw offsets) some time in the future. Those IDs must be
355+ * added to the list of zone IDs for getAvailableIDs(). Also they must
356+ * be examined for getAvailableIDs(int) to determine the
357+ * <em>current</em> GMT offsets.
358+ * <br>
359+ * <blockquote>
360+ * <pre>
361+ * excluded_list {
362+ * u1 tag; // 0x45 : constant
363+ * u2 length; // byte length of whole values
364+ * u2 nentries; // number of zone_ids
365+ * zone_id value[nentries]; // excluded zone IDs
366+ * }
367+ * </pre>
368+ * </blockquote>
369+ *
370+ * @since 1.4
371+ */
372+
373+public class ZoneInfoFile {
374+
375+ /**
376+ * The magic number for the ZoneInfo data file format.
377+ */
378+ public static final byte[] JAVAZI_LABEL = {
379+ (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'i', (byte)'\0'
380+ };
381+ private static final int JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;
382+
383+ /**
384+ * The ZoneInfo data file format version number. Must increase
385+ * one when any incompatible change has been made.
386+ */
387+ public static final byte JAVAZI_VERSION = 0x01;
388+
389+ /**
390+ * Raw offset data item tag.
391+ */
392+ public static final byte TAG_RawOffset = 1;
393+
394+ /**
395+ * Known last Daylight Saving Time save value data item tag.
396+ */
397+ public static final byte TAG_LastDSTSaving = 2;
398+
399+ /**
400+ * Checksum data item tag.
401+ */
402+ public static final byte TAG_CRC32 = 3;
403+
404+ /**
405+ * Transition data item tag.
406+ */
407+ public static final byte TAG_Transition = 4;
408+
409+ /**
410+ * Offset table data item tag.
411+ */
412+ public static final byte TAG_Offset = 5;
413+
414+ /**
415+ * SimpleTimeZone parameters data item tag.
416+ */
417+ public static final byte TAG_SimpleTimeZone = 6;
418+
419+ /**
420+ * Raw GMT offset will change in the future.
421+ */
422+ public static final byte TAG_GMTOffsetWillChange = 7;
423+
424+
425+ /**
426+ * The ZoneInfoMappings file name.
427+ */
428+ public static final String JAVAZM_FILE_NAME = "ZoneInfoMappings";
429+
430+ /**
431+ * The magic number for the ZoneInfoMappings file format.
432+ */
433+ public static final byte[] JAVAZM_LABEL = {
434+ (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'m', (byte)'\0'
435+ };
436+ private static final int JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;
437+
438+ /**
439+ * The ZoneInfoMappings file format version number. Must increase
440+ * one when any incompatible change has been made.
441+ */
442+ public static final byte JAVAZM_VERSION = 0x01;
443+
444+ /**
445+ * Time zone IDs data item tag.
446+ */
447+ public static final byte TAG_ZoneIDs = 64;
448+
449+ /**
450+ * Raw GMT offsets table data item tag.
451+ */
452+ public static final byte TAG_RawOffsets = 65;
453+
454+ /**
455+ * Indices to the raw GMT offset table data item tag.
456+ */
457+ public static final byte TAG_RawOffsetIndices = 66;
458+
459+ /**
460+ * Time zone aliases table data item tag.
461+ */
462+ public static final byte TAG_ZoneAliases = 67;
463+
464+ /**
465+ * Olson's public zone information version tag.
466+ */
467+ public static final byte TAG_TZDataVersion = 68;
468+
469+ /**
470+ * Excluded zones item tag. (Added in Mustang)
471+ */
472+ public static final byte TAG_ExcludedZones = 69;
473+
474+ private static Map<String, ZoneInfo> zoneInfoObjects = null;
475+
476+ private static final String ziDir = AccessController.doPrivileged(
477+ new PrivilegedAction<String>() {
478+ public String run() {
479+ String zi = System.getProperty("java.home") +
480+ File.separator + "lib" + File.separator + "zi";
481+ try {
482+ zi = FileSystems.getDefault().getPath(zi).toRealPath().toString();
483+ } catch(Exception e) {
484+ }
485+ return zi;
486+ }
487+ });
488+
489+ /**
490+ * Converts the given time zone ID to a platform dependent path
491+ * name. For example, "America/Los_Angeles" is converted to
492+ * "America\Los_Angeles" on Win32.
493+ * @return a modified ID replacing '/' with {@link
494+ * java.io.File#separatorChar File.separatorChar} if needed.
495+ */
496+ public static String getFileName(String ID) {
497+ if (File.separatorChar == '/') {
498+ return ID;
499+ }
500+ return ID.replace('/', File.separatorChar);
501+ }
502+
503+ /**
504+ * Gets a ZoneInfo with the given GMT offset. The object
505+ * has its ID in the format of GMT{+|-}hh:mm.
506+ *
507+ * @param originalId the given custom id (before normalized such as "GMT+9")
508+ * @param gmtOffset GMT offset <em>in milliseconds</em>
509+ * @return a ZoneInfo constructed with the given GMT offset
510+ */
511+ public static ZoneInfo getCustomTimeZone(String originalId, int gmtOffset) {
512+ String id = toCustomID(gmtOffset);
513+
514+ ZoneInfo zi = getFromCache(id);
515+ if (zi == null) {
516+ zi = new ZoneInfo(id, gmtOffset);
517+ zi = addToCache(id, zi);
518+ if (!id.equals(originalId)) {
519+ zi = addToCache(originalId, zi);
520+ }
521+ }
522+ return (ZoneInfo) zi.clone();
523+ }
524+
525+ public static String toCustomID(int gmtOffset) {
526+ char sign;
527+ int offset = gmtOffset / 60000;
528+
529+ if (offset >= 0) {
530+ sign = '+';
531+ } else {
532+ sign = '-';
533+ offset = -offset;
534+ }
535+ int hh = offset / 60;
536+ int mm = offset % 60;
537+
538+ char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' };
539+ if (hh >= 10) {
540+ buf[4] += hh / 10;
541+ }
542+ buf[5] += hh % 10;
543+ if (mm != 0) {
544+ buf[7] += mm / 10;
545+ buf[8] += mm % 10;
546+ }
547+ return new String(buf);
548+ }
549+
550+ /**
551+ * @return a ZoneInfo instance created for the specified id, or
552+ * null if there is no time zone data file found for the specified
553+ * id.
554+ */
555+ public static ZoneInfo getZoneInfo(String id) {
556+ ZoneInfo zi = getFromCache(id);
557+ if (zi == null) {
558+ zi = createZoneInfo(id);
559+ if (zi == null) {
560+ return null;
561+ }
562+ zi = addToCache(id, zi);
563+ }
564+ return (ZoneInfo) zi.clone();
565+ }
566+
567+ synchronized static ZoneInfo getFromCache(String id) {
568+ if (zoneInfoObjects == null) {
569+ return null;
570+ }
571+ return zoneInfoObjects.get(id);
572+ }
573+
574+ synchronized static ZoneInfo addToCache(String id, ZoneInfo zi) {
575+ if (zoneInfoObjects == null) {
576+ zoneInfoObjects = new HashMap<String, ZoneInfo>();
577+ } else {
578+ ZoneInfo zone = zoneInfoObjects.get(id);
579+ if (zone != null) {
580+ return zone;
581+ }
582+ }
583+ zoneInfoObjects.put(id, zi);
584+ return zi;
585+ }
586+
587+ private static ZoneInfo createZoneInfo(String id) {
588+ byte[] buf = readZoneInfoFile(getFileName(id));
589+ if (buf == null) {
590+ return null;
591+ }
592+
593+ int index = 0;
594+ int filesize = buf.length;
595+ int rawOffset = 0;
596+ int dstSavings = 0;
597+ int checksum = 0;
598+ boolean willGMTOffsetChange = false;
599+ long[] transitions = null;
600+ int[] offsets = null;
601+ int[] simpleTimeZoneParams = null;
602+
603+ try {
604+ for (index = 0; index < JAVAZI_LABEL.length; index++) {
605+ if (buf[index] != JAVAZI_LABEL[index]) {
606+ System.err.println("ZoneInfo: wrong magic number: " + id);
607+ return null;
608+ }
609+ }
610+ if (buf[index++] > JAVAZI_VERSION) {
611+ System.err.println("ZoneInfo: incompatible version ("
612+ + buf[index - 1] + "): " + id);
613+ return null;
614+ }
615+
616+ while (index < filesize) {
617+ byte tag = buf[index++];
618+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
619+
620+ if (filesize < index+len) {
621+ break;
622+ }
623+
624+ switch (tag) {
625+ case TAG_CRC32:
626+ {
627+ int val = buf[index++] & 0xff;
628+ val = (val << 8) + (buf[index++] & 0xff);
629+ val = (val << 8) + (buf[index++] & 0xff);
630+ val = (val << 8) + (buf[index++] & 0xff);
631+ checksum = val;
632+ }
633+ break;
634+
635+ case TAG_LastDSTSaving:
636+ {
637+ short val = (short)(buf[index++] & 0xff);
638+ val = (short)((val << 8) + (buf[index++] & 0xff));
639+ dstSavings = val * 1000;
640+ }
641+ break;
642+
643+ case TAG_RawOffset:
644+ {
645+ int val = buf[index++] & 0xff;
646+ val = (val << 8) + (buf[index++] & 0xff);
647+ val = (val << 8) + (buf[index++] & 0xff);
648+ val = (val << 8) + (buf[index++] & 0xff);
649+ rawOffset = val;
650+ }
651+ break;
652+
653+ case TAG_Transition:
654+ {
655+ int n = len / 8;
656+ transitions = new long[n];
657+ for (int i = 0; i < n; i ++) {
658+ long val = buf[index++] & 0xff;
659+ val = (val << 8) + (buf[index++] & 0xff);
660+ val = (val << 8) + (buf[index++] & 0xff);
661+ val = (val << 8) + (buf[index++] & 0xff);
662+ val = (val << 8) + (buf[index++] & 0xff);
663+ val = (val << 8) + (buf[index++] & 0xff);
664+ val = (val << 8) + (buf[index++] & 0xff);
665+ val = (val << 8) + (buf[index++] & 0xff);
666+ transitions[i] = val;
667+ }
668+ }
669+ break;
670+
671+ case TAG_Offset:
672+ {
673+ int n = len / 4;
674+ offsets = new int[n];
675+ for (int i = 0; i < n; i ++) {
676+ int val = buf[index++] & 0xff;
677+ val = (val << 8) + (buf[index++] & 0xff);
678+ val = (val << 8) + (buf[index++] & 0xff);
679+ val = (val << 8) + (buf[index++] & 0xff);
680+ offsets[i] = val;
681+ }
682+ }
683+ break;
684+
685+ case TAG_SimpleTimeZone:
686+ {
687+ if (len != 32 && len != 40) {
688+ System.err.println("ZoneInfo: wrong SimpleTimeZone parameter size");
689+ return null;
690+ }
691+ int n = len / 4;
692+ simpleTimeZoneParams = new int[n];
693+ for (int i = 0; i < n; i++) {
694+ int val = buf[index++] & 0xff;
695+ val = (val << 8) + (buf[index++] & 0xff);
696+ val = (val << 8) + (buf[index++] & 0xff);
697+ val = (val << 8) + (buf[index++] & 0xff);
698+ simpleTimeZoneParams[i] = val;
699+ }
700+ }
701+ break;
702+
703+ case TAG_GMTOffsetWillChange:
704+ {
705+ if (len != 1) {
706+ System.err.println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange");
707+ }
708+ willGMTOffsetChange = buf[index++] == 1;
709+ }
710+ break;
711+
712+ default:
713+ System.err.println("ZoneInfo: unknown tag < " + tag + ">. ignored.");
714+ index += len;
715+ break;
716+ }
717+ }
718+ } catch (Exception e) {
719+ System.err.println("ZoneInfo: corrupted zoneinfo file: " + id);
720+ return null;
721+ }
722+
723+ if (index != filesize) {
724+ System.err.println("ZoneInfo: wrong file size: " + id);
725+ return null;
726+ }
727+
728+ return new ZoneInfo(id, rawOffset, dstSavings, checksum,
729+ transitions, offsets, simpleTimeZoneParams,
730+ willGMTOffsetChange);
731+ }
732+
733+ private volatile static SoftReference<List<String>> zoneIDs = null;
734+
735+ static List<String> getZoneIDs() {
736+ List<String> ids = null;
737+
738+ SoftReference<List<String>> cache = zoneIDs;
739+ if (cache != null) {
740+ ids = cache.get();
741+ if (ids != null) {
742+ return ids;
743+ }
744+ }
745+
746+ byte[] buf = null;
747+ buf = getZoneInfoMappings();
748+ int index = JAVAZM_LABEL_LENGTH + 1;
749+ int filesize = buf.length;
750+
751+ try {
752+ loop:
753+ while (index < filesize) {
754+ byte tag = buf[index++];
755+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
756+
757+ switch (tag) {
758+ case TAG_ZoneIDs:
759+ {
760+ int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
761+ ids = new ArrayList<String>(n);
762+
763+ for (int i = 0; i < n; i++) {
764+ byte m = buf[index++];
765+ ids.add(new String(buf, index, m, "UTF-8"));
766+ index += m;
767+ }
768+ }
769+ break loop;
770+
771+ default:
772+ index += len;
773+ break;
774+ }
775+ }
776+ } catch (Exception e) {
777+ System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
778+ }
779+
780+ zoneIDs = new SoftReference<List<String>>(ids);
781+ return ids;
782+ }
783+
784+ /**
785+ * @return an alias table in HashMap where a key is an alias ID
786+ * (e.g., "PST") and its value is a real time zone ID (e.g.,
787+ * "America/Los_Angeles").
788+ */
789+ static Map<String, String> getZoneAliases() {
790+ byte[] buf = getZoneInfoMappings();
791+ int index = JAVAZM_LABEL_LENGTH + 1;
792+ int filesize = buf.length;
793+ Map<String, String> aliases = null;
794+
795+ try {
796+ loop:
797+ while (index < filesize) {
798+ byte tag = buf[index++];
799+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
800+
801+ switch (tag) {
802+ case TAG_ZoneAliases:
803+ {
804+ int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
805+ aliases = new HashMap<String, String>(n);
806+ for (int i = 0; i < n; i++) {
807+ byte m = buf[index++];
808+ String name = new String(buf, index, m, "UTF-8");
809+ index += m;
810+ m = buf[index++];
811+ String realName = new String(buf, index, m, "UTF-8");
812+ index += m;
813+ aliases.put(name, realName);
814+ }
815+ }
816+ break loop;
817+
818+ default:
819+ index += len;
820+ break;
821+ }
822+ }
823+ } catch (Exception e) {
824+ System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
825+ return null;
826+ }
827+ return aliases;
828+ }
829+
830+ private volatile static SoftReference<List<String>> excludedIDs = null;
831+ private volatile static boolean hasNoExcludeList = false;
832+
833+ /**
834+ * @return a List of zone IDs for zones that will change their GMT
835+ * offsets in some future time.
836+ *
837+ * @since 1.6
838+ */
839+ static List<String> getExcludedZones() {
840+ if (hasNoExcludeList) {
841+ return null;
842+ }
843+
844+ List<String> excludeList = null;
845+
846+ SoftReference<List<String>> cache = excludedIDs;
847+ if (cache != null) {
848+ excludeList = cache.get();
849+ if (excludeList != null) {
850+ return excludeList;
851+ }
852+ }
853+
854+ byte[] buf = getZoneInfoMappings();
855+ int index = JAVAZM_LABEL_LENGTH + 1;
856+ int filesize = buf.length;
857+
858+ try {
859+ loop:
860+ while (index < filesize) {
861+ byte tag = buf[index++];
862+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
863+
864+ switch (tag) {
865+ case TAG_ExcludedZones:
866+ {
867+ int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
868+ excludeList = new ArrayList<String>();
869+ for (int i = 0; i < n; i++) {
870+ byte m = buf[index++];
871+ String name = new String(buf, index, m, "UTF-8");
872+ index += m;
873+ excludeList.add(name);
874+ }
875+ }
876+ break loop;
877+
878+ default:
879+ index += len;
880+ break;
881+ }
882+ }
883+ } catch (Exception e) {
884+ System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
885+ return null;
886+ }
887+
888+ if (excludeList != null) {
889+ excludedIDs = new SoftReference<List<String>>(excludeList);
890+ } else {
891+ hasNoExcludeList = true;
892+ }
893+ return excludeList;
894+ }
895+
896+ private volatile static SoftReference<byte[]> rawOffsetIndices = null;
897+
898+ static byte[] getRawOffsetIndices() {
899+ byte[] indices = null;
900+
901+ SoftReference<byte[]> cache = rawOffsetIndices;
902+ if (cache != null) {
903+ indices = cache.get();
904+ if (indices != null) {
905+ return indices;
906+ }
907+ }
908+
909+ byte[] buf = getZoneInfoMappings();
910+ int index = JAVAZM_LABEL_LENGTH + 1;
911+ int filesize = buf.length;
912+
913+ try {
914+ loop:
915+ while (index < filesize) {
916+ byte tag = buf[index++];
917+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
918+
919+ switch (tag) {
920+ case TAG_RawOffsetIndices:
921+ {
922+ indices = new byte[len];
923+ for (int i = 0; i < len; i++) {
924+ indices[i] = buf[index++];
925+ }
926+ }
927+ break loop;
928+
929+ default:
930+ index += len;
931+ break;
932+ }
933+ }
934+ } catch (ArrayIndexOutOfBoundsException e) {
935+ System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
936+ }
937+
938+ rawOffsetIndices = new SoftReference<byte[]>(indices);
939+ return indices;
940+ }
941+
942+ private volatile static SoftReference<int[]> rawOffsets = null;
943+
944+ static int[] getRawOffsets() {
945+ int[] offsets = null;
946+
947+ SoftReference<int[]> cache = rawOffsets;
948+ if (cache != null) {
949+ offsets = cache.get();
950+ if (offsets != null) {
951+ return offsets;
952+ }
953+ }
954+
955+ byte[] buf = getZoneInfoMappings();
956+ int index = JAVAZM_LABEL_LENGTH + 1;
957+ int filesize = buf.length;
958+
959+ try {
960+ loop:
961+ while (index < filesize) {
962+ byte tag = buf[index++];
963+ int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
964+
965+ switch (tag) {
966+ case TAG_RawOffsets:
967+ {
968+ int n = len/4;
969+ offsets = new int[n];
970+ for (int i = 0; i < n; i++) {
971+ int val = buf[index++] & 0xff;
972+ val = (val << 8) + (buf[index++] & 0xff);
973+ val = (val << 8) + (buf[index++] & 0xff);
974+ val = (val << 8) + (buf[index++] & 0xff);
975+ offsets[i] = val;
976+ }
977+ }
978+ break loop;
979+
980+ default:
981+ index += len;
982+ break;
983+ }
984+ }
985+ } catch (ArrayIndexOutOfBoundsException e) {
986+ System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
987+ }
988+
989+ rawOffsets = new SoftReference<int[]>(offsets);
990+ return offsets;
991+ }
992+
993+ private volatile static SoftReference<byte[]> zoneInfoMappings = null;
994+
995+ private static byte[] getZoneInfoMappings() {
996+ byte[] data;
997+
998+ SoftReference<byte[]> cache = zoneInfoMappings;
999+ if (cache != null) {
1000+ data = cache.get();
1001+ if (data != null) {
1002+ return data;
1003+ }
1004+ }
1005+
1006+ data = readZoneInfoFile(JAVAZM_FILE_NAME);
1007+
1008+ if (data == null) {
1009+ return null;
1010+ }
1011+
1012+ int index;
1013+ for (index = 0; index < JAVAZM_LABEL.length; index++) {
1014+ if (data[index] != JAVAZM_LABEL[index]) {
1015+ System.err.println("ZoneInfo: wrong magic number: " + JAVAZM_FILE_NAME);
1016+ return null;
1017+ }
1018+ }
1019+ if (data[index++] > JAVAZM_VERSION) {
1020+ System.err.println("ZoneInfo: incompatible version ("
1021+ + data[index - 1] + "): " + JAVAZM_FILE_NAME);
1022+ return null;
1023+ }
1024+
1025+ zoneInfoMappings = new SoftReference<byte[]>(data);
1026+ return data;
1027+ }
1028+
1029+ /**
1030+ * Reads the specified file under &lt;java.home&gt;/lib/zi into a buffer.
1031+ * @return the buffer, or null if any I/O error occurred.
1032+ */
1033+ private static byte[] readZoneInfoFile(final String fileName) {
1034+ byte[] buffer = null;
1035+
1036+ try {
1037+ buffer = (byte[]) AccessController.doPrivileged(new PrivilegedExceptionAction() {
1038+ public Object run() throws IOException {
1039+ File file = new File(ziDir, fileName);
1040+ if (!file.exists() || !file.isFile()) {
1041+ return null;
1042+ }
1043+ file = file.getCanonicalFile();
1044+ String path = file.getCanonicalPath();
1045+ byte[] buf = null;
1046+ if (path != null && path.startsWith(ziDir)) {
1047+ int filesize = (int)file.length();
1048+ if (filesize > 0) {
1049+ FileInputStream fis = new FileInputStream(file);
1050+ buf = new byte[filesize];
1051+ try {
1052+ if (fis.read(buf) != filesize) {
1053+ throw new IOException("read error on " + fileName);
1054+ }
1055+ } finally {
1056+ fis.close();
1057+ }
1058+ }
1059+ }
1060+ return buf;
1061+ }
1062+ });
1063+ } catch (PrivilegedActionException e) {
1064+ Exception ex = e.getException();
1065+ if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) {
1066+ System.err.println("ZoneInfo: " + ex.getMessage());
1067+ }
1068+ }
1069+ return buffer;
1070+ }
1071+}