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

Thu, 31 Aug 2017 15:17:03 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:17:03 +0800
changeset 2525
2eb010b6cb22
parent 1467
189b26e3818f
parent 0
959103a6100f
permissions
-rw-r--r--

merge

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

mercurial