1 /* 2 * Copyright (c) 2000, 2013, 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 import java.io.IOException; 27 import java.io.File; 28 import java.io.FileOutputStream; 29 import java.io.DataOutputStream; 30 import java.io.RandomAccessFile; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Set; 34 35 /** 36 * <code>Gen</code> is one of back-end classes of javazic, and generates 37 * ZoneInfoMappings and zone-specific file for each zone. 38 */ 39 class Gen extends BackEnd { 40 41 /** 42 * Generates datafile in binary TLV format for each time zone. 43 * Regarding contents of output files, see {@link ZoneInfoFile}. 44 * 45 * @param Timezone 46 * @return 0 if no errors, or 1 if error occurred. 47 */ 48 int processZoneinfo(Timezone tz) { 49 try { 50 int size; 51 String outputDir = Main.getOutputDir(); 52 String zonefile = ZoneInfoFile.getFileName(tz.getName()); 53 54 /* If outputDir doesn't end with file-separator, adds it. */ 55 if (!outputDir.endsWith(File.separator)) { 56 outputDir += File.separatorChar; 57 } 58 59 /* If zonefile includes file-separator, it's treated as part of 60 * pathname. And make directory if necessary. 61 */ 62 int index = zonefile.lastIndexOf(File.separatorChar); 63 if (index != -1) { 64 outputDir += zonefile.substring(0, index+1); 65 } 66 File outD = new File(outputDir); 67 outD.mkdirs(); 68 69 FileOutputStream fos = 70 new FileOutputStream(outputDir + zonefile.substring(index+1)); 71 DataOutputStream dos = new DataOutputStream(fos); 72 73 /* Output Label */ 74 dos.write(ZoneInfoFile.JAVAZI_LABEL, 0, 75 ZoneInfoFile.JAVAZI_LABEL.length); 76 77 /* Output Version of ZoneInfoFile */ 78 dos.writeByte(ZoneInfoFile.JAVAZI_VERSION); 79 80 List<Long> transitions = tz.getTransitions(); 81 if (transitions != null) { 82 List<Integer> dstOffsets = tz.getDstOffsets(); 83 List<Integer> offsets = tz.getOffsets(); 84 85 if ((dstOffsets == null && offsets != null) || 86 (dstOffsets != null && offsets == null)) { 87 Main.panic("Data not exist. (dstOffsets or offsets)"); 88 return 1; 89 } 90 91 /* Output Transition records */ 92 dos.writeByte(ZoneInfoFile.TAG_Transition); 93 size = transitions.size(); 94 dos.writeShort((size * 8) & 0xFFFF); 95 int dstoffset; 96 for (int i = 0; i < size; i++) { 97 /* if DST offset is 0, this means DST isn't used. 98 * (NOT: offset's index is 0.) 99 */ 100 if ((dstoffset = dstOffsets.get(i).intValue()) == -1) { 101 dstoffset = 0; 102 } 103 104 dos.writeLong((transitions.get(i).longValue() << 12) 105 | (dstoffset << 4) 106 | offsets.get(i).intValue()); 107 108 } 109 110 /* Output data for GMTOffset */ 111 List<Integer> gmtoffset = tz.getGmtOffsets(); 112 dos.writeByte(ZoneInfoFile.TAG_Offset); 113 size = gmtoffset.size(); 114 dos.writeShort((size * 4) & 0xFFFF); 115 for (int i = 0; i < size; i++) { 116 dos.writeInt(gmtoffset.get(i)); 117 } 118 } 119 120 /* Output data for SimpleTimeZone */ 121 List<RuleRec> stz = tz.getLastRules(); 122 if (stz != null) { 123 RuleRec[] rr = new RuleRec[2]; 124 boolean wall = true; 125 126 rr[0] = stz.get(0); 127 rr[1] = stz.get(1); 128 129 dos.writeByte(ZoneInfoFile.TAG_SimpleTimeZone); 130 wall = rr[0].getTime().isWall() && rr[1].getTime().isWall(); 131 if (wall) { 132 dos.writeShort(32); 133 } else { 134 dos.writeShort(40); 135 } 136 137 for (int i = 0; i < 2; i++) { 138 dos.writeInt(rr[i].getMonthNum() - 1); // 0-based month number 139 dos.writeInt(rr[i].getDay().getDayForSimpleTimeZone()); 140 dos.writeInt(rr[i].getDay().getDayOfWeekForSimpleTimeZoneInt()); 141 dos.writeInt((int)rr[i].getTime().getTime()); 142 if (!wall) { 143 dos.writeInt((rr[i].getTime().getType() & 0xFF) - 1); 144 } 145 } 146 } 147 148 /* Output RawOffset */ 149 dos.writeByte(ZoneInfoFile.TAG_RawOffset); 150 dos.writeShort(4); 151 dos.writeInt(tz.getRawOffset()); 152 153 /* Output willGMTOffsetChange flag */ 154 if (tz.willGMTOffsetChange()) { 155 dos.writeByte(ZoneInfoFile.TAG_GMTOffsetWillChange); 156 dos.writeShort(1); 157 dos.writeByte(1); 158 } 159 160 /* Output LastDSTSaving */ 161 dos.writeByte(ZoneInfoFile.TAG_LastDSTSaving); 162 dos.writeShort(2); 163 dos.writeShort(tz.getLastDSTSaving()/1000); 164 165 /* Output checksum */ 166 dos.writeByte(ZoneInfoFile.TAG_CRC32); 167 dos.writeShort(4); 168 dos.writeInt(tz.getCRC32()); 169 170 fos.close(); 171 dos.close(); 172 } catch(IOException e) { 173 Main.panic("IO error: "+e.getMessage()); 174 return 1; 175 } 176 177 return 0; 178 } 179 180 /** 181 * Generates ZoneInfoMappings in binary TLV format for each zone. 182 * Regarding contents of output files, see {@link ZoneInfoFile}. 183 * 184 * @param Mappings 185 * @return 0 if no errors, or 1 if error occurred. 186 */ 187 int generateSrc(Mappings map) { 188 try { 189 int index; 190 int block_size; 191 int roi_size; 192 long fp; 193 String outputDir = Main.getOutputDir(); 194 195 /* If outputDir doesn't end with file-separator, adds it. */ 196 if (!outputDir.endsWith(File.separator)) { 197 outputDir += File.separatorChar; 198 } 199 200 File outD = new File(outputDir); 201 outD.mkdirs(); 202 203 /* Open ZoneInfoMapping file to write. */ 204 RandomAccessFile raf = 205 new RandomAccessFile(outputDir + ZoneInfoFile.JAVAZM_FILE_NAME, "rw"); 206 207 /* Whether rawOffsetIndex list exists or not. */ 208 List<Integer> roi = map.getRawOffsetsIndex(); 209 if (roi == null) { 210 Main.panic("Data not exist. (rawOffsetsIndex)"); 211 return 1; 212 } 213 roi_size = roi.size(); 214 215 /* Whether rawOffsetIndexTable list exists or not. */ 216 List<Set<String>> roit = map.getRawOffsetsIndexTable(); 217 if (roit == null || roit.size() != roi_size) { 218 Main.panic("Data not exist. (rawOffsetsIndexTable) Otherwise, Invalid size"); 219 return 1; 220 } 221 222 /* Output Label */ 223 raf.write(ZoneInfoFile.JAVAZM_LABEL, 0, 224 ZoneInfoFile.JAVAZM_LABEL.length); 225 226 /* Output Version */ 227 raf.writeByte(ZoneInfoFile.JAVAZM_VERSION); 228 229 index = ZoneInfoFile.JAVAZM_LABEL.length + 2; 230 231 /* Output Version of Olson's tzdata */ 232 byte[] b = Main.getVersionName().getBytes("UTF-8"); 233 raf.writeByte(ZoneInfoFile.TAG_TZDataVersion); 234 raf.writeShort((b.length+1) & 0xFFFF); 235 raf.write(b); 236 raf.writeByte(0x00); 237 index += b.length + 4; 238 239 /* Output ID list. */ 240 raf.writeByte(ZoneInfoFile.TAG_ZoneIDs); 241 block_size = 2; 242 raf.writeShort(block_size & 0xFFFF); 243 short nID = 0; 244 raf.writeShort(nID & 0xFFFF); 245 for (int i = 0; i < roi_size; i++) { 246 for (String key : roit.get(i)) { 247 byte size = (byte)key.getBytes("UTF-8").length; 248 raf.writeByte(size & 0xFF); 249 raf.write(key.getBytes("UTF-8"), 0, size); 250 block_size += 1 + size; 251 nID++; 252 } 253 } 254 fp = raf.getFilePointer(); 255 raf.seek(index); 256 raf.writeShort((block_size) & 0xFFFF); 257 raf.writeShort(nID & 0xFFFF); 258 raf.seek(fp); 259 260 /* Output sorted rawOffset list. */ 261 raf.writeByte(ZoneInfoFile.TAG_RawOffsets); 262 index += 3 + block_size; 263 block_size = roi_size * 4; 264 raf.writeShort(block_size & 0xFFFF); 265 for (int i = 0; i < roi_size; i++) { 266 raf.writeInt(Integer.parseInt(roi.get(i).toString())); 267 } 268 269 /* Output sorted rawOffsetIndex list. */ 270 raf.writeByte(ZoneInfoFile.TAG_RawOffsetIndices); 271 index += 3 + block_size; 272 block_size = 0; 273 raf.writeShort(block_size & 0xFFFF); 274 int num; 275 for (int i = 0; i < roi_size; i++) { 276 num = roit.get(i).size(); 277 block_size += num; 278 for (int j = 0; j < num; j++) { 279 raf.writeByte(i); 280 } 281 } 282 fp = raf.getFilePointer(); 283 raf.seek(index); 284 raf.writeShort((block_size) & 0xFFFF); 285 raf.seek(fp); 286 287 /* Whether alias list exists or not. */ 288 Map<String,String> a = map.getAliases(); 289 if (a == null) { 290 Main.panic("Data not exist. (aliases)"); 291 return 0; 292 } 293 294 /* Output ID list. */ 295 raf.writeByte(ZoneInfoFile.TAG_ZoneAliases); 296 index += 3 + block_size; 297 block_size = 2; 298 raf.writeShort(block_size & 0xFFFF); 299 raf.writeShort(a.size() & 0xFFFF); 300 for (String key : a.keySet()) { 301 String alias = a.get(key); 302 byte key_size = (byte)key.length(); 303 byte alias_size = (byte)alias.length(); 304 raf.writeByte(key_size & 0xFF); 305 raf.write(key.getBytes("UTF-8"), 0, key_size); 306 raf.writeByte(alias_size & 0xFF); 307 raf.write(alias.getBytes("UTF-8"), 0, alias_size); 308 block_size += 2 + key_size + alias_size; 309 } 310 fp = raf.getFilePointer(); 311 raf.seek(index); 312 raf.writeShort((block_size) & 0xFFFF); 313 raf.seek(fp); 314 315 /* Output the exclude list if it exists. */ 316 List<String> excludedZones = map.getExcludeList(); 317 if (excludedZones != null) { 318 raf.writeByte(ZoneInfoFile.TAG_ExcludedZones); 319 index += 3 + block_size; 320 block_size = 2; 321 raf.writeShort(block_size & 0xFFFF); // place holder 322 raf.writeShort(excludedZones.size()); // the number of excluded zones 323 for (String name : excludedZones) { 324 byte size = (byte) name.length(); 325 raf.writeByte(size); // byte length 326 raf.write(name.getBytes("UTF-8"), 0, size); // zone name 327 block_size += 1 + size; 328 } 329 fp = raf.getFilePointer(); 330 raf.seek(index); 331 raf.writeShort(block_size & 0xFFFF); 332 raf.seek(fp); 333 } 334 335 /* Close ZoneInfoMapping file. */ 336 raf.close(); 337 } catch(IOException e) { 338 Main.panic("IO error: "+e.getMessage()); 339 return 1; 340 } 341 342 return 0; 343 } 344 }