Home » openjdk-7 » com.sun.tools » javac » zip » [javadoc | source]

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

Home » openjdk-7 » com.sun.tools » javac » zip » [javadoc | source]