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