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