Path: blob/master/test/jdk/sun/util/calendar/zi/ZoneInfoFile.java
41153 views
/*1* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.File;24import java.io.FileInputStream;25import java.io.FileNotFoundException;26import java.io.IOException;27import java.lang.ref.SoftReference;28import java.nio.file.FileSystems;29import java.util.ArrayList;30import java.util.HashMap;31import java.util.List;32import java.util.Map;3334import sun.util.calendar.*;3536/**37* <code>ZoneInfoFile</code> reads Zone information files in the38* <java.home>/lib/zi directory and provides time zone39* information in the form of a {@link ZoneInfo} object. Also, it40* reads the ZoneInfoMappings file to obtain time zone IDs information41* that is used by the {@link ZoneInfo} class. The directory layout42* and data file formats are as follows.43*44* <p><strong>Directory layout</strong><p>45*46* All zone data files and ZoneInfoMappings are put under the47* <java.home>/lib/zi directory. A path name for a given time48* zone ID is a concatenation of <java.home>/lib/zi/ and the49* time zone ID. (The file separator is replaced with the platform50* dependent value. e.g., '\' for Win32.) An example layout will look51* like as follows.52* <blockquote>53* <pre>54* <java.home>/lib/zi/Africa/Addis_Ababa55* /Africa/Dakar56* /America/Los_Angeles57* /Asia/Singapore58* /EET59* /Europe/Oslo60* /GMT61* /Pacific/Galapagos62* ...63* /ZoneInfoMappings64* </pre>65* </blockquote>66*67* A zone data file has specific information of each zone.68* <code>ZoneInfoMappings</code> has global information of zone IDs so69* that the information can be obtained without instantiating all time70* zones.71*72* <p><strong>File format</strong><p>73*74* Two binary-file formats based on a simple Tag-Length-Value format are used75* to describe TimeZone information. The generic format of a data file is:76* <blockquote>77* <pre>78* DataFile {79* u1 magic[7];80* u1 version;81* data_item data[];82* }83* </pre>84* </blockquote>85* where <code>magic</code> is a magic number identifying a file86* format, <code>version</code> is the format version number, and87* <code>data</code> is one or more <code>data_item</code>s. The88* <code>data_item</code> structure is:89* <blockquote>90* <pre>91* data_item {92* u1 tag;93* u2 length;94* u1 value[length];95* }96* </pre>97* </blockquote>98* where <code>tag</code> indicates the data type of the item,99* <code>length</code> is a byte count of the following100* <code>value</code> that is the content of item data.101* <p>102* All data is stored in the big-endian order. There is no boundary103* alignment between date items.104*105* <p><strong>1. ZoneInfo data file</strong><p>106*107* Each ZoneInfo data file consists of the following members.108* <br>109* <blockquote>110* <pre>111* ZoneInfoDataFile {112* u1 magic[7];113* u1 version;114* SET OF<sup>1</sup> {115* transition transitions<sup>2</sup>;116* offset_table offsets<sup>2</sup>;117* simpletimezone stzparams<sup>2</sup>;118* raw_offset rawoffset;119* dstsaving dst;120* checksum crc32;121* gmtoffsetwillchange gmtflag<sup>2</sup>;122* }123* }124* 1: an unordered collection of zero or one occurrences of each item125* 2: optional item126* </pre>127* </blockquote>128* <code>magic</code> is a byte-string constant identifying the129* ZoneInfo data file. This field must be <code>"javazi\0"</code>130* defined as {@link #JAVAZI_LABEL}.131* <p>132* <code>version</code> is the version number of the file format. This133* will be used for compatibility check. This field must be134* <code>0x01</code> in this version.135* <p>136* <code>transition</code>, <code>offset_table</code> and137* <code>simpletimezone</code> have information of time transition138* from the past to the future. Therefore, these structures don't139* exist if the zone didn't change zone names and haven't applied DST in140* the past, and haven't planned to apply it. (e.g. Asia/Tokyo zone)141* <p>142* <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code>143* exist in every zoneinfo file. They are used by TimeZone.class indirectly.144*145* <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a>146* <blockquote>147* <pre>148* transition {149* u1 tag; // 0x04 : constant150* u2 length; // byte length of whole values151* s8 value[length/8]; // transitions in `long'152* }153* </pre>154* </blockquote>155* See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value.156*157* <p><strong>1.2 <code>offset_table</code> structure</strong><p>158* <blockquote>159* <pre>160* offset_table {161* u1 tag; // 0x05 : constant162* u2 length; // byte length of whole values163* s4 value[length/4]; // offset values in `int'164* }165* </pre>166* </blockquote>167*168* <p><strong>1.3 <code>simpletimezone</code> structure</strong><p>169* See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams}170* about the value.171* <blockquote>172* <pre>173* simpletimezone {174* u1 tag; // 0x06 : constant175* u2 length; // byte length of whole values176* s4 value[length/4]; // SimpleTimeZone parameters177* }178* </pre>179* </blockquote>180* See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value.181*182* <p><strong>1.4 <code>raw_offset</code> structure</strong><p>183* <blockquote>184* <pre>185* raw_offset {186* u1 tag; // 0x01 : constant187* u2 length; // must be 4.188* s4 value; // raw GMT offset [millisecond]189* }190* </pre>191* </blockquote>192* See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value.193*194* <p><strong>1.5 <code>dstsaving</code> structure</strong><p>195* Value has dstSaving in seconds.196* <blockquote>197* <pre>198* dstsaving {199* u1 tag; // 0x02 : constant200* u2 length; // must be 2.201* s2 value; // DST save value [second]202* }203* </pre>204* </blockquote>205* See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value.206*207* <p><strong>1.6 <code>checksum</code> structure</strong><p>208* <blockquote>209* <pre>210* checksum {211* u1 tag; // 0x03 : constant212* u2 length; // must be 4.213* s4 value; // CRC32 value of transitions214* }215* </pre>216* </blockquote>217* See {@link ZoneInfo#checksum ZoneInfo.checksum}.218*219* <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p>220* This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}.221* If this record is not present in a zoneinfo file, 0 is assumed for222* the value.223* <blockquote>224* <pre>225* gmtoffsetwillchange {226* u1 tag; // 0x07 : constant227* u2 length; // must be 1.228* u1 value; // 1: if the GMT raw offset will change229* // in the future, 0, otherwise.230* }231* </pre>232* </blockquote>233*234*235* <p><strong>2. ZoneInfoMappings file</strong><p>236*237* The ZoneInfoMappings file consists of the following members.238* <br>239* <blockquote>240* <pre>241* ZoneInfoMappings {242* u1 magic[7];243* u1 version;244* SET OF {245* versionName version;246* zone_id_table zoneIDs;247* raw_offset_table rawoffsets;248* raw_offset_index_table rawoffsetindices;249* alias_table aliases;250* excluded_list excludedList;251* }252* }253* </pre>254* </blockquote>255*256* <code>magic</code> is a byte-string constant which has the file type.257* This field must be <code>"javazm\0"</code> defined as {@link #JAVAZM_LABEL}.258* <p>259* <code>version</code> is the version number of this file260* format. This will be used for compatibility check. This field must261* be <code>0x01</code> in this version.262* <p>263* <code>versionName</code> shows which version of Olson's data has been used264* to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br>265* This field is for trouble-shooting and isn't usually used in runtime.266* <p>267* <code>zone_id_table</code>, <code>raw_offset_index_table</code> and268* <code>alias_table</code> are general information of supported269* zones.270*271* <p><strong>2.1 <code>zone_id_table</code> structure</strong><p>272* The list of zone IDs included in the zi database. The list does273* <em>not</em> include zone IDs, if any, listed in excludedList.274* <br>275* <blockquote>276* <pre>277* zone_id_table {278* u1 tag; // 0x40 : constant279* u2 length; // byte length of whole values280* u2 zone_id_count;281* zone_id value[zone_id_count];282* }283*284* zone_id {285* u1 byte_length; // byte length of id286* u1 id[byte_length]; // zone name string287* }288* </pre>289* </blockquote>290*291* <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p>292* <br>293* <blockquote>294* <pre>295* raw_offset_table {296* u1 tag; // 0x41 : constant297* u2 length; // byte length of whole values298* s4 value[length/4]; // raw GMT offset in milliseconds299* }300* </pre>301* </blockquote>302*303* <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p>304* <br>305* <blockquote>306* <pre>307* raw_offset_index_table {308* u1 tag; // 0x42 : constant309* u2 length; // byte length of whole values310* u1 value[length];311* }312* </pre>313* </blockquote>314*315* <p><strong>2.4 <code>alias_table</code> structure</strong><p>316* <br>317* <blockquote>318* <pre>319* alias_table {320* u1 tag; // 0x43 : constant321* u2 length; // byte length of whole values322* u2 nentries; // number of id-pairs323* id_pair value[nentries];324* }325*326* id_pair {327* zone_id aliasname;328* zone_id ID;329* }330* </pre>331* </blockquote>332*333* <p><strong>2.5 <code>versionName</code> structure</strong><p>334* <br>335* <blockquote>336* <pre>337* versionName {338* u1 tag; // 0x44 : constant339* u2 length; // byte length of whole values340* u1 value[length];341* }342* </pre>343* </blockquote>344*345* <p><strong>2.6 <code>excludeList</code> structure</strong><p>346* The list of zone IDs whose zones will change their GMT offsets347* (a.k.a. raw offsets) some time in the future. Those IDs must be348* added to the list of zone IDs for getAvailableIDs(). Also they must349* be examined for getAvailableIDs(int) to determine the350* <em>current</em> GMT offsets.351* <br>352* <blockquote>353* <pre>354* excluded_list {355* u1 tag; // 0x45 : constant356* u2 length; // byte length of whole values357* u2 nentries; // number of zone_ids358* zone_id value[nentries]; // excluded zone IDs359* }360* </pre>361* </blockquote>362*363* @since 1.4364*/365366public class ZoneInfoFile {367368/**369* The magic number for the ZoneInfo data file format.370*/371public static final byte[] JAVAZI_LABEL = {372(byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'i', (byte)'\0'373};374private static final int JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;375376/**377* The ZoneInfo data file format version number. Must increase378* one when any incompatible change has been made.379*/380public static final byte JAVAZI_VERSION = 0x01;381382/**383* Raw offset data item tag.384*/385public static final byte TAG_RawOffset = 1;386387/**388* Known last Daylight Saving Time save value data item tag.389*/390public static final byte TAG_LastDSTSaving = 2;391392/**393* Checksum data item tag.394*/395public static final byte TAG_CRC32 = 3;396397/**398* Transition data item tag.399*/400public static final byte TAG_Transition = 4;401402/**403* Offset table data item tag.404*/405public static final byte TAG_Offset = 5;406407/**408* SimpleTimeZone parameters data item tag.409*/410public static final byte TAG_SimpleTimeZone = 6;411412/**413* Raw GMT offset will change in the future.414*/415public static final byte TAG_GMTOffsetWillChange = 7;416417418/**419* The ZoneInfoMappings file name.420*/421public static final String JAVAZM_FILE_NAME = "ZoneInfoMappings";422423/**424* The magic number for the ZoneInfoMappings file format.425*/426public static final byte[] JAVAZM_LABEL = {427(byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'m', (byte)'\0'428};429private static final int JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;430431/**432* The ZoneInfoMappings file format version number. Must increase433* one when any incompatible change has been made.434*/435public static final byte JAVAZM_VERSION = 0x01;436437/**438* Time zone IDs data item tag.439*/440public static final byte TAG_ZoneIDs = 64;441442/**443* Raw GMT offsets table data item tag.444*/445public static final byte TAG_RawOffsets = 65;446447/**448* Indices to the raw GMT offset table data item tag.449*/450public static final byte TAG_RawOffsetIndices = 66;451452/**453* Time zone aliases table data item tag.454*/455public static final byte TAG_ZoneAliases = 67;456457/**458* Olson's public zone information version tag.459*/460public static final byte TAG_TZDataVersion = 68;461462/**463* Excluded zones item tag. (Added in Mustang)464*/465public static final byte TAG_ExcludedZones = 69;466467private static Map<String, ZoneInfoOld> zoneInfoObjects = null;468469private static final ZoneInfoOld GMT = new ZoneInfoOld("GMT", 0);470471static String ziDir;472473/**474* Converts the given time zone ID to a platform dependent path475* name. For example, "America/Los_Angeles" is converted to476* "America\Los_Angeles" on Win32.477* @return a modified ID replacing '/' with {@link478* java.io.File#separatorChar File.separatorChar} if needed.479*/480public static String getFileName(String ID) {481if (File.separatorChar == '/') {482return ID;483}484return ID.replace('/', File.separatorChar);485}486487/**488* Gets a ZoneInfo with the given GMT offset. The object489* has its ID in the format of GMT{+|-}hh:mm.490*491* @param originalId the given custom id (before normalized such as "GMT+9")492* @param gmtOffset GMT offset <em>in milliseconds</em>493* @return a ZoneInfo constructed with the given GMT offset494*/495public static ZoneInfoOld getCustomTimeZone(String originalId, int gmtOffset) {496String id = toCustomID(gmtOffset);497498ZoneInfoOld zi = getFromCache(id);499if (zi == null) {500zi = new ZoneInfoOld(id, gmtOffset);501zi = addToCache(id, zi);502if (!id.equals(originalId)) {503zi = addToCache(originalId, zi);504}505}506return (ZoneInfoOld) zi.clone();507}508509public static String toCustomID(int gmtOffset) {510char sign;511int offset = gmtOffset / 60000;512513if (offset >= 0) {514sign = '+';515} else {516sign = '-';517offset = -offset;518}519int hh = offset / 60;520int mm = offset % 60;521522char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' };523if (hh >= 10) {524buf[4] += hh / 10;525}526buf[5] += hh % 10;527if (mm != 0) {528buf[7] += mm / 10;529buf[8] += mm % 10;530}531return new String(buf);532}533534/**535* @return a ZoneInfo instance created for the specified id, or536* null if there is no time zone data file found for the specified537* id.538*/539public static ZoneInfoOld getZoneInfoOld(String id) {540//treat GMT zone as special541if ("GMT".equals(id))542return (ZoneInfoOld) GMT.clone();543ZoneInfoOld zi = getFromCache(id);544if (zi == null) {545Map<String, String> aliases = ZoneInfoOld.getCachedAliasTable();546if (aliases != null && aliases.get(id) != null) {547return null;548}549zi = createZoneInfoOld(id);550if (zi == null) {551return null;552}553zi = addToCache(id, zi);554}555return (ZoneInfoOld) zi.clone();556}557558synchronized static ZoneInfoOld getFromCache(String id) {559if (zoneInfoObjects == null) {560return null;561}562return zoneInfoObjects.get(id);563}564565synchronized static ZoneInfoOld addToCache(String id, ZoneInfoOld zi) {566if (zoneInfoObjects == null) {567zoneInfoObjects = new HashMap<>();568} else {569ZoneInfoOld zone = zoneInfoObjects.get(id);570if (zone != null) {571return zone;572}573}574zoneInfoObjects.put(id, zi);575return zi;576}577578private static ZoneInfoOld createZoneInfoOld(String id) {579byte[] buf = readZoneInfoFile(getFileName(id));580if (buf == null) {581return null;582}583584int index = 0;585int filesize = buf.length;586int rawOffset = 0;587int dstSavings = 0;588int checksum = 0;589boolean willGMTOffsetChange = false;590long[] transitions = null;591int[] offsets = null;592int[] simpleTimeZoneParams = null;593594try {595for (index = 0; index < JAVAZI_LABEL.length; index++) {596if (buf[index] != JAVAZI_LABEL[index]) {597System.err.println("ZoneInfoOld: wrong magic number: " + id);598return null;599}600}601if (buf[index++] > JAVAZI_VERSION) {602System.err.println("ZoneInfo: incompatible version ("603+ buf[index - 1] + "): " + id);604return null;605}606607while (index < filesize) {608byte tag = buf[index++];609int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);610611if (filesize < index+len) {612break;613}614615switch (tag) {616case TAG_CRC32:617{618int val = buf[index++] & 0xff;619val = (val << 8) + (buf[index++] & 0xff);620val = (val << 8) + (buf[index++] & 0xff);621val = (val << 8) + (buf[index++] & 0xff);622checksum = val;623}624break;625626case TAG_LastDSTSaving:627{628short val = (short)(buf[index++] & 0xff);629val = (short)((val << 8) + (buf[index++] & 0xff));630dstSavings = val * 1000;631}632break;633634case TAG_RawOffset:635{636int val = buf[index++] & 0xff;637val = (val << 8) + (buf[index++] & 0xff);638val = (val << 8) + (buf[index++] & 0xff);639val = (val << 8) + (buf[index++] & 0xff);640rawOffset = val;641}642break;643644case TAG_Transition:645{646int n = len / 8;647transitions = new long[n];648for (int i = 0; i < n; i ++) {649long val = buf[index++] & 0xff;650val = (val << 8) + (buf[index++] & 0xff);651val = (val << 8) + (buf[index++] & 0xff);652val = (val << 8) + (buf[index++] & 0xff);653val = (val << 8) + (buf[index++] & 0xff);654val = (val << 8) + (buf[index++] & 0xff);655val = (val << 8) + (buf[index++] & 0xff);656val = (val << 8) + (buf[index++] & 0xff);657transitions[i] = val;658}659}660break;661662case TAG_Offset:663{664int n = len / 4;665offsets = new int[n];666for (int i = 0; i < n; i ++) {667int val = buf[index++] & 0xff;668val = (val << 8) + (buf[index++] & 0xff);669val = (val << 8) + (buf[index++] & 0xff);670val = (val << 8) + (buf[index++] & 0xff);671offsets[i] = val;672}673}674break;675676case TAG_SimpleTimeZone:677{678if (len != 32 && len != 40) {679System.err.println("ZoneInfo: wrong SimpleTimeZone parameter size");680return null;681}682int n = len / 4;683simpleTimeZoneParams = new int[n];684for (int i = 0; i < n; i++) {685int val = buf[index++] & 0xff;686val = (val << 8) + (buf[index++] & 0xff);687val = (val << 8) + (buf[index++] & 0xff);688val = (val << 8) + (buf[index++] & 0xff);689simpleTimeZoneParams[i] = val;690}691}692break;693694case TAG_GMTOffsetWillChange:695{696if (len != 1) {697System.err.println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange");698}699willGMTOffsetChange = buf[index++] == 1;700}701break;702703default:704System.err.println("ZoneInfo: unknown tag < " + tag + ">. ignored.");705index += len;706break;707}708}709} catch (Exception e) {710System.err.println("ZoneInfo: corrupted zoneinfo file: " + id);711return null;712}713714if (index != filesize) {715System.err.println("ZoneInfo: wrong file size: " + id);716return null;717}718719return new ZoneInfoOld(id, rawOffset, dstSavings, checksum,720transitions, offsets, simpleTimeZoneParams,721willGMTOffsetChange);722}723724private volatile static SoftReference<List<String>> zoneIDs = null;725726static List<String> getZoneIDs() {727List<String> ids = null;728SoftReference<List<String>> cache = zoneIDs;729if (cache != null) {730ids = cache.get();731if (ids != null) {732return ids;733}734}735byte[] buf = null;736buf = getZoneInfoOldMappings();737int index = JAVAZM_LABEL_LENGTH + 1;738int filesize = buf.length;739try {740loop:741while (index < filesize) {742byte tag = buf[index++];743int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);744745switch (tag) {746case TAG_ZoneIDs:747{748int n = (buf[index++] << 8) + (buf[index++] & 0xFF);749ids = new ArrayList<>(n);750751for (int i = 0; i < n; i++) {752byte m = buf[index++];753ids.add(new String(buf, index, m, "UTF-8"));754index += m;755}756}757break loop;758759default:760index += len;761break;762}763}764} catch (Exception e) {765System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);766}767768zoneIDs = new SoftReference<>(ids);769return ids;770}771772/**773* @return an alias table in HashMap where a key is an alias ID774* (e.g., "PST") and its value is a real time zone ID (e.g.,775* "America/Los_Angeles").776*/777static Map<String, String> getZoneAliases() {778byte[] buf = getZoneInfoOldMappings();779int index = JAVAZM_LABEL_LENGTH + 1;780int filesize = buf.length;781Map<String, String> aliases = null;782783try {784loop:785while (index < filesize) {786byte tag = buf[index++];787int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);788789switch (tag) {790case TAG_ZoneAliases:791{792int n = (buf[index++] << 8) + (buf[index++] & 0xFF);793aliases = new HashMap<>(n);794for (int i = 0; i < n; i++) {795byte m = buf[index++];796String name = new String(buf, index, m, "UTF-8");797index += m;798m = buf[index++];799String realName = new String(buf, index, m, "UTF-8");800index += m;801aliases.put(name, realName);802}803}804break loop;805806default:807index += len;808break;809}810}811} catch (Exception e) {812System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);813return null;814}815return aliases;816}817818private volatile static SoftReference<List<String>> excludedIDs = null;819private volatile static boolean hasNoExcludeList = false;820821/**822* @return a List of zone IDs for zones that will change their GMT823* offsets in some future time.824*825* @since 1.6826*/827static List<String> getExcludedZones() {828if (hasNoExcludeList) {829return null;830}831832List<String> excludeList = null;833834SoftReference<List<String>> cache = excludedIDs;835if (cache != null) {836excludeList = cache.get();837if (excludeList != null) {838return excludeList;839}840}841842byte[] buf = getZoneInfoOldMappings();843int index = JAVAZM_LABEL_LENGTH + 1;844int filesize = buf.length;845846try {847loop:848while (index < filesize) {849byte tag = buf[index++];850int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);851852switch (tag) {853case TAG_ExcludedZones:854{855int n = (buf[index++] << 8) + (buf[index++] & 0xFF);856excludeList = new ArrayList<>();857for (int i = 0; i < n; i++) {858byte m = buf[index++];859String name = new String(buf, index, m, "UTF-8");860index += m;861excludeList.add(name);862}863}864break loop;865866default:867index += len;868break;869}870}871} catch (Exception e) {872System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);873return null;874}875876if (excludeList != null) {877excludedIDs = new SoftReference<>(excludeList);878} else {879hasNoExcludeList = true;880}881return excludeList;882}883884private volatile static SoftReference<byte[]> rawOffsetIndices = null;885886static byte[] getRawOffsetIndices() {887byte[] indices = null;888889SoftReference<byte[]> cache = rawOffsetIndices;890if (cache != null) {891indices = cache.get();892if (indices != null) {893return indices;894}895}896897byte[] buf = getZoneInfoOldMappings();898int index = JAVAZM_LABEL_LENGTH + 1;899int filesize = buf.length;900901try {902loop:903while (index < filesize) {904byte tag = buf[index++];905int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);906907switch (tag) {908case TAG_RawOffsetIndices:909{910indices = new byte[len];911for (int i = 0; i < len; i++) {912indices[i] = buf[index++];913}914}915break loop;916917default:918index += len;919break;920}921}922} catch (ArrayIndexOutOfBoundsException e) {923System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);924}925926rawOffsetIndices = new SoftReference<>(indices);927return indices;928}929930private volatile static SoftReference<int[]> rawOffsets = null;931932static int[] getRawOffsets() {933int[] offsets = null;934935SoftReference<int[]> cache = rawOffsets;936if (cache != null) {937offsets = cache.get();938if (offsets != null) {939return offsets;940}941}942943byte[] buf = getZoneInfoOldMappings();944int index = JAVAZM_LABEL_LENGTH + 1;945int filesize = buf.length;946947try {948loop:949while (index < filesize) {950byte tag = buf[index++];951int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);952953switch (tag) {954case TAG_RawOffsets:955{956int n = len/4;957offsets = new int[n];958for (int i = 0; i < n; i++) {959int val = buf[index++] & 0xff;960val = (val << 8) + (buf[index++] & 0xff);961val = (val << 8) + (buf[index++] & 0xff);962val = (val << 8) + (buf[index++] & 0xff);963offsets[i] = val;964}965}966break loop;967968default:969index += len;970break;971}972}973} catch (ArrayIndexOutOfBoundsException e) {974System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);975}976977rawOffsets = new SoftReference<>(offsets);978return offsets;979}980981private volatile static SoftReference<byte[]> zoneInfoMappings = null;982983private static byte[] getZoneInfoOldMappings() {984byte[] data;985SoftReference<byte[]> cache = zoneInfoMappings;986if (cache != null) {987data = cache.get();988if (data != null) {989return data;990}991}992data = readZoneInfoFile(JAVAZM_FILE_NAME);993if (data == null) {994throw new RuntimeException("ZoneInfoOldMapping " +995JAVAZM_FILE_NAME + " either doesn't exist or doesn't have data");996}997998int index;999for (index = 0; index < JAVAZM_LABEL.length; index++) {1000if (data[index] != JAVAZM_LABEL[index]) {1001System.err.println("ZoneInfoOld: wrong magic number: " + JAVAZM_FILE_NAME);1002return null;1003}1004}1005if (data[index++] > JAVAZM_VERSION) {1006System.err.println("ZoneInfoOld: incompatible version ("1007+ data[index - 1] + "): " + JAVAZM_FILE_NAME);1008return null;1009}10101011zoneInfoMappings = new SoftReference<>(data);1012return data;1013}10141015/**1016* Reads the specified file under <java.home>/lib/zi into a buffer.1017* @return the buffer, or null if any I/O error occurred.1018*/1019private static byte[] readZoneInfoFile(final String fileName) {1020if (fileName.indexOf("..") >= 0) {1021return null;1022}1023byte[] buffer = null;1024File file = new File(ziDir, fileName);1025try {1026int filesize = (int)file.length();1027if (filesize > 0) {1028FileInputStream fis = new FileInputStream(file);1029buffer = new byte[filesize];1030try {1031if (fis.read(buffer) != filesize) {1032throw new IOException("read error on " + fileName);1033}1034} finally {1035fis.close();1036}1037}1038} catch (Exception ex) {1039if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) {1040System.err.println("ZoneInfoOld: " + ex.getMessage());1041}1042}1043return buffer;1044}10451046private ZoneInfoFile() {1047}1048}104910501051