1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/tools/javac/zip/ZipFileIndex.java Sat Dec 01 00:00:00 2007 +0000 1.3 @@ -0,0 +1,1211 @@ 1.4 +package com.sun.tools.javac.zip; 1.5 + 1.6 +import java.io.*; 1.7 +import java.text.MessageFormat; 1.8 +import java.util.*; 1.9 +import java.util.List; 1.10 +import java.util.concurrent.locks.ReentrantLock; 1.11 +import java.util.zip.*; 1.12 + 1.13 +/** This class implements building of index of a zip archive and access to it's context. 1.14 + * It also uses prebuild index if available. It supports invocations where it will 1.15 + * serialize an optimized zip index file to disk. 1.16 + * 1.17 + * In oreder to use secondary index file make sure the option "usezipindex" is in the Options object, 1.18 + * when JavacFileManager is invoked. (You can pass "-XDusezipindex" on the command line. 1.19 + * 1.20 + * Location where to look for/generate optimized zip index files can be provided using 1.21 + * "-XDcachezipindexdir=<directory>". If this flag is not provided, the dfault location is 1.22 + * the value of the "java.io.tmpdir" system property. 1.23 + * 1.24 + * If key "-XDwritezipindexfiles" is specified, there will be new optimized index file 1.25 + * created for each archive, used by the compiler for compilation, at location, 1.26 + * specified by "cachezipindexdir" option. 1.27 + * 1.28 + * If nonBatchMode option is specified (-XDnonBatchMode) the compiler will use timestamp 1.29 + * checking to reindex the zip files if it is needed. In batch mode the timestamps are not checked 1.30 + * and the compiler uses the cached indexes. 1.31 + */ 1.32 +public class ZipFileIndex { 1.33 + private static final String MIN_CHAR = String.valueOf(Character.MIN_VALUE); 1.34 + private static final String MAX_CHAR = String.valueOf(Character.MAX_VALUE); 1.35 + 1.36 + public final static long NOT_MODIFIED = Long.MIN_VALUE; 1.37 + 1.38 + private static Map<File, ZipFileIndex> zipFileIndexCache = new HashMap<File, ZipFileIndex>(); 1.39 + private static ReentrantLock lock = new ReentrantLock(); 1.40 + 1.41 + private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this. 1.42 + 1.43 + private Map<String, DirectoryEntry> directories = Collections.<String, DirectoryEntry>emptyMap(); 1.44 + private Set<String> allDirs = Collections.<String>emptySet(); 1.45 + 1.46 + // ZipFileIndex data entries 1.47 + private File zipFile; 1.48 + private long zipFileLastModified = NOT_MODIFIED; 1.49 + private RandomAccessFile zipRandomFile; 1.50 + private ZipFileIndexEntry[] entries; 1.51 + 1.52 + private boolean readFromIndex = false; 1.53 + private File zipIndexFile = null; 1.54 + private boolean triedToReadIndex = false; 1.55 + private int symbolFilePrefixLength = 0; 1.56 + private boolean hasPopulatedData = false; 1.57 + private long lastReferenceTimeStamp = NOT_MODIFIED; 1.58 + 1.59 + private boolean usePreindexedCache = false; 1.60 + private String preindexedCacheLocation = null; 1.61 + 1.62 + private boolean writeIndex = false; 1.63 + 1.64 + /** 1.65 + * Returns a list of all ZipFileIndex entries 1.66 + * 1.67 + * @return A list of ZipFileIndex entries, or an empty list 1.68 + */ 1.69 + public static List<ZipFileIndex> getZipFileIndexes() { 1.70 + return getZipFileIndexes(false); 1.71 + } 1.72 + 1.73 + /** 1.74 + * Returns a list of all ZipFileIndex entries 1.75 + * 1.76 + * @param openedOnly If true it returns a list of only opened ZipFileIndex entries, otherwise 1.77 + * all ZipFileEntry(s) are included into the list. 1.78 + * @return A list of ZipFileIndex entries, or an empty list 1.79 + */ 1.80 + public static List<ZipFileIndex> getZipFileIndexes(boolean openedOnly) { 1.81 + List<ZipFileIndex> zipFileIndexes = new ArrayList<ZipFileIndex>(); 1.82 + lock.lock(); 1.83 + try { 1.84 + zipFileIndexes.addAll(zipFileIndexCache.values()); 1.85 + 1.86 + if (openedOnly) { 1.87 + for(ZipFileIndex elem : zipFileIndexes) { 1.88 + if (!elem.isOpen()) { 1.89 + zipFileIndexes.remove(elem); 1.90 + } 1.91 + } 1.92 + } 1.93 + } 1.94 + finally { 1.95 + lock.unlock(); 1.96 + } 1.97 + return zipFileIndexes; 1.98 + } 1.99 + 1.100 + public boolean isOpen() { 1.101 + lock.lock(); 1.102 + try { 1.103 + return zipRandomFile != null; 1.104 + } 1.105 + finally { 1.106 + lock.unlock(); 1.107 + } 1.108 + } 1.109 + 1.110 + public static ZipFileIndex getZipFileIndex(File zipFile, int symbolFilePrefixLen, boolean useCache, String cacheLocation, boolean writeIndex) throws IOException { 1.111 + ZipFileIndex zi = null; 1.112 + lock.lock(); 1.113 + try { 1.114 + zi = getExistingZipIndex(zipFile); 1.115 + 1.116 + if (zi == null || (zi != null && zipFile.lastModified() != zi.zipFileLastModified)) { 1.117 + zi = new ZipFileIndex(zipFile, symbolFilePrefixLen, writeIndex, 1.118 + useCache, cacheLocation); 1.119 + zipFileIndexCache.put(zipFile, zi); 1.120 + } 1.121 + } 1.122 + finally { 1.123 + lock.unlock(); 1.124 + } 1.125 + return zi; 1.126 + } 1.127 + 1.128 + public static ZipFileIndex getExistingZipIndex(File zipFile) { 1.129 + lock.lock(); 1.130 + try { 1.131 + return zipFileIndexCache.get(zipFile); 1.132 + } 1.133 + finally { 1.134 + lock.unlock(); 1.135 + } 1.136 + } 1.137 + 1.138 + public static void clearCache() { 1.139 + lock.lock(); 1.140 + try { 1.141 + zipFileIndexCache.clear(); 1.142 + } 1.143 + finally { 1.144 + lock.unlock(); 1.145 + } 1.146 + } 1.147 + 1.148 + public static void clearCache(long timeNotUsed) { 1.149 + lock.lock(); 1.150 + try { 1.151 + Iterator<File> cachedFileIterator = zipFileIndexCache.keySet().iterator(); 1.152 + while (cachedFileIterator.hasNext()) { 1.153 + File cachedFile = cachedFileIterator.next(); 1.154 + ZipFileIndex cachedZipIndex = zipFileIndexCache.get(cachedFile); 1.155 + if (cachedZipIndex != null) { 1.156 + long timeToTest = cachedZipIndex.lastReferenceTimeStamp + timeNotUsed; 1.157 + if (timeToTest < cachedZipIndex.lastReferenceTimeStamp || // Overflow... 1.158 + System.currentTimeMillis() > timeToTest) { 1.159 + zipFileIndexCache.remove(cachedFile); 1.160 + } 1.161 + } 1.162 + } 1.163 + } 1.164 + finally { 1.165 + lock.unlock(); 1.166 + } 1.167 + } 1.168 + 1.169 + public static void removeFromCache(File file) { 1.170 + lock.lock(); 1.171 + try { 1.172 + zipFileIndexCache.remove(file); 1.173 + } 1.174 + finally { 1.175 + lock.unlock(); 1.176 + } 1.177 + } 1.178 + 1.179 + /** Sets already opened list of ZipFileIndexes from an outside client 1.180 + * of the compiler. This functionality should be used in a non-batch clients of the compiler. 1.181 + */ 1.182 + public static void setOpenedIndexes(List<ZipFileIndex>indexes) throws IllegalStateException { 1.183 + lock.lock(); 1.184 + try { 1.185 + if (zipFileIndexCache.isEmpty()) { 1.186 + throw new IllegalStateException("Setting opened indexes should be called only when the ZipFileCache is empty. Call JavacFileManager.flush() before calling this method."); 1.187 + } 1.188 + 1.189 + for (ZipFileIndex zfi : indexes) { 1.190 + zipFileIndexCache.put(zfi.zipFile, zfi); 1.191 + } 1.192 + } 1.193 + finally { 1.194 + lock.unlock(); 1.195 + } 1.196 + } 1.197 + 1.198 + private ZipFileIndex(File zipFile, int symbolFilePrefixLen, boolean writeIndex, 1.199 + boolean useCache, String cacheLocation) throws IOException { 1.200 + this.zipFile = zipFile; 1.201 + this.symbolFilePrefixLength = symbolFilePrefixLen; 1.202 + this.writeIndex = writeIndex; 1.203 + this.usePreindexedCache = useCache; 1.204 + this.preindexedCacheLocation = cacheLocation; 1.205 + 1.206 + if (zipFile != null) { 1.207 + this.zipFileLastModified = zipFile.lastModified(); 1.208 + } 1.209 + 1.210 + // Validate integrity of the zip file 1.211 + checkIndex(); 1.212 + } 1.213 + 1.214 + public String toString() { 1.215 + return "ZipFileIndex of file:(" + zipFile + ")"; 1.216 + } 1.217 + 1.218 + // Just in case... 1.219 + protected void finalize() { 1.220 + closeFile(); 1.221 + } 1.222 + 1.223 + private boolean isUpToDate() { 1.224 + if (zipFile != null && 1.225 + ((!NON_BATCH_MODE) || zipFileLastModified == zipFile.lastModified()) && 1.226 + hasPopulatedData) { 1.227 + return true; 1.228 + } 1.229 + 1.230 + return false; 1.231 + } 1.232 + 1.233 + /** 1.234 + * Here we need to make sure that the ZipFileIndex is valid. Check the timestamp of the file and 1.235 + * if its the same as the one at the time the index was build we don't need to reopen anything. 1.236 + */ 1.237 + private void checkIndex() throws IOException { 1.238 + boolean isUpToDate = true; 1.239 + if (!isUpToDate()) { 1.240 + closeFile(); 1.241 + isUpToDate = false; 1.242 + } 1.243 + 1.244 + if (zipRandomFile != null || isUpToDate) { 1.245 + lastReferenceTimeStamp = System.currentTimeMillis(); 1.246 + return; 1.247 + } 1.248 + 1.249 + hasPopulatedData = true; 1.250 + 1.251 + if (readIndex()) { 1.252 + lastReferenceTimeStamp = System.currentTimeMillis(); 1.253 + return; 1.254 + } 1.255 + 1.256 + directories = Collections.<String, DirectoryEntry>emptyMap(); 1.257 + allDirs = Collections.<String>emptySet(); 1.258 + 1.259 + try { 1.260 + openFile(); 1.261 + long totalLength = zipRandomFile.length(); 1.262 + ZipDirectory directory = new ZipDirectory(zipRandomFile, 0L, totalLength, this); 1.263 + directory.buildIndex(); 1.264 + } finally { 1.265 + if (zipRandomFile != null) { 1.266 + closeFile(); 1.267 + } 1.268 + } 1.269 + 1.270 + lastReferenceTimeStamp = System.currentTimeMillis(); 1.271 + } 1.272 + 1.273 + private void openFile() throws FileNotFoundException { 1.274 + if (zipRandomFile == null && zipFile != null) { 1.275 + zipRandomFile = new RandomAccessFile(zipFile, "r"); 1.276 + } 1.277 + } 1.278 + 1.279 + private void cleanupState() { 1.280 + // Make sure there is a valid but empty index if the file doesn't exist 1.281 + entries = ZipFileIndexEntry.EMPTY_ARRAY; 1.282 + directories = Collections.<String, DirectoryEntry>emptyMap(); 1.283 + zipFileLastModified = NOT_MODIFIED; 1.284 + allDirs = Collections.<String>emptySet(); 1.285 + } 1.286 + 1.287 + public void close() { 1.288 + lock.lock(); 1.289 + try { 1.290 + writeIndex(); 1.291 + closeFile(); 1.292 + } 1.293 + finally { 1.294 + lock.unlock(); 1.295 + } 1.296 + } 1.297 + 1.298 + private void closeFile() { 1.299 + if (zipRandomFile != null) { 1.300 + try { 1.301 + zipRandomFile.close(); 1.302 + } catch (IOException ex) { 1.303 + } 1.304 + zipRandomFile = null; 1.305 + } 1.306 + } 1.307 + 1.308 + /** 1.309 + * Returns the ZipFileIndexEntry for an absolute path, if there is one. 1.310 + */ 1.311 + public ZipFileIndexEntry getZipIndexEntry(String path) { 1.312 + if (File.separatorChar != '/') { 1.313 + path = path.replace('/', File.separatorChar); 1.314 + } 1.315 + lock.lock(); 1.316 + try { 1.317 + checkIndex(); 1.318 + String lookFor = ""; 1.319 + int lastSepIndex = path.lastIndexOf(File.separatorChar); 1.320 + boolean noSeparator = false; 1.321 + if (lastSepIndex == -1) { 1.322 + noSeparator = true; 1.323 + } 1.324 + 1.325 + DirectoryEntry de = directories.get(noSeparator ? "" : path.substring(0, lastSepIndex)); 1.326 + 1.327 + lookFor = path.substring(noSeparator ? 0 : lastSepIndex + 1); 1.328 + 1.329 + return de == null ? null : de.getEntry(lookFor); 1.330 + } 1.331 + catch (IOException e) { 1.332 + return null; 1.333 + } 1.334 + finally { 1.335 + lock.unlock(); 1.336 + } 1.337 + } 1.338 + 1.339 + /** 1.340 + * Returns a javac List of filenames within an absolute path in the ZipFileIndex. 1.341 + */ 1.342 + public com.sun.tools.javac.util.List<String> getFiles(String path) { 1.343 + if (File.separatorChar != '/') { 1.344 + path = path.replace('/', File.separatorChar); 1.345 + } 1.346 + 1.347 + lock.lock(); 1.348 + try { 1.349 + checkIndex(); 1.350 + 1.351 + DirectoryEntry de = directories.get(path); 1.352 + com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getFiles(); 1.353 + 1.354 + if (ret == null) { 1.355 + return com.sun.tools.javac.util.List.<String>nil(); 1.356 + } 1.357 + return ret; 1.358 + } 1.359 + catch (IOException e) { 1.360 + return com.sun.tools.javac.util.List.<String>nil(); 1.361 + } 1.362 + finally { 1.363 + lock.unlock(); 1.364 + } 1.365 + } 1.366 + 1.367 + public List<String> getAllDirectories(String path) { 1.368 + 1.369 + if (File.separatorChar != '/') { 1.370 + path = path.replace('/', File.separatorChar); 1.371 + } 1.372 + 1.373 + lock.lock(); 1.374 + try { 1.375 + checkIndex(); 1.376 + path = path.intern(); 1.377 + 1.378 + DirectoryEntry de = directories.get(path); 1.379 + com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories(); 1.380 + 1.381 + if (ret == null) { 1.382 + return com.sun.tools.javac.util.List.<String>nil(); 1.383 + } 1.384 + 1.385 + return ret; 1.386 + } 1.387 + catch (IOException e) { 1.388 + return com.sun.tools.javac.util.List.<String>nil(); 1.389 + } 1.390 + finally { 1.391 + lock.unlock(); 1.392 + } 1.393 + } 1.394 + 1.395 + public Set<String> getAllDirectories() { 1.396 + lock.lock(); 1.397 + try { 1.398 + checkIndex(); 1.399 + if (allDirs == Collections.EMPTY_SET) { 1.400 + Set<String> alldirs = new HashSet<String>(); 1.401 + Iterator<String> dirsIter = directories.keySet().iterator(); 1.402 + while (dirsIter.hasNext()) { 1.403 + alldirs.add(new String(dirsIter.next())); 1.404 + } 1.405 + 1.406 + allDirs = alldirs; 1.407 + } 1.408 + 1.409 + return allDirs; 1.410 + } 1.411 + catch (IOException e) { 1.412 + return Collections.<String>emptySet(); 1.413 + } 1.414 + finally { 1.415 + lock.unlock(); 1.416 + } 1.417 + } 1.418 + 1.419 + /** 1.420 + * Tests if a specific path exists in the zip. This method will return true 1.421 + * for file entries and directories. 1.422 + * 1.423 + * @param path A path within the zip. 1.424 + * @return True if the path is a file or dir, false otherwise. 1.425 + */ 1.426 + public boolean contains(String path) { 1.427 + lock.lock(); 1.428 + try { 1.429 + checkIndex(); 1.430 + return getZipIndexEntry(path) != null; 1.431 + } 1.432 + catch (IOException e) { 1.433 + return false; 1.434 + } 1.435 + finally { 1.436 + lock.unlock(); 1.437 + } 1.438 + } 1.439 + 1.440 + public boolean isDirectory(String path) throws IOException { 1.441 + lock.lock(); 1.442 + try { 1.443 + // The top level in a zip file is always a directory. 1.444 + if (path.length() == 0) { 1.445 + lastReferenceTimeStamp = System.currentTimeMillis(); 1.446 + return true; 1.447 + } 1.448 + 1.449 + if (File.separatorChar != '/') 1.450 + path = path.replace('/', File.separatorChar); 1.451 + checkIndex(); 1.452 + return directories.get(path) != null; 1.453 + } 1.454 + finally { 1.455 + lock.unlock(); 1.456 + } 1.457 + } 1.458 + 1.459 + public long getLastModified(String path) throws IOException { 1.460 + lock.lock(); 1.461 + try { 1.462 + ZipFileIndexEntry entry = getZipIndexEntry(path); 1.463 + if (entry == null) 1.464 + throw new FileNotFoundException(); 1.465 + return entry.getLastModified(); 1.466 + } 1.467 + finally { 1.468 + lock.unlock(); 1.469 + } 1.470 + } 1.471 + 1.472 + public int length(String path) throws IOException { 1.473 + lock.lock(); 1.474 + try { 1.475 + ZipFileIndexEntry entry = getZipIndexEntry(path); 1.476 + if (entry == null) 1.477 + throw new FileNotFoundException(); 1.478 + 1.479 + if (entry.isDir) { 1.480 + return 0; 1.481 + } 1.482 + 1.483 + byte[] header = getHeader(entry); 1.484 + // entry is not compressed? 1.485 + if (get2ByteLittleEndian(header, 8) == 0) { 1.486 + return entry.compressedSize; 1.487 + } else { 1.488 + return entry.size; 1.489 + } 1.490 + } 1.491 + finally { 1.492 + lock.unlock(); 1.493 + } 1.494 + } 1.495 + 1.496 + public byte[] read(String path) throws IOException { 1.497 + lock.lock(); 1.498 + try { 1.499 + ZipFileIndexEntry entry = getZipIndexEntry(path); 1.500 + if (entry == null) 1.501 + throw new FileNotFoundException(MessageFormat.format("Path not found in ZIP: {0}", path)); 1.502 + return read(entry); 1.503 + } 1.504 + finally { 1.505 + lock.unlock(); 1.506 + } 1.507 + } 1.508 + 1.509 + public byte[] read(ZipFileIndexEntry entry) throws IOException { 1.510 + lock.lock(); 1.511 + try { 1.512 + openFile(); 1.513 + byte[] result = readBytes(entry); 1.514 + closeFile(); 1.515 + return result; 1.516 + } 1.517 + finally { 1.518 + lock.unlock(); 1.519 + } 1.520 + } 1.521 + 1.522 + public int read(String path, byte[] buffer) throws IOException { 1.523 + lock.lock(); 1.524 + try { 1.525 + ZipFileIndexEntry entry = getZipIndexEntry(path); 1.526 + if (entry == null) 1.527 + throw new FileNotFoundException(); 1.528 + return read(entry, buffer); 1.529 + } 1.530 + finally { 1.531 + lock.unlock(); 1.532 + } 1.533 + } 1.534 + 1.535 + public int read(ZipFileIndexEntry entry, byte[] buffer) 1.536 + throws IOException { 1.537 + lock.lock(); 1.538 + try { 1.539 + int result = readBytes(entry, buffer); 1.540 + return result; 1.541 + } 1.542 + finally { 1.543 + lock.unlock(); 1.544 + } 1.545 + } 1.546 + 1.547 + private byte[] readBytes(ZipFileIndexEntry entry) throws IOException { 1.548 + byte[] header = getHeader(entry); 1.549 + int csize = entry.compressedSize; 1.550 + byte[] cbuf = new byte[csize]; 1.551 + zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28)); 1.552 + zipRandomFile.readFully(cbuf, 0, csize); 1.553 + 1.554 + // is this compressed - offset 8 in the ZipEntry header 1.555 + if (get2ByteLittleEndian(header, 8) == 0) 1.556 + return cbuf; 1.557 + 1.558 + int size = entry.size; 1.559 + byte[] buf = new byte[size]; 1.560 + if (inflate(cbuf, buf) != size) 1.561 + throw new ZipException("corrupted zip file"); 1.562 + 1.563 + return buf; 1.564 + } 1.565 + 1.566 + /** 1.567 + * 1.568 + */ 1.569 + private int readBytes(ZipFileIndexEntry entry, byte[] buffer) throws IOException { 1.570 + byte[] header = getHeader(entry); 1.571 + 1.572 + // entry is not compressed? 1.573 + if (get2ByteLittleEndian(header, 8) == 0) { 1.574 + zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28)); 1.575 + int offset = 0; 1.576 + int size = buffer.length; 1.577 + while (offset < size) { 1.578 + int count = zipRandomFile.read(buffer, offset, size - offset); 1.579 + if (count == -1) 1.580 + break; 1.581 + offset += count; 1.582 + } 1.583 + return entry.size; 1.584 + } 1.585 + 1.586 + int csize = entry.compressedSize; 1.587 + byte[] cbuf = new byte[csize]; 1.588 + zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28)); 1.589 + zipRandomFile.readFully(cbuf, 0, csize); 1.590 + 1.591 + int count = inflate(cbuf, buffer); 1.592 + if (count == -1) 1.593 + throw new ZipException("corrupted zip file"); 1.594 + 1.595 + return entry.size; 1.596 + } 1.597 + 1.598 + //---------------------------------------------------------------------------- 1.599 + // Zip utilities 1.600 + //---------------------------------------------------------------------------- 1.601 + 1.602 + private byte[] getHeader(ZipFileIndexEntry entry) throws IOException { 1.603 + zipRandomFile.seek(entry.offset); 1.604 + byte[] header = new byte[30]; 1.605 + zipRandomFile.readFully(header); 1.606 + if (get4ByteLittleEndian(header, 0) != 0x04034b50) 1.607 + throw new ZipException("corrupted zip file"); 1.608 + if ((get2ByteLittleEndian(header, 6) & 1) != 0) 1.609 + throw new ZipException("encrypted zip file"); // offset 6 in the header of the ZipFileEntry 1.610 + return header; 1.611 + } 1.612 + 1.613 + /* 1.614 + * Inflate using the java.util.zip.Inflater class 1.615 + */ 1.616 + private static Inflater inflater; 1.617 + private int inflate(byte[] src, byte[] dest) { 1.618 + 1.619 + // construct the inflater object or reuse an existing one 1.620 + if (inflater == null) 1.621 + inflater = new Inflater(true); 1.622 + 1.623 + synchronized (inflater) { 1.624 + inflater.reset(); 1.625 + inflater.setInput(src); 1.626 + try { 1.627 + return inflater.inflate(dest); 1.628 + } catch (DataFormatException ex) { 1.629 + return -1; 1.630 + } 1.631 + } 1.632 + } 1.633 + 1.634 + /** 1.635 + * return the two bytes buf[pos], buf[pos+1] as an unsigned integer in little 1.636 + * endian format. 1.637 + */ 1.638 + private static int get2ByteLittleEndian(byte[] buf, int pos) { 1.639 + return (buf[pos] & 0xFF) + ((buf[pos+1] & 0xFF) << 8); 1.640 + } 1.641 + 1.642 + /** 1.643 + * return the 4 bytes buf[i..i+3] as an integer in little endian format. 1.644 + */ 1.645 + private static int get4ByteLittleEndian(byte[] buf, int pos) { 1.646 + return (buf[pos] & 0xFF) + ((buf[pos + 1] & 0xFF) << 8) + 1.647 + ((buf[pos + 2] & 0xFF) << 16) + ((buf[pos + 3] & 0xFF) << 24); 1.648 + } 1.649 + 1.650 + /* ---------------------------------------------------------------------------- 1.651 + * ZipDirectory 1.652 + * ----------------------------------------------------------------------------*/ 1.653 + 1.654 + private class ZipDirectory { 1.655 + private String lastDir; 1.656 + private int lastStart; 1.657 + private int lastLen; 1.658 + 1.659 + byte[] zipDir; 1.660 + RandomAccessFile zipRandomFile = null; 1.661 + ZipFileIndex zipFileIndex = null; 1.662 + 1.663 + public ZipDirectory(RandomAccessFile zipRandomFile, long start, long end, ZipFileIndex index) throws IOException { 1.664 + this.zipRandomFile = zipRandomFile; 1.665 + this.zipFileIndex = index; 1.666 + 1.667 + findCENRecord(start, end); 1.668 + } 1.669 + 1.670 + /* 1.671 + * Reads zip file central directory. 1.672 + * For more details see readCEN in zip_util.c from the JDK sources. 1.673 + * This is a Java port of that function. 1.674 + */ 1.675 + private void findCENRecord(long start, long end) throws IOException { 1.676 + long totalLength = end - start; 1.677 + int endbuflen = 1024; 1.678 + byte[] endbuf = new byte[endbuflen]; 1.679 + long endbufend = end - start; 1.680 + 1.681 + // There is a variable-length field after the dir offset record. We need to do consequential search. 1.682 + while (endbufend >= 22) { 1.683 + if (endbufend < endbuflen) 1.684 + endbuflen = (int)endbufend; 1.685 + long endbufpos = endbufend - endbuflen; 1.686 + zipRandomFile.seek(start + endbufpos); 1.687 + zipRandomFile.readFully(endbuf, 0, endbuflen); 1.688 + int i = endbuflen - 22; 1.689 + while (i >= 0 && 1.690 + !(endbuf[i] == 0x50 && 1.691 + endbuf[i + 1] == 0x4b && 1.692 + endbuf[i + 2] == 0x05 && 1.693 + endbuf[i + 3] == 0x06 && 1.694 + endbufpos + i + 22 + 1.695 + get2ByteLittleEndian(endbuf, i + 20) == totalLength)) { 1.696 + i--; 1.697 + } 1.698 + 1.699 + if (i >= 0) { 1.700 + zipDir = new byte[get4ByteLittleEndian(endbuf, i + 12) + 2]; 1.701 + zipDir[0] = endbuf[i + 10]; 1.702 + zipDir[1] = endbuf[i + 11]; 1.703 + zipRandomFile.seek(start + get4ByteLittleEndian(endbuf, i + 16)); 1.704 + zipRandomFile.readFully(zipDir, 2, zipDir.length - 2); 1.705 + return; 1.706 + } else { 1.707 + endbufend = endbufpos + 21; 1.708 + } 1.709 + } 1.710 + throw new ZipException("cannot read zip file"); 1.711 + } 1.712 + private void buildIndex() throws IOException { 1.713 + int entryCount = get2ByteLittleEndian(zipDir, 0); 1.714 + 1.715 + entries = new ZipFileIndexEntry[entryCount]; 1.716 + // Add each of the files 1.717 + if (entryCount > 0) { 1.718 + directories = new HashMap<String, DirectoryEntry>(); 1.719 + ArrayList<ZipFileIndexEntry> entryList = new ArrayList<ZipFileIndexEntry>(); 1.720 + int pos = 2; 1.721 + for (int i = 0; i < entryCount; i++) { 1.722 + pos = readEntry(pos, entryList, directories); 1.723 + } 1.724 + 1.725 + // Add the accumulated dirs into the same list 1.726 + Iterator i = directories.keySet().iterator(); 1.727 + while (i.hasNext()) { 1.728 + ZipFileIndexEntry zipFileIndexEntry = new ZipFileIndexEntry( (String) i.next()); 1.729 + zipFileIndexEntry.isDir = true; 1.730 + entryList.add(zipFileIndexEntry); 1.731 + } 1.732 + 1.733 + entries = entryList.toArray(new ZipFileIndexEntry[entryList.size()]); 1.734 + Arrays.sort(entries); 1.735 + } else { 1.736 + cleanupState(); 1.737 + } 1.738 + } 1.739 + 1.740 + private int readEntry(int pos, List<ZipFileIndexEntry> entryList, 1.741 + Map<String, DirectoryEntry> directories) throws IOException { 1.742 + if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) { 1.743 + throw new ZipException("cannot read zip file entry"); 1.744 + } 1.745 + 1.746 + int dirStart = pos + 46; 1.747 + int fileStart = dirStart; 1.748 + int fileEnd = fileStart + get2ByteLittleEndian(zipDir, pos + 28); 1.749 + 1.750 + if (zipFileIndex.symbolFilePrefixLength != 0 && 1.751 + ((fileEnd - fileStart) >= symbolFilePrefixLength)) { 1.752 + dirStart += zipFileIndex.symbolFilePrefixLength; 1.753 + fileStart += zipFileIndex.symbolFilePrefixLength; 1.754 + } 1.755 + 1.756 + // Use the OS's path separator. Keep the position of the last one. 1.757 + for (int index = fileStart; index < fileEnd; index++) { 1.758 + byte nextByte = zipDir[index]; 1.759 + if (nextByte == (byte)'\\' || nextByte == (byte)'/') { 1.760 + zipDir[index] = (byte)File.separatorChar; 1.761 + fileStart = index + 1; 1.762 + } 1.763 + } 1.764 + 1.765 + String directory = null; 1.766 + if (fileStart == dirStart) 1.767 + directory = ""; 1.768 + else if (lastDir != null && lastLen == fileStart - dirStart - 1) { 1.769 + int index = lastLen - 1; 1.770 + while (zipDir[lastStart + index] == zipDir[dirStart + index]) { 1.771 + if (index == 0) { 1.772 + directory = lastDir; 1.773 + break; 1.774 + } 1.775 + index--; 1.776 + } 1.777 + } 1.778 + 1.779 + // Sub directories 1.780 + if (directory == null) { 1.781 + lastStart = dirStart; 1.782 + lastLen = fileStart - dirStart - 1; 1.783 + 1.784 + directory = new String(zipDir, dirStart, lastLen, "UTF-8").intern(); 1.785 + lastDir = directory; 1.786 + 1.787 + // Enter also all the parent directories 1.788 + String tempDirectory = directory; 1.789 + 1.790 + while (directories.get(tempDirectory) == null) { 1.791 + directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex)); 1.792 + int separator = tempDirectory.lastIndexOf(File.separatorChar); 1.793 + if (separator == -1) 1.794 + break; 1.795 + tempDirectory = tempDirectory.substring(0, separator); 1.796 + } 1.797 + } 1.798 + else { 1.799 + directory = directory.intern(); 1.800 + if (directories.get(directory) == null) { 1.801 + directories.put(directory, new DirectoryEntry(directory, zipFileIndex)); 1.802 + } 1.803 + } 1.804 + 1.805 + // For each dir create also a file 1.806 + if (fileStart != fileEnd) { 1.807 + ZipFileIndexEntry entry = new ZipFileIndexEntry(directory, 1.808 + new String(zipDir, fileStart, fileEnd - fileStart, "UTF-8")); 1.809 + 1.810 + entry.setNativeTime(get4ByteLittleEndian(zipDir, pos + 12)); 1.811 + entry.compressedSize = get4ByteLittleEndian(zipDir, pos + 20); 1.812 + entry.size = get4ByteLittleEndian(zipDir, pos + 24); 1.813 + entry.offset = get4ByteLittleEndian(zipDir, pos + 42); 1.814 + entryList.add(entry); 1.815 + } 1.816 + 1.817 + return pos + 46 + 1.818 + get2ByteLittleEndian(zipDir, pos + 28) + 1.819 + get2ByteLittleEndian(zipDir, pos + 30) + 1.820 + get2ByteLittleEndian(zipDir, pos + 32); 1.821 + } 1.822 + } 1.823 + 1.824 + /** 1.825 + * Returns the last modified timestamp of a zip file. 1.826 + * @return long 1.827 + */ 1.828 + public long getZipFileLastModified() throws IOException { 1.829 + lock.lock(); 1.830 + try { 1.831 + checkIndex(); 1.832 + return zipFileLastModified; 1.833 + } 1.834 + finally { 1.835 + lock.unlock(); 1.836 + } 1.837 + } 1.838 + 1.839 + /** ------------------------------------------------------------------------ 1.840 + * DirectoryEntry class 1.841 + * -------------------------------------------------------------------------*/ 1.842 + static class DirectoryEntry { 1.843 + private boolean filesInited; 1.844 + private boolean directoriesInited; 1.845 + private boolean zipFileEntriesInited; 1.846 + private boolean entriesInited; 1.847 + 1.848 + private long writtenOffsetOffset = 0; 1.849 + 1.850 + private String dirName; 1.851 + 1.852 + private com.sun.tools.javac.util.List<String> zipFileEntriesFiles = com.sun.tools.javac.util.List.<String>nil(); 1.853 + private com.sun.tools.javac.util.List<String> zipFileEntriesDirectories = com.sun.tools.javac.util.List.<String>nil(); 1.854 + private com.sun.tools.javac.util.List<ZipFileIndexEntry> zipFileEntries = com.sun.tools.javac.util.List.<ZipFileIndexEntry>nil(); 1.855 + 1.856 + private List<ZipFileIndexEntry> entries = new ArrayList<ZipFileIndexEntry>(); 1.857 + 1.858 + private ZipFileIndex zipFileIndex; 1.859 + 1.860 + private int numEntries; 1.861 + 1.862 + DirectoryEntry(String dirName, ZipFileIndex index) { 1.863 + filesInited = false; 1.864 + directoriesInited = false; 1.865 + entriesInited = false; 1.866 + 1.867 + if (File.separatorChar == '/') { 1.868 + dirName.replace('\\', '/'); 1.869 + } 1.870 + else { 1.871 + dirName.replace('/', '\\'); 1.872 + } 1.873 + 1.874 + this.dirName = dirName.intern(); 1.875 + this.zipFileIndex = index; 1.876 + } 1.877 + 1.878 + private com.sun.tools.javac.util.List<String> getFiles() { 1.879 + if (filesInited) { 1.880 + return zipFileEntriesFiles; 1.881 + } 1.882 + 1.883 + initEntries(); 1.884 + 1.885 + for (ZipFileIndexEntry e : entries) { 1.886 + if (!e.isDir) { 1.887 + zipFileEntriesFiles = zipFileEntriesFiles.append(e.name); 1.888 + } 1.889 + } 1.890 + filesInited = true; 1.891 + return zipFileEntriesFiles; 1.892 + } 1.893 + 1.894 + private com.sun.tools.javac.util.List<String> getDirectories() { 1.895 + if (directoriesInited) { 1.896 + return zipFileEntriesFiles; 1.897 + } 1.898 + 1.899 + initEntries(); 1.900 + 1.901 + for (ZipFileIndexEntry e : entries) { 1.902 + if (e.isDir) { 1.903 + zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name); 1.904 + } 1.905 + } 1.906 + 1.907 + directoriesInited = true; 1.908 + 1.909 + return zipFileEntriesDirectories; 1.910 + } 1.911 + 1.912 + private com.sun.tools.javac.util.List<ZipFileIndexEntry> getEntries() { 1.913 + if (zipFileEntriesInited) { 1.914 + return zipFileEntries; 1.915 + } 1.916 + 1.917 + initEntries(); 1.918 + 1.919 + zipFileEntries = com.sun.tools.javac.util.List.nil(); 1.920 + for (ZipFileIndexEntry zfie : entries) { 1.921 + zipFileEntries = zipFileEntries.append(zfie); 1.922 + } 1.923 + 1.924 + zipFileEntriesInited = true; 1.925 + 1.926 + return zipFileEntries; 1.927 + } 1.928 + 1.929 + private ZipFileIndexEntry getEntry(String rootName) { 1.930 + initEntries(); 1.931 + int index = Collections.binarySearch(entries, new ZipFileIndexEntry(dirName, rootName)); 1.932 + if (index < 0) { 1.933 + return null; 1.934 + } 1.935 + 1.936 + return entries.get(index); 1.937 + } 1.938 + 1.939 + private void initEntries() { 1.940 + if (entriesInited) { 1.941 + return; 1.942 + } 1.943 + 1.944 + if (!zipFileIndex.readFromIndex) { 1.945 + int from = -Arrays.binarySearch(zipFileIndex.entries, 1.946 + new ZipFileIndexEntry(dirName, ZipFileIndex.MIN_CHAR)) - 1; 1.947 + int to = -Arrays.binarySearch(zipFileIndex.entries, 1.948 + new ZipFileIndexEntry(dirName, MAX_CHAR)) - 1; 1.949 + 1.950 + boolean emptyList = false; 1.951 + 1.952 + for (int i = from; i < to; i++) { 1.953 + entries.add(zipFileIndex.entries[i]); 1.954 + } 1.955 + } else { 1.956 + File indexFile = zipFileIndex.getIndexFile(); 1.957 + if (indexFile != null) { 1.958 + RandomAccessFile raf = null; 1.959 + try { 1.960 + raf = new RandomAccessFile(indexFile, "r"); 1.961 + raf.seek(writtenOffsetOffset); 1.962 + 1.963 + for (int nFiles = 0; nFiles < numEntries; nFiles++) { 1.964 + // Read the name bytes 1.965 + int zfieNameBytesLen = raf.readInt(); 1.966 + byte [] zfieNameBytes = new byte[zfieNameBytesLen]; 1.967 + raf.read(zfieNameBytes); 1.968 + String eName = new String(zfieNameBytes, "UTF-8"); 1.969 + 1.970 + // Read isDir 1.971 + boolean eIsDir = raf.readByte() == (byte)0 ? false : true; 1.972 + 1.973 + // Read offset of bytes in the real Jar/Zip file 1.974 + int eOffset = raf.readInt(); 1.975 + 1.976 + // Read size of the file in the real Jar/Zip file 1.977 + int eSize = raf.readInt(); 1.978 + 1.979 + // Read compressed size of the file in the real Jar/Zip file 1.980 + int eCsize = raf.readInt(); 1.981 + 1.982 + // Read java time stamp of the file in the real Jar/Zip file 1.983 + long eJavaTimestamp = raf.readLong(); 1.984 + 1.985 + ZipFileIndexEntry rfie = new ZipFileIndexEntry(dirName, eName); 1.986 + rfie.isDir = eIsDir; 1.987 + rfie.offset = eOffset; 1.988 + rfie.size = eSize; 1.989 + rfie.compressedSize = eCsize; 1.990 + rfie.javatime = eJavaTimestamp; 1.991 + entries.add(rfie); 1.992 + } 1.993 + } catch (Throwable t) { 1.994 + // Do nothing 1.995 + } finally { 1.996 + try { 1.997 + if (raf == null) { 1.998 + raf.close(); 1.999 + } 1.1000 + } catch (Throwable t) { 1.1001 + // Do nothing 1.1002 + } 1.1003 + } 1.1004 + } 1.1005 + } 1.1006 + 1.1007 + entriesInited = true; 1.1008 + } 1.1009 + 1.1010 + List<ZipFileIndexEntry> getEntriesAsCollection() { 1.1011 + initEntries(); 1.1012 + 1.1013 + return entries; 1.1014 + } 1.1015 + } 1.1016 + 1.1017 + private boolean readIndex() { 1.1018 + if (triedToReadIndex || !usePreindexedCache) { 1.1019 + return false; 1.1020 + } 1.1021 + 1.1022 + boolean ret = false; 1.1023 + lock.lock(); 1.1024 + try { 1.1025 + triedToReadIndex = true; 1.1026 + RandomAccessFile raf = null; 1.1027 + try { 1.1028 + File indexFileName = getIndexFile(); 1.1029 + raf = new RandomAccessFile(indexFileName, "r"); 1.1030 + 1.1031 + long fileStamp = raf.readLong(); 1.1032 + if (zipFile.lastModified() != fileStamp) { 1.1033 + ret = false; 1.1034 + } else { 1.1035 + directories = new HashMap<String, DirectoryEntry>(); 1.1036 + int numDirs = raf.readInt(); 1.1037 + for (int nDirs = 0; nDirs < numDirs; nDirs++) { 1.1038 + int dirNameBytesLen = raf.readInt(); 1.1039 + byte [] dirNameBytes = new byte[dirNameBytesLen]; 1.1040 + raf.read(dirNameBytes); 1.1041 + 1.1042 + String dirNameStr = new String(dirNameBytes, "UTF-8"); 1.1043 + DirectoryEntry de = new DirectoryEntry(dirNameStr, this); 1.1044 + de.numEntries = raf.readInt(); 1.1045 + de.writtenOffsetOffset = raf.readLong(); 1.1046 + directories.put(dirNameStr, de); 1.1047 + } 1.1048 + ret = true; 1.1049 + zipFileLastModified = fileStamp; 1.1050 + } 1.1051 + } catch (Throwable t) { 1.1052 + // Do nothing 1.1053 + } finally { 1.1054 + if (raf != null) { 1.1055 + try { 1.1056 + raf.close(); 1.1057 + } catch (Throwable tt) { 1.1058 + // Do nothing 1.1059 + } 1.1060 + } 1.1061 + } 1.1062 + if (ret == true) { 1.1063 + readFromIndex = true; 1.1064 + } 1.1065 + } 1.1066 + finally { 1.1067 + lock.unlock(); 1.1068 + } 1.1069 + 1.1070 + return ret; 1.1071 + } 1.1072 + 1.1073 + private boolean writeIndex() { 1.1074 + boolean ret = false; 1.1075 + if (readFromIndex || !usePreindexedCache) { 1.1076 + return true; 1.1077 + } 1.1078 + 1.1079 + if (!writeIndex) { 1.1080 + return true; 1.1081 + } 1.1082 + 1.1083 + File indexFile = getIndexFile(); 1.1084 + if (indexFile == null) { 1.1085 + return false; 1.1086 + } 1.1087 + 1.1088 + RandomAccessFile raf = null; 1.1089 + long writtenSoFar = 0; 1.1090 + try { 1.1091 + raf = new RandomAccessFile(indexFile, "rw"); 1.1092 + 1.1093 + raf.writeLong(zipFileLastModified); 1.1094 + writtenSoFar += 8; 1.1095 + 1.1096 + 1.1097 + Iterator<String> iterDirName = directories.keySet().iterator(); 1.1098 + List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>(); 1.1099 + Map<String, Long> offsets = new HashMap<String, Long>(); 1.1100 + raf.writeInt(directories.keySet().size()); 1.1101 + writtenSoFar += 4; 1.1102 + 1.1103 + while(iterDirName.hasNext()) { 1.1104 + String dirName = iterDirName.next(); 1.1105 + DirectoryEntry dirEntry = directories.get(dirName); 1.1106 + 1.1107 + directoriesToWrite.add(dirEntry); 1.1108 + 1.1109 + // Write the dir name bytes 1.1110 + byte [] dirNameBytes = dirName.getBytes("UTF-8"); 1.1111 + int dirNameBytesLen = dirNameBytes.length; 1.1112 + raf.writeInt(dirNameBytesLen); 1.1113 + writtenSoFar += 4; 1.1114 + 1.1115 + raf.write(dirNameBytes); 1.1116 + writtenSoFar += dirNameBytesLen; 1.1117 + 1.1118 + // Write the number of files in the dir 1.1119 + List dirEntries = dirEntry.getEntriesAsCollection(); 1.1120 + raf.writeInt(dirEntries.size()); 1.1121 + writtenSoFar += 4; 1.1122 + 1.1123 + offsets.put(dirName, new Long(writtenSoFar)); 1.1124 + 1.1125 + // Write the offset of the file's data in the dir 1.1126 + dirEntry.writtenOffsetOffset = 0L; 1.1127 + raf.writeLong(0L); 1.1128 + writtenSoFar += 8; 1.1129 + } 1.1130 + 1.1131 + for (DirectoryEntry de : directoriesToWrite) { 1.1132 + // Fix up the offset in the directory table 1.1133 + long currFP = raf.getFilePointer(); 1.1134 + 1.1135 + long offsetOffset = offsets.get(de.dirName).longValue(); 1.1136 + raf.seek(offsetOffset); 1.1137 + raf.writeLong(writtenSoFar); 1.1138 + 1.1139 + raf.seek(currFP); 1.1140 + 1.1141 + // Now write each of the files in the DirectoryEntry 1.1142 + List<ZipFileIndexEntry> entries = de.getEntriesAsCollection(); 1.1143 + for (ZipFileIndexEntry zfie : entries) { 1.1144 + // Write the name bytes 1.1145 + byte [] zfieNameBytes = zfie.name.getBytes("UTF-8"); 1.1146 + int zfieNameBytesLen = zfieNameBytes.length; 1.1147 + raf.writeInt(zfieNameBytesLen); 1.1148 + writtenSoFar += 4; 1.1149 + raf.write(zfieNameBytes); 1.1150 + writtenSoFar += zfieNameBytesLen; 1.1151 + 1.1152 + // Write isDir 1.1153 + raf.writeByte(zfie.isDir ? (byte)1 : (byte)0); 1.1154 + writtenSoFar += 1; 1.1155 + 1.1156 + // Write offset of bytes in the real Jar/Zip file 1.1157 + raf.writeInt(zfie.offset); 1.1158 + writtenSoFar += 4; 1.1159 + 1.1160 + // Write size of the file in the real Jar/Zip file 1.1161 + raf.writeInt(zfie.size); 1.1162 + writtenSoFar += 4; 1.1163 + 1.1164 + // Write compressed size of the file in the real Jar/Zip file 1.1165 + raf.writeInt(zfie.compressedSize); 1.1166 + writtenSoFar += 4; 1.1167 + 1.1168 + // Write java time stamp of the file in the real Jar/Zip file 1.1169 + raf.writeLong(zfie.getLastModified()); 1.1170 + writtenSoFar += 8; 1.1171 + } 1.1172 + } 1.1173 + } catch (Throwable t) { 1.1174 + // Do nothing 1.1175 + } finally { 1.1176 + try { 1.1177 + if (raf != null) { 1.1178 + raf.close(); 1.1179 + } 1.1180 + } catch(IOException ioe) { 1.1181 + // Do nothing 1.1182 + } 1.1183 + } 1.1184 + 1.1185 + return ret; 1.1186 + } 1.1187 + 1.1188 + public boolean writeZipIndex() { 1.1189 + lock.lock(); 1.1190 + try { 1.1191 + return writeIndex(); 1.1192 + } 1.1193 + finally { 1.1194 + lock.unlock(); 1.1195 + } 1.1196 + } 1.1197 + 1.1198 + private File getIndexFile() { 1.1199 + if (zipIndexFile == null) { 1.1200 + if (zipFile == null) { 1.1201 + return null; 1.1202 + } 1.1203 + 1.1204 + zipIndexFile = new File((preindexedCacheLocation == null ? "" : preindexedCacheLocation) + 1.1205 + zipFile.getName() + ".index"); 1.1206 + } 1.1207 + 1.1208 + return zipIndexFile; 1.1209 + } 1.1210 + 1.1211 + public File getZipFile() { 1.1212 + return zipFile; 1.1213 + } 1.1214 +}