langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java
changeset 1205 b316e32eb90c
parent 865 21668f049d28
child 1789 7ac8c0815000
equal deleted inserted replaced
1109:853d8c191eac 1205:b316e32eb90c
    23  * have any questions.
    23  * have any questions.
    24  */
    24  */
    25 
    25 
    26 package com.sun.tools.javac.file;
    26 package com.sun.tools.javac.file;
    27 
    27 
       
    28 
    28 import java.io.File;
    29 import java.io.File;
    29 import java.io.FileNotFoundException;
    30 import java.io.FileNotFoundException;
    30 import java.io.IOException;
    31 import java.io.IOException;
    31 import java.io.RandomAccessFile;
    32 import java.io.RandomAccessFile;
    32 import java.text.MessageFormat;
    33 import java.lang.ref.SoftReference;
    33 import java.util.ArrayList;
    34 import java.util.ArrayList;
    34 import java.util.Arrays;
    35 import java.util.Arrays;
    35 import java.util.Calendar;
    36 import java.util.Calendar;
    36 import java.util.Collections;
    37 import java.util.Collections;
    37 import java.util.HashMap;
    38 import java.util.HashMap;
    42 import java.util.Set;
    43 import java.util.Set;
    43 import java.util.concurrent.locks.ReentrantLock;
    44 import java.util.concurrent.locks.ReentrantLock;
    44 import java.util.zip.DataFormatException;
    45 import java.util.zip.DataFormatException;
    45 import java.util.zip.Inflater;
    46 import java.util.zip.Inflater;
    46 import java.util.zip.ZipException;
    47 import java.util.zip.ZipException;
       
    48 
       
    49 import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
       
    50 import com.sun.tools.javac.file.RelativePath.RelativeFile;
    47 
    51 
    48 /** This class implements building of index of a zip archive and access to it's context.
    52 /** This class implements building of index of a zip archive and access to it's context.
    49  *  It also uses prebuild index if available. It supports invocations where it will
    53  *  It also uses prebuild index if available. It supports invocations where it will
    50  *  serialize an optimized zip index file to disk.
    54  *  serialize an optimized zip index file to disk.
    51  *
    55  *
    73     private static Map<File, ZipFileIndex> zipFileIndexCache = new HashMap<File, ZipFileIndex>();
    77     private static Map<File, ZipFileIndex> zipFileIndexCache = new HashMap<File, ZipFileIndex>();
    74     private static ReentrantLock lock = new ReentrantLock();
    78     private static ReentrantLock lock = new ReentrantLock();
    75 
    79 
    76     private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
    80     private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
    77 
    81 
    78     private Map<String, DirectoryEntry> directories = Collections.<String, DirectoryEntry>emptyMap();
    82     private Map<RelativeDirectory, DirectoryEntry> directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
    79     private Set<String> allDirs = Collections.<String>emptySet();
    83     private Set<RelativeDirectory> allDirs = Collections.<RelativeDirectory>emptySet();
    80 
    84 
    81     // ZipFileIndex data entries
    85     // ZipFileIndex data entries
    82     private File zipFile;
    86     private File zipFile;
    83     private long zipFileLastModified = NOT_MODIFIED;
    87     private long zipFileLastModified = NOT_MODIFIED;
    84     private RandomAccessFile zipRandomFile;
    88     private RandomAccessFile zipRandomFile;
    85     private Entry[] entries;
    89     private Entry[] entries;
    86 
    90 
    87     private boolean readFromIndex = false;
    91     private boolean readFromIndex = false;
    88     private File zipIndexFile = null;
    92     private File zipIndexFile = null;
    89     private boolean triedToReadIndex = false;
    93     private boolean triedToReadIndex = false;
    90     final String symbolFilePrefix;
    94     final RelativeDirectory symbolFilePrefix;
    91     private int symbolFilePrefixLength = 0;
    95     private int symbolFilePrefixLength = 0;
    92     private boolean hasPopulatedData = false;
    96     private boolean hasPopulatedData = false;
    93     private long lastReferenceTimeStamp = NOT_MODIFIED;
    97     private long lastReferenceTimeStamp = NOT_MODIFIED;
    94 
    98 
    95     private boolean usePreindexedCache = false;
    99     private boolean usePreindexedCache = false;
    96     private String preindexedCacheLocation = null;
   100     private String preindexedCacheLocation = null;
    97 
   101 
    98     private boolean writeIndex = false;
   102     private boolean writeIndex = false;
       
   103 
       
   104     private Map <String, SoftReference<RelativeDirectory>> relativeDirectoryCache =
       
   105             new HashMap<String, SoftReference<RelativeDirectory>>();
    99 
   106 
   100     /**
   107     /**
   101      * Returns a list of all ZipFileIndex entries
   108      * Returns a list of all ZipFileIndex entries
   102      *
   109      *
   103      * @return A list of ZipFileIndex entries, or an empty list
   110      * @return A list of ZipFileIndex entries, or an empty list
   141         finally {
   148         finally {
   142             lock.unlock();
   149             lock.unlock();
   143         }
   150         }
   144     }
   151     }
   145 
   152 
   146     public static ZipFileIndex getZipFileIndex(File zipFile, String symbolFilePrefix, boolean useCache, String cacheLocation, boolean writeIndex) throws IOException {
   153     public static ZipFileIndex getZipFileIndex(File zipFile,
       
   154             RelativeDirectory symbolFilePrefix,
       
   155             boolean useCache, String cacheLocation,
       
   156             boolean writeIndex) throws IOException {
   147         ZipFileIndex zi = null;
   157         ZipFileIndex zi = null;
   148         lock.lock();
   158         lock.lock();
   149         try {
   159         try {
   150             zi = getExistingZipIndex(zipFile);
   160             zi = getExistingZipIndex(zipFile);
   151 
   161 
   229         finally {
   239         finally {
   230             lock.unlock();
   240             lock.unlock();
   231         }
   241         }
   232     }
   242     }
   233 
   243 
   234     private ZipFileIndex(File zipFile, String symbolFilePrefix, boolean writeIndex,
   244     private ZipFileIndex(File zipFile, RelativeDirectory symbolFilePrefix, boolean writeIndex,
   235             boolean useCache, String cacheLocation) throws IOException {
   245             boolean useCache, String cacheLocation) throws IOException {
   236         this.zipFile = zipFile;
   246         this.zipFile = zipFile;
   237         this.symbolFilePrefix = symbolFilePrefix;
   247         this.symbolFilePrefix = symbolFilePrefix;
   238         this.symbolFilePrefixLength = (symbolFilePrefix == null ? 0 :
   248         this.symbolFilePrefixLength = (symbolFilePrefix == null ? 0 :
   239             symbolFilePrefix.getBytes("UTF-8").length);
   249             symbolFilePrefix.getPath().getBytes("UTF-8").length);
   240         this.writeIndex = writeIndex;
   250         this.writeIndex = writeIndex;
   241         this.usePreindexedCache = useCache;
   251         this.usePreindexedCache = useCache;
   242         this.preindexedCacheLocation = cacheLocation;
   252         this.preindexedCacheLocation = cacheLocation;
   243 
   253 
   244         if (zipFile != null) {
   254         if (zipFile != null) {
   248         // Validate integrity of the zip file
   258         // Validate integrity of the zip file
   249         checkIndex();
   259         checkIndex();
   250     }
   260     }
   251 
   261 
   252     public String toString() {
   262     public String toString() {
   253         return "ZipFileIndex of file:(" + zipFile + ")";
   263         return "ZipFileIndex[" + zipFile + "]";
   254     }
   264     }
   255 
   265 
   256     // Just in case...
   266     // Just in case...
   257     protected void finalize() {
   267     protected void finalize() {
   258         closeFile();
   268         closeFile();
   289         if (readIndex()) {
   299         if (readIndex()) {
   290             lastReferenceTimeStamp = System.currentTimeMillis();
   300             lastReferenceTimeStamp = System.currentTimeMillis();
   291             return;
   301             return;
   292         }
   302         }
   293 
   303 
   294         directories = Collections.<String, DirectoryEntry>emptyMap();
   304         directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
   295         allDirs = Collections.<String>emptySet();
   305         allDirs = Collections.<RelativeDirectory>emptySet();
   296 
   306 
   297         try {
   307         try {
   298             openFile();
   308             openFile();
   299             long totalLength = zipRandomFile.length();
   309             long totalLength = zipRandomFile.length();
   300             ZipDirectory directory = new ZipDirectory(zipRandomFile, 0L, totalLength, this);
   310             ZipDirectory directory = new ZipDirectory(zipRandomFile, 0L, totalLength, this);
   315     }
   325     }
   316 
   326 
   317     private void cleanupState() {
   327     private void cleanupState() {
   318         // Make sure there is a valid but empty index if the file doesn't exist
   328         // Make sure there is a valid but empty index if the file doesn't exist
   319         entries = Entry.EMPTY_ARRAY;
   329         entries = Entry.EMPTY_ARRAY;
   320         directories = Collections.<String, DirectoryEntry>emptyMap();
   330         directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
   321         zipFileLastModified = NOT_MODIFIED;
   331         zipFileLastModified = NOT_MODIFIED;
   322         allDirs = Collections.<String>emptySet();
   332         allDirs = Collections.<RelativeDirectory>emptySet();
   323     }
   333     }
   324 
   334 
   325     public void close() {
   335     public void close() {
   326         lock.lock();
   336         lock.lock();
   327         try {
   337         try {
   344     }
   354     }
   345 
   355 
   346     /**
   356     /**
   347      * Returns the ZipFileIndexEntry for an absolute path, if there is one.
   357      * Returns the ZipFileIndexEntry for an absolute path, if there is one.
   348      */
   358      */
   349     Entry getZipIndexEntry(String path) {
   359     Entry getZipIndexEntry(RelativePath path) {
   350         if (File.separatorChar != '/') {
       
   351             path = path.replace('/', File.separatorChar);
       
   352         }
       
   353         lock.lock();
   360         lock.lock();
   354         try {
   361         try {
   355             checkIndex();
   362             checkIndex();
   356             String lookFor = "";
   363             DirectoryEntry de = directories.get(path.dirname());
   357             int lastSepIndex = path.lastIndexOf(File.separatorChar);
   364             String lookFor = path.basename();
   358             boolean noSeparator = false;
       
   359             if (lastSepIndex == -1) {
       
   360                 noSeparator = true;
       
   361             }
       
   362 
       
   363             DirectoryEntry de = directories.get(noSeparator ? "" : path.substring(0, lastSepIndex));
       
   364 
       
   365             lookFor = path.substring(noSeparator ? 0 : lastSepIndex + 1);
       
   366 
       
   367             return de == null ? null : de.getEntry(lookFor);
   365             return de == null ? null : de.getEntry(lookFor);
   368         }
   366         }
   369         catch (IOException e) {
   367         catch (IOException e) {
   370             return null;
   368             return null;
   371         }
   369         }
   375     }
   373     }
   376 
   374 
   377     /**
   375     /**
   378      * Returns a javac List of filenames within an absolute path in the ZipFileIndex.
   376      * Returns a javac List of filenames within an absolute path in the ZipFileIndex.
   379      */
   377      */
   380     public com.sun.tools.javac.util.List<String> getFiles(String path) {
   378     public com.sun.tools.javac.util.List<String> getFiles(RelativeDirectory path) {
   381         if (File.separatorChar != '/') {
       
   382             path = path.replace('/', File.separatorChar);
       
   383         }
       
   384 
       
   385         lock.lock();
   379         lock.lock();
   386         try {
   380         try {
   387             checkIndex();
   381             checkIndex();
   388 
   382 
   389             DirectoryEntry de = directories.get(path);
   383             DirectoryEntry de = directories.get(path);
   400         finally {
   394         finally {
   401             lock.unlock();
   395             lock.unlock();
   402         }
   396         }
   403     }
   397     }
   404 
   398 
   405     public List<String> getAllDirectories(String path) {
   399     public List<String> getDirectories(RelativeDirectory path) {
   406 
       
   407         if (File.separatorChar != '/') {
       
   408             path = path.replace('/', File.separatorChar);
       
   409         }
       
   410 
       
   411         lock.lock();
   400         lock.lock();
   412         try {
   401         try {
   413             checkIndex();
   402             checkIndex();
   414             path = path.intern();
       
   415 
   403 
   416             DirectoryEntry de = directories.get(path);
   404             DirectoryEntry de = directories.get(path);
   417             com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories();
   405             com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories();
   418 
   406 
   419             if (ret == null) {
   407             if (ret == null) {
   428         finally {
   416         finally {
   429             lock.unlock();
   417             lock.unlock();
   430         }
   418         }
   431     }
   419     }
   432 
   420 
   433     public Set<String> getAllDirectories() {
   421     public Set<RelativeDirectory> getAllDirectories() {
   434         lock.lock();
   422         lock.lock();
   435         try {
   423         try {
   436             checkIndex();
   424             checkIndex();
   437             if (allDirs == Collections.EMPTY_SET) {
   425             if (allDirs == Collections.EMPTY_SET) {
   438                 Set<String> alldirs = new HashSet<String>();
   426                 allDirs = new HashSet<RelativeDirectory>(directories.keySet());
   439                 Iterator<String> dirsIter = directories.keySet().iterator();
       
   440                 while (dirsIter.hasNext()) {
       
   441                     alldirs.add(new String(dirsIter.next()));
       
   442                 }
       
   443 
       
   444                 allDirs = alldirs;
       
   445             }
   427             }
   446 
   428 
   447             return allDirs;
   429             return allDirs;
   448         }
   430         }
   449         catch (IOException e) {
   431         catch (IOException e) {
   450             return Collections.<String>emptySet();
   432             return Collections.<RelativeDirectory>emptySet();
   451         }
   433         }
   452         finally {
   434         finally {
   453             lock.unlock();
   435             lock.unlock();
   454         }
   436         }
   455     }
   437     }
   459      * for file entries and directories.
   441      * for file entries and directories.
   460      *
   442      *
   461      * @param path A path within the zip.
   443      * @param path A path within the zip.
   462      * @return True if the path is a file or dir, false otherwise.
   444      * @return True if the path is a file or dir, false otherwise.
   463      */
   445      */
   464     public boolean contains(String path) {
   446     public boolean contains(RelativePath path) {
   465         lock.lock();
   447         lock.lock();
   466         try {
   448         try {
   467             checkIndex();
   449             checkIndex();
   468             return getZipIndexEntry(path) != null;
   450             return getZipIndexEntry(path) != null;
   469         }
   451         }
   473         finally {
   455         finally {
   474             lock.unlock();
   456             lock.unlock();
   475         }
   457         }
   476     }
   458     }
   477 
   459 
   478     public boolean isDirectory(String path) throws IOException {
   460     public boolean isDirectory(RelativePath path) throws IOException {
   479         lock.lock();
   461         lock.lock();
   480         try {
   462         try {
   481             // The top level in a zip file is always a directory.
   463             // The top level in a zip file is always a directory.
   482             if (path.length() == 0) {
   464             if (path.getPath().length() == 0) {
   483                 lastReferenceTimeStamp = System.currentTimeMillis();
   465                 lastReferenceTimeStamp = System.currentTimeMillis();
   484                 return true;
   466                 return true;
   485             }
   467             }
   486 
   468 
   487             if (File.separatorChar != '/')
       
   488                 path = path.replace('/', File.separatorChar);
       
   489             checkIndex();
   469             checkIndex();
   490             return directories.get(path) != null;
   470             return directories.get(path) != null;
   491         }
   471         }
   492         finally {
   472         finally {
   493             lock.unlock();
   473             lock.unlock();
   494         }
   474         }
   495     }
   475     }
   496 
   476 
   497     public long getLastModified(String path) throws IOException {
   477     public long getLastModified(RelativeFile path) throws IOException {
   498         lock.lock();
   478         lock.lock();
   499         try {
   479         try {
   500             Entry entry = getZipIndexEntry(path);
   480             Entry entry = getZipIndexEntry(path);
   501             if (entry == null)
   481             if (entry == null)
   502                 throw new FileNotFoundException();
   482                 throw new FileNotFoundException();
   505         finally {
   485         finally {
   506             lock.unlock();
   486             lock.unlock();
   507         }
   487         }
   508     }
   488     }
   509 
   489 
   510     public int length(String path) throws IOException {
   490     public int length(RelativeFile path) throws IOException {
   511         lock.lock();
   491         lock.lock();
   512         try {
   492         try {
   513             Entry entry = getZipIndexEntry(path);
   493             Entry entry = getZipIndexEntry(path);
   514             if (entry == null)
   494             if (entry == null)
   515                 throw new FileNotFoundException();
   495                 throw new FileNotFoundException();
   529         finally {
   509         finally {
   530             lock.unlock();
   510             lock.unlock();
   531         }
   511         }
   532     }
   512     }
   533 
   513 
   534     public byte[] read(String path) throws IOException {
   514     public byte[] read(RelativeFile path) throws IOException {
   535         lock.lock();
   515         lock.lock();
   536         try {
   516         try {
   537             Entry entry = getZipIndexEntry(path);
   517             Entry entry = getZipIndexEntry(path);
   538             if (entry == null)
   518             if (entry == null)
   539                 throw new FileNotFoundException(MessageFormat.format("Path not found in ZIP: {0}", path));
   519                 throw new FileNotFoundException("Path not found in ZIP: " + path.path);
   540             return read(entry);
   520             return read(entry);
   541         }
   521         }
   542         finally {
   522         finally {
   543             lock.unlock();
   523             lock.unlock();
   544         }
   524         }
   555         finally {
   535         finally {
   556             lock.unlock();
   536             lock.unlock();
   557         }
   537         }
   558     }
   538     }
   559 
   539 
   560     public int read(String path, byte[] buffer) throws IOException {
   540     public int read(RelativeFile path, byte[] buffer) throws IOException {
   561         lock.lock();
   541         lock.lock();
   562         try {
   542         try {
   563             Entry entry = getZipIndexEntry(path);
   543             Entry entry = getZipIndexEntry(path);
   564             if (entry == null)
   544             if (entry == null)
   565                 throw new FileNotFoundException();
   545                 throw new FileNotFoundException();
   688     /* ----------------------------------------------------------------------------
   668     /* ----------------------------------------------------------------------------
   689      * ZipDirectory
   669      * ZipDirectory
   690      * ----------------------------------------------------------------------------*/
   670      * ----------------------------------------------------------------------------*/
   691 
   671 
   692     private class ZipDirectory {
   672     private class ZipDirectory {
   693         private String lastDir;
   673         private RelativeDirectory lastDir;
   694         private int lastStart;
   674         private int lastStart;
   695         private int lastLen;
   675         private int lastLen;
   696 
   676 
   697         byte[] zipDir;
   677         byte[] zipDir;
   698         RandomAccessFile zipRandomFile = null;
   678         RandomAccessFile zipRandomFile = null;
   745                     endbufend = endbufpos + 21;
   725                     endbufend = endbufpos + 21;
   746                 }
   726                 }
   747             }
   727             }
   748             throw new ZipException("cannot read zip file");
   728             throw new ZipException("cannot read zip file");
   749         }
   729         }
       
   730 
   750         private void buildIndex() throws IOException {
   731         private void buildIndex() throws IOException {
   751             int entryCount = get2ByteLittleEndian(zipDir, 0);
   732             int entryCount = get2ByteLittleEndian(zipDir, 0);
   752 
   733 
   753             entries = new Entry[entryCount];
       
   754             // Add each of the files
   734             // Add each of the files
   755             if (entryCount > 0) {
   735             if (entryCount > 0) {
   756                 directories = new HashMap<String, DirectoryEntry>();
   736                 directories = new HashMap<RelativeDirectory, DirectoryEntry>();
   757                 ArrayList<Entry> entryList = new ArrayList<Entry>();
   737                 ArrayList<Entry> entryList = new ArrayList<Entry>();
   758                 int pos = 2;
   738                 int pos = 2;
   759                 for (int i = 0; i < entryCount; i++) {
   739                 for (int i = 0; i < entryCount; i++) {
   760                     pos = readEntry(pos, entryList, directories);
   740                     pos = readEntry(pos, entryList, directories);
   761                 }
   741                 }
   762 
   742 
   763                 // Add the accumulated dirs into the same list
   743                 // Add the accumulated dirs into the same list
   764                 Iterator i = directories.keySet().iterator();
   744                 for (RelativeDirectory d: directories.keySet()) {
   765                 while (i.hasNext()) {
   745                     // use shared RelativeDirectory objects for parent dirs
   766                     Entry zipFileIndexEntry = new Entry( (String) i.next());
   746                     RelativeDirectory parent = getRelativeDirectory(d.dirname().getPath());
       
   747                     String file = d.basename();
       
   748                     Entry zipFileIndexEntry = new Entry(parent, file);
   767                     zipFileIndexEntry.isDir = true;
   749                     zipFileIndexEntry.isDir = true;
   768                     entryList.add(zipFileIndexEntry);
   750                     entryList.add(zipFileIndexEntry);
   769                 }
   751                 }
   770 
   752 
   771                 entries = entryList.toArray(new Entry[entryList.size()]);
   753                 entries = entryList.toArray(new Entry[entryList.size()]);
   774                 cleanupState();
   756                 cleanupState();
   775             }
   757             }
   776         }
   758         }
   777 
   759 
   778         private int readEntry(int pos, List<Entry> entryList,
   760         private int readEntry(int pos, List<Entry> entryList,
   779                 Map<String, DirectoryEntry> directories) throws IOException {
   761                 Map<RelativeDirectory, DirectoryEntry> directories) throws IOException {
   780             if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) {
   762             if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) {
   781                 throw new ZipException("cannot read zip file entry");
   763                 throw new ZipException("cannot read zip file entry");
   782             }
   764             }
   783 
   765 
   784             int dirStart = pos + 46;
   766             int dirStart = pos + 46;
   788             if (zipFileIndex.symbolFilePrefixLength != 0 &&
   770             if (zipFileIndex.symbolFilePrefixLength != 0 &&
   789                     ((fileEnd - fileStart) >= symbolFilePrefixLength)) {
   771                     ((fileEnd - fileStart) >= symbolFilePrefixLength)) {
   790                 dirStart += zipFileIndex.symbolFilePrefixLength;
   772                 dirStart += zipFileIndex.symbolFilePrefixLength;
   791                fileStart += zipFileIndex.symbolFilePrefixLength;
   773                fileStart += zipFileIndex.symbolFilePrefixLength;
   792             }
   774             }
   793 
   775             // Force any '\' to '/'. Keep the position of the last separator.
   794             // Use the OS's path separator. Keep the position of the last one.
       
   795             for (int index = fileStart; index < fileEnd; index++) {
   776             for (int index = fileStart; index < fileEnd; index++) {
   796                 byte nextByte = zipDir[index];
   777                 byte nextByte = zipDir[index];
   797                 if (nextByte == (byte)'\\' || nextByte == (byte)'/') {
   778                 if (nextByte == (byte)'\\') {
   798                     zipDir[index] = (byte)File.separatorChar;
   779                     zipDir[index] = (byte)'/';
   799                     fileStart = index + 1;
   780                     fileStart = index + 1;
   800                 }
   781                 } else if (nextByte == (byte)'/') {
   801             }
   782                     fileStart = index + 1;
   802 
   783                 }
   803             String directory = null;
   784             }
       
   785 
       
   786             RelativeDirectory directory = null;
   804             if (fileStart == dirStart)
   787             if (fileStart == dirStart)
   805                 directory = "";
   788                 directory = getRelativeDirectory("");
   806             else if (lastDir != null && lastLen == fileStart - dirStart - 1) {
   789             else if (lastDir != null && lastLen == fileStart - dirStart - 1) {
   807                 int index = lastLen - 1;
   790                 int index = lastLen - 1;
   808                 while (zipDir[lastStart + index] == zipDir[dirStart + index]) {
   791                 while (zipDir[lastStart + index] == zipDir[dirStart + index]) {
   809                     if (index == 0) {
   792                     if (index == 0) {
   810                         directory = lastDir;
   793                         directory = lastDir;
   817             // Sub directories
   800             // Sub directories
   818             if (directory == null) {
   801             if (directory == null) {
   819                 lastStart = dirStart;
   802                 lastStart = dirStart;
   820                 lastLen = fileStart - dirStart - 1;
   803                 lastLen = fileStart - dirStart - 1;
   821 
   804 
   822                 directory = new String(zipDir, dirStart, lastLen, "UTF-8").intern();
   805                 directory = getRelativeDirectory(new String(zipDir, dirStart, lastLen, "UTF-8"));
   823                 lastDir = directory;
   806                 lastDir = directory;
   824 
   807 
   825                 // Enter also all the parent directories
   808                 // Enter also all the parent directories
   826                 String tempDirectory = directory;
   809                 RelativeDirectory tempDirectory = directory;
   827 
   810 
   828                 while (directories.get(tempDirectory) == null) {
   811                 while (directories.get(tempDirectory) == null) {
   829                     directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex));
   812                     directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex));
   830                     int separator = tempDirectory.lastIndexOf(File.separatorChar);
   813                     if (tempDirectory.path.indexOf("/") == tempDirectory.path.length() - 1)
   831                     if (separator == -1)
       
   832                         break;
   814                         break;
   833                     tempDirectory = tempDirectory.substring(0, separator);
   815                     else {
       
   816                         // use shared RelativeDirectory objects for parent dirs
       
   817                         tempDirectory = getRelativeDirectory(tempDirectory.dirname().getPath());
       
   818                     }
   834                 }
   819                 }
   835             }
   820             }
   836             else {
   821             else {
   837                 directory = directory.intern();
       
   838                 if (directories.get(directory) == null) {
   822                 if (directories.get(directory) == null) {
   839                     directories.put(directory, new DirectoryEntry(directory, zipFileIndex));
   823                     directories.put(directory, new DirectoryEntry(directory, zipFileIndex));
   840                 }
   824                 }
   841             }
   825             }
   842 
   826 
   884         private boolean zipFileEntriesInited;
   868         private boolean zipFileEntriesInited;
   885         private boolean entriesInited;
   869         private boolean entriesInited;
   886 
   870 
   887         private long writtenOffsetOffset = 0;
   871         private long writtenOffsetOffset = 0;
   888 
   872 
   889         private String dirName;
   873         private RelativeDirectory dirName;
   890 
   874 
   891         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> zipFileEntriesFiles = com.sun.tools.javac.util.List.<String>nil();
   892         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<String> zipFileEntriesDirectories = com.sun.tools.javac.util.List.<String>nil();
   893         private com.sun.tools.javac.util.List<Entry>  zipFileEntries = com.sun.tools.javac.util.List.<Entry>nil();
   877         private com.sun.tools.javac.util.List<Entry>  zipFileEntries = com.sun.tools.javac.util.List.<Entry>nil();
   894 
   878 
   896 
   880 
   897         private ZipFileIndex zipFileIndex;
   881         private ZipFileIndex zipFileIndex;
   898 
   882 
   899         private int numEntries;
   883         private int numEntries;
   900 
   884 
   901         DirectoryEntry(String dirName, ZipFileIndex index) {
   885         DirectoryEntry(RelativeDirectory dirName, ZipFileIndex index) {
   902         filesInited = false;
   886             filesInited = false;
   903             directoriesInited = false;
   887             directoriesInited = false;
   904             entriesInited = false;
   888             entriesInited = false;
   905 
   889 
   906             if (File.separatorChar == '/') {
   890             this.dirName = dirName;
   907                 dirName.replace('\\', '/');
       
   908             }
       
   909             else {
       
   910                 dirName.replace('/', '\\');
       
   911             }
       
   912 
       
   913             this.dirName = dirName.intern();
       
   914             this.zipFileIndex = index;
   891             this.zipFileIndex = index;
   915         }
   892         }
   916 
   893 
   917         private com.sun.tools.javac.util.List<String> getFiles() {
   894         private com.sun.tools.javac.util.List<String> getFiles() {
   918             if (filesInited) {
   895             if (!filesInited) {
   919                 return zipFileEntriesFiles;
   896                 initEntries();
   920             }
   897                 for (Entry e : entries) {
   921 
   898                     if (!e.isDir) {
   922             initEntries();
   899                         zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
   923 
   900                     }
   924             for (Entry e : entries) {
   901                 }
   925                 if (!e.isDir) {
   902                 filesInited = true;
   926                     zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
   903             }
   927                 }
       
   928             }
       
   929             filesInited = true;
       
   930             return zipFileEntriesFiles;
   904             return zipFileEntriesFiles;
   931         }
   905         }
   932 
   906 
   933         private com.sun.tools.javac.util.List<String> getDirectories() {
   907         private com.sun.tools.javac.util.List<String> getDirectories() {
   934             if (directoriesInited) {
   908             if (!directoriesInited) {
   935                 return zipFileEntriesFiles;
   909                 initEntries();
   936             }
   910                 for (Entry e : entries) {
   937 
   911                     if (e.isDir) {
   938             initEntries();
   912                         zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
   939 
   913                     }
   940             for (Entry e : entries) {
   914                 }
   941                 if (e.isDir) {
   915                 directoriesInited = true;
   942                     zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
   916             }
   943                 }
       
   944             }
       
   945 
       
   946             directoriesInited = true;
       
   947 
       
   948             return zipFileEntriesDirectories;
   917             return zipFileEntriesDirectories;
   949         }
   918         }
   950 
   919 
   951         private com.sun.tools.javac.util.List<Entry> getEntries() {
   920         private com.sun.tools.javac.util.List<Entry> getEntries() {
   952             if (zipFileEntriesInited) {
   921             if (!zipFileEntriesInited) {
   953                 return zipFileEntries;
   922                 initEntries();
   954             }
   923                 zipFileEntries = com.sun.tools.javac.util.List.nil();
   955 
   924                 for (Entry zfie : entries) {
   956             initEntries();
   925                     zipFileEntries = zipFileEntries.append(zfie);
   957 
   926                 }
   958             zipFileEntries = com.sun.tools.javac.util.List.nil();
   927                 zipFileEntriesInited = true;
   959             for (Entry zfie : entries) {
   928             }
   960                 zipFileEntries = zipFileEntries.append(zfie);
       
   961             }
       
   962 
       
   963             zipFileEntriesInited = true;
       
   964 
       
   965             return zipFileEntries;
   929             return zipFileEntries;
   966         }
   930         }
   967 
   931 
   968         private Entry getEntry(String rootName) {
   932         private Entry getEntry(String rootName) {
   969             initEntries();
   933             initEntries();
   983             if (!zipFileIndex.readFromIndex) {
   947             if (!zipFileIndex.readFromIndex) {
   984                 int from = -Arrays.binarySearch(zipFileIndex.entries,
   948                 int from = -Arrays.binarySearch(zipFileIndex.entries,
   985                         new Entry(dirName, ZipFileIndex.MIN_CHAR)) - 1;
   949                         new Entry(dirName, ZipFileIndex.MIN_CHAR)) - 1;
   986                 int to = -Arrays.binarySearch(zipFileIndex.entries,
   950                 int to = -Arrays.binarySearch(zipFileIndex.entries,
   987                         new Entry(dirName, MAX_CHAR)) - 1;
   951                         new Entry(dirName, MAX_CHAR)) - 1;
   988 
       
   989                 boolean emptyList = false;
       
   990 
   952 
   991                 for (int i = from; i < to; i++) {
   953                 for (int i = from; i < to; i++) {
   992                     entries.add(zipFileIndex.entries[i]);
   954                     entries.add(zipFileIndex.entries[i]);
   993                 }
   955                 }
   994             } else {
   956             } else {
  1069 
  1031 
  1070                 long fileStamp = raf.readLong();
  1032                 long fileStamp = raf.readLong();
  1071                 if (zipFile.lastModified() != fileStamp) {
  1033                 if (zipFile.lastModified() != fileStamp) {
  1072                     ret = false;
  1034                     ret = false;
  1073                 } else {
  1035                 } else {
  1074                     directories = new HashMap<String, DirectoryEntry>();
  1036                     directories = new HashMap<RelativeDirectory, DirectoryEntry>();
  1075                     int numDirs = raf.readInt();
  1037                     int numDirs = raf.readInt();
  1076                     for (int nDirs = 0; nDirs < numDirs; nDirs++) {
  1038                     for (int nDirs = 0; nDirs < numDirs; nDirs++) {
  1077                         int dirNameBytesLen = raf.readInt();
  1039                         int dirNameBytesLen = raf.readInt();
  1078                         byte [] dirNameBytes = new byte[dirNameBytesLen];
  1040                         byte [] dirNameBytes = new byte[dirNameBytesLen];
  1079                         raf.read(dirNameBytes);
  1041                         raf.read(dirNameBytes);
  1080 
  1042 
  1081                         String dirNameStr = new String(dirNameBytes, "UTF-8");
  1043                         RelativeDirectory dirNameStr = getRelativeDirectory(new String(dirNameBytes, "UTF-8"));
  1082                         DirectoryEntry de = new DirectoryEntry(dirNameStr, this);
  1044                         DirectoryEntry de = new DirectoryEntry(dirNameStr, this);
  1083                         de.numEntries = raf.readInt();
  1045                         de.numEntries = raf.readInt();
  1084                         de.writtenOffsetOffset = raf.readLong();
  1046                         de.writtenOffsetOffset = raf.readLong();
  1085                         directories.put(dirNameStr, de);
  1047                         directories.put(dirNameStr, de);
  1086                     }
  1048                     }
  1130             raf = new RandomAccessFile(indexFile, "rw");
  1092             raf = new RandomAccessFile(indexFile, "rw");
  1131 
  1093 
  1132             raf.writeLong(zipFileLastModified);
  1094             raf.writeLong(zipFileLastModified);
  1133             writtenSoFar += 8;
  1095             writtenSoFar += 8;
  1134 
  1096 
  1135 
       
  1136             Iterator<String> iterDirName = directories.keySet().iterator();
       
  1137             List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>();
  1097             List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>();
  1138             Map<String, Long> offsets = new HashMap<String, Long>();
  1098             Map<RelativeDirectory, Long> offsets = new HashMap<RelativeDirectory, Long>();
  1139             raf.writeInt(directories.keySet().size());
  1099             raf.writeInt(directories.keySet().size());
  1140             writtenSoFar += 4;
  1100             writtenSoFar += 4;
  1141 
  1101 
  1142             while(iterDirName.hasNext()) {
  1102             for (RelativeDirectory dirName: directories.keySet()) {
  1143                 String dirName = iterDirName.next();
       
  1144                 DirectoryEntry dirEntry = directories.get(dirName);
  1103                 DirectoryEntry dirEntry = directories.get(dirName);
  1145 
  1104 
  1146                 directoriesToWrite.add(dirEntry);
  1105                 directoriesToWrite.add(dirEntry);
  1147 
  1106 
  1148                 // Write the dir name bytes
  1107                 // Write the dir name bytes
  1149                 byte [] dirNameBytes = dirName.getBytes("UTF-8");
  1108                 byte [] dirNameBytes = dirName.getPath().getBytes("UTF-8");
  1150                 int dirNameBytesLen = dirNameBytes.length;
  1109                 int dirNameBytesLen = dirNameBytes.length;
  1151                 raf.writeInt(dirNameBytesLen);
  1110                 raf.writeInt(dirNameBytesLen);
  1152                 writtenSoFar += 4;
  1111                 writtenSoFar += 4;
  1153 
  1112 
  1154                 raf.write(dirNameBytes);
  1113                 raf.write(dirNameBytes);
  1249 
  1208 
  1250     public File getZipFile() {
  1209     public File getZipFile() {
  1251         return zipFile;
  1210         return zipFile;
  1252     }
  1211     }
  1253 
  1212 
       
  1213     private RelativeDirectory getRelativeDirectory(String path) {
       
  1214         RelativeDirectory rd;
       
  1215         SoftReference<RelativeDirectory> ref = relativeDirectoryCache.get(path);
       
  1216         if (ref != null) {
       
  1217             rd = ref.get();
       
  1218             if (rd != null)
       
  1219                 return rd;
       
  1220         }
       
  1221         rd = new RelativeDirectory(path);
       
  1222         relativeDirectoryCache.put(path, new SoftReference<RelativeDirectory>(rd));
       
  1223         return rd;
       
  1224     }
  1254 
  1225 
  1255     static class Entry implements Comparable<Entry> {
  1226     static class Entry implements Comparable<Entry> {
  1256         public static final Entry[] EMPTY_ARRAY = {};
  1227         public static final Entry[] EMPTY_ARRAY = {};
  1257 
  1228 
  1258         // Directory related
  1229         // Directory related
  1259         String dir;
  1230         RelativeDirectory dir;
  1260         boolean isDir;
  1231         boolean isDir;
  1261 
  1232 
  1262         // File related
  1233         // File related
  1263         String name;
  1234         String name;
  1264 
  1235 
  1267         int compressedSize;
  1238         int compressedSize;
  1268         long javatime;
  1239         long javatime;
  1269 
  1240 
  1270         private int nativetime;
  1241         private int nativetime;
  1271 
  1242 
  1272         public Entry(String path) {
  1243         public Entry(RelativePath path) {
  1273             int separator = path.lastIndexOf(File.separatorChar);
  1244             this(path.dirname(), path.basename());
  1274             if (separator == -1) {
  1245         }
  1275                 dir = "".intern();
  1246 
  1276                 name = path;
  1247         public Entry(RelativeDirectory directory, String name) {
  1277             } else {
  1248             this.dir = directory;
  1278                 dir = path.substring(0, separator).intern();
       
  1279                 name = path.substring(separator + 1);
       
  1280             }
       
  1281         }
       
  1282 
       
  1283         public Entry(String directory, String name) {
       
  1284             this.dir = directory.intern();
       
  1285             this.name = name;
  1249             this.name = name;
  1286         }
  1250         }
  1287 
  1251 
  1288         public String getName() {
  1252         public String getName() {
  1289             if (dir == null || dir.length() == 0) {
  1253             return new RelativeFile(dir, name).getPath();
  1290                 return name;
       
  1291             }
       
  1292 
       
  1293             StringBuilder sb = new StringBuilder();
       
  1294             sb.append(dir);
       
  1295             sb.append(File.separatorChar);
       
  1296             sb.append(name);
       
  1297             return sb.toString();
       
  1298         }
  1254         }
  1299 
  1255 
  1300         public String getFileName() {
  1256         public String getFileName() {
  1301             return name;
  1257             return name;
  1302         }
  1258         }
  1329         public boolean isDirectory() {
  1285         public boolean isDirectory() {
  1330             return isDir;
  1286             return isDir;
  1331         }
  1287         }
  1332 
  1288 
  1333         public int compareTo(Entry other) {
  1289         public int compareTo(Entry other) {
  1334             String otherD = other.dir;
  1290             RelativeDirectory otherD = other.dir;
  1335             if (dir != otherD) {
  1291             if (dir != otherD) {
  1336                 int c = dir.compareTo(otherD);
  1292                 int c = dir.compareTo(otherD);
  1337                 if (c != 0)
  1293                 if (c != 0)
  1338                     return c;
  1294                     return c;
  1339             }
  1295             }
  1340             return name.compareTo(other.name);
  1296             return name.compareTo(other.name);
  1341         }
  1297         }
  1342 
  1298 
       
  1299         @Override
       
  1300         public boolean equals(Object o) {
       
  1301             if (!(o instanceof Entry))
       
  1302                 return false;
       
  1303             Entry other = (Entry) o;
       
  1304             return dir.equals(other.dir) && name.equals(other.name);
       
  1305         }
       
  1306 
       
  1307         @Override
       
  1308         public int hashCode() {
       
  1309             int hash = 7;
       
  1310             hash = 97 * hash + (this.dir != null ? this.dir.hashCode() : 0);
       
  1311             hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0);
       
  1312             return hash;
       
  1313         }
       
  1314 
  1343 
  1315 
  1344         public String toString() {
  1316         public String toString() {
  1345             return isDir ? ("Dir:" + dir + " : " + name) :
  1317             return isDir ? ("Dir:" + dir + " : " + name) :
  1346                 (dir + ":" + name);
  1318                 (dir + ":" + name);
  1347         }
  1319         }