diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarHandler.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarHandler.java new file mode 100644 index 000000000000..ba6b29892178 --- /dev/null +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarHandler.java @@ -0,0 +1,221 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vfs.impl.jar; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ArrayUtil; +import com.intellij.util.TimedReference; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public abstract class CoreJarHandler { + protected final TimedReference myZipFile = new TimedReference(null); + protected SoftReference> myRelPathsToEntries = new SoftReference>(null); + protected final Object lock = new Object(); + protected final String myBasePath; + + protected static class EntryInfo { + public EntryInfo(final String shortName, final EntryInfo parent, final boolean directory) { + this.shortName = new String(shortName); + this.parent = parent; + isDirectory = directory; + } + + final boolean isDirectory; + protected final String shortName; + final EntryInfo parent; + } + + public CoreJarHandler(String path) { + myBasePath = path; + } + + @NotNull + protected Map initEntries() { + synchronized (lock) { + Map map = myRelPathsToEntries.get(); + if (map == null) { + final ZipFile zip = getZip(); + + map = new THashMap(); + if (zip != null) { + map.put("", new EntryInfo("", null, true)); + final Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + final String name = entry.getName(); + final boolean isDirectory = name.endsWith("/"); + getOrCreate(isDirectory ? name.substring(0, name.length() - 1) : name, isDirectory, map); + } + + myRelPathsToEntries = new SoftReference>(map); + } + } + return map; + } + } + + public File getMirrorFile(File originalFile) { + return originalFile; + } + + @Nullable + public ZipFile getZip() { + ZipFile zip = myZipFile.get(); + if (zip == null) { + try { + zip = new ZipFile(getMirrorFile(getOriginalFile())); + myZipFile.set(zip); + } + catch (IOException e) { + return null; + } + } + + return zip; + } + + protected File getOriginalFile() { + return new File(myBasePath); + } + + private static EntryInfo getOrCreate(String entryName, boolean isDirectory, Map map) { + EntryInfo info = map.get(entryName); + if (info == null) { + int idx = entryName.lastIndexOf('/'); + final String parentEntryName = idx > 0 ? entryName.substring(0, idx) : ""; + String shortName = idx > 0 ? entryName.substring(idx + 1) : entryName; + if (".".equals(shortName)) return getOrCreate(parentEntryName, true, map); + + info = new EntryInfo(shortName, getOrCreate(parentEntryName, true, map), isDirectory); + map.put(entryName, info); + } + + return info; + } + + @NotNull + public String[] list(@NotNull final VirtualFile file) { + synchronized (lock) { + EntryInfo parentEntry = getEntryInfo(file); + + Set names = new HashSet(); + for (EntryInfo info : getEntriesMap().values()) { + if (info.parent == parentEntry) { + names.add(info.shortName); + } + } + + return ArrayUtil.toStringArray(names); + } + } + + protected EntryInfo getEntryInfo(final VirtualFile file) { + String parentPath = getRelativePath(file); + return getEntriesMap().get(parentPath); + } + + protected Map getEntriesMap() { + return initEntries(); + } + + private String getRelativePath(final VirtualFile file) { + final String path = file.getPath().substring(myBasePath.length() + 1); + return path.startsWith("/") ? path.substring(1) : path; + } + + @Nullable + private ZipEntry convertToEntry(VirtualFile file) { + String path = getRelativePath(file); + final ZipFile zip = getZip(); + return zip != null ? zip.getEntry(path) : null; + } + + public long getLength(@NotNull final VirtualFile file) { + synchronized (lock) { + final ZipEntry entry = convertToEntry(file); + return entry != null ? entry.getSize() : 0; + } + } + + @NotNull + public InputStream getInputStream(@NotNull final VirtualFile file) throws IOException { + return new ByteArrayInputStream(contentsToByteArray(file)); + } + + @NotNull + public byte[] contentsToByteArray(@NotNull final VirtualFile file) throws IOException { + synchronized (lock) { + final ZipEntry entry = convertToEntry(file); + if (entry == null) { + return new byte[0]; + } + + final ZipFile zip = getZip(); + assert zip != null : file; + + final InputStream stream = zip.getInputStream(entry); + assert stream != null : file; + + try { + return FileUtil.loadBytes(stream, (int)entry.getSize()); + } + finally { + stream.close(); + } + } + } + + public long getTimeStamp(@NotNull final VirtualFile file) { + if (file.getParent() == null) return -1L; // Optimization + synchronized (lock) { + final ZipEntry entry = convertToEntry(file); + return entry != null ? entry.getTime() : -1L; + } + } + + public boolean isDirectory(@NotNull final VirtualFile file) { + if (file.getParent() == null) return true; // Optimization + synchronized (lock) { + final String path = getRelativePath(file); + final EntryInfo info = getEntriesMap().get(path); + return info == null || info.isDirectory; + } + } + + public boolean exists(@NotNull final VirtualFile fileOrDirectory) { + if (fileOrDirectory.getParent() == null) { + // Optimization. Do not build entries if asked for jar root existence. + return myZipFile.get() != null || getOriginalFile().exists(); + } + + return getEntryInfo(fileOrDirectory) != null; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java index fa27d38d932a..9c46380b94ab 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandler.java @@ -30,46 +30,22 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.FileSystemInterface; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.TimedReference; -import gnu.trove.THashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; -import java.lang.ref.SoftReference; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; -public class JarHandler implements FileSystemInterface { +public class JarHandler extends CoreJarHandler implements FileSystemInterface { @NonNls private static final String JARS_FOLDER = "jars"; - private final Object lock = new Object(); - private final TimedReference myZipFile = new TimedReference(null); private final JarFileSystemImpl myFileSystem; - private final String myBasePath; - private SoftReference> myRelPathsToEntries = new SoftReference>(null); - - private static class EntryInfo { - public EntryInfo(final String shortName, final EntryInfo parent, final boolean directory) { - this.shortName = new String(shortName); - this.parent = parent; - isDirectory = directory; - } - - final boolean isDirectory; - private final String shortName; - final EntryInfo parent; - } public JarHandler(final JarFileSystemImpl fileSystem, String path) { + super(path); myFileSystem = fileSystem; - myBasePath = path; } public void refreshLocalFileForJar() { @@ -98,75 +74,7 @@ public class JarHandler implements FileSystemInterface { } } - @NotNull - private Map initEntries() { - synchronized (lock) { - Map map = myRelPathsToEntries.get(); - if (map == null) { - final ZipFile zip = getZip(); - - map = new THashMap(); - if (zip != null) { - map.put("", new EntryInfo("", null, true)); - final Enumeration entries = zip.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - final String name = entry.getName(); - final boolean isDirectory = name.endsWith("/"); - getOrCreate(isDirectory ? name.substring(0, name.length() - 1) : name, isDirectory, map); - } - - myRelPathsToEntries = new SoftReference>(map); - } - } - return map; - } - } - - private static EntryInfo getOrCreate(String entryName, boolean isDirectory, Map map) { - EntryInfo info = map.get(entryName); - if (info == null) { - int idx = entryName.lastIndexOf('/'); - final String parentEntryName = idx > 0 ? entryName.substring(0, idx) : ""; - String shortName = idx > 0 ? entryName.substring(idx + 1) : entryName; - if (".".equals(shortName)) return getOrCreate(parentEntryName, true, map); - - info = new EntryInfo(shortName, getOrCreate(parentEntryName, true, map), isDirectory); - map.put(entryName, info); - } - - return info; - } - @Override - @NotNull - public String[] list(@NotNull final VirtualFile file) { - synchronized (lock) { - EntryInfo parentEntry = getEntryInfo(file); - - Set names = new HashSet(); - for (EntryInfo info : getEntriesMap().values()) { - if (info.parent == parentEntry) { - names.add(info.shortName); - } - } - - return ArrayUtil.toStringArray(names); - } - } - - private EntryInfo getEntryInfo(final VirtualFile file) { - synchronized (lock) { - String parentPath = getRelativePath(file); - return getEntriesMap().get(parentPath); - } - } - - private String getRelativePath(final VirtualFile file) { - final String path = file.getPath().substring(myBasePath.length() + 1); - return path.startsWith("/") ? path.substring(1) : path; - } - public File getMirrorFile(File originalFile) { if (!myFileSystem.isMakeCopyOfJar(originalFile) || !originalFile.exists()) return originalFile; @@ -225,33 +133,6 @@ public class JarHandler implements FileSystemInterface { return mirror; } - @Nullable - public ZipFile getZip() { - ZipFile zip = myZipFile.get(); - if (zip == null) { - try { - zip = new ZipFile(getMirrorFile(getOriginalFile())); - myZipFile.set(zip); - } - catch (IOException e) { - return null; - } - } - - return zip; - } - - private File getOriginalFile() { - return new File(myBasePath); - } - - @Nullable - private ZipEntry convertToEntry(VirtualFile file) { - String path = getRelativePath(file); - final ZipFile zip = getZip(); - return zip != null ? zip.getEntry(path) : null; - } - @Override public long getLength(@NotNull final VirtualFile file) { synchronized (lock) { @@ -263,7 +144,7 @@ public class JarHandler implements FileSystemInterface { @Override @NotNull public InputStream getInputStream(@NotNull final VirtualFile file) throws IOException { - return new BufferExposingByteArrayInputStream(contentsToByteArray(file)); + return new ByteArrayInputStream(contentsToByteArray(file)); } @Override @@ -318,16 +199,6 @@ public class JarHandler implements FileSystemInterface { return false; } - @Override - public boolean exists(@NotNull final VirtualFile fileOrDirectory) { - if (fileOrDirectory.getParent() == null) { - // Optimization. Do not build entries if asked for jar root existence. - return myZipFile.get() != null || getOriginalFile().exists(); - } - - return getEntryInfo(fileOrDirectory) != null; - } - private static void throwReadOnly() throws IOException { throw new IOException("Jar file system is read-only"); }