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

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

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