Save This Page
Home » openjdk-7 » sun » util » calendar » [javadoc | source]
    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   }

Save This Page
Home » openjdk-7 » sun » util » calendar » [javadoc | source]