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

Mon, 27 Jul 2009 19:52:42 -0700

author
jjg
date
Mon, 27 Jul 2009 19:52:42 -0700
changeset 333
7c2d6da61646
parent 184
905e151a185a
child 424
86b773b7cb40
permissions
-rw-r--r--

6865399: some javac files are missing Sun internal API comment
Reviewed-by: darcy

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

mercurial