001: /*
002: * @(#)ZoneInfoFile.java 1.10 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package sun.util.calendar;
028:
029: import java.io.File;
030: import java.io.FileInputStream;
031: import java.io.FileNotFoundException;
032: import java.io.IOException;
033: import java.lang.ref.SoftReference;
034: import java.util.HashMap;
035: import java.security.AccessController;
036: import java.security.PrivilegedAction;
037: import java.security.PrivilegedActionException;
038: import java.security.PrivilegedExceptionAction;
039:
040: /**
041: * <code>ZoneInfoFile</code> reads Zone information files in the
042: * <java.home>/lib/zi directory and provides time zone
043: * information in the form of a {@link ZoneInfo} object. Also, it
044: * reads the ZoneInfoMappings file to obtain time zone IDs information
045: * that is used by the {@link ZoneInfo} class. The directory layout
046: * and data file formats are as follows.
047: *
048: * <p><strong>Directory layout</strong><p>
049: *
050: * All zone data files and ZoneInfoMappings are put under the
051: * <java.home>/lib/zi directory. A path name for a given time
052: * zone ID is a concatenation of <java.home>/lib/zi/ and the
053: * time zone ID. (The file separator is replaced with the platform
054: * dependent value. e.g., '\' for Win32.) An example layout will look
055: * like as follows.
056: * <blockquote>
057: * <pre>
058: * <java.home>/lib/zi/Africa/Addis_Ababa
059: * /Africa/Dakar
060: * /America/Los_Angeles
061: * /Asia/Singapore
062: * /EET
063: * /Europe/Oslo
064: * /GMT
065: * /Pacific/Galapagos
066: * ...
067: * /ZoneInfoMappings
068: * </pre>
069: * </blockquote>
070: *
071: * A zone data file has specific information of each zone.
072: * <code>ZoneInfoMappings</code> has global information of zone IDs so
073: * that the information can be obtained without instantiating all time
074: * zones.
075: *
076: * <p><strong>File format</strong><p>
077: *
078: * Two binary-file formats based on a simple Tag-Length-Value format are used
079: * to describe TimeZone information. The generic format of a data file is:
080: * <blockquote>
081: * <pre>
082: * DataFile {
083: * u1 magic[7];
084: * u1 version;
085: * data_item data[];
086: * }
087: * </pre>
088: * </blockquote>
089: * where <code>magic</code> is a magic number identifying a file
090: * format, <code>version</code> is the format version number, and
091: * <code>data</code> is one or more <code>data_item</code>s. The
092: * <code>data_item</code> structure is:
093: * <blockquote>
094: * <pre>
095: * data_item {
096: * u1 tag;
097: * u2 length;
098: * u1 value[length];
099: * }
100: * </pre>
101: * </blockquote>
102: * where <code>tag</code> indicates the data type of the item,
103: * <code>length</code> is a byte count of the following
104: * <code>value</code> that is the content of item data.
105: * <p>
106: * All data is stored in the big-endian order. There is no boundary
107: * alignment between date items.
108: *
109: * <p><strong>1. ZoneInfo data file</strong><p>
110: *
111: * Each ZoneInfo data file consists of the following members.
112: * <br>
113: * <blockquote>
114: * <pre>
115: * ZoneInfoDataFile {
116: * u1 magic[7];
117: * u1 version;
118: * SET OF<sup>1</sup> {
119: * transition transitions<sup>2</sup>;
120: * offset_table offsets<sup>2</sup>;
121: * simpletimezone stzparams<sup>2</sup>;
122: * raw_offset rawoffset;
123: * dstsaving dst;
124: * checksum crc32;
125: * gmtoffsetwillchange gmtflag<sup>2</sup>;
126: * }
127: * }
128: * 1: an unordered collection of zero or one occurrences of each item
129: * 2: optional item
130: * </pre>
131: * </blockquote>
132: * <code>magic</code> is a byte-string constant identifying the
133: * ZoneInfo data file. This field must be <code>"javazi\0"</code>
134: * defined as {@link #JAVAZI_LABEL}.
135: * <p>
136: * <code>version</code> is the version number of the file format. This
137: * will be used for compatibility check. This field must be
138: * <code>0x01</code> in this version.
139: * <p>
140: * <code>transition</code>, <code>offset_table</code> and
141: * <code>simpletimezone</code> have information of time transition
142: * from the past to the future. Therefore, these structures don't
143: * exist if the zone didn't change zone names and haven't applied DST in
144: * the past, and haven't planned to apply it. (e.g. Asia/Tokyo zone)
145: * <p>
146: * <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code>
147: * exist in every zoneinfo file. They are used by TimeZone.class indirectly.
148: *
149: * <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a>
150: * <blockquote>
151: * <pre>
152: * transition {
153: * u1 tag; // 0x04 : constant
154: * u2 length; // byte length of whole values
155: * s8 value[length/8]; // transitions in `long'
156: * }
157: * </pre>
158: * </blockquote>
159: * See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value.
160: *
161: * <p><strong>1.2 <code>offset_table</code> structure</strong><p>
162: * <blockquote>
163: * <pre>
164: * offset_table {
165: * u1 tag; // 0x05 : constant
166: * u2 length; // byte length of whole values
167: * s4 value[length/4]; // offset values in `int'
168: * }
169: * </pre>
170: * </blockquote>
171: *
172: * <p><strong>1.3 <code>simpletimezone</code> structure</strong><p>
173: * See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams}
174: * about the value.
175: * <blockquote>
176: * <pre>
177: * simpletimezone {
178: * u1 tag; // 0x06 : constant
179: * u2 length; // byte length of whole values
180: * s4 value[length/4]; // SimpleTimeZone parameters
181: * }
182: * </pre>
183: * </blockquote>
184: * See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value.
185: *
186: * <p><strong>1.4 <code>raw_offset</code> structure</strong><p>
187: * <blockquote>
188: * <pre>
189: * raw_offset {
190: * u1 tag; // 0x01 : constant
191: * u2 length; // must be 4.
192: * s4 value; // raw GMT offset [millisecond]
193: * }
194: * </pre>
195: * </blockquote>
196: * See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value.
197: *
198: * <p><strong>1.5 <code>dstsaving</code> structure</strong><p>
199: * Value has dstSaving in seconds.
200: * <blockquote>
201: * <pre>
202: * dstsaving {
203: * u1 tag; // 0x02 : constant
204: * u2 length; // must be 2.
205: * s2 value; // DST save value [second]
206: * }
207: * </pre>
208: * </blockquote>
209: * See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value.
210: *
211: * <p><strong>1.6 <code>checksum</code> structure</strong><p>
212: * <blockquote>
213: * <pre>
214: * checksum {
215: * u1 tag; // 0x03 : constant
216: * u2 length; // must be 4.
217: * s4 value; // CRC32 value of transitions
218: * }
219: * </pre>
220: * </blockquote>
221: * See {@link ZoneInfo#checksum ZoneInfo.checksum}.
222: *
223: * <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p>
224: * This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}.
225: * If this record is not present in a zoneinfo file, 0 is assumed for
226: * the value.
227: * <blockquote>
228: * <pre>
229: * gmtoffsetwillchange {
230: * u1 tag; // 0x07 : constant
231: * u2 length; // must be 1.
232: * u1 value; // 1: if the GMT raw offset will change
233: * // in the future, 0, otherwise.
234: * }
235: * </pre>
236: * </blockquote>
237: *
238: *
239: * <p><strong>2. ZoneInfoMappings file</strong><p>
240: *
241: * The ZoneInfoMappings file consists of the following members.
242: * <br>
243: * <blockquote>
244: * <pre>
245: * ZoneInfoMappings {
246: * u1 magic[7];
247: * u1 version;
248: * SET OF {
249: * versionName version;
250: * zone_id_table zoneIDs;
251: * raw_offset_table rawoffsets;
252: * raw_offset_index_table rawoffsetindices;
253: * alias_table aliases;
254: * }
255: * }
256: * </pre>
257: * </blockquote>
258: *
259: * <code>magic</code> is a byte-string constant which has the file type.
260: * This field must be <code>"javazm\0"</code> defined as {@link #JAVAZM_LABEL}.
261: * <p>
262: * <code>version</code> is the version number of this file
263: * format. This will be used for compatibility check. This field must
264: * be <code>0x01</code> in this version.
265: * <p>
266: * <code>versionName</code> shows which version of Olson's data has been used
267: * to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br>
268: * This field is for trouble-shooting and isn't usually used in runtime.
269: * <p>
270: * <code>zone_id_table</code, <code>raw_offset_index_table</code> and
271: * <code>alias_table</code> are general information of supported
272: * zones.
273: *
274: * <p><strong>2.1 <code>zone_id_table</code> structure</strong><p>
275: * <br>
276: * <blockquote>
277: * <pre>
278: * zone_id_table {
279: * u1 tag; // 0x40 : constant
280: * u2 length; // byte length of whole values
281: * u2 zone_id_count;
282: * zone_id value[zone_id_count];
283: * }
284: *
285: * zone_id {
286: * u1 byte_length; // byte length of id
287: * u1 id[byte_length]; // zone name string
288: * }
289: * </pre>
290: * </blockquote>
291: *
292: * <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p>
293: * <br>
294: * <blockquote>
295: * <pre>
296: * raw_offset_table {
297: * u1 tag; // 0x41 : constant
298: * u2 length; // byte length of whole values
299: * s4 value[length/4]; // raw GMT offset in milliseconds
300: * }
301: * </pre>
302: * </blockquote>
303: *
304: * <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p>
305: * <br>
306: * <blockquote>
307: * <pre>
308: * raw_offset_index_table {
309: * u1 tag; // 0x42 : constant
310: * u2 length; // byte length of whole values
311: * u1 value[length];
312: * }
313: * </pre>
314: * </blockquote>
315: *
316: * <p><strong>2.4 <code>alias_table</code> structure</strong><p>
317: * <br>
318: * <blockquote>
319: * <pre>
320: * alias_table {
321: * u1 tag; // 0x43 : constant
322: * u2 length; // byte length of whole values
323: * u2 nentries; // number of id-pairs
324: * id_pair value[nentries];
325: * }
326: *
327: * id_pair {
328: * zone_id aliasname;
329: * zone_id ID;
330: * }
331: * </pre>
332: * </blockquote>
333: *
334: * <p><strong>2.5 <code>versionName</code> structure</strong><p>
335: * <br>
336: * <blockquote>
337: * <pre>
338: * versionName {
339: * u1 tag; // 0x44 : constant
340: * u2 length; // byte length of whole values
341: * u1 value[length];
342: * }
343: * </pre>
344: * </blockquote>
345:
346: * @since 1.4
347: */
348:
349: public class ZoneInfoFile {
350:
351: /**
352: * The magic number for the ZoneInfo data file format.
353: */
354: public static final byte[] JAVAZI_LABEL = { (byte) 'j', (byte) 'a',
355: (byte) 'v', (byte) 'a', (byte) 'z', (byte) 'i', (byte) '\0' };
356: private static final int JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;
357:
358: /**
359: * The ZoneInfo data file formation version number.
360: */
361: public static final byte JAVAZI_VERSION = 0x01;
362:
363: /**
364: * Raw offset data item tag.
365: */
366: public static final byte TAG_RawOffset = 1;
367:
368: /**
369: * Known last Daylight Saving Time save value data item tag.
370: */
371: public static final byte TAG_LastDSTSaving = 2;
372:
373: /**
374: * Checksum data item tag.
375: */
376: public static final byte TAG_CRC32 = 3;
377:
378: /**
379: * Transition data item tag.
380: */
381: public static final byte TAG_Transition = 4;
382:
383: /**
384: * Offset table data item tag.
385: */
386: public static final byte TAG_Offset = 5;
387:
388: /**
389: * SimpleTimeZone parameters data item tag.
390: */
391: public static final byte TAG_SimpleTimeZone = 6;
392:
393: /**
394: * Raw GMT offset will change in the future.
395: */
396: public static final byte TAG_GMTOffsetWillChange = 7;
397:
398: /**
399: * The ZoneInfoMappings file name.
400: */
401: public static final String JAVAZM_FILE_NAME = "ZoneInfoMappings";
402:
403: /**
404: * The magic number for the ZoneInfoMappings file format.
405: */
406: public static final byte[] JAVAZM_LABEL = { (byte) 'j', (byte) 'a',
407: (byte) 'v', (byte) 'a', (byte) 'z', (byte) 'm', (byte) '\0' };
408: private static final int JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;
409:
410: /**
411: * The ZoneInfoMappings file format version number.
412: */
413: public static final byte JAVAZM_VERSION = 0x01;
414:
415: /**
416: * Time zone IDs data item tag.
417: */
418: public static final byte TAG_ZoneIDs = 64;
419:
420: /**
421: * Raw GMT offsets table data item tag.
422: */
423: public static final byte TAG_RawOffsets = 65;
424:
425: /**
426: * Indices to the raw GMT offset table data item tag.
427: */
428: public static final byte TAG_RawOffsetIndices = 66;
429:
430: /**
431: * Time zone aliases table data item tag.
432: */
433: public static final byte TAG_ZoneAliases = 67;
434:
435: /**
436: * Olson's public zone information version tag.
437: */
438: public static final byte TAG_TZDataVersion = 68;
439:
440: private static HashMap zoneInfoObjects = null;
441:
442: /**
443: * Converts the given time zone ID to a platform dependent path
444: * name. For example, "America/Los_Angeles" is converted to
445: * "America\Los_Angeles" on Win32.
446: * @return a modified ID replacing '/' with {@link
447: * java.io.File#separatorChar File.separatorChar} if needed.
448: */
449: public static String getFileName(String ID) {
450: if (File.separatorChar == '/') {
451: return ID;
452: }
453: return ID.replace('/', File.separatorChar);
454: }
455:
456: /**
457: * Gets a ZoneInfo with the given GMT offset. The object
458: * has its ID in the format of GMT{+|-}hh:mm.
459: *
460: * @param originalId the given custom id (before normalized such as "GMT+9")
461: * @param gmtOffset GMT offset <em>in minutes</em>
462: * @return a ZoneInfo constructed with the given GMT offset
463: */
464: public static ZoneInfo getCustomTimeZone(String originalId,
465: int gmtOffset) {
466: char sign;
467: int offset;
468:
469: if (gmtOffset >= 0) {
470: sign = '+';
471: offset = gmtOffset;
472: } else {
473: sign = '-';
474: offset = -gmtOffset;
475: }
476: int hh = offset / 60;
477: int mm = offset % 60;
478:
479: StringBuffer sb = new StringBuffer("GMT");
480: sb.append(sign);
481: if (hh < 10) {
482: sb.append('0');
483: }
484: sb.append(hh).append(':');
485: if (mm < 10) {
486: sb.append('0');
487: }
488: sb.append(mm);
489: String id = sb.toString();
490:
491: ZoneInfo zi;
492: synchronized (ZoneInfoFile.class) {
493: if (zoneInfoObjects == null) {
494: zoneInfoObjects = new HashMap();
495: }
496: zi = (ZoneInfo) zoneInfoObjects.get(id);
497: if (zi == null) {
498: zi = new ZoneInfo(id, gmtOffset * 60000);
499: zoneInfoObjects.put(id, zi);
500: if (!id.equals(originalId)) {
501: zoneInfoObjects.put(originalId, zi);
502: }
503: }
504: }
505: return (ZoneInfo) zi.clone();
506: }
507:
508: /**
509: * @return a ZoneInfo instance created for the specified ID or
510: * null if there is no time zone data file found for the specified
511: * ID.
512: */
513: public synchronized static ZoneInfo getZoneInfo(String ID) {
514: ZoneInfo zi = null;
515: if (zoneInfoObjects == null) {
516: zi = createZoneInfo(ID);
517: if (zi != null) {
518: zoneInfoObjects = new HashMap();
519: zoneInfoObjects.put(ID, zi);
520: return (ZoneInfo) zi.clone();
521: }
522: return null;
523: }
524: zi = (ZoneInfo) zoneInfoObjects.get(ID);
525: if (zi != null) {
526: return (ZoneInfo) zi.clone();
527: }
528: zi = createZoneInfo(ID);
529: if (zi != null) {
530: zoneInfoObjects.put(ID, zi);
531: return (ZoneInfo) zi.clone();
532: }
533: return null;
534: }
535:
536: private static ZoneInfo createZoneInfo(String ID) {
537: byte[] buf = readZoneInfoFile(getFileName(ID));
538: if (buf == null) {
539: return null;
540: }
541:
542: int index;
543: for (index = 0; index < JAVAZI_LABEL.length; index++) {
544: if (buf[index] != JAVAZI_LABEL[index]) {
545: System.err.println("ZoneInfo: wrong magic number: "
546: + ID);
547: return null;
548: }
549: }
550:
551: if (buf[index++] > JAVAZI_VERSION) {
552: System.err.println("ZoneInfo: incompatible version ("
553: + buf[index - 1] + "): " + ID);
554: return null;
555: }
556:
557: int filesize = buf.length;
558: int rawOffset = 0;
559: int dstSavings = 0;
560: int checksum = 0;
561: boolean willGMTOffsetChange = false;
562: long[] transitions = null;
563: int[] offsets = null;
564: int[] simpleTimeZoneParams = null;
565:
566: try {
567: while (index < filesize) {
568: byte tag = buf[index++];
569: int len = ((buf[index++] & 0xFF) << 8)
570: + (buf[index++] & 0xFF);
571:
572: if (filesize < index + len) {
573: break;
574: }
575:
576: switch (tag) {
577: case TAG_CRC32: {
578: int val = buf[index++] & 0xff;
579: val = (val << 8) + (buf[index++] & 0xff);
580: val = (val << 8) + (buf[index++] & 0xff);
581: val = (val << 8) + (buf[index++] & 0xff);
582: checksum = val;
583: }
584: break;
585:
586: case TAG_LastDSTSaving: {
587: short val = (short) (buf[index++] & 0xff);
588: val = (short) ((val << 8) + (buf[index++] & 0xff));
589: dstSavings = val * 1000;
590: }
591: break;
592:
593: case TAG_RawOffset: {
594: int val = buf[index++] & 0xff;
595: val = (val << 8) + (buf[index++] & 0xff);
596: val = (val << 8) + (buf[index++] & 0xff);
597: val = (val << 8) + (buf[index++] & 0xff);
598: rawOffset = val;
599: }
600: break;
601:
602: case TAG_Transition: {
603: int n = len / 8;
604: transitions = new long[n];
605: for (int i = 0; i < n; i++) {
606: long val = buf[index++] & 0xff;
607: val = (val << 8) + (buf[index++] & 0xff);
608: val = (val << 8) + (buf[index++] & 0xff);
609: val = (val << 8) + (buf[index++] & 0xff);
610: val = (val << 8) + (buf[index++] & 0xff);
611: val = (val << 8) + (buf[index++] & 0xff);
612: val = (val << 8) + (buf[index++] & 0xff);
613: val = (val << 8) + (buf[index++] & 0xff);
614: transitions[i] = val;
615: }
616: }
617: break;
618:
619: case TAG_Offset: {
620: int n = len / 4;
621: offsets = new int[n];
622: for (int i = 0; i < n; i++) {
623: int val = buf[index++] & 0xff;
624: val = (val << 8) + (buf[index++] & 0xff);
625: val = (val << 8) + (buf[index++] & 0xff);
626: val = (val << 8) + (buf[index++] & 0xff);
627: offsets[i] = val;
628: }
629: }
630: break;
631:
632: case TAG_SimpleTimeZone: {
633: if (len != 32 && len != 40) {
634: System.err
635: .println("ZoneInfo: wrong SimpleTimeZone parameter size");
636: return null;
637: }
638: int n = len / 4;
639: simpleTimeZoneParams = new int[n];
640: for (int i = 0; i < n; i++) {
641: int val = buf[index++] & 0xff;
642: val = (val << 8) + (buf[index++] & 0xff);
643: val = (val << 8) + (buf[index++] & 0xff);
644: val = (val << 8) + (buf[index++] & 0xff);
645: simpleTimeZoneParams[i] = val;
646: }
647: }
648: break;
649:
650: case TAG_GMTOffsetWillChange: {
651: if (len != 1) {
652: System.err
653: .println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange");
654: }
655: willGMTOffsetChange = buf[index++] == 1;
656: }
657: break;
658:
659: default:
660: System.err.println("ZoneInfo: unknown tag < " + tag
661: + ">. ignored.");
662: index += len;
663: break;
664: }
665: }
666: } catch (Exception e) {
667: System.err.println("ZoneInfo: corrupted zoneinfo file: "
668: + ID);
669: return null;
670: }
671:
672: if (index != filesize) {
673: System.err.println("ZoneInfo: wrong file size: " + ID);
674: return null;
675: }
676:
677: return new ZoneInfo(ID, rawOffset, dstSavings, checksum,
678: transitions, offsets, simpleTimeZoneParams,
679: willGMTOffsetChange);
680: }
681:
682: private static SoftReference zoneIDs = null;
683:
684: static String[] getZoneIDs() {
685: String[] ids = null;
686:
687: if (zoneIDs != null) {
688: ids = (String[]) zoneIDs.get();
689: if (ids != null) {
690: return ids;
691: }
692: }
693:
694: byte[] buf = null;
695: buf = getZoneInfoMappings();
696: int index = JAVAZM_LABEL_LENGTH + 1;
697: int filesize = buf.length;
698:
699: try {
700: loop: while (index < filesize) {
701: byte tag = buf[index++];
702: int len = ((buf[index++] & 0xFF) << 8)
703: + (buf[index++] & 0xFF);
704:
705: switch (tag) {
706: case TAG_ZoneIDs: {
707: int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
708: ids = new String[n];
709:
710: for (int i = 0; i < n; i++) {
711: byte m = buf[index++];
712: ids[i] = new String(buf, index, m, "US-ASCII");
713: index += m;
714: }
715: }
716: break loop;
717:
718: default:
719: index += len;
720: break;
721: }
722: }
723: } catch (Exception e) {
724: System.err.println("ZoneInfo: corrupted "
725: + JAVAZM_FILE_NAME);
726: }
727:
728: zoneIDs = new SoftReference(ids);
729: return ids;
730: }
731:
732: /**
733: * @return an alias table in HashMap where a key is an alias ID
734: * (e.g., "PST") and its value is a real time zone ID (e.g.,
735: * "America/Los_Angeles").
736: */
737: static HashMap getZoneAliases() {
738: byte[] buf = getZoneInfoMappings();
739: int index = JAVAZM_LABEL_LENGTH + 1;
740: int filesize = buf.length;
741: HashMap aliases = null;
742:
743: try {
744: loop: while (index < filesize) {
745: byte tag = buf[index++];
746: int len = ((buf[index++] & 0xFF) << 8)
747: + (buf[index++] & 0xFF);
748:
749: switch (tag) {
750: case TAG_ZoneAliases: {
751: int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
752: aliases = new HashMap(n);
753: for (int i = 0; i < n; i++) {
754: byte m = buf[index++];
755: String name = new String(buf, index, m,
756: "US-ASCII");
757: index += m;
758: m = buf[index++];
759: String realName = new String(buf, index, m,
760: "US-ASCII");
761: index += m;
762: aliases.put(name, realName);
763: }
764: }
765: break loop;
766:
767: default:
768: index += len;
769: break;
770: }
771: }
772: } catch (Exception e) {
773: System.err.println("ZoneInfo: corrupted "
774: + JAVAZM_FILE_NAME);
775: return null;
776: }
777: return aliases;
778: }
779:
780: private static SoftReference rawOffsetIndices = null;
781:
782: static byte[] getRawOffsetIndices() {
783: byte[] indices = null;
784:
785: if (rawOffsetIndices != null) {
786: indices = (byte[]) rawOffsetIndices.get();
787: if (indices != null) {
788: return indices;
789: }
790: }
791:
792: byte[] buf = getZoneInfoMappings();
793: int index = JAVAZM_LABEL_LENGTH + 1;
794: int filesize = buf.length;
795:
796: try {
797: loop: while (index < filesize) {
798: byte tag = buf[index++];
799: int len = ((buf[index++] & 0xFF) << 8)
800: + (buf[index++] & 0xFF);
801:
802: switch (tag) {
803: case TAG_RawOffsetIndices: {
804: indices = new byte[len];
805: for (int i = 0; i < len; i++) {
806: indices[i] = buf[index++];
807: }
808: }
809: break loop;
810:
811: default:
812: index += len;
813: break;
814: }
815: }
816: } catch (ArrayIndexOutOfBoundsException e) {
817: System.err.println("ZoneInfo: corrupted "
818: + JAVAZM_FILE_NAME);
819: }
820:
821: rawOffsetIndices = new SoftReference(indices);
822: return indices;
823: }
824:
825: private static SoftReference rawOffsets = null;
826:
827: static int[] getRawOffsets() {
828: int[] offsets = null;
829:
830: if (rawOffsets != null) {
831: offsets = (int[]) rawOffsets.get();
832: if (offsets != null) {
833: return offsets;
834: }
835: }
836:
837: byte[] buf = getZoneInfoMappings();
838: int index = JAVAZM_LABEL_LENGTH + 1;
839: int filesize = buf.length;
840:
841: try {
842: loop: while (index < filesize) {
843: byte tag = buf[index++];
844: int len = ((buf[index++] & 0xFF) << 8)
845: + (buf[index++] & 0xFF);
846:
847: switch (tag) {
848: case TAG_RawOffsets: {
849: int n = len / 4;
850: offsets = new int[n];
851: for (int i = 0; i < n; i++) {
852: int val = buf[index++] & 0xff;
853: val = (val << 8) + (buf[index++] & 0xff);
854: val = (val << 8) + (buf[index++] & 0xff);
855: val = (val << 8) + (buf[index++] & 0xff);
856: offsets[i] = val;
857: }
858: }
859: break loop;
860:
861: default:
862: index += len;
863: break;
864: }
865: }
866: } catch (ArrayIndexOutOfBoundsException e) {
867: System.err.println("ZoneInfo: corrupted "
868: + JAVAZM_FILE_NAME);
869: }
870:
871: rawOffsets = new SoftReference(offsets);
872: return offsets;
873: }
874:
875: private static SoftReference zoneInfoMappings = null;
876:
877: private static byte[] getZoneInfoMappings() {
878: byte[] data;
879:
880: if (zoneInfoMappings != null) {
881: data = (byte[]) zoneInfoMappings.get();
882: if (data != null) {
883: return data;
884: }
885: }
886:
887: data = readZoneInfoFile(JAVAZM_FILE_NAME);
888:
889: if (data == null) {
890: return null;
891: }
892:
893: int index;
894: for (index = 0; index < JAVAZM_LABEL.length; index++) {
895: if (data[index] != JAVAZM_LABEL[index]) {
896: System.err.println("ZoneInfo: wrong magic number: "
897: + JAVAZM_FILE_NAME);
898: return null;
899: }
900: }
901: if (data[index++] > JAVAZM_VERSION) {
902: System.err.println("ZoneInfo: incompatible version ("
903: + data[index - 1] + "): " + JAVAZM_FILE_NAME);
904: return null;
905: }
906:
907: zoneInfoMappings = new SoftReference(data);
908: return data;
909: }
910:
911: /**
912: * Reads the specified file under <java.home>/lib/zi into a buffer.
913: * @return the buffer, or null if any I/O error occurred.
914: */
915: private static byte[] readZoneInfoFile(String fileName) {
916: byte[] buffer = null;
917:
918: try {
919: String homeDir = (String) AccessController
920: .doPrivileged(new sun.security.action.GetPropertyAction(
921: "java.home"));
922: final String fname = homeDir + File.separator + "lib"
923: + File.separator + "zi" + File.separator + fileName;
924: buffer = (byte[]) AccessController
925: .doPrivileged(new PrivilegedExceptionAction() {
926: public Object run() throws IOException {
927: File file = new File(fname);
928: int filesize = (int) file.length();
929: byte[] buf = new byte[filesize];
930:
931: FileInputStream fis = new FileInputStream(
932: file);
933:
934: if (fis.read(buf) != filesize) {
935: fis.close();
936: throw new IOException("read error on "
937: + fname);
938: }
939: fis.close();
940: return buf;
941: }
942: });
943: } catch (PrivilegedActionException e) {
944: Exception ex = e.getException();
945: if (!(ex instanceof FileNotFoundException)
946: || JAVAZM_FILE_NAME.equals(fileName)) {
947: System.err.println("ZoneInfo: " + ex.getMessage());
948: }
949: }
950: return buffer;
951: }
952: }
|