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

Wed, 09 Apr 2008 13:41:45 +0100

author
mcimadamore
date
Wed, 09 Apr 2008 13:41:45 +0100
changeset 24
d032d5090fd5
parent 1
9a66ca7c79fa
child 36
58e352559a41
permissions
-rw-r--r--

5009937: hiding versus generics versus binary compatibility
Summary: missing implementation of JLS 8.4.8.3 (different arguments with same erasure not always triggering a compiler error)
Reviewed-by: jjg

     1 package com.sun.tools.javac.zip;
     3 import java.io.*;
     4 import java.text.MessageFormat;
     5 import java.util.*;
     6 import java.util.List;
     7 import java.util.concurrent.locks.ReentrantLock;
     8 import java.util.zip.*;
    10 /** This class implements building of index of a zip archive and access to it's context.
    11  *  It also uses prebuild index if available. It supports invocations where it will
    12  *  serialize an optimized zip index file to disk.
    13  *
    14  *  In oreder to use secondary index file make sure the option "usezipindex" is in the Options object,
    15  *  when JavacFileManager is invoked. (You can pass "-XDusezipindex" on the command line.
    16  *
    17  *  Location where to look for/generate optimized zip index files can be provided using
    18  *  "-XDcachezipindexdir=<directory>". If this flag is not provided, the dfault location is
    19  *  the value of the "java.io.tmpdir" system property.
    20  *
    21  *  If key "-XDwritezipindexfiles" is specified, there will be new optimized index file
    22  *  created for each archive, used by the compiler for compilation, at location,
    23  *  specified by "cachezipindexdir" option.
    24  *
    25  * If nonBatchMode option is specified (-XDnonBatchMode) the compiler will use timestamp
    26  * checking to reindex the zip files if it is needed. In batch mode the timestamps are not checked
    27  * and the compiler uses the cached indexes.
    28  */
    29 public class ZipFileIndex {
    30     private static final String MIN_CHAR = String.valueOf(Character.MIN_VALUE);
    31     private static final String MAX_CHAR = String.valueOf(Character.MAX_VALUE);
    33     public final static long NOT_MODIFIED = Long.MIN_VALUE;
    35     private static Map<File, ZipFileIndex> zipFileIndexCache = new HashMap<File, ZipFileIndex>();
    36     private static ReentrantLock lock = new ReentrantLock();
    38     private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
    40     private Map<String, DirectoryEntry> directories = Collections.<String, DirectoryEntry>emptyMap();
    41     private Set<String> allDirs = Collections.<String>emptySet();
    43     // ZipFileIndex data entries
    44     private File zipFile;
    45     private long zipFileLastModified = NOT_MODIFIED;
    46     private RandomAccessFile zipRandomFile;
    47     private ZipFileIndexEntry[] entries;
    49     private boolean readFromIndex = false;
    50     private File zipIndexFile = null;
    51     private boolean triedToReadIndex = false;
    52     private int symbolFilePrefixLength = 0;
    53     private boolean hasPopulatedData = false;
    54     private long lastReferenceTimeStamp = NOT_MODIFIED;
    56     private boolean usePreindexedCache = false;
    57     private String preindexedCacheLocation = null;
    59     private boolean writeIndex = false;
    61     /**
    62      * Returns a list of all ZipFileIndex entries
    63      *
    64      * @return A list of ZipFileIndex entries, or an empty list
    65      */
    66     public static List<ZipFileIndex> getZipFileIndexes() {
    67         return getZipFileIndexes(false);
    68     }
    70     /**
    71      * Returns a list of all ZipFileIndex entries
    72      *
    73      * @param openedOnly If true it returns a list of only opened ZipFileIndex entries, otherwise
    74      *                   all ZipFileEntry(s) are included into the list.
    75      * @return A list of ZipFileIndex entries, or an empty list
    76      */
    77     public static List<ZipFileIndex> getZipFileIndexes(boolean openedOnly) {
    78         List<ZipFileIndex> zipFileIndexes = new ArrayList<ZipFileIndex>();
    79         lock.lock();
    80         try {
    81             zipFileIndexes.addAll(zipFileIndexCache.values());
    83             if (openedOnly) {
    84                 for(ZipFileIndex elem : zipFileIndexes) {
    85                     if (!elem.isOpen()) {
    86                         zipFileIndexes.remove(elem);
    87                     }
    88                 }
    89             }
    90         }
    91         finally {
    92             lock.unlock();
    93         }
    94         return zipFileIndexes;
    95     }
    97     public boolean isOpen() {
    98         lock.lock();
    99         try {
   100             return zipRandomFile != null;
   101         }
   102         finally {
   103             lock.unlock();
   104         }
   105     }
   107     public static ZipFileIndex getZipFileIndex(File zipFile, int symbolFilePrefixLen, boolean useCache, String cacheLocation, boolean writeIndex) throws IOException {
   108         ZipFileIndex zi = null;
   109         lock.lock();
   110         try {
   111             zi = getExistingZipIndex(zipFile);
   113             if (zi == null || (zi != null && zipFile.lastModified() != zi.zipFileLastModified)) {
   114                 zi = new ZipFileIndex(zipFile, symbolFilePrefixLen, writeIndex,
   115                         useCache, cacheLocation);
   116                 zipFileIndexCache.put(zipFile, zi);
   117             }
   118         }
   119         finally {
   120             lock.unlock();
   121         }
   122         return zi;
   123     }
   125     public static ZipFileIndex getExistingZipIndex(File zipFile) {
   126         lock.lock();
   127         try {
   128             return zipFileIndexCache.get(zipFile);
   129         }
   130         finally {
   131             lock.unlock();
   132         }
   133     }
   135     public static void clearCache() {
   136         lock.lock();
   137         try {
   138             zipFileIndexCache.clear();
   139         }
   140         finally {
   141             lock.unlock();
   142         }
   143     }
   145     public static void clearCache(long timeNotUsed) {
   146         lock.lock();
   147         try {
   148             Iterator<File> cachedFileIterator = zipFileIndexCache.keySet().iterator();
   149             while (cachedFileIterator.hasNext()) {
   150                 File cachedFile = cachedFileIterator.next();
   151                 ZipFileIndex cachedZipIndex = zipFileIndexCache.get(cachedFile);
   152                 if (cachedZipIndex != null) {
   153                     long timeToTest = cachedZipIndex.lastReferenceTimeStamp + timeNotUsed;
   154                     if (timeToTest < cachedZipIndex.lastReferenceTimeStamp || // Overflow...
   155                             System.currentTimeMillis() > timeToTest) {
   156                         zipFileIndexCache.remove(cachedFile);
   157                     }
   158                 }
   159             }
   160         }
   161         finally {
   162             lock.unlock();
   163         }
   164     }
   166     public static void removeFromCache(File file) {
   167         lock.lock();
   168         try {
   169             zipFileIndexCache.remove(file);
   170         }
   171         finally {
   172             lock.unlock();
   173         }
   174     }
   176     /** Sets already opened list of ZipFileIndexes from an outside client
   177       * of the compiler. This functionality should be used in a non-batch clients of the compiler.
   178       */
   179     public static void setOpenedIndexes(List<ZipFileIndex>indexes) throws IllegalStateException {
   180         lock.lock();
   181         try {
   182             if (zipFileIndexCache.isEmpty()) {
   183                 throw new IllegalStateException("Setting opened indexes should be called only when the ZipFileCache is empty. Call JavacFileManager.flush() before calling this method.");
   184             }
   186             for (ZipFileIndex zfi : indexes) {
   187                 zipFileIndexCache.put(zfi.zipFile, zfi);
   188             }
   189         }
   190         finally {
   191             lock.unlock();
   192         }
   193     }
   195     private ZipFileIndex(File zipFile, int symbolFilePrefixLen, boolean writeIndex,
   196             boolean useCache, String cacheLocation) throws IOException {
   197         this.zipFile = zipFile;
   198         this.symbolFilePrefixLength = symbolFilePrefixLen;
   199         this.writeIndex = writeIndex;
   200         this.usePreindexedCache = useCache;
   201         this.preindexedCacheLocation = cacheLocation;
   203         if (zipFile != null) {
   204             this.zipFileLastModified = zipFile.lastModified();
   205         }
   207         // Validate integrity of the zip file
   208         checkIndex();
   209     }
   211     public String toString() {
   212         return "ZipFileIndex of file:(" + zipFile + ")";
   213     }
   215     // Just in case...
   216     protected void finalize() {
   217         closeFile();
   218     }
   220     private boolean isUpToDate() {
   221         if (zipFile != null &&
   222                 ((!NON_BATCH_MODE) || zipFileLastModified == zipFile.lastModified()) &&
   223                 hasPopulatedData) {
   224             return true;
   225         }
   227         return false;
   228     }
   230     /**
   231      * Here we need to make sure that the ZipFileIndex is valid. Check the timestamp of the file and
   232      * if its the same as the one at the time the index was build we don't need to reopen anything.
   233      */
   234     private void checkIndex() throws IOException {
   235         boolean isUpToDate = true;
   236         if (!isUpToDate()) {
   237             closeFile();
   238             isUpToDate = false;
   239         }
   241         if (zipRandomFile != null || isUpToDate) {
   242             lastReferenceTimeStamp = System.currentTimeMillis();
   243             return;
   244         }
   246         hasPopulatedData = true;
   248         if (readIndex()) {
   249             lastReferenceTimeStamp = System.currentTimeMillis();
   250             return;
   251         }
   253         directories = Collections.<String, DirectoryEntry>emptyMap();
   254         allDirs = Collections.<String>emptySet();
   256         try {
   257             openFile();
   258             long totalLength = zipRandomFile.length();
   259             ZipDirectory directory = new ZipDirectory(zipRandomFile, 0L, totalLength, this);
   260             directory.buildIndex();
   261         } finally {
   262             if (zipRandomFile != null) {
   263                 closeFile();
   264             }
   265         }
   267         lastReferenceTimeStamp = System.currentTimeMillis();
   268     }
   270     private void openFile() throws FileNotFoundException {
   271         if (zipRandomFile == null && zipFile != null) {
   272             zipRandomFile = new RandomAccessFile(zipFile, "r");
   273         }
   274     }
   276     private void cleanupState() {
   277         // Make sure there is a valid but empty index if the file doesn't exist
   278         entries = ZipFileIndexEntry.EMPTY_ARRAY;
   279         directories = Collections.<String, DirectoryEntry>emptyMap();
   280         zipFileLastModified = NOT_MODIFIED;
   281         allDirs = Collections.<String>emptySet();
   282     }
   284     public void close() {
   285         lock.lock();
   286         try {
   287             writeIndex();
   288             closeFile();
   289         }
   290         finally {
   291             lock.unlock();
   292         }
   293     }
   295     private void closeFile() {
   296         if (zipRandomFile != null) {
   297             try {
   298                 zipRandomFile.close();
   299             } catch (IOException ex) {
   300             }
   301             zipRandomFile = null;
   302         }
   303     }
   305     /**
   306      * Returns the ZipFileIndexEntry for an absolute path, if there is one.
   307      */
   308     public ZipFileIndexEntry getZipIndexEntry(String path) {
   309         if (File.separatorChar != '/') {
   310             path = path.replace('/', File.separatorChar);
   311         }
   312         lock.lock();
   313         try {
   314             checkIndex();
   315             String lookFor = "";
   316             int lastSepIndex = path.lastIndexOf(File.separatorChar);
   317             boolean noSeparator = false;
   318             if (lastSepIndex == -1) {
   319                 noSeparator = true;
   320             }
   322             DirectoryEntry de = directories.get(noSeparator ? "" : path.substring(0, lastSepIndex));
   324             lookFor = path.substring(noSeparator ? 0 : lastSepIndex + 1);
   326             return de == null ? null : de.getEntry(lookFor);
   327         }
   328         catch (IOException e) {
   329             return null;
   330         }
   331         finally {
   332             lock.unlock();
   333         }
   334     }
   336     /**
   337      * Returns a javac List of filenames within an absolute path in the ZipFileIndex.
   338      */
   339     public com.sun.tools.javac.util.List<String> getFiles(String path) {
   340         if (File.separatorChar != '/') {
   341             path = path.replace('/', File.separatorChar);
   342         }
   344         lock.lock();
   345         try {
   346             checkIndex();
   348             DirectoryEntry de = directories.get(path);
   349             com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getFiles();
   351             if (ret == null) {
   352                 return com.sun.tools.javac.util.List.<String>nil();
   353             }
   354             return ret;
   355         }
   356         catch (IOException e) {
   357             return com.sun.tools.javac.util.List.<String>nil();
   358         }
   359         finally {
   360             lock.unlock();
   361         }
   362     }
   364     public List<String> getAllDirectories(String path) {
   366         if (File.separatorChar != '/') {
   367             path = path.replace('/', File.separatorChar);
   368         }
   370         lock.lock();
   371         try {
   372             checkIndex();
   373             path = path.intern();
   375             DirectoryEntry de = directories.get(path);
   376             com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories();
   378             if (ret == null) {
   379                 return com.sun.tools.javac.util.List.<String>nil();
   380             }
   382             return ret;
   383         }
   384         catch (IOException e) {
   385             return com.sun.tools.javac.util.List.<String>nil();
   386         }
   387         finally {
   388             lock.unlock();
   389         }
   390     }
   392     public Set<String> getAllDirectories() {
   393         lock.lock();
   394         try {
   395             checkIndex();
   396             if (allDirs == Collections.EMPTY_SET) {
   397                 Set<String> alldirs = new HashSet<String>();
   398                 Iterator<String> dirsIter = directories.keySet().iterator();
   399                 while (dirsIter.hasNext()) {
   400                     alldirs.add(new String(dirsIter.next()));
   401                 }
   403                 allDirs = alldirs;
   404             }
   406             return allDirs;
   407         }
   408         catch (IOException e) {
   409             return Collections.<String>emptySet();
   410         }
   411         finally {
   412             lock.unlock();
   413         }
   414     }
   416     /**
   417      * Tests if a specific path exists in the zip.  This method will return true
   418      * for file entries and directories.
   419      *
   420      * @param path A path within the zip.
   421      * @return True if the path is a file or dir, false otherwise.
   422      */
   423     public boolean contains(String path) {
   424         lock.lock();
   425         try {
   426             checkIndex();
   427             return getZipIndexEntry(path) != null;
   428         }
   429         catch (IOException e) {
   430             return false;
   431         }
   432         finally {
   433             lock.unlock();
   434         }
   435     }
   437     public boolean isDirectory(String path) throws IOException {
   438         lock.lock();
   439         try {
   440             // The top level in a zip file is always a directory.
   441             if (path.length() == 0) {
   442                 lastReferenceTimeStamp = System.currentTimeMillis();
   443                 return true;
   444             }
   446             if (File.separatorChar != '/')
   447                 path = path.replace('/', File.separatorChar);
   448             checkIndex();
   449             return directories.get(path) != null;
   450         }
   451         finally {
   452             lock.unlock();
   453         }
   454     }
   456     public long getLastModified(String path) throws IOException {
   457         lock.lock();
   458         try {
   459             ZipFileIndexEntry entry = getZipIndexEntry(path);
   460             if (entry == null)
   461                 throw new FileNotFoundException();
   462             return entry.getLastModified();
   463         }
   464         finally {
   465             lock.unlock();
   466         }
   467     }
   469     public int length(String path) throws IOException {
   470         lock.lock();
   471         try {
   472             ZipFileIndexEntry entry = getZipIndexEntry(path);
   473             if (entry == null)
   474                 throw new FileNotFoundException();
   476             if (entry.isDir) {
   477                 return 0;
   478             }
   480             byte[] header = getHeader(entry);
   481             // entry is not compressed?
   482             if (get2ByteLittleEndian(header, 8) == 0) {
   483                 return entry.compressedSize;
   484             } else {
   485                 return entry.size;
   486             }
   487         }
   488         finally {
   489             lock.unlock();
   490         }
   491     }
   493     public byte[] read(String path) throws IOException {
   494         lock.lock();
   495         try {
   496             ZipFileIndexEntry entry = getZipIndexEntry(path);
   497             if (entry == null)
   498                 throw new FileNotFoundException(MessageFormat.format("Path not found in ZIP: {0}", path));
   499             return read(entry);
   500         }
   501         finally {
   502             lock.unlock();
   503         }
   504     }
   506     public byte[] read(ZipFileIndexEntry entry) throws IOException {
   507         lock.lock();
   508         try {
   509             openFile();
   510             byte[] result = readBytes(entry);
   511             closeFile();
   512             return result;
   513         }
   514         finally {
   515             lock.unlock();
   516         }
   517     }
   519     public int read(String path, byte[] buffer) throws IOException {
   520         lock.lock();
   521         try {
   522             ZipFileIndexEntry entry = getZipIndexEntry(path);
   523             if (entry == null)
   524                 throw new FileNotFoundException();
   525             return read(entry, buffer);
   526         }
   527         finally {
   528             lock.unlock();
   529         }
   530     }
   532     public int read(ZipFileIndexEntry entry, byte[] buffer)
   533             throws IOException {
   534         lock.lock();
   535         try {
   536             int result = readBytes(entry, buffer);
   537             return result;
   538         }
   539         finally {
   540             lock.unlock();
   541         }
   542     }
   544     private byte[] readBytes(ZipFileIndexEntry entry) throws IOException {
   545         byte[] header = getHeader(entry);
   546         int csize = entry.compressedSize;
   547         byte[] cbuf = new byte[csize];
   548         zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
   549         zipRandomFile.readFully(cbuf, 0, csize);
   551         // is this compressed - offset 8 in the ZipEntry header
   552         if (get2ByteLittleEndian(header, 8) == 0)
   553             return cbuf;
   555         int size = entry.size;
   556         byte[] buf = new byte[size];
   557         if (inflate(cbuf, buf) != size)
   558             throw new ZipException("corrupted zip file");
   560         return buf;
   561     }
   563     /**
   564      *
   565      */
   566     private int readBytes(ZipFileIndexEntry entry, byte[] buffer) throws IOException {
   567         byte[] header = getHeader(entry);
   569         // entry is not compressed?
   570         if (get2ByteLittleEndian(header, 8) == 0) {
   571             zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
   572             int offset = 0;
   573             int size = buffer.length;
   574             while (offset < size) {
   575                 int count = zipRandomFile.read(buffer, offset, size - offset);
   576                 if (count == -1)
   577                     break;
   578                 offset += count;
   579             }
   580             return entry.size;
   581         }
   583         int csize = entry.compressedSize;
   584         byte[] cbuf = new byte[csize];
   585         zipRandomFile.skipBytes(get2ByteLittleEndian(header, 26) + get2ByteLittleEndian(header, 28));
   586         zipRandomFile.readFully(cbuf, 0, csize);
   588         int count = inflate(cbuf, buffer);
   589         if (count == -1)
   590             throw new ZipException("corrupted zip file");
   592         return entry.size;
   593     }
   595     //----------------------------------------------------------------------------
   596     // Zip utilities
   597     //----------------------------------------------------------------------------
   599     private byte[] getHeader(ZipFileIndexEntry entry) throws IOException {
   600         zipRandomFile.seek(entry.offset);
   601         byte[] header = new byte[30];
   602         zipRandomFile.readFully(header);
   603         if (get4ByteLittleEndian(header, 0) != 0x04034b50)
   604             throw new ZipException("corrupted zip file");
   605         if ((get2ByteLittleEndian(header, 6) & 1) != 0)
   606             throw new ZipException("encrypted zip file"); // offset 6 in the header of the ZipFileEntry
   607         return header;
   608     }
   610   /*
   611    * Inflate using the java.util.zip.Inflater class
   612    */
   613     private static Inflater inflater;
   614     private int inflate(byte[] src, byte[] dest) {
   616         // construct the inflater object or reuse an existing one
   617         if (inflater == null)
   618             inflater = new Inflater(true);
   620         synchronized (inflater) {
   621             inflater.reset();
   622             inflater.setInput(src);
   623             try {
   624                 return inflater.inflate(dest);
   625             } catch (DataFormatException ex) {
   626                 return -1;
   627             }
   628         }
   629     }
   631     /**
   632      * return the two bytes buf[pos], buf[pos+1] as an unsigned integer in little
   633      * endian format.
   634      */
   635     private static int get2ByteLittleEndian(byte[] buf, int pos) {
   636         return (buf[pos] & 0xFF) + ((buf[pos+1] & 0xFF) << 8);
   637     }
   639     /**
   640      * return the 4 bytes buf[i..i+3] as an integer in little endian format.
   641      */
   642     private static int get4ByteLittleEndian(byte[] buf, int pos) {
   643         return (buf[pos] & 0xFF) + ((buf[pos + 1] & 0xFF) << 8) +
   644                 ((buf[pos + 2] & 0xFF) << 16) + ((buf[pos + 3] & 0xFF) << 24);
   645     }
   647     /* ----------------------------------------------------------------------------
   648      * ZipDirectory
   649      * ----------------------------------------------------------------------------*/
   651     private class ZipDirectory {
   652         private String lastDir;
   653         private int lastStart;
   654         private int lastLen;
   656         byte[] zipDir;
   657         RandomAccessFile zipRandomFile = null;
   658         ZipFileIndex zipFileIndex = null;
   660         public ZipDirectory(RandomAccessFile zipRandomFile, long start, long end, ZipFileIndex index) throws IOException {
   661             this.zipRandomFile = zipRandomFile;
   662             this.zipFileIndex = index;
   664             findCENRecord(start, end);
   665         }
   667         /*
   668          * Reads zip file central directory.
   669          * For more details see readCEN in zip_util.c from the JDK sources.
   670          * This is a Java port of that function.
   671          */
   672         private void findCENRecord(long start, long end) throws IOException {
   673             long totalLength = end - start;
   674             int endbuflen = 1024;
   675             byte[] endbuf = new byte[endbuflen];
   676             long endbufend = end - start;
   678             // There is a variable-length field after the dir offset record. We need to do consequential search.
   679             while (endbufend >= 22) {
   680                 if (endbufend < endbuflen)
   681                     endbuflen = (int)endbufend;
   682                 long endbufpos = endbufend - endbuflen;
   683                 zipRandomFile.seek(start + endbufpos);
   684                 zipRandomFile.readFully(endbuf, 0, endbuflen);
   685                 int i = endbuflen - 22;
   686                 while (i >= 0 &&
   687                         !(endbuf[i] == 0x50 &&
   688                         endbuf[i + 1] == 0x4b &&
   689                         endbuf[i + 2] == 0x05 &&
   690                         endbuf[i + 3] == 0x06 &&
   691                         endbufpos + i + 22 +
   692                         get2ByteLittleEndian(endbuf, i + 20) == totalLength)) {
   693                     i--;
   694                 }
   696                 if (i >= 0) {
   697                     zipDir = new byte[get4ByteLittleEndian(endbuf, i + 12) + 2];
   698                     zipDir[0] = endbuf[i + 10];
   699                     zipDir[1] = endbuf[i + 11];
   700                     zipRandomFile.seek(start + get4ByteLittleEndian(endbuf, i + 16));
   701                     zipRandomFile.readFully(zipDir, 2, zipDir.length - 2);
   702                     return;
   703                 } else {
   704                     endbufend = endbufpos + 21;
   705                 }
   706             }
   707             throw new ZipException("cannot read zip file");
   708         }
   709         private void buildIndex() throws IOException {
   710             int entryCount = get2ByteLittleEndian(zipDir, 0);
   712             entries = new ZipFileIndexEntry[entryCount];
   713             // Add each of the files
   714             if (entryCount > 0) {
   715                 directories = new HashMap<String, DirectoryEntry>();
   716                 ArrayList<ZipFileIndexEntry> entryList = new ArrayList<ZipFileIndexEntry>();
   717                 int pos = 2;
   718                 for (int i = 0; i < entryCount; i++) {
   719                     pos = readEntry(pos, entryList, directories);
   720                 }
   722                 // Add the accumulated dirs into the same list
   723                 Iterator i = directories.keySet().iterator();
   724                 while (i.hasNext()) {
   725                     ZipFileIndexEntry zipFileIndexEntry = new ZipFileIndexEntry( (String) i.next());
   726                     zipFileIndexEntry.isDir = true;
   727                     entryList.add(zipFileIndexEntry);
   728                 }
   730                 entries = entryList.toArray(new ZipFileIndexEntry[entryList.size()]);
   731                 Arrays.sort(entries);
   732             } else {
   733                 cleanupState();
   734             }
   735         }
   737         private int readEntry(int pos, List<ZipFileIndexEntry> entryList,
   738                 Map<String, DirectoryEntry> directories) throws IOException {
   739             if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) {
   740                 throw new ZipException("cannot read zip file entry");
   741             }
   743             int dirStart = pos + 46;
   744             int fileStart = dirStart;
   745             int fileEnd = fileStart + get2ByteLittleEndian(zipDir, pos + 28);
   747             if (zipFileIndex.symbolFilePrefixLength != 0 &&
   748                     ((fileEnd - fileStart) >= symbolFilePrefixLength)) {
   749                 dirStart += zipFileIndex.symbolFilePrefixLength;
   750                fileStart += zipFileIndex.symbolFilePrefixLength;
   751             }
   753             // Use the OS's path separator. Keep the position of the last one.
   754             for (int index = fileStart; index < fileEnd; index++) {
   755                 byte nextByte = zipDir[index];
   756                 if (nextByte == (byte)'\\' || nextByte == (byte)'/') {
   757                     zipDir[index] = (byte)File.separatorChar;
   758                     fileStart = index + 1;
   759                 }
   760             }
   762             String directory = null;
   763             if (fileStart == dirStart)
   764                 directory = "";
   765             else if (lastDir != null && lastLen == fileStart - dirStart - 1) {
   766                 int index = lastLen - 1;
   767                 while (zipDir[lastStart + index] == zipDir[dirStart + index]) {
   768                     if (index == 0) {
   769                         directory = lastDir;
   770                         break;
   771                     }
   772                     index--;
   773                 }
   774             }
   776             // Sub directories
   777             if (directory == null) {
   778                 lastStart = dirStart;
   779                 lastLen = fileStart - dirStart - 1;
   781                 directory = new String(zipDir, dirStart, lastLen, "UTF-8").intern();
   782                 lastDir = directory;
   784                 // Enter also all the parent directories
   785                 String tempDirectory = directory;
   787                 while (directories.get(tempDirectory) == null) {
   788                     directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex));
   789                     int separator = tempDirectory.lastIndexOf(File.separatorChar);
   790                     if (separator == -1)
   791                         break;
   792                     tempDirectory = tempDirectory.substring(0, separator);
   793                 }
   794             }
   795             else {
   796                 directory = directory.intern();
   797                 if (directories.get(directory) == null) {
   798                     directories.put(directory, new DirectoryEntry(directory, zipFileIndex));
   799                 }
   800             }
   802             // For each dir create also a file
   803             if (fileStart != fileEnd) {
   804                 ZipFileIndexEntry entry = new ZipFileIndexEntry(directory,
   805                         new String(zipDir, fileStart, fileEnd - fileStart, "UTF-8"));
   807                 entry.setNativeTime(get4ByteLittleEndian(zipDir, pos + 12));
   808                 entry.compressedSize = get4ByteLittleEndian(zipDir, pos + 20);
   809                 entry.size = get4ByteLittleEndian(zipDir, pos + 24);
   810                 entry.offset = get4ByteLittleEndian(zipDir, pos + 42);
   811                 entryList.add(entry);
   812             }
   814             return pos + 46 +
   815                     get2ByteLittleEndian(zipDir, pos + 28) +
   816                     get2ByteLittleEndian(zipDir, pos + 30) +
   817                     get2ByteLittleEndian(zipDir, pos + 32);
   818         }
   819     }
   821     /**
   822      * Returns the last modified timestamp of a zip file.
   823      * @return long
   824      */
   825     public long getZipFileLastModified() throws IOException {
   826         lock.lock();
   827         try {
   828             checkIndex();
   829             return zipFileLastModified;
   830         }
   831         finally {
   832             lock.unlock();
   833         }
   834     }
   836     /** ------------------------------------------------------------------------
   837      *  DirectoryEntry class
   838      * -------------------------------------------------------------------------*/
   839     static class DirectoryEntry {
   840         private boolean filesInited;
   841         private boolean directoriesInited;
   842         private boolean zipFileEntriesInited;
   843         private boolean entriesInited;
   845         private long writtenOffsetOffset = 0;
   847         private String dirName;
   849         private com.sun.tools.javac.util.List<String> zipFileEntriesFiles = com.sun.tools.javac.util.List.<String>nil();
   850         private com.sun.tools.javac.util.List<String> zipFileEntriesDirectories = com.sun.tools.javac.util.List.<String>nil();
   851         private com.sun.tools.javac.util.List<ZipFileIndexEntry>  zipFileEntries = com.sun.tools.javac.util.List.<ZipFileIndexEntry>nil();
   853         private List<ZipFileIndexEntry> entries = new ArrayList<ZipFileIndexEntry>();
   855         private ZipFileIndex zipFileIndex;
   857         private int numEntries;
   859         DirectoryEntry(String dirName, ZipFileIndex index) {
   860         filesInited = false;
   861             directoriesInited = false;
   862             entriesInited = false;
   864             if (File.separatorChar == '/') {
   865                 dirName.replace('\\', '/');
   866             }
   867             else {
   868                 dirName.replace('/', '\\');
   869             }
   871             this.dirName = dirName.intern();
   872             this.zipFileIndex = index;
   873         }
   875         private com.sun.tools.javac.util.List<String> getFiles() {
   876             if (filesInited) {
   877                 return zipFileEntriesFiles;
   878             }
   880             initEntries();
   882             for (ZipFileIndexEntry e : entries) {
   883                 if (!e.isDir) {
   884                     zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
   885                 }
   886             }
   887             filesInited = true;
   888             return zipFileEntriesFiles;
   889         }
   891         private com.sun.tools.javac.util.List<String> getDirectories() {
   892             if (directoriesInited) {
   893                 return zipFileEntriesFiles;
   894             }
   896             initEntries();
   898             for (ZipFileIndexEntry e : entries) {
   899                 if (e.isDir) {
   900                     zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
   901                 }
   902             }
   904             directoriesInited = true;
   906             return zipFileEntriesDirectories;
   907         }
   909         private com.sun.tools.javac.util.List<ZipFileIndexEntry> getEntries() {
   910             if (zipFileEntriesInited) {
   911                 return zipFileEntries;
   912             }
   914             initEntries();
   916             zipFileEntries = com.sun.tools.javac.util.List.nil();
   917             for (ZipFileIndexEntry zfie : entries) {
   918                 zipFileEntries = zipFileEntries.append(zfie);
   919             }
   921             zipFileEntriesInited = true;
   923             return zipFileEntries;
   924         }
   926         private ZipFileIndexEntry getEntry(String rootName) {
   927             initEntries();
   928             int index = Collections.binarySearch(entries, new ZipFileIndexEntry(dirName, rootName));
   929             if (index < 0) {
   930                 return null;
   931             }
   933             return entries.get(index);
   934         }
   936         private void initEntries() {
   937             if (entriesInited) {
   938                 return;
   939             }
   941             if (!zipFileIndex.readFromIndex) {
   942                 int from = -Arrays.binarySearch(zipFileIndex.entries,
   943                         new ZipFileIndexEntry(dirName, ZipFileIndex.MIN_CHAR)) - 1;
   944                 int to = -Arrays.binarySearch(zipFileIndex.entries,
   945                         new ZipFileIndexEntry(dirName, MAX_CHAR)) - 1;
   947                 boolean emptyList = false;
   949                 for (int i = from; i < to; i++) {
   950                     entries.add(zipFileIndex.entries[i]);
   951                 }
   952             } else {
   953                 File indexFile = zipFileIndex.getIndexFile();
   954                 if (indexFile != null) {
   955                     RandomAccessFile raf = null;
   956                     try {
   957                         raf = new RandomAccessFile(indexFile, "r");
   958                         raf.seek(writtenOffsetOffset);
   960                         for (int nFiles = 0; nFiles < numEntries; nFiles++) {
   961                             // Read the name bytes
   962                             int zfieNameBytesLen = raf.readInt();
   963                             byte [] zfieNameBytes = new byte[zfieNameBytesLen];
   964                             raf.read(zfieNameBytes);
   965                             String eName = new String(zfieNameBytes, "UTF-8");
   967                             // Read isDir
   968                             boolean eIsDir = raf.readByte() == (byte)0 ? false : true;
   970                             // Read offset of bytes in the real Jar/Zip file
   971                             int eOffset = raf.readInt();
   973                             // Read size of the file in the real Jar/Zip file
   974                             int eSize = raf.readInt();
   976                             // Read compressed size of the file in the real Jar/Zip file
   977                             int eCsize = raf.readInt();
   979                             // Read java time stamp of the file in the real Jar/Zip file
   980                             long eJavaTimestamp = raf.readLong();
   982                             ZipFileIndexEntry rfie = new ZipFileIndexEntry(dirName, eName);
   983                             rfie.isDir = eIsDir;
   984                             rfie.offset = eOffset;
   985                             rfie.size = eSize;
   986                             rfie.compressedSize = eCsize;
   987                             rfie.javatime = eJavaTimestamp;
   988                             entries.add(rfie);
   989                         }
   990                     } catch (Throwable t) {
   991                         // Do nothing
   992                     } finally {
   993                         try {
   994                             if (raf == null) {
   995                                 raf.close();
   996                             }
   997                         } catch (Throwable t) {
   998                             // Do nothing
   999                         }
  1004             entriesInited = true;
  1007         List<ZipFileIndexEntry> getEntriesAsCollection() {
  1008             initEntries();
  1010             return entries;
  1014     private boolean readIndex() {
  1015         if (triedToReadIndex || !usePreindexedCache) {
  1016             return false;
  1019         boolean ret = false;
  1020         lock.lock();
  1021         try {
  1022             triedToReadIndex = true;
  1023             RandomAccessFile raf = null;
  1024             try {
  1025                 File indexFileName = getIndexFile();
  1026                 raf = new RandomAccessFile(indexFileName, "r");
  1028                 long fileStamp = raf.readLong();
  1029                 if (zipFile.lastModified() != fileStamp) {
  1030                     ret = false;
  1031                 } else {
  1032                     directories = new HashMap<String, DirectoryEntry>();
  1033                     int numDirs = raf.readInt();
  1034                     for (int nDirs = 0; nDirs < numDirs; nDirs++) {
  1035                         int dirNameBytesLen = raf.readInt();
  1036                         byte [] dirNameBytes = new byte[dirNameBytesLen];
  1037                         raf.read(dirNameBytes);
  1039                         String dirNameStr = new String(dirNameBytes, "UTF-8");
  1040                         DirectoryEntry de = new DirectoryEntry(dirNameStr, this);
  1041                         de.numEntries = raf.readInt();
  1042                         de.writtenOffsetOffset = raf.readLong();
  1043                         directories.put(dirNameStr, de);
  1045                     ret = true;
  1046                     zipFileLastModified = fileStamp;
  1048             } catch (Throwable t) {
  1049                 // Do nothing
  1050             } finally {
  1051                 if (raf != null) {
  1052                     try {
  1053                         raf.close();
  1054                     } catch (Throwable tt) {
  1055                         // Do nothing
  1059             if (ret == true) {
  1060                 readFromIndex = true;
  1063         finally {
  1064             lock.unlock();
  1067         return ret;
  1070     private boolean writeIndex() {
  1071         boolean ret = false;
  1072         if (readFromIndex || !usePreindexedCache) {
  1073             return true;
  1076         if (!writeIndex) {
  1077             return true;
  1080         File indexFile = getIndexFile();
  1081         if (indexFile == null) {
  1082             return false;
  1085         RandomAccessFile raf = null;
  1086         long writtenSoFar = 0;
  1087         try {
  1088             raf = new RandomAccessFile(indexFile, "rw");
  1090             raf.writeLong(zipFileLastModified);
  1091             writtenSoFar += 8;
  1094             Iterator<String> iterDirName = directories.keySet().iterator();
  1095             List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>();
  1096             Map<String, Long> offsets = new HashMap<String, Long>();
  1097             raf.writeInt(directories.keySet().size());
  1098             writtenSoFar += 4;
  1100             while(iterDirName.hasNext()) {
  1101                 String dirName = iterDirName.next();
  1102                 DirectoryEntry dirEntry = directories.get(dirName);
  1104                 directoriesToWrite.add(dirEntry);
  1106                 // Write the dir name bytes
  1107                 byte [] dirNameBytes = dirName.getBytes("UTF-8");
  1108                 int dirNameBytesLen = dirNameBytes.length;
  1109                 raf.writeInt(dirNameBytesLen);
  1110                 writtenSoFar += 4;
  1112                 raf.write(dirNameBytes);
  1113                 writtenSoFar += dirNameBytesLen;
  1115                 // Write the number of files in the dir
  1116                 List dirEntries = dirEntry.getEntriesAsCollection();
  1117                 raf.writeInt(dirEntries.size());
  1118                 writtenSoFar += 4;
  1120                 offsets.put(dirName, new Long(writtenSoFar));
  1122                 // Write the offset of the file's data in the dir
  1123                 dirEntry.writtenOffsetOffset = 0L;
  1124                 raf.writeLong(0L);
  1125                 writtenSoFar += 8;
  1128             for (DirectoryEntry de : directoriesToWrite) {
  1129                 // Fix up the offset in the directory table
  1130                 long currFP = raf.getFilePointer();
  1132                 long offsetOffset = offsets.get(de.dirName).longValue();
  1133                 raf.seek(offsetOffset);
  1134                 raf.writeLong(writtenSoFar);
  1136                 raf.seek(currFP);
  1138                 // Now write each of the files in the DirectoryEntry
  1139                 List<ZipFileIndexEntry> entries = de.getEntriesAsCollection();
  1140                 for (ZipFileIndexEntry zfie : entries) {
  1141                     // Write the name bytes
  1142                     byte [] zfieNameBytes = zfie.name.getBytes("UTF-8");
  1143                     int zfieNameBytesLen = zfieNameBytes.length;
  1144                     raf.writeInt(zfieNameBytesLen);
  1145                     writtenSoFar += 4;
  1146                     raf.write(zfieNameBytes);
  1147                     writtenSoFar += zfieNameBytesLen;
  1149                     // Write isDir
  1150                     raf.writeByte(zfie.isDir ? (byte)1 : (byte)0);
  1151                     writtenSoFar += 1;
  1153                     // Write offset of bytes in the real Jar/Zip file
  1154                     raf.writeInt(zfie.offset);
  1155                     writtenSoFar += 4;
  1157                     // Write size of the file in the real Jar/Zip file
  1158                     raf.writeInt(zfie.size);
  1159                     writtenSoFar += 4;
  1161                     // Write compressed size of the file in the real Jar/Zip file
  1162                     raf.writeInt(zfie.compressedSize);
  1163                     writtenSoFar += 4;
  1165                     // Write java time stamp of the file in the real Jar/Zip file
  1166                     raf.writeLong(zfie.getLastModified());
  1167                     writtenSoFar += 8;
  1170         } catch (Throwable t) {
  1171             // Do nothing
  1172         } finally {
  1173             try {
  1174                 if (raf != null) {
  1175                     raf.close();
  1177             } catch(IOException ioe) {
  1178                 // Do nothing
  1182         return ret;
  1185     public boolean writeZipIndex() {
  1186         lock.lock();
  1187         try {
  1188             return writeIndex();
  1190         finally {
  1191             lock.unlock();
  1195     private File getIndexFile() {
  1196         if (zipIndexFile == null) {
  1197             if (zipFile == null) {
  1198                 return null;
  1201             zipIndexFile = new File((preindexedCacheLocation == null ? "" : preindexedCacheLocation) +
  1202                     zipFile.getName() + ".index");
  1205         return zipIndexFile;
  1208     public File getZipFile() {
  1209         return zipFile;

mercurial