langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java
changeset 1205 b316e32eb90c
parent 865 21668f049d28
child 1789 7ac8c0815000
--- a/langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java	Fri Aug 22 11:46:29 2008 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java	Tue Aug 26 14:52:59 2008 -0700
@@ -25,11 +25,12 @@
 
 package com.sun.tools.javac.file;
 
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
-import java.text.MessageFormat;
+import java.lang.ref.SoftReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -45,6 +46,9 @@
 import java.util.zip.Inflater;
 import java.util.zip.ZipException;
 
+import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
+import com.sun.tools.javac.file.RelativePath.RelativeFile;
+
 /** This class implements building of index of a zip archive and access to it's context.
  *  It also uses prebuild index if available. It supports invocations where it will
  *  serialize an optimized zip index file to disk.
@@ -75,8 +79,8 @@
 
     private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
 
-    private Map<String, DirectoryEntry> directories = Collections.<String, DirectoryEntry>emptyMap();
-    private Set<String> allDirs = Collections.<String>emptySet();
+    private Map<RelativeDirectory, DirectoryEntry> directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
+    private Set<RelativeDirectory> allDirs = Collections.<RelativeDirectory>emptySet();
 
     // ZipFileIndex data entries
     private File zipFile;
@@ -87,7 +91,7 @@
     private boolean readFromIndex = false;
     private File zipIndexFile = null;
     private boolean triedToReadIndex = false;
-    final String symbolFilePrefix;
+    final RelativeDirectory symbolFilePrefix;
     private int symbolFilePrefixLength = 0;
     private boolean hasPopulatedData = false;
     private long lastReferenceTimeStamp = NOT_MODIFIED;
@@ -97,6 +101,9 @@
 
     private boolean writeIndex = false;
 
+    private Map <String, SoftReference<RelativeDirectory>> relativeDirectoryCache =
+            new HashMap<String, SoftReference<RelativeDirectory>>();
+
     /**
      * Returns a list of all ZipFileIndex entries
      *
@@ -143,7 +150,10 @@
         }
     }
 
-    public static ZipFileIndex getZipFileIndex(File zipFile, String symbolFilePrefix, boolean useCache, String cacheLocation, boolean writeIndex) throws IOException {
+    public static ZipFileIndex getZipFileIndex(File zipFile,
+            RelativeDirectory symbolFilePrefix,
+            boolean useCache, String cacheLocation,
+            boolean writeIndex) throws IOException {
         ZipFileIndex zi = null;
         lock.lock();
         try {
@@ -231,12 +241,12 @@
         }
     }
 
-    private ZipFileIndex(File zipFile, String symbolFilePrefix, boolean writeIndex,
+    private ZipFileIndex(File zipFile, RelativeDirectory symbolFilePrefix, boolean writeIndex,
             boolean useCache, String cacheLocation) throws IOException {
         this.zipFile = zipFile;
         this.symbolFilePrefix = symbolFilePrefix;
         this.symbolFilePrefixLength = (symbolFilePrefix == null ? 0 :
-            symbolFilePrefix.getBytes("UTF-8").length);
+            symbolFilePrefix.getPath().getBytes("UTF-8").length);
         this.writeIndex = writeIndex;
         this.usePreindexedCache = useCache;
         this.preindexedCacheLocation = cacheLocation;
@@ -250,7 +260,7 @@
     }
 
     public String toString() {
-        return "ZipFileIndex of file:(" + zipFile + ")";
+        return "ZipFileIndex[" + zipFile + "]";
     }
 
     // Just in case...
@@ -291,8 +301,8 @@
             return;
         }
 
-        directories = Collections.<String, DirectoryEntry>emptyMap();
-        allDirs = Collections.<String>emptySet();
+        directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
+        allDirs = Collections.<RelativeDirectory>emptySet();
 
         try {
             openFile();
@@ -317,9 +327,9 @@
     private void cleanupState() {
         // Make sure there is a valid but empty index if the file doesn't exist
         entries = Entry.EMPTY_ARRAY;
-        directories = Collections.<String, DirectoryEntry>emptyMap();
+        directories = Collections.<RelativeDirectory, DirectoryEntry>emptyMap();
         zipFileLastModified = NOT_MODIFIED;
-        allDirs = Collections.<String>emptySet();
+        allDirs = Collections.<RelativeDirectory>emptySet();
     }
 
     public void close() {
@@ -346,24 +356,12 @@
     /**
      * Returns the ZipFileIndexEntry for an absolute path, if there is one.
      */
-    Entry getZipIndexEntry(String path) {
-        if (File.separatorChar != '/') {
-            path = path.replace('/', File.separatorChar);
-        }
+    Entry getZipIndexEntry(RelativePath path) {
         lock.lock();
         try {
             checkIndex();
-            String lookFor = "";
-            int lastSepIndex = path.lastIndexOf(File.separatorChar);
-            boolean noSeparator = false;
-            if (lastSepIndex == -1) {
-                noSeparator = true;
-            }
-
-            DirectoryEntry de = directories.get(noSeparator ? "" : path.substring(0, lastSepIndex));
-
-            lookFor = path.substring(noSeparator ? 0 : lastSepIndex + 1);
-
+            DirectoryEntry de = directories.get(path.dirname());
+            String lookFor = path.basename();
             return de == null ? null : de.getEntry(lookFor);
         }
         catch (IOException e) {
@@ -377,11 +375,7 @@
     /**
      * Returns a javac List of filenames within an absolute path in the ZipFileIndex.
      */
-    public com.sun.tools.javac.util.List<String> getFiles(String path) {
-        if (File.separatorChar != '/') {
-            path = path.replace('/', File.separatorChar);
-        }
-
+    public com.sun.tools.javac.util.List<String> getFiles(RelativeDirectory path) {
         lock.lock();
         try {
             checkIndex();
@@ -402,16 +396,10 @@
         }
     }
 
-    public List<String> getAllDirectories(String path) {
-
-        if (File.separatorChar != '/') {
-            path = path.replace('/', File.separatorChar);
-        }
-
+    public List<String> getDirectories(RelativeDirectory path) {
         lock.lock();
         try {
             checkIndex();
-            path = path.intern();
 
             DirectoryEntry de = directories.get(path);
             com.sun.tools.javac.util.List<String> ret = de == null ? null : de.getDirectories();
@@ -430,24 +418,18 @@
         }
     }
 
-    public Set<String> getAllDirectories() {
+    public Set<RelativeDirectory> getAllDirectories() {
         lock.lock();
         try {
             checkIndex();
             if (allDirs == Collections.EMPTY_SET) {
-                Set<String> alldirs = new HashSet<String>();
-                Iterator<String> dirsIter = directories.keySet().iterator();
-                while (dirsIter.hasNext()) {
-                    alldirs.add(new String(dirsIter.next()));
-                }
-
-                allDirs = alldirs;
+                allDirs = new HashSet<RelativeDirectory>(directories.keySet());
             }
 
             return allDirs;
         }
         catch (IOException e) {
-            return Collections.<String>emptySet();
+            return Collections.<RelativeDirectory>emptySet();
         }
         finally {
             lock.unlock();
@@ -461,7 +443,7 @@
      * @param path A path within the zip.
      * @return True if the path is a file or dir, false otherwise.
      */
-    public boolean contains(String path) {
+    public boolean contains(RelativePath path) {
         lock.lock();
         try {
             checkIndex();
@@ -475,17 +457,15 @@
         }
     }
 
-    public boolean isDirectory(String path) throws IOException {
+    public boolean isDirectory(RelativePath path) throws IOException {
         lock.lock();
         try {
             // The top level in a zip file is always a directory.
-            if (path.length() == 0) {
+            if (path.getPath().length() == 0) {
                 lastReferenceTimeStamp = System.currentTimeMillis();
                 return true;
             }
 
-            if (File.separatorChar != '/')
-                path = path.replace('/', File.separatorChar);
             checkIndex();
             return directories.get(path) != null;
         }
@@ -494,7 +474,7 @@
         }
     }
 
-    public long getLastModified(String path) throws IOException {
+    public long getLastModified(RelativeFile path) throws IOException {
         lock.lock();
         try {
             Entry entry = getZipIndexEntry(path);
@@ -507,7 +487,7 @@
         }
     }
 
-    public int length(String path) throws IOException {
+    public int length(RelativeFile path) throws IOException {
         lock.lock();
         try {
             Entry entry = getZipIndexEntry(path);
@@ -531,12 +511,12 @@
         }
     }
 
-    public byte[] read(String path) throws IOException {
+    public byte[] read(RelativeFile path) throws IOException {
         lock.lock();
         try {
             Entry entry = getZipIndexEntry(path);
             if (entry == null)
-                throw new FileNotFoundException(MessageFormat.format("Path not found in ZIP: {0}", path));
+                throw new FileNotFoundException("Path not found in ZIP: " + path.path);
             return read(entry);
         }
         finally {
@@ -557,7 +537,7 @@
         }
     }
 
-    public int read(String path, byte[] buffer) throws IOException {
+    public int read(RelativeFile path, byte[] buffer) throws IOException {
         lock.lock();
         try {
             Entry entry = getZipIndexEntry(path);
@@ -690,7 +670,7 @@
      * ----------------------------------------------------------------------------*/
 
     private class ZipDirectory {
-        private String lastDir;
+        private RelativeDirectory lastDir;
         private int lastStart;
         private int lastLen;
 
@@ -747,13 +727,13 @@
             }
             throw new ZipException("cannot read zip file");
         }
+
         private void buildIndex() throws IOException {
             int entryCount = get2ByteLittleEndian(zipDir, 0);
 
-            entries = new Entry[entryCount];
             // Add each of the files
             if (entryCount > 0) {
-                directories = new HashMap<String, DirectoryEntry>();
+                directories = new HashMap<RelativeDirectory, DirectoryEntry>();
                 ArrayList<Entry> entryList = new ArrayList<Entry>();
                 int pos = 2;
                 for (int i = 0; i < entryCount; i++) {
@@ -761,9 +741,11 @@
                 }
 
                 // Add the accumulated dirs into the same list
-                Iterator i = directories.keySet().iterator();
-                while (i.hasNext()) {
-                    Entry zipFileIndexEntry = new Entry( (String) i.next());
+                for (RelativeDirectory d: directories.keySet()) {
+                    // use shared RelativeDirectory objects for parent dirs
+                    RelativeDirectory parent = getRelativeDirectory(d.dirname().getPath());
+                    String file = d.basename();
+                    Entry zipFileIndexEntry = new Entry(parent, file);
                     zipFileIndexEntry.isDir = true;
                     entryList.add(zipFileIndexEntry);
                 }
@@ -776,7 +758,7 @@
         }
 
         private int readEntry(int pos, List<Entry> entryList,
-                Map<String, DirectoryEntry> directories) throws IOException {
+                Map<RelativeDirectory, DirectoryEntry> directories) throws IOException {
             if (get4ByteLittleEndian(zipDir, pos) != 0x02014b50) {
                 throw new ZipException("cannot read zip file entry");
             }
@@ -790,19 +772,20 @@
                 dirStart += zipFileIndex.symbolFilePrefixLength;
                fileStart += zipFileIndex.symbolFilePrefixLength;
             }
-
-            // Use the OS's path separator. Keep the position of the last one.
+            // Force any '\' to '/'. Keep the position of the last separator.
             for (int index = fileStart; index < fileEnd; index++) {
                 byte nextByte = zipDir[index];
-                if (nextByte == (byte)'\\' || nextByte == (byte)'/') {
-                    zipDir[index] = (byte)File.separatorChar;
+                if (nextByte == (byte)'\\') {
+                    zipDir[index] = (byte)'/';
+                    fileStart = index + 1;
+                } else if (nextByte == (byte)'/') {
                     fileStart = index + 1;
                 }
             }
 
-            String directory = null;
+            RelativeDirectory directory = null;
             if (fileStart == dirStart)
-                directory = "";
+                directory = getRelativeDirectory("");
             else if (lastDir != null && lastLen == fileStart - dirStart - 1) {
                 int index = lastLen - 1;
                 while (zipDir[lastStart + index] == zipDir[dirStart + index]) {
@@ -819,22 +802,23 @@
                 lastStart = dirStart;
                 lastLen = fileStart - dirStart - 1;
 
-                directory = new String(zipDir, dirStart, lastLen, "UTF-8").intern();
+                directory = getRelativeDirectory(new String(zipDir, dirStart, lastLen, "UTF-8"));
                 lastDir = directory;
 
                 // Enter also all the parent directories
-                String tempDirectory = directory;
+                RelativeDirectory tempDirectory = directory;
 
                 while (directories.get(tempDirectory) == null) {
                     directories.put(tempDirectory, new DirectoryEntry(tempDirectory, zipFileIndex));
-                    int separator = tempDirectory.lastIndexOf(File.separatorChar);
-                    if (separator == -1)
+                    if (tempDirectory.path.indexOf("/") == tempDirectory.path.length() - 1)
                         break;
-                    tempDirectory = tempDirectory.substring(0, separator);
+                    else {
+                        // use shared RelativeDirectory objects for parent dirs
+                        tempDirectory = getRelativeDirectory(tempDirectory.dirname().getPath());
+                    }
                 }
             }
             else {
-                directory = directory.intern();
                 if (directories.get(directory) == null) {
                     directories.put(directory, new DirectoryEntry(directory, zipFileIndex));
                 }
@@ -886,7 +870,7 @@
 
         private long writtenOffsetOffset = 0;
 
-        private String dirName;
+        private RelativeDirectory dirName;
 
         private com.sun.tools.javac.util.List<String> zipFileEntriesFiles = com.sun.tools.javac.util.List.<String>nil();
         private com.sun.tools.javac.util.List<String> zipFileEntriesDirectories = com.sun.tools.javac.util.List.<String>nil();
@@ -898,70 +882,50 @@
 
         private int numEntries;
 
-        DirectoryEntry(String dirName, ZipFileIndex index) {
-        filesInited = false;
+        DirectoryEntry(RelativeDirectory dirName, ZipFileIndex index) {
+            filesInited = false;
             directoriesInited = false;
             entriesInited = false;
 
-            if (File.separatorChar == '/') {
-                dirName.replace('\\', '/');
-            }
-            else {
-                dirName.replace('/', '\\');
-            }
-
-            this.dirName = dirName.intern();
+            this.dirName = dirName;
             this.zipFileIndex = index;
         }
 
         private com.sun.tools.javac.util.List<String> getFiles() {
-            if (filesInited) {
-                return zipFileEntriesFiles;
+            if (!filesInited) {
+                initEntries();
+                for (Entry e : entries) {
+                    if (!e.isDir) {
+                        zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
+                    }
+                }
+                filesInited = true;
             }
-
-            initEntries();
-
-            for (Entry e : entries) {
-                if (!e.isDir) {
-                    zipFileEntriesFiles = zipFileEntriesFiles.append(e.name);
-                }
-            }
-            filesInited = true;
             return zipFileEntriesFiles;
         }
 
         private com.sun.tools.javac.util.List<String> getDirectories() {
-            if (directoriesInited) {
-                return zipFileEntriesFiles;
+            if (!directoriesInited) {
+                initEntries();
+                for (Entry e : entries) {
+                    if (e.isDir) {
+                        zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
+                    }
+                }
+                directoriesInited = true;
             }
-
-            initEntries();
-
-            for (Entry e : entries) {
-                if (e.isDir) {
-                    zipFileEntriesDirectories = zipFileEntriesDirectories.append(e.name);
-                }
-            }
-
-            directoriesInited = true;
-
             return zipFileEntriesDirectories;
         }
 
         private com.sun.tools.javac.util.List<Entry> getEntries() {
-            if (zipFileEntriesInited) {
-                return zipFileEntries;
+            if (!zipFileEntriesInited) {
+                initEntries();
+                zipFileEntries = com.sun.tools.javac.util.List.nil();
+                for (Entry zfie : entries) {
+                    zipFileEntries = zipFileEntries.append(zfie);
+                }
+                zipFileEntriesInited = true;
             }
-
-            initEntries();
-
-            zipFileEntries = com.sun.tools.javac.util.List.nil();
-            for (Entry zfie : entries) {
-                zipFileEntries = zipFileEntries.append(zfie);
-            }
-
-            zipFileEntriesInited = true;
-
             return zipFileEntries;
         }
 
@@ -986,8 +950,6 @@
                 int to = -Arrays.binarySearch(zipFileIndex.entries,
                         new Entry(dirName, MAX_CHAR)) - 1;
 
-                boolean emptyList = false;
-
                 for (int i = from; i < to; i++) {
                     entries.add(zipFileIndex.entries[i]);
                 }
@@ -1071,14 +1033,14 @@
                 if (zipFile.lastModified() != fileStamp) {
                     ret = false;
                 } else {
-                    directories = new HashMap<String, DirectoryEntry>();
+                    directories = new HashMap<RelativeDirectory, DirectoryEntry>();
                     int numDirs = raf.readInt();
                     for (int nDirs = 0; nDirs < numDirs; nDirs++) {
                         int dirNameBytesLen = raf.readInt();
                         byte [] dirNameBytes = new byte[dirNameBytesLen];
                         raf.read(dirNameBytes);
 
-                        String dirNameStr = new String(dirNameBytes, "UTF-8");
+                        RelativeDirectory dirNameStr = getRelativeDirectory(new String(dirNameBytes, "UTF-8"));
                         DirectoryEntry de = new DirectoryEntry(dirNameStr, this);
                         de.numEntries = raf.readInt();
                         de.writtenOffsetOffset = raf.readLong();
@@ -1132,21 +1094,18 @@
             raf.writeLong(zipFileLastModified);
             writtenSoFar += 8;
 
-
-            Iterator<String> iterDirName = directories.keySet().iterator();
             List<DirectoryEntry> directoriesToWrite = new ArrayList<DirectoryEntry>();
-            Map<String, Long> offsets = new HashMap<String, Long>();
+            Map<RelativeDirectory, Long> offsets = new HashMap<RelativeDirectory, Long>();
             raf.writeInt(directories.keySet().size());
             writtenSoFar += 4;
 
-            while(iterDirName.hasNext()) {
-                String dirName = iterDirName.next();
+            for (RelativeDirectory dirName: directories.keySet()) {
                 DirectoryEntry dirEntry = directories.get(dirName);
 
                 directoriesToWrite.add(dirEntry);
 
                 // Write the dir name bytes
-                byte [] dirNameBytes = dirName.getBytes("UTF-8");
+                byte [] dirNameBytes = dirName.getPath().getBytes("UTF-8");
                 int dirNameBytesLen = dirNameBytes.length;
                 raf.writeInt(dirNameBytesLen);
                 writtenSoFar += 4;
@@ -1251,12 +1210,24 @@
         return zipFile;
     }
 
+    private RelativeDirectory getRelativeDirectory(String path) {
+        RelativeDirectory rd;
+        SoftReference<RelativeDirectory> ref = relativeDirectoryCache.get(path);
+        if (ref != null) {
+            rd = ref.get();
+            if (rd != null)
+                return rd;
+        }
+        rd = new RelativeDirectory(path);
+        relativeDirectoryCache.put(path, new SoftReference<RelativeDirectory>(rd));
+        return rd;
+    }
 
     static class Entry implements Comparable<Entry> {
         public static final Entry[] EMPTY_ARRAY = {};
 
         // Directory related
-        String dir;
+        RelativeDirectory dir;
         boolean isDir;
 
         // File related
@@ -1269,32 +1240,17 @@
 
         private int nativetime;
 
-        public Entry(String path) {
-            int separator = path.lastIndexOf(File.separatorChar);
-            if (separator == -1) {
-                dir = "".intern();
-                name = path;
-            } else {
-                dir = path.substring(0, separator).intern();
-                name = path.substring(separator + 1);
-            }
+        public Entry(RelativePath path) {
+            this(path.dirname(), path.basename());
         }
 
-        public Entry(String directory, String name) {
-            this.dir = directory.intern();
+        public Entry(RelativeDirectory directory, String name) {
+            this.dir = directory;
             this.name = name;
         }
 
         public String getName() {
-            if (dir == null || dir.length() == 0) {
-                return name;
-            }
-
-            StringBuilder sb = new StringBuilder();
-            sb.append(dir);
-            sb.append(File.separatorChar);
-            sb.append(name);
-            return sb.toString();
+            return new RelativeFile(dir, name).getPath();
         }
 
         public String getFileName() {
@@ -1331,7 +1287,7 @@
         }
 
         public int compareTo(Entry other) {
-            String otherD = other.dir;
+            RelativeDirectory otherD = other.dir;
             if (dir != otherD) {
                 int c = dir.compareTo(otherD);
                 if (c != 0)
@@ -1340,6 +1296,22 @@
             return name.compareTo(other.name);
         }
 
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Entry))
+                return false;
+            Entry other = (Entry) o;
+            return dir.equals(other.dir) && name.equals(other.name);
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 7;
+            hash = 97 * hash + (this.dir != null ? this.dir.hashCode() : 0);
+            hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0);
+            return hash;
+        }
+
 
         public String toString() {
             return isDir ? ("Dir:" + dir + " : " + name) :