src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java

Fri, 31 Aug 2012 10:37:46 +0100

author
jfranck
date
Fri, 31 Aug 2012 10:37:46 +0100
changeset 1313
873ddd9f4900
parent 1305
9d47f4850714
child 1357
c75be5bc5283
permissions
-rw-r--r--

7151010: Add compiler support for repeating annotations
Reviewed-by: jjg, mcimadamore

jjg@36 1 /*
jjh@1305 2 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
jjg@36 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jjg@36 4 *
jjg@36 5 * This code is free software; you can redistribute it and/or modify it
jjg@36 6 * under the terms of the GNU General Public License version 2 only, as
ohair@554 7 * published by the Free Software Foundation. Oracle designates this
jjg@36 8 * particular file as subject to the "Classpath" exception as provided
ohair@554 9 * by Oracle in the LICENSE file that accompanied this code.
jjg@36 10 *
jjg@36 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jjg@36 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jjg@36 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jjg@36 14 * version 2 for more details (a copy is included in the LICENSE file that
jjg@36 15 * accompanied this code).
jjg@36 16 *
jjg@36 17 * You should have received a copy of the GNU General Public License version
jjg@36 18 * 2 along with this work; if not, write to the Free Software Foundation,
jjg@36 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jjg@36 20 *
ohair@554 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554 22 * or visit www.oracle.com if you need additional information or have any
ohair@554 23 * questions.
jjg@36 24 */
jjg@36 25
jjg@50 26 package com.sun.tools.javac.file;
duke@1 27
jjg@103 28
jjg@50 29 import java.io.File;
jjg@50 30 import java.io.FileNotFoundException;
jjg@50 31 import java.io.IOException;
jjg@50 32 import java.io.RandomAccessFile;
jjg@424 33 import java.lang.ref.Reference;
jjg@103 34 import java.lang.ref.SoftReference;
jjg@50 35 import java.util.ArrayList;
jjg@50 36 import java.util.Arrays;
jjg@71 37 import java.util.Calendar;
jjg@50 38 import java.util.Collections;
mcimadamore@1279 39 import java.util.LinkedHashMap;
jjg@50 40 import java.util.HashMap;
jjg@50 41 import java.util.HashSet;
duke@1 42 import java.util.List;
jjg@50 43 import java.util.Map;
jjg@50 44 import java.util.Set;
jjg@50 45 import java.util.zip.DataFormatException;
jjg@50 46 import java.util.zip.Inflater;
jjg@50 47 import java.util.zip.ZipException;
duke@1 48
jjg@103 49 import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
jjg@103 50 import com.sun.tools.javac.file.RelativePath.RelativeFile;
jjg@103 51
jjg@839 52 /**
jjg@839 53 * This class implements the building of index of a zip archive and access to
jjg@839 54 * its context. It also uses a prebuilt index if available.
jjg@839 55 * It supports invocations where it will serialize an optimized zip index file
jjg@839 56 * to disk.
duke@1 57 *
jjg@839 58 * In order to use a secondary index file, set "usezipindex" in the Options
jjg@839 59 * object when JavacFileManager is invoked. (You can pass "-XDusezipindex" on
jjg@839 60 * the command line.)
duke@1 61 *
jjg@839 62 * Location where to look for/generate optimized zip index files can be
jjg@839 63 * provided using "-XDcachezipindexdir=<directory>". If this flag is not
jjg@839 64 * provided, the default location is the value of the "java.io.tmpdir" system
jjg@839 65 * property.
duke@1 66 *
jjg@839 67 * If "-XDwritezipindexfiles" is specified, there will be new optimized index
jjg@839 68 * file created for each archive, used by the compiler for compilation, at the
jjg@839 69 * location specified by the "cachezipindexdir" option.
duke@1 70 *
jjg@839 71 * If system property nonBatchMode option is specified the compiler will use
jjg@839 72 * timestamp checking to reindex the zip files if it is needed. In batch mode
jjg@839 73 * the timestamps are not checked and the compiler uses the cached indexes.
jjg@333 74 *
jjg@581 75 * <p><b>This is NOT part of any supported API.
jjg@333 76 * If you write code that depends on this, you do so at your own risk.
jjg@333 77 * This code and its internal interfaces are subject to change or
jjg@333 78 * deletion without notice.</b>
duke@1 79 */
duke@1 80 public class ZipFileIndex {
duke@1 81 private static final String MIN_CHAR = String.valueOf(Character.MIN_VALUE);
duke@1 82 private static final String MAX_CHAR = String.valueOf(Character.MAX_VALUE);
duke@1 83
duke@1 84 public final static long NOT_MODIFIED = Long.MIN_VALUE;
duke@1 85
duke@1 86
duke@1 87 private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
duke@1 88
jjg@839 89 private Map<RelativeDirectory, DirectoryEntry> directories =
jjg@839 90 Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
jjg@839 91 private Set<RelativeDirectory> allDirs =
jjg@839 92 Collections.<RelativeDirectory>emptySet();
duke@1 93
duke@1 94 // ZipFileIndex data entries
jjg@839 95 final File zipFile;
jjg@424 96 private Reference<File> absFileRef;
jjg@839 97 long zipFileLastModified = NOT_MODIFIED;
duke@1 98 private RandomAccessFile zipRandomFile;
jjg@57 99 private Entry[] entries;
duke@1 100
duke@1 101 private boolean readFromIndex = false;
duke@1 102 private File zipIndexFile = null;
duke@1 103 private boolean triedToReadIndex = false;
jjg@103 104 final RelativeDirectory symbolFilePrefix;
jjg@839 105 private final int symbolFilePrefixLength;
duke@1 106 private boolean hasPopulatedData = false;
jjg@839 107 long lastReferenceTimeStamp = NOT_MODIFIED;
duke@1 108
jjg@839 109 private final boolean usePreindexedCache;
jjg@839 110 private final String preindexedCacheLocation;
duke@1 111
duke@1 112 private boolean writeIndex = false;
duke@1 113
jjg@839 114 private Map<String, SoftReference<RelativeDirectory>> relativeDirectoryCache =
jjg@103 115 new HashMap<String, SoftReference<RelativeDirectory>>();
jjg@103 116
jjg@839 117
jjg@839 118 public synchronized boolean isOpen() {
jjg@839 119 return (zipRandomFile != null);
duke@1 120 }
duke@1 121
jjg@839 122 ZipFileIndex(File zipFile, RelativeDirectory symbolFilePrefix, boolean writeIndex,
duke@1 123 boolean useCache, String cacheLocation) throws IOException {
duke@1 124 this.zipFile = zipFile;
jjg@57 125 this.symbolFilePrefix = symbolFilePrefix;
jjg@57 126 this.symbolFilePrefixLength = (symbolFilePrefix == null ? 0 :
jjg@103 127 symbolFilePrefix.getPath().getBytes("UTF-8").length);
duke@1 128 this.writeIndex = writeIndex;
duke@1 129 this.usePreindexedCache = useCache;
duke@1 130 this.preindexedCacheLocation = cacheLocation;
duke@1 131
duke@1 132 if (zipFile != null) {
duke@1 133 this.zipFileLastModified = zipFile.lastModified();
duke@1 134 }
duke@1 135
duke@1 136 // Validate integrity of the zip file
duke@1 137 checkIndex();
duke@1 138 }
duke@1 139
jjg@839 140 @Override
duke@1 141 public String toString() {
jjg@103 142 return "ZipFileIndex[" + zipFile + "]";
duke@1 143 }
duke@1 144
duke@1 145 // Just in case...
jjg@839 146 @Override
jjg@839 147 protected void finalize() throws Throwable {
duke@1 148 closeFile();
jjg@839 149 super.finalize();
duke@1 150 }
duke@1 151
duke@1 152 private boolean isUpToDate() {
jjg@839 153 if (zipFile != null
jjg@839 154 && ((!NON_BATCH_MODE) || zipFileLastModified == zipFile.lastModified())
jjg@839 155 && hasPopulatedData) {
duke@1 156 return true;
duke@1 157 }
duke@1 158
duke@1 159 return false;
duke@1 160 }
duke@1 161
duke@1 162 /**
duke@1 163 * Here we need to make sure that the ZipFileIndex is valid. Check the timestamp of the file and
duke@1 164 * if its the same as the one at the time the index was build we don't need to reopen anything.
duke@1 165 */
duke@1 166 private void checkIndex() throws IOException {
duke@1 167 boolean isUpToDate = true;
duke@1 168 if (!isUpToDate()) {
duke@1 169 closeFile();
duke@1 170 isUpToDate = false;
duke@1 171 }
duke@1 172
duke@1 173 if (zipRandomFile != null || isUpToDate) {
duke@1 174 lastReferenceTimeStamp = System.currentTimeMillis();
duke@1 175 return;
duke@1 176 }
duke@1 177
duke@1 178 hasPopulatedData = true;
duke@1 179
duke@1 180 if (readIndex()) {
duke@1 181 lastReferenceTimeStamp = System.currentTimeMillis();
duke@1 182 return;
duke@1 183 }
duke@1 184
jjg@103 185 directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
jjg@103 186 allDirs = Collections.<RelativeDirectory>emptySet();
duke@1 187
duke@1 188 try {
duke@1 189 openFile();
duke@1 190 long totalLength = zipRandomFile.length();
duke@1 191 ZipDirectory directory = new ZipDirectory(zipRandomFile, 0L, totalLength, this);
duke@1 192 directory.buildIndex();
duke@1 193 } finally {
duke@1 194 if (zipRandomFile != null) {
duke@1 195 closeFile();
duke@1 196 }
duke@1 197 }
duke@1 198
duke@1 199 lastReferenceTimeStamp = System.currentTimeMillis();
duke@1 200 }
duke@1 201
duke@1 202 private void openFile() throws FileNotFoundException {
duke@1 203 if (zipRandomFile == null && zipFile != null) {
duke@1 204 zipRandomFile = new RandomAccessFile(zipFile, "r");
duke@1 205 }
duke@1 206 }
duke@1 207
duke@1 208 private void cleanupState() {
duke@1 209 // Make sure there is a valid but empty index if the file doesn't exist
jjg@57 210 entries = Entry.EMPTY_ARRAY;
jjg@103 211 directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
duke@1 212 zipFileLastModified = NOT_MODIFIED;
jjg@103 213 allDirs = Collections.<RelativeDirectory>emptySet();
duke@1 214 }
duke@1 215
jjg@839 216 public synchronized void close() {
jjg@839 217 writeIndex();
jjg@839 218 closeFile();
duke@1 219 }
duke@1 220
duke@1 221 private void closeFile() {
duke@1 222 if (zipRandomFile != null) {
duke@1 223 try {
duke@1 224 zipRandomFile.close();
duke@1 225 } catch (IOException ex) {
duke@1 226 }
duke@1 227 zipRandomFile = null;
duke@1 228 }
duke@1 229 }
duke@1 230
duke@1 231 /**
jjg@839 232 * Returns the ZipFileIndexEntry for a path, if there is one.
duke@1 233 */
jjg@839 234 synchronized Entry getZipIndexEntry(RelativePath path) {
duke@1 235 try {
duke@1 236 checkIndex();
jjg@103 237 DirectoryEntry de = directories.get(path.dirname());
jjg@103 238 String lookFor = path.basename();
jjg@839 239 return (de == null) ? null : de.getEntry(lookFor);
duke@1 240 }
duke@1 241 catch (IOException e) {
duke@1 242 return null;
duke@1 243 }
duke@1 244 }
duke@1 245
duke@1 246 /**
jjg@839 247 * Returns a javac List of filenames within a directory in the ZipFileIndex.
duke@1 248 */
jjg@839 249 public synchronized com.sun.tools.javac.util.List<String> getFiles(RelativeDirectory path) {
duke@1 250 try {
duke@1 251 checkIndex();
duke@1 252
duke@1 253 DirectoryEntry de = directories.get(path);
duke@1 254 com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getFiles();
duke@1 255
duke@1 256 if (ret == null) {
duke@1 257 return com.sun.tools.javac.util.List.<String>nil();
duke@1 258 }
duke@1 259 return ret;
duke@1 260 }
duke@1 261 catch (IOException e) {
duke@1 262 return com.sun.tools.javac.util.List.<String>nil();
duke@1 263 }
duke@1 264 }
duke@1 265
jjg@839 266 public synchronized List<String> getDirectories(RelativeDirectory path) {
duke@1 267 try {
duke@1 268 checkIndex();
duke@1 269
duke@1 270 DirectoryEntry de = directories.get(path);
duke@1 271 com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories();
duke@1 272
duke@1 273 if (ret == null) {
duke@1 274 return com.sun.tools.javac.util.List.<String>nil();
duke@1 275 }
duke@1 276
duke@1 277 return ret;
duke@1 278 }
duke@1 279 catch (IOException e) {
duke@1 280 return com.sun.tools.javac.util.List.<String>nil();
duke@1 281 }
duke@1 282 }
duke@1 283
jjg@839 284 public synchronized Set<RelativeDirectory> getAllDirectories() {
duke@1 285 try {
duke@1 286 checkIndex();
duke@1 287 if (allDirs == Collections.EMPTY_SET) {
mcimadamore@1279 288 allDirs = new java.util.LinkedHashSet<RelativeDirectory>(directories.keySet());
duke@1 289 }
duke@1 290
duke@1 291 return allDirs;
duke@1 292 }
duke@1 293 catch (IOException e) {
jjg@103 294 return Collections.<RelativeDirectory>emptySet();
duke@1 295 }
duke@1 296 }
duke@1 297
duke@1 298 /**
duke@1 299 * Tests if a specific path exists in the zip. This method will return true
duke@1 300 * for file entries and directories.
duke@1 301 *
duke@1 302 * @param path A path within the zip.
duke@1 303 * @return True if the path is a file or dir, false otherwise.
duke@1 304 */
jjg@839 305 public synchronized boolean contains(RelativePath path) {
duke@1 306 try {
duke@1 307 checkIndex();
duke@1 308 return getZipIndexEntry(path) != null;
duke@1 309 }
duke@1 310 catch (IOException e) {
duke@1 311 return false;
duke@1 312 }
jjg@839 313 }
jjg@839 314
jjg@839 315 public synchronized boolean isDirectory(RelativePath path) throws IOException {
jjg@839 316 // The top level in a zip file is always a directory.
jjg@839 317 if (path.getPath().length() == 0) {
jjg@839 318 lastReferenceTimeStamp = System.currentTimeMillis();
jjg@839 319 return true;
jjg@839 320 }
jjg@839 321
jjg@839 322 checkIndex();
jjg@839 323 return directories.get(path) != null;
jjg@839 324 }
jjg@839 325
jjg@839 326 public synchronized long getLastModified(RelativeFile path) throws IOException {
jjg@839 327 Entry entry = getZipIndexEntry(path);
jjg@839 328 if (entry == null)
jjg@839 329 throw new FileNotFoundException();
jjg@839 330 return entry.getLastModified();
jjg@839 331 }
jjg@839 332
jjg@839 333 public synchronized int length(RelativeFile path) throws IOException {
jjg@839 334 Entry entry = getZipIndexEntry(path);
jjg@839 335 if (entry == null)
jjg@839 336 throw new FileNotFoundException();
jjg@839 337
jjg@839 338 if (entry.isDir) {
jjg@839 339 return 0;
jjg@839 340 }
jjg@839 341
jjg@839 342 byte[] header = getHeader(entry);
jjg@839 343 // entry is not compressed?
jjg@839 344 if (get2ByteLittleEndian(header, 8) == 0) {
jjg@839 345 return entry.compressedSize;
jjg@839 346 } else {
jjg@839 347 return entry.size;
duke@1 348 }
duke@1 349 }
duke@1 350
jjg@839 351 public synchronized byte[] read(RelativeFile path) throws IOException {
jjg@839 352 Entry entry = getZipIndexEntry(path);
jjg@839 353 if (entry == null)
jjg@839 354 throw new FileNotFoundException("Path not found in ZIP: " + path.path);
jjg@839 355 return read(entry);
duke@1 356 }
duke@1 357
jjg@839 358 synchronized byte[] read(Entry entry) throws IOException {
jjg@839 359 openFile();
jjg@839 360 byte[] result = readBytes(entry);
jjg@839 361 closeFile();
jjg@839 362 return result;
duke@1 363 }
duke@1 364
jjg@839 365 public synchronized int read(RelativeFile path, byte[] buffer) throws IOException {
jjg@839 366 Entry entry = getZipIndexEntry(path);
jjg@839 367 if (entry == null)
jjg@839 368 throw new FileNotFoundException();
jjg@839 369 return read(entry, buffer);
duke@1 370 }
duke@1 371
jjg@839 372 synchronized int read(Entry entry, byte[] buffer)
duke@1 373 throws IOException {
jjg@839 374 int result = readBytes(entry, buffer);
jjg@839 375 return result;
duke@1 376 }
duke@1 377
jjg@57 378 private byte[] readBytes(Entry entry) throws IOException {
duke@1 379 byte[] header = getHeader(entry);
duke@1 380 int csize = entry.compressedSize;
duke@1 381 byte[] cbuf = new byte[csize];
duke@1 382 zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
duke@1 383 zipRandomFile.readFully(cbuf, 0, csize);
duke@1 384
duke@1 385 // is this compressed - offset 8 in the ZipEntry header
duke@1 386 if (get2ByteLittleEndian(header, 8) == 0)
duke@1 387 return cbuf;
duke@1 388
duke@1 389 int size = entry.size;
duke@1 390 byte[] buf = new byte[size];
duke@1 391 if (inflate(cbuf, buf) != size)
duke@1 392 throw new ZipException("corrupted zip file");
duke@1 393
duke@1 394 return buf;
duke@1 395 }
duke@1 396
duke@1 397 /**
duke@1 398 *
duke@1 399 */
jjg@57 400 private int readBytes(Entry entry, byte[] buffer) throws IOException {
duke@1 401 byte[] header = getHeader(entry);
duke@1 402
duke@1 403 // entry is not compressed?
duke@1 404 if (get2ByteLittleEndian(header, 8) == 0) {
duke@1 405 zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
duke@1 406 int offset = 0;
duke@1 407 int size = buffer.length;
duke@1 408 while (offset < size) {
duke@1 409 int count = zipRandomFile.read(buffer, offset, size - offset);
duke@1 410 if (count == -1)
duke@1 411 break;
duke@1 412 offset += count;
duke@1 413 }
duke@1 414 return entry.size;
duke@1 415 }
duke@1 416
duke@1 417 int csize = entry.compressedSize;
duke@1 418 byte[] cbuf = new byte[csize];
duke@1 419 zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
duke@1 420 zipRandomFile.readFully(cbuf, 0, csize);
duke@1 421
duke@1 422 int count = inflate(cbuf, buffer);
duke@1 423 if (count == -1)
duke@1 424 throw new ZipException("corrupted zip file");
duke@1 425
duke@1 426 return entry.size;
duke@1 427 }
duke@1 428
duke@1 429 //----------------------------------------------------------------------------
duke@1 430 // Zip utilities
duke@1 431 //----------------------------------------------------------------------------
duke@1 432
jjg@57 433 private byte[] getHeader(Entry entry) throws IOException {
duke@1 434 zipRandomFile.seek(entry.offset);
duke@1 435 byte[] header = new byte[30];
duke@1 436 zipRandomFile.readFully(header);
duke@1 437 if (get4ByteLittleEndian(header, 0) != 0x04034b50)
duke@1 438 throw new ZipException("corrupted zip file");
duke@1 439 if ((get2ByteLittleEndian(header, 6) & 1) != 0)
duke@1 440 throw new ZipException("encrypted zip file"); // offset 6 in the header of the ZipFileEntry
duke@1 441 return header;
duke@1 442 }
duke@1 443
duke@1 444 /*
duke@1 445 * Inflate using the java.util.zip.Inflater class
duke@1 446 */
jjg@839 447 private SoftReference<Inflater> inflaterRef;
duke@1 448 private int inflate(byte[] src, byte[] dest) {
jjg@839 449 Inflater inflater = (inflaterRef == null ? null : inflaterRef.get());
duke@1 450
duke@1 451 // construct the inflater object or reuse an existing one
duke@1 452 if (inflater == null)
jjg@839 453 inflaterRef = new SoftReference<Inflater>(inflater = new Inflater(true));
duke@1 454
jjg@839 455 inflater.reset();
jjg@839 456 inflater.setInput(src);
jjg@839 457 try {
jjg@839 458 return inflater.inflate(dest);
jjg@839 459 } catch (DataFormatException ex) {
jjg@839 460 return -1;
duke@1 461 }
duke@1 462 }
duke@1 463
duke@1 464 /**
duke@1 465 * return the two bytes buf[pos], buf[pos+1] as an unsigned integer in little
duke@1 466 * endian format.
duke@1 467 */
duke@1 468 private static int get2ByteLittleEndian(byte[] buf, int pos) {
duke@1 469 return (buf[pos] & 0xFF) + ((buf[pos+1] & 0xFF) << 8);
duke@1 470 }
duke@1 471
duke@1 472 /**
duke@1 473 * return the 4 bytes buf[i..i+3] as an integer in little endian format.
duke@1 474 */
duke@1 475 private static int get4ByteLittleEndian(byte[] buf, int pos) {
duke@1 476 return (buf[pos] & 0xFF) + ((buf[pos + 1] & 0xFF) << 8) +
duke@1 477 ((buf[pos + 2] & 0xFF) << 16) + ((buf[pos + 3] & 0xFF) << 24);
duke@1 478 }
duke@1 479
duke@1 480 /* ----------------------------------------------------------------------------
duke@1 481 * ZipDirectory
duke@1 482 * ----------------------------------------------------------------------------*/
duke@1 483
duke@1 484 private class ZipDirectory {
jjg@103 485 private RelativeDirectory lastDir;
duke@1 486 private int lastStart;
duke@1 487 private int lastLen;
duke@1 488
duke@1 489 byte[] zipDir;
duke@1 490 RandomAccessFile zipRandomFile = null;
duke@1 491 ZipFileIndex zipFileIndex = null;
duke@1 492
duke@1 493 public ZipDirectory(RandomAccessFile zipRandomFile, long start, long end, ZipFileIndex index) throws IOException {
duke@1 494 this.zipRandomFile = zipRandomFile;
duke@1 495 this.zipFileIndex = index;
ksrini@923 496 hasValidHeader();
ksrini@923 497 findCENRecord(start, end);
ksrini@923 498 }
duke@1 499
ksrini@923 500 /*
ksrini@923 501 * the zip entry signature should be at offset 0, otherwise allow the
ksrini@923 502 * calling logic to take evasive action by throwing ZipFormatException.
ksrini@923 503 */
ksrini@923 504 private boolean hasValidHeader() throws IOException {
ksrini@923 505 final long pos = zipRandomFile.getFilePointer();
ksrini@923 506 try {
ksrini@923 507 if (zipRandomFile.read() == 'P') {
ksrini@923 508 if (zipRandomFile.read() == 'K') {
ksrini@923 509 if (zipRandomFile.read() == 0x03) {
ksrini@923 510 if (zipRandomFile.read() == 0x04) {
ksrini@923 511 return true;
ksrini@923 512 }
ksrini@923 513 }
ksrini@923 514 }
ksrini@923 515 }
ksrini@923 516 } finally {
ksrini@923 517 zipRandomFile.seek(pos);
ksrini@923 518 }
ksrini@923 519 throw new ZipFormatException("invalid zip magic");
duke@1 520 }
duke@1 521
duke@1 522 /*
duke@1 523 * Reads zip file central directory.
duke@1 524 * For more details see readCEN in zip_util.c from the JDK sources.
duke@1 525 * This is a Java port of that function.
duke@1 526 */
duke@1 527 private void findCENRecord(long start, long end) throws IOException {
duke@1 528 long totalLength = end - start;
duke@1 529 int endbuflen = 1024;
duke@1 530 byte[] endbuf = new byte[endbuflen];
duke@1 531 long endbufend = end - start;
duke@1 532
duke@1 533 // There is a variable-length field after the dir offset record. We need to do consequential search.
duke@1 534 while (endbufend >= 22) {
duke@1 535 if (endbufend < endbuflen)
duke@1 536 endbuflen = (int)endbufend;
duke@1 537 long endbufpos = endbufend - endbuflen;
duke@1 538 zipRandomFile.seek(start + endbufpos);
duke@1 539 zipRandomFile.readFully(endbuf, 0, endbuflen);
duke@1 540 int i = endbuflen - 22;
duke@1 541 while (i >= 0 &&
duke@1 542 !(endbuf[i] == 0x50 &&
duke@1 543 endbuf[i + 1] == 0x4b &&
duke@1 544 endbuf[i + 2] == 0x05 &&
duke@1 545 endbuf[i + 3] == 0x06 &&
duke@1 546 endbufpos + i + 22 +
duke@1 547 get2ByteLittleEndian(endbuf, i + 20) == totalLength)) {
duke@1 548 i--;
duke@1 549 }
duke@1 550
duke@1 551 if (i >= 0) {
duke@1 552 zipDir = new byte[get4ByteLittleEndian(endbuf, i + 12) + 2];
duke@1 553 zipDir[0] = endbuf[i + 10];
duke@1 554 zipDir[1] = endbuf[i + 11];
ksrini@923 555 int sz = get4ByteLittleEndian(endbuf, i + 16);
ksrini@923 556 // a negative offset or the entries field indicates a
ksrini@923 557 // potential zip64 archive
ksrini@923 558 if (sz < 0 || get2ByteLittleEndian(zipDir, 0) == 0xffff) {
ksrini@923 559 throw new ZipFormatException("detected a zip64 archive");
ksrini@923 560 }
ksrini@923 561 zipRandomFile.seek(start + sz);
duke@1 562 zipRandomFile.readFully(zipDir, 2, zipDir.length - 2);
duke@1 563 return;
duke@1 564 } else {
duke@1 565 endbufend = endbufpos + 21;
duke@1 566 }
duke@1 567 }
duke@1 568 throw new ZipException("cannot read zip file");
duke@1 569 }
jjg@103 570
duke@1 571 private void buildIndex() throws IOException {
duke@1 572 int entryCount = get2ByteLittleEndian(zipDir, 0);
duke@1 573
duke@1 574 // Add each of the files
duke@1 575 if (entryCount > 0) {
mcimadamore@1279 576 directories = new LinkedHashMap<RelativeDirectory, DirectoryEntry>();
jjg@57 577 ArrayList<Entry> entryList = new ArrayList<Entry>();
duke@1 578 int pos = 2;
duke@1 579 for (int i = 0; i < entryCount; i++) {
duke@1 580 pos = readEntry(pos, entryList, directories);
duke@1 581 }
duke@1 582
duke@1 583 // Add the accumulated dirs into the same list
jjg@103 584 for (RelativeDirectory d: directories.keySet()) {
jjg@103 585 // use shared RelativeDirectory objects for parent dirs
jjg@103 586 RelativeDirectory parent = getRelativeDirectory(d.dirname().getPath());
jjg@103 587 String file = d.basename();
jjg@103 588 Entry zipFileIndexEntry = new Entry(parent, file);
duke@1 589 zipFileIndexEntry.isDir = true;
duke@1 590 entryList.add(zipFileIndexEntry);
duke@1 591 }
duke@1 592
jjg@57 593 entries = entryList.toArray(new Entry[entryList.size()]);
duke@1 594 Arrays.sort(entries);
duke@1 595 } else {
duke@1 596 cleanupState();
duke@1 597 }
duke@1 598 }
duke@1 599
jjg@57 600 private int readEntry(int pos, List<Entry> entryList,
jjg@103 601 Map<RelativeDirectory, DirectoryEntry> directories) throws IOException {
duke@1 602 if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) {
duke@1 603 throw new ZipException("cannot read zip file entry");
duke@1 604 }
duke@1 605
duke@1 606 int dirStart = pos + 46;
duke@1 607 int fileStart = dirStart;
duke@1 608 int fileEnd = fileStart + get2ByteLittleEndian(zipDir, pos + 28);
duke@1 609
duke@1 610 if (zipFileIndex.symbolFilePrefixLength != 0 &&
duke@1 611 ((fileEnd - fileStart) >= symbolFilePrefixLength)) {
duke@1 612 dirStart += zipFileIndex.symbolFilePrefixLength;
duke@1 613 fileStart += zipFileIndex.symbolFilePrefixLength;
duke@1 614 }
jjg@103 615 // Force any '\' to '/'. Keep the position of the last separator.
duke@1 616 for (int index = fileStart; index < fileEnd; index++) {
duke@1 617 byte nextByte = zipDir[index];
jjg@103 618 if (nextByte == (byte)'\\') {
jjg@103 619 zipDir[index] = (byte)'/';
jjg@103 620 fileStart = index + 1;
jjg@103 621 } else if (nextByte == (byte)'/') {
duke@1 622 fileStart = index + 1;
duke@1 623 }
duke@1 624 }
duke@1 625
jjg@103 626 RelativeDirectory directory = null;
duke@1 627 if (fileStart == dirStart)
jjg@103 628 directory = getRelativeDirectory("");
duke@1 629 else if (lastDir != null && lastLen == fileStart - dirStart - 1) {
duke@1 630 int index = lastLen - 1;
duke@1 631 while (zipDir[lastStart + index] == zipDir[dirStart + index]) {
duke@1 632 if (index == 0) {
duke@1 633 directory = lastDir;
duke@1 634 break;
duke@1 635 }
duke@1 636 index--;
duke@1 637 }
duke@1 638 }
duke@1 639
duke@1 640 // Sub directories
duke@1 641 if (directory == null) {
duke@1 642 lastStart = dirStart;
duke@1 643 lastLen = fileStart - dirStart - 1;
duke@1 644
jjg@103 645 directory = getRelativeDirectory(new String(zipDir, dirStart, lastLen, "UTF-8"));
duke@1 646 lastDir = directory;
duke@1 647
duke@1 648 // Enter also all the parent directories
jjg@103 649 RelativeDirectory tempDirectory = directory;
duke@1 650
duke@1 651 while (directories.get(tempDirectory) == null) {
duke@1 652 directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex));
jjg@103 653 if (tempDirectory.path.indexOf("/") == tempDirectory.path.length() - 1)
duke@1 654 break;
jjg@103 655 else {
jjg@103 656 // use shared RelativeDirectory objects for parent dirs
jjg@103 657 tempDirectory = getRelativeDirectory(tempDirectory.dirname().getPath());
jjg@103 658 }
duke@1 659 }
duke@1 660 }
duke@1 661 else {
duke@1 662 if (directories.get(directory) == null) {
duke@1 663 directories.put(directory, new DirectoryEntry(directory, zipFileIndex));
duke@1 664 }
duke@1 665 }
duke@1 666
duke@1 667 // For each dir create also a file
duke@1 668 if (fileStart != fileEnd) {
jjg@57 669 Entry entry = new Entry(directory,
duke@1 670 new String(zipDir, fileStart, fileEnd - fileStart, "UTF-8"));
duke@1 671
duke@1 672 entry.setNativeTime(get4ByteLittleEndian(zipDir, pos + 12));
duke@1 673 entry.compressedSize = get4ByteLittleEndian(zipDir, pos + 20);
duke@1 674 entry.size = get4ByteLittleEndian(zipDir, pos + 24);
duke@1 675 entry.offset = get4ByteLittleEndian(zipDir, pos + 42);
duke@1 676 entryList.add(entry);
duke@1 677 }
duke@1 678
duke@1 679 return pos + 46 +
duke@1 680 get2ByteLittleEndian(zipDir, pos + 28) +
duke@1 681 get2ByteLittleEndian(zipDir, pos + 30) +
duke@1 682 get2ByteLittleEndian(zipDir, pos + 32);
duke@1 683 }
duke@1 684 }
duke@1 685
duke@1 686 /**
duke@1 687 * Returns the last modified timestamp of a zip file.
duke@1 688 * @return long
duke@1 689 */
duke@1 690 public long getZipFileLastModified() throws IOException {
jjg@839 691 synchronized (this) {
duke@1 692 checkIndex();
duke@1 693 return zipFileLastModified;
duke@1 694 }
duke@1 695 }
duke@1 696
duke@1 697 /** ------------------------------------------------------------------------
duke@1 698 * DirectoryEntry class
duke@1 699 * -------------------------------------------------------------------------*/
jjg@57 700
duke@1 701 static class DirectoryEntry {
duke@1 702 private boolean filesInited;
duke@1 703 private boolean directoriesInited;
duke@1 704 private boolean zipFileEntriesInited;
duke@1 705 private boolean entriesInited;
duke@1 706
duke@1 707 private long writtenOffsetOffset = 0;
duke@1 708
jjg@103 709 private RelativeDirectory dirName;
duke@1 710
duke@1 711 private com.sun.tools.javac.util.List<String> zipFileEntriesFiles = com.sun.tools.javac.util.List.<String>nil();
duke@1 712 private com.sun.tools.javac.util.List<String> zipFileEntriesDirectories = com.sun.tools.javac.util.List.<String>nil();
jjg@57 713 private com.sun.tools.javac.util.List<Entry> zipFileEntries = com.sun.tools.javac.util.List.<Entry>nil();
duke@1 714
jjg@57 715 private List<Entry> entries = new ArrayList<Entry>();
duke@1 716
duke@1 717 private ZipFileIndex zipFileIndex;
duke@1 718
duke@1 719 private int numEntries;
duke@1 720
jjg@103 721 DirectoryEntry(RelativeDirectory dirName, ZipFileIndex index) {
jjg@103 722 filesInited = false;
duke@1 723 directoriesInited = false;
duke@1 724 entriesInited = false;
duke@1 725
jjg@103 726 this.dirName = dirName;
duke@1 727 this.zipFileIndex = index;
duke@1 728 }
duke@1 729
duke@1 730 private com.sun.tools.javac.util.List<String> getFiles() {
jjg@103 731 if (!filesInited) {
jjg@103 732 initEntries();
jjg@103 733 for (Entry e : entries) {
jjg@103 734 if (!e.isDir) {
jjg@103 735 zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
jjg@103 736 }
jjg@103 737 }
jjg@103 738 filesInited = true;
duke@1 739 }
duke@1 740 return zipFileEntriesFiles;
duke@1 741 }
duke@1 742
duke@1 743 private com.sun.tools.javac.util.List<String> getDirectories() {
jjg@103 744 if (!directoriesInited) {
jjg@103 745 initEntries();
jjg@103 746 for (Entry e : entries) {
jjg@103 747 if (e.isDir) {
jjg@103 748 zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
jjg@103 749 }
jjg@103 750 }
jjg@103 751 directoriesInited = true;
duke@1 752 }
duke@1 753 return zipFileEntriesDirectories;
duke@1 754 }
duke@1 755
jjg@57 756 private com.sun.tools.javac.util.List<Entry> getEntries() {
jjg@103 757 if (!zipFileEntriesInited) {
jjg@103 758 initEntries();
jjg@103 759 zipFileEntries = com.sun.tools.javac.util.List.nil();
jjg@103 760 for (Entry zfie : entries) {
jjg@103 761 zipFileEntries = zipFileEntries.append(zfie);
jjg@103 762 }
jjg@103 763 zipFileEntriesInited = true;
duke@1 764 }
duke@1 765 return zipFileEntries;
duke@1 766 }
duke@1 767
jjg@57 768 private Entry getEntry(String rootName) {
duke@1 769 initEntries();
jjg@57 770 int index = Collections.binarySearch(entries, new Entry(dirName, rootName));
duke@1 771 if (index < 0) {
duke@1 772 return null;
duke@1 773 }
duke@1 774
duke@1 775 return entries.get(index);
duke@1 776 }
duke@1 777
duke@1 778 private void initEntries() {
duke@1 779 if (entriesInited) {
duke@1 780 return;
duke@1 781 }
duke@1 782
duke@1 783 if (!zipFileIndex.readFromIndex) {
duke@1 784 int from = -Arrays.binarySearch(zipFileIndex.entries,
jjg@57 785 new Entry(dirName, ZipFileIndex.MIN_CHAR)) - 1;
duke@1 786 int to = -Arrays.binarySearch(zipFileIndex.entries,
jjg@57 787 new Entry(dirName, MAX_CHAR)) - 1;
duke@1 788
duke@1 789 for (int i = from; i < to; i++) {
duke@1 790 entries.add(zipFileIndex.entries[i]);
duke@1 791 }
duke@1 792 } else {
duke@1 793 File indexFile = zipFileIndex.getIndexFile();
duke@1 794 if (indexFile != null) {
duke@1 795 RandomAccessFile raf = null;
duke@1 796 try {
duke@1 797 raf = new RandomAccessFile(indexFile, "r");
duke@1 798 raf.seek(writtenOffsetOffset);
duke@1 799
duke@1 800 for (int nFiles = 0; nFiles < numEntries; nFiles++) {
duke@1 801 // Read the name bytes
duke@1 802 int zfieNameBytesLen = raf.readInt();
duke@1 803 byte [] zfieNameBytes = new byte[zfieNameBytesLen];
duke@1 804 raf.read(zfieNameBytes);
duke@1 805 String eName = new String(zfieNameBytes, "UTF-8");
duke@1 806
duke@1 807 // Read isDir
duke@1 808 boolean eIsDir = raf.readByte() == (byte)0 ? false : true;
duke@1 809
duke@1 810 // Read offset of bytes in the real Jar/Zip file
duke@1 811 int eOffset = raf.readInt();
duke@1 812
duke@1 813 // Read size of the file in the real Jar/Zip file
duke@1 814 int eSize = raf.readInt();
duke@1 815
duke@1 816 // Read compressed size of the file in the real Jar/Zip file
duke@1 817 int eCsize = raf.readInt();
duke@1 818
duke@1 819 // Read java time stamp of the file in the real Jar/Zip file
duke@1 820 long eJavaTimestamp = raf.readLong();
duke@1 821
jjg@57 822 Entry rfie = new Entry(dirName, eName);
duke@1 823 rfie.isDir = eIsDir;
duke@1 824 rfie.offset = eOffset;
duke@1 825 rfie.size = eSize;
duke@1 826 rfie.compressedSize = eCsize;
duke@1 827 rfie.javatime = eJavaTimestamp;
duke@1 828 entries.add(rfie);
duke@1 829 }
duke@1 830 } catch (Throwable t) {
duke@1 831 // Do nothing
duke@1 832 } finally {
duke@1 833 try {
jjg@444 834 if (raf != null) {
duke@1 835 raf.close();
duke@1 836 }
duke@1 837 } catch (Throwable t) {
duke@1 838 // Do nothing
duke@1 839 }
duke@1 840 }
duke@1 841 }
duke@1 842 }
duke@1 843
duke@1 844 entriesInited = true;
duke@1 845 }
duke@1 846
jjg@57 847 List<Entry> getEntriesAsCollection() {
duke@1 848 initEntries();
duke@1 849
duke@1 850 return entries;
duke@1 851 }
duke@1 852 }
duke@1 853
duke@1 854 private boolean readIndex() {
duke@1 855 if (triedToReadIndex || !usePreindexedCache) {
duke@1 856 return false;
duke@1 857 }
duke@1 858
duke@1 859 boolean ret = false;
jjg@839 860 synchronized (this) {
duke@1 861 triedToReadIndex = true;
duke@1 862 RandomAccessFile raf = null;
duke@1 863 try {
duke@1 864 File indexFileName = getIndexFile();
duke@1 865 raf = new RandomAccessFile(indexFileName, "r");
duke@1 866
duke@1 867 long fileStamp = raf.readLong();
duke@1 868 if (zipFile.lastModified() != fileStamp) {
duke@1 869 ret = false;
duke@1 870 } else {
mcimadamore@1279 871 directories = new LinkedHashMap<RelativeDirectory, DirectoryEntry>();
duke@1 872 int numDirs = raf.readInt();
duke@1 873 for (int nDirs = 0; nDirs < numDirs; nDirs++) {
duke@1 874 int dirNameBytesLen = raf.readInt();
duke@1 875 byte [] dirNameBytes = new byte[dirNameBytesLen];
duke@1 876 raf.read(dirNameBytes);
duke@1 877
jjg@103 878 RelativeDirectory dirNameStr = getRelativeDirectory(new String(dirNameBytes, "UTF-8"));
duke@1 879 DirectoryEntry de = new DirectoryEntry(dirNameStr, this);
duke@1 880 de.numEntries = raf.readInt();
duke@1 881 de.writtenOffsetOffset = raf.readLong();
duke@1 882 directories.put(dirNameStr, de);
duke@1 883 }
duke@1 884 ret = true;
duke@1 885 zipFileLastModified = fileStamp;
duke@1 886 }
duke@1 887 } catch (Throwable t) {
duke@1 888 // Do nothing
duke@1 889 } finally {
duke@1 890 if (raf != null) {
duke@1 891 try {
duke@1 892 raf.close();
duke@1 893 } catch (Throwable tt) {
duke@1 894 // Do nothing
duke@1 895 }
duke@1 896 }
duke@1 897 }
duke@1 898 if (ret == true) {
duke@1 899 readFromIndex = true;
duke@1 900 }
duke@1 901 }
duke@1 902
duke@1 903 return ret;
duke@1 904 }
duke@1 905
duke@1 906 private boolean writeIndex() {
duke@1 907 boolean ret = false;
duke@1 908 if (readFromIndex || !usePreindexedCache) {
duke@1 909 return true;
duke@1 910 }
duke@1 911
duke@1 912 if (!writeIndex) {
duke@1 913 return true;
duke@1 914 }
duke@1 915
duke@1 916 File indexFile = getIndexFile();
duke@1 917 if (indexFile == null) {
duke@1 918 return false;
duke@1 919 }
duke@1 920
duke@1 921 RandomAccessFile raf = null;
duke@1 922 long writtenSoFar = 0;
duke@1 923 try {
duke@1 924 raf = new RandomAccessFile(indexFile, "rw");
duke@1 925
duke@1 926 raf.writeLong(zipFileLastModified);
duke@1 927 writtenSoFar += 8;
duke@1 928
duke@1 929 List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>();
jjg@103 930 Map<RelativeDirectory, Long> offsets = new HashMap<RelativeDirectory, Long>();
duke@1 931 raf.writeInt(directories.keySet().size());
duke@1 932 writtenSoFar += 4;
duke@1 933
jjg@103 934 for (RelativeDirectory dirName: directories.keySet()) {
duke@1 935 DirectoryEntry dirEntry = directories.get(dirName);
duke@1 936
duke@1 937 directoriesToWrite.add(dirEntry);
duke@1 938
duke@1 939 // Write the dir name bytes
jjg@103 940 byte [] dirNameBytes = dirName.getPath().getBytes("UTF-8");
duke@1 941 int dirNameBytesLen = dirNameBytes.length;
duke@1 942 raf.writeInt(dirNameBytesLen);
duke@1 943 writtenSoFar += 4;
duke@1 944
duke@1 945 raf.write(dirNameBytes);
duke@1 946 writtenSoFar += dirNameBytesLen;
duke@1 947
duke@1 948 // Write the number of files in the dir
mcimadamore@184 949 List<Entry> dirEntries = dirEntry.getEntriesAsCollection();
duke@1 950 raf.writeInt(dirEntries.size());
duke@1 951 writtenSoFar += 4;
duke@1 952
duke@1 953 offsets.put(dirName, new Long(writtenSoFar));
duke@1 954
duke@1 955 // Write the offset of the file's data in the dir
duke@1 956 dirEntry.writtenOffsetOffset = 0L;
duke@1 957 raf.writeLong(0L);
duke@1 958 writtenSoFar += 8;
duke@1 959 }
duke@1 960
duke@1 961 for (DirectoryEntry de : directoriesToWrite) {
duke@1 962 // Fix up the offset in the directory table
duke@1 963 long currFP = raf.getFilePointer();
duke@1 964
duke@1 965 long offsetOffset = offsets.get(de.dirName).longValue();
duke@1 966 raf.seek(offsetOffset);
duke@1 967 raf.writeLong(writtenSoFar);
duke@1 968
duke@1 969 raf.seek(currFP);
duke@1 970
duke@1 971 // Now write each of the files in the DirectoryEntry
jjg@839 972 List<Entry> list = de.getEntriesAsCollection();
jjg@839 973 for (Entry zfie : list) {
duke@1 974 // Write the name bytes
duke@1 975 byte [] zfieNameBytes = zfie.name.getBytes("UTF-8");
duke@1 976 int zfieNameBytesLen = zfieNameBytes.length;
duke@1 977 raf.writeInt(zfieNameBytesLen);
duke@1 978 writtenSoFar += 4;
duke@1 979 raf.write(zfieNameBytes);
duke@1 980 writtenSoFar += zfieNameBytesLen;
duke@1 981
duke@1 982 // Write isDir
duke@1 983 raf.writeByte(zfie.isDir ? (byte)1 : (byte)0);
duke@1 984 writtenSoFar += 1;
duke@1 985
duke@1 986 // Write offset of bytes in the real Jar/Zip file
duke@1 987 raf.writeInt(zfie.offset);
duke@1 988 writtenSoFar += 4;
duke@1 989
duke@1 990 // Write size of the file in the real Jar/Zip file
duke@1 991 raf.writeInt(zfie.size);
duke@1 992 writtenSoFar += 4;
duke@1 993
duke@1 994 // Write compressed size of the file in the real Jar/Zip file
duke@1 995 raf.writeInt(zfie.compressedSize);
duke@1 996 writtenSoFar += 4;
duke@1 997
duke@1 998 // Write java time stamp of the file in the real Jar/Zip file
duke@1 999 raf.writeLong(zfie.getLastModified());
duke@1 1000 writtenSoFar += 8;
duke@1 1001 }
duke@1 1002 }
duke@1 1003 } catch (Throwable t) {
duke@1 1004 // Do nothing
duke@1 1005 } finally {
duke@1 1006 try {
duke@1 1007 if (raf != null) {
duke@1 1008 raf.close();
duke@1 1009 }
duke@1 1010 } catch(IOException ioe) {
duke@1 1011 // Do nothing
duke@1 1012 }
duke@1 1013 }
duke@1 1014
duke@1 1015 return ret;
duke@1 1016 }
duke@1 1017
duke@1 1018 public boolean writeZipIndex() {
jjg@839 1019 synchronized (this) {
duke@1 1020 return writeIndex();
duke@1 1021 }
duke@1 1022 }
duke@1 1023
duke@1 1024 private File getIndexFile() {
duke@1 1025 if (zipIndexFile == null) {
duke@1 1026 if (zipFile == null) {
duke@1 1027 return null;
duke@1 1028 }
duke@1 1029
duke@1 1030 zipIndexFile = new File((preindexedCacheLocation == null ? "" : preindexedCacheLocation) +
duke@1 1031 zipFile.getName() + ".index");
duke@1 1032 }
duke@1 1033
duke@1 1034 return zipIndexFile;
duke@1 1035 }
duke@1 1036
duke@1 1037 public File getZipFile() {
duke@1 1038 return zipFile;
duke@1 1039 }
jjg@57 1040
jjg@424 1041 File getAbsoluteFile() {
jjg@424 1042 File absFile = (absFileRef == null ? null : absFileRef.get());
jjg@424 1043 if (absFile == null) {
jjg@424 1044 absFile = zipFile.getAbsoluteFile();
jjg@424 1045 absFileRef = new SoftReference<File>(absFile);
jjg@424 1046 }
jjg@424 1047 return absFile;
jjg@424 1048 }
jjg@424 1049
jjg@103 1050 private RelativeDirectory getRelativeDirectory(String path) {
jjg@103 1051 RelativeDirectory rd;
jjg@103 1052 SoftReference<RelativeDirectory> ref = relativeDirectoryCache.get(path);
jjg@103 1053 if (ref != null) {
jjg@103 1054 rd = ref.get();
jjg@103 1055 if (rd != null)
jjg@103 1056 return rd;
jjg@103 1057 }
jjg@103 1058 rd = new RelativeDirectory(path);
jjg@103 1059 relativeDirectoryCache.put(path, new SoftReference<RelativeDirectory>(rd));
jjg@103 1060 return rd;
jjg@103 1061 }
jjg@57 1062
jjg@57 1063 static class Entry implements Comparable<Entry> {
jjg@57 1064 public static final Entry[] EMPTY_ARRAY = {};
jjg@57 1065
jjg@57 1066 // Directory related
jjg@103 1067 RelativeDirectory dir;
jjg@57 1068 boolean isDir;
jjg@57 1069
jjg@57 1070 // File related
jjg@57 1071 String name;
jjg@57 1072
jjg@57 1073 int offset;
jjg@57 1074 int size;
jjg@57 1075 int compressedSize;
jjg@57 1076 long javatime;
jjg@57 1077
jjg@57 1078 private int nativetime;
jjg@57 1079
jjg@103 1080 public Entry(RelativePath path) {
jjg@103 1081 this(path.dirname(), path.basename());
jjg@57 1082 }
jjg@57 1083
jjg@103 1084 public Entry(RelativeDirectory directory, String name) {
jjg@103 1085 this.dir = directory;
jjg@57 1086 this.name = name;
jjg@57 1087 }
jjg@57 1088
jjg@57 1089 public String getName() {
jjg@103 1090 return new RelativeFile(dir, name).getPath();
jjg@57 1091 }
jjg@57 1092
jjg@57 1093 public String getFileName() {
jjg@57 1094 return name;
jjg@57 1095 }
jjg@57 1096
jjg@57 1097 public long getLastModified() {
jjg@57 1098 if (javatime == 0) {
jjg@57 1099 javatime = dosToJavaTime(nativetime);
jjg@57 1100 }
jjg@57 1101 return javatime;
jjg@57 1102 }
jjg@57 1103
jjg@71 1104 // based on dosToJavaTime in java.util.Zip, but avoiding the
jjg@71 1105 // use of deprecated Date constructor
jjg@71 1106 private static long dosToJavaTime(int dtime) {
jjg@71 1107 Calendar c = Calendar.getInstance();
jjg@71 1108 c.set(Calendar.YEAR, ((dtime >> 25) & 0x7f) + 1980);
jjg@71 1109 c.set(Calendar.MONTH, ((dtime >> 21) & 0x0f) - 1);
jjg@71 1110 c.set(Calendar.DATE, ((dtime >> 16) & 0x1f));
jjg@71 1111 c.set(Calendar.HOUR_OF_DAY, ((dtime >> 11) & 0x1f));
jjg@71 1112 c.set(Calendar.MINUTE, ((dtime >> 5) & 0x3f));
jjg@71 1113 c.set(Calendar.SECOND, ((dtime << 1) & 0x3e));
jjg@71 1114 c.set(Calendar.MILLISECOND, 0);
jjg@71 1115 return c.getTimeInMillis();
jjg@57 1116 }
jjg@57 1117
jjg@57 1118 void setNativeTime(int natTime) {
jjg@57 1119 nativetime = natTime;
jjg@57 1120 }
jjg@57 1121
jjg@57 1122 public boolean isDirectory() {
jjg@57 1123 return isDir;
jjg@57 1124 }
jjg@57 1125
jjg@57 1126 public int compareTo(Entry other) {
jjg@103 1127 RelativeDirectory otherD = other.dir;
jjg@57 1128 if (dir != otherD) {
jjg@57 1129 int c = dir.compareTo(otherD);
jjg@57 1130 if (c != 0)
jjg@57 1131 return c;
jjg@57 1132 }
jjg@57 1133 return name.compareTo(other.name);
jjg@57 1134 }
jjg@57 1135
jjg@103 1136 @Override
jjg@103 1137 public boolean equals(Object o) {
jjg@103 1138 if (!(o instanceof Entry))
jjg@103 1139 return false;
jjg@103 1140 Entry other = (Entry) o;
jjg@103 1141 return dir.equals(other.dir) && name.equals(other.name);
jjg@103 1142 }
jjg@103 1143
jjg@103 1144 @Override
jjg@103 1145 public int hashCode() {
jjg@103 1146 int hash = 7;
jjg@103 1147 hash = 97 * hash + (this.dir != null ? this.dir.hashCode() : 0);
jjg@103 1148 hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0);
jjg@103 1149 return hash;
jjg@103 1150 }
jjg@103 1151
jjg@839 1152 @Override
jjg@57 1153 public String toString() {
jjg@57 1154 return isDir ? ("Dir:" + dir + " : " + name) :
jjg@57 1155 (dir + ":" + name);
jjg@57 1156 }
jjg@57 1157 }
jjg@57 1158
ksrini@923 1159 /*
ksrini@923 1160 * Exception primarily used to implement a failover, used exclusively here.
ksrini@923 1161 */
ksrini@923 1162
ksrini@923 1163 static final class ZipFormatException extends IOException {
ksrini@923 1164 private static final long serialVersionUID = 8000196834066748623L;
ksrini@923 1165 protected ZipFormatException(String message) {
ksrini@923 1166 super(message);
ksrini@923 1167 }
ksrini@923 1168
ksrini@923 1169 protected ZipFormatException(String message, Throwable cause) {
ksrini@923 1170 super(message, cause);
ksrini@923 1171 }
ksrini@923 1172 }
duke@1 1173 }

mercurial