[jboss-cvs] JBossAS SVN: r91185 - in projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual: spi and 1 other directory.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue Jul 14 00:56:40 EDT 2009


Author: david.lloyd at jboss.com
Date: 2009-07-14 00:56:39 -0400 (Tue, 14 Jul 2009)
New Revision: 91185

Added:
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterable.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterator.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JZipFileSystem.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JavaZipFileSystem.java
Removed:
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java
Modified:
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFS.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VirtualFile.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/FileSystem.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/RealFileSystem.java
Log:
Fix really a lot of bugs; rewrite for performance

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFS.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFS.java	2009-07-14 02:44:23 UTC (rev 91184)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFS.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -26,14 +26,17 @@
 import java.util.List;
 import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Collection;
+import java.util.AbstractSet;
 import java.util.HashMap;
-import java.util.ArrayList;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
 
-import org.jboss.logging.Logger;
 import org.jboss.virtual.spi.RealFileSystem;
 import org.jboss.virtual.spi.FileSystem;
-import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
 
 /**
  * Virtual File System
@@ -45,13 +48,14 @@
  */
 public class VFS
 {
-   /** The log */
-   private static final Logger log = Logger.getLogger(VFS.class);
-
-   private final MountNode rootMountNode = new MountNode();
+   private final ConcurrentMap<VirtualFile, Map<String, Mount>> mounts = new ConcurrentHashMap<VirtualFile, Map<String, Mount>>();
    private final VirtualFile rootVirtualFile;
+   private final Mount rootMount;
    private static VFS instance = new VFS();
 
+   // todo - LRU VirtualFiles?
+   // todo - LRU String intern?
+
    static
    {
       init();
@@ -73,10 +77,9 @@
    public VFS()
    {
       // By default, there's a root mount which points to the "real" FS
-      final List<String> emptyList = Collections.<String>emptyList();
-      rootMountNode.mount = new Mount(RealFileSystem.ROOT_INSTANCE, emptyList);
       //noinspection ThisEscapedInObjectConstruction
-      rootVirtualFile = new VirtualFile(this, emptyList, "");
+      rootVirtualFile = new VirtualFile(this, "", null);
+      rootMount = new Mount(RealFileSystem.ROOT_INSTANCE, rootVirtualFile);
    }
 
    /**
@@ -113,42 +116,32 @@
     * @return a handle which can be used to unmount the filesystem
     * @throws IOException if an I/O error occurs, such as a filesystem already being mounted at the given mount point
     */
-   public Closeable mount(String mountPoint, FileSystem fileSystem) throws IOException {
-      final List<String> realMountPoint = PathTokenizer.applySpecialPaths(PathTokenizer.getTokens(mountPoint));
-      MountNode mountNode = rootMountNode;
-      for (String seg : realMountPoint)
-      {
-         synchronized (mountNode) {
-            Map<String, MountNode> childMap = mountNode.nodeMap;
-            MountNode subNode;
-            if (childMap == null) {
-               childMap = new HashMap<String, MountNode>();
-               subNode = new MountNode();
-               childMap.put(seg, subNode);
-               mountNode.nodeMap = childMap;
-               mountNode = subNode;
-            } else {
-               subNode = childMap.get(seg);
-               if (subNode != null) {
-                  mountNode = subNode;
-               } else {
-                  childMap = new HashMap<String, MountNode>(childMap);
-                  subNode = new MountNode();
-                  childMap.put(seg, subNode);
-                  mountNode.nodeMap = childMap;
-                  mountNode = subNode;
-               }
+   public Closeable mount(VirtualFile mountPoint, FileSystem fileSystem) throws IOException {
+      if (mountPoint.getVFS() != this) {
+         throw new IOException("VirtualFile does not match VFS instance");
+      }
+      final VirtualFile parent = mountPoint.getParent();
+      if (parent == null) {
+         throw new IOException("Root filsystem already mounted");
+      }
+      final String name = mountPoint.getName();
+      final Mount mount = new Mount(fileSystem, mountPoint);
+      for (;;) {
+         Map<String, Mount> childMountMap = mounts.get(parent);
+         Map<String, Mount> newMap;
+         if (childMountMap == null) {
+            childMountMap = mounts.putIfAbsent(parent, Collections.singletonMap(name, mount));
+            if (childMountMap == null) {
+               return mount;
             }
          }
-      }
-      synchronized (mountNode) {
-         if (mountNode.mount != null) {
+         newMap = new HashMap<String, Mount>(childMountMap);
+         if (newMap.put(name, mount) != null) {
             throw new IOException("Filsystem already mounted at mount point \"" + mountPoint + "\"");
          }
-         final Mount mount = new Mount(fileSystem, realMountPoint);
-         mountNode.mount = mount;
-         log.debugf("Created mount %s for %s on %s at mount point '%s'", mount, fileSystem, this, mountPoint);
-         return mount;
+         if (mounts.replace(parent, childMountMap, newMap)) {
+            return mount;
+         }
       }
    }
 
@@ -164,9 +157,7 @@
    {
       if (path == null)
          throw new IllegalArgumentException("Null path");
-      final List<String> realPath = PathTokenizer.applySpecialPaths(PathTokenizer.getTokens(path));
-      final String realPathString = PathTokenizer.getRemainingPath(realPath, 0);
-      return new VirtualFile(this, realPath, realPathString);
+      return rootVirtualFile.getChild(path);
    }
 
    /**
@@ -260,63 +251,75 @@
       visitor.visit(file);
    }
 
-   /**
-    * Get the enclosing mounted FileSystem for the given path.
-    *
-    * @param pathTokens the path tokens
-    * @return the filesystem
-    */
-   Mount getMount(List<String> pathTokens)
-   {
-      MountNode mountNode = rootMountNode;
-      Mount mount = mountNode.mount;
-      for (String pathToken : pathTokens)
-      {
-         final Map<String, MountNode> childMap = mountNode.nodeMap;
-         if (childMap != null) {
-            mountNode = childMap.get(pathToken);
-            final Mount subMount = mountNode.mount;
-            if (subMount != null) {
-               mount = subMount;
-            }
+   Mount getMount(VirtualFile virtualFile) {
+      final ConcurrentMap<VirtualFile, Map<String, Mount>> mounts = this.mounts;
+      for (;;) {
+         final VirtualFile parent = virtualFile.getParent();
+         if (parent == null) {
+            return rootMount;
+         }
+         final Map<String, Mount> parentMounts = mounts.get(parent);
+         if (parentMounts == null) {
+            virtualFile = parent;
          } else {
-            break;
+            final Mount mount = parentMounts.get(virtualFile.getName());
+            if (mount == null) {
+               virtualFile = parent;
+            } else {
+               return mount;
+            }
          }
       }
-      return mount;
    }
 
    /**
     * Get all immediate submounts for a path.
     *
-    * @param tokens the path tokens
+    * @param virtualFile the path
     * @return the collection of present mount (simple) names
     */
-   Iterator<String> getSubmounts(List<String> tokens)
+   Set<String> getSubmounts(VirtualFile virtualFile)
    {
-      MountNode mountNode = rootMountNode;
-      for (String pathToken : tokens)
+      final ConcurrentMap<VirtualFile, Map<String, Mount>> mounts = this.mounts;
+      final Map<String, Mount> mountMap = mounts.get(virtualFile);
+      if (mountMap == null) {
+         return emptyRemovableSet();
+      }
+      return new HashSet<String>(mountMap.keySet());
+   }
+
+   @SuppressWarnings({ "unchecked" })
+   private static <E> Set<E> emptyRemovableSet() {
+      return EMPTY_REMOVABLE_SET;
+   }
+
+   private static final Set EMPTY_REMOVABLE_SET = new EmptyRemovableSet();
+
+   private static final class EmptyRemovableSet<E> extends AbstractSet<E> {
+
+      public boolean remove(Object o)
       {
-         final Map<String, MountNode> childMap = mountNode.nodeMap;
-         if (childMap != null) {
-            mountNode = childMap.get(pathToken);
-         } else {
-            return Collections.<String>emptyList().iterator();
-         }
+         return false;
       }
-      final List<String> list = new ArrayList<String>();
-      final Map<String, MountNode> childMap = mountNode.nodeMap;
-      if (childMap == null) {
-         return Collections.<String>emptySet().iterator();
+
+      public boolean retainAll(Collection<?> c)
+      {
+         return false;
       }
-      for (Map.Entry<String, MountNode> entry : childMap.entrySet())
+
+      public void clear()
       {
-         final MountNode subNode = entry.getValue();
-         if (subNode.mount != null) {
-            list.add(entry.getKey());
-         }
       }
-      return list.iterator();
+
+      public Iterator<E> iterator()
+      {
+         return Collections.<E>emptySet().iterator();
+      }
+
+      public int size()
+      {
+         return 0;
+      }
    }
 
    /**
@@ -326,61 +329,50 @@
     */
    final class Mount implements Closeable {
       private final FileSystem fileSystem;
-      private final List<String> realMountPoint;
+      private final VirtualFile mountPoint;
 
-      private Mount(FileSystem fileSystem, List<String> realMountPoint)
+      Mount(FileSystem fileSystem, VirtualFile mountPoint)
       {
          this.fileSystem = fileSystem;
-         this.realMountPoint = realMountPoint;
+         this.mountPoint = mountPoint;
       }
 
       public void close() throws IOException
       {
-         unmountFrom(rootMountNode, realMountPoint.iterator());
-      }
-
-      private boolean unmountFrom(MountNode node, Iterator<String> iter)
-      {
-         synchronized (node) {
-            final Map<String, MountNode> nodeMap = node.nodeMap;
-            if (iter.hasNext()) {
-               if (nodeMap != null) {
-                  final String key = iter.next();
-                  final MountNode nextNode = nodeMap.get(key);
-                  if (nextNode == null) {
-                     return nodeMap.isEmpty();
-                  }
-                  final boolean emptySubNode = unmountFrom(nextNode, iter);
-                  if (emptySubNode) {
-                     final boolean otherChildren = nodeMap.size() > 1;
-                     // subnode is dead; remove it from our map
-                     if (otherChildren) {
-                        // there's other children; not dead yet
-                        final HashMap<String, MountNode> newMap = new HashMap<String, MountNode>(nodeMap);
-                        newMap.remove(key);
-                        node.nodeMap = newMap;
-                        return false;
-                     } else {
-                        // no other children; dead if there's no mount here
-                        node.nodeMap = null;
-                        return node.mount == null;
-                     }
-                  }
-                  // subnode isn't empty; not dead
-                  return false;
+         final String name = mountPoint.getName();
+         final VirtualFile parent = mountPoint.getParent();
+         final ConcurrentMap<VirtualFile, Map<String, Mount>> mounts = VFS.this.mounts;
+         for (;;) {
+            final Map<String, Mount> parentMounts = mounts.get(parent);
+            if (parentMounts == null) {
+               return;
+            }
+            final VFS.Mount mount = parentMounts.get(name);
+            if (mount != this) {
+               return;
+            }
+            final Map<String, Mount> newParentMounts;
+            if (parentMounts.size() == 2) {
+               final Iterator<Map.Entry<String, Mount>> ei = parentMounts.entrySet().iterator();
+               final Map.Entry<String, Mount> e1 = ei.next();
+               if (e1.getKey().equals(name)) {
+                  final Map.Entry<String, Mount> e2 = ei.next();
+                  newParentMounts = Collections.singletonMap(e2.getKey(), e2.getValue());
                } else {
-                  // dead node if there's no mount here
-                  return node.mount == null;
+                  newParentMounts = Collections.singletonMap(e1.getKey(), e1.getValue());
                }
+               if (mounts.replace(parent, parentMounts, newParentMounts)) {
+                  return;
+               }
+            } else if (parentMounts.size() == 1) {
+               if (mounts.remove(parent, parentMounts)) {
+                  return;
+               }
             } else {
-               if (node.mount == this) {
-                  node.mount = null;
-                  log.debugf("Unmounted %s for %s on %s", this, fileSystem, this);
-                  // the node is dead if there are no children
-                  return nodeMap == null;
-               } else {
-                  // Node must be already unmounted; do cleanup work anyway.
-                  return node.mount == null && nodeMap == null;
+               newParentMounts = new HashMap<String, Mount>(parentMounts);
+               newParentMounts.remove(name);
+               if (mounts.replace(parent, parentMounts, newParentMounts)) {
+                  return;
                }
             }
          }
@@ -391,29 +383,9 @@
          return fileSystem;
       }
 
-      List<String> getRealMountPoint()
+      VirtualFile getMountPoint()
       {
-         return realMountPoint;
+         return mountPoint;
       }
    }
-
-   /**
-    * A mount point node.  These nodes form a tree of possible mount points.
-    */
-   private static final class MountNode {
-
-      /**
-       * The immutable node map.  Since the map is immutable, changes to this field must be accomplished by replacing
-       * the field value with a new map (copy on write).  Modifications to this field are protected by {@code this}.
-       */
-      private volatile Map<String, MountNode> nodeMap;
-      /**
-       * The current mount at this point.  Modifications to this field are protected by {@code this}.
-       */
-      private volatile Mount mount;
-
-      private MountNode()
-      {
-      }
-   }
 }

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VirtualFile.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VirtualFile.java	2009-07-14 02:44:23 UTC (rev 91184)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VirtualFile.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -28,9 +28,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.Set;
-import java.util.HashSet;
 
 import org.jboss.virtual.plugins.vfs.helpers.FilterVirtualFileVisitor;
 import org.jboss.virtual.plugins.vfs.helpers.MatchAllVirtualFileFilter;
@@ -49,18 +47,21 @@
 public class VirtualFile implements Serializable
 {
    private static final long serialVersionUID = 1L;
-   private final String path;
    private final String name;
-   private final List<String> tokens;
+   private final String lcname;
    private final VFS vfs;
+   private final VirtualFile parent;
+   private final int hashCode;
 
-   VirtualFile(VFS vfs, List<String> realPath, String realPathString)
+   VirtualFile(VFS vfs, String name, VirtualFile parent)
    {
-      path = realPathString;
-      final int size = realPath.size();
-      name = size == 0 ? "" : realPath.get(size - 1);
-      tokens = realPath;
+      this.name = name;
+      lcname = name.toLowerCase();
       this.vfs = vfs;
+      this.parent = parent;
+      int result = parent == null ? vfs.hashCode() : parent.hashCode();
+      result = 31 * result + name.hashCode();
+      hashCode = result;
    }
 
    /**
@@ -73,14 +74,30 @@
       return name;
    }
 
+   public String getLowerCaseName()
+   {
+      return lcname;
+   }
+
    /**
-    * Get the VFS relative path name (org/jboss/X.java)
+    * Get the absolute VFS full path name (/xxx/yyy/foo.ear/baz.jar/org/jboss/X.java)
     *
     * @return the VFS relative path name
     */
    public String getPathName()
    {
-      return path;
+      final StringBuilder builder = new StringBuilder(160);
+      final VirtualFile parent = this.parent;
+      if (parent == null) {
+         return "/";
+      } else {
+         builder.append(parent.getPathName());
+         if (parent.parent != null) {
+            builder.append('/');
+         }
+         builder.append(name);
+      }
+      return builder.toString();
    }
 
    /**
@@ -91,9 +108,8 @@
     */
    public long getLastModified() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().getLastModified(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().getLastModified(mount.getMountPoint(), this);
    }
 
    /**
@@ -104,9 +120,8 @@
     */
    public long getSize() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().getSize(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().getSize(mount.getMountPoint(), this);
    }
 
    /**
@@ -116,9 +131,8 @@
     */
    public boolean exists() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().exists(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().exists(mount.getMountPoint(), this);
    }
 
    /**
@@ -143,9 +157,8 @@
     */
    public boolean isDirectory() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().isDirectory(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().isDirectory(mount.getMountPoint(), this);
    }
 
    /**
@@ -156,9 +169,8 @@
     */
    public InputStream openStream() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().openInputStream(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().openInputStream(mount.getMountPoint(), this);
    }
 
    /**
@@ -169,9 +181,8 @@
     */
    public boolean delete() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().delete(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().delete(mount.getMountPoint(), this);
    }
 
    /**
@@ -185,9 +196,8 @@
     */
    public File getPhysicalFile() throws IOException
    {
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      return mount.getFileSystem().getFile(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
+      final VFS.Mount mount = vfs.getMount(this);
+      return mount.getFileSystem().getFile(mount.getMountPoint(), this);
    }
 
    /**
@@ -206,19 +216,26 @@
     * @return the parent or {@code null} if there is no parent
     * @throws IOException for any problem accessing the virtual file system
     */
-   public VirtualFile getParent() throws IOException
+   public VirtualFile getParent()
    {
-      final List<String> tokens = this.tokens;
-      final String path = this.path;
-      final int size = tokens.size();
-      if (size == 0)
-         return null;
-      else if (size == 1)
-         return vfs.getRootVirtualFile();
-      else
-         return new VirtualFile(vfs, tokens.subList(0, size - 1), path.substring(0, path.lastIndexOf('/')));
+      return parent;
    }
 
+   public VirtualFile[] getParentFiles() {
+      return getParentFiles(0);
+   }
+
+   private VirtualFile[] getParentFiles(int idx) {
+      final VirtualFile[] array;
+      if (parent == null) {
+         array = new VirtualFile[idx + 1];
+      } else {
+         array = parent.getParentFiles(idx + 1);
+      }
+      array[idx] = this;
+      return array;
+   }
+
    /**
     * Get the children.  This is the combined list of real children within this directory, as well as virtual
     * children created by submounts.
@@ -228,32 +245,19 @@
     */
    public List<VirtualFile> getChildren() throws IOException
    {
-      final ArrayList<VirtualFile> list = new ArrayList<VirtualFile>();
-      for (String name : getChildrenNames())
+      final VFS.Mount mount = vfs.getMount(this);
+      final Set<String> submounts = vfs.getSubmounts(this);
+      final List<String> names = mount.getFileSystem().getDirectoryEntries(mount.getMountPoint(), this);
+      final List<VirtualFile> virtualFiles = new ArrayList<VirtualFile>(names.size() + submounts.size());
+      for (String name : names)
       {
-         list.add(getChild(name));
+         final VirtualFile child = new VirtualFile(vfs, name, this);
+         virtualFiles.add(child);
+         submounts.remove(name);
       }
-      return list;
+      return virtualFiles;
    }
 
-   private Set<String> getChildrenNames() throws IOException
-   {
-      // Add the files physically present
-      final List<String> tokens = this.tokens;
-      final VFS.Mount mount = vfs.getMount(tokens);
-      final Iterator<String> iter = mount.getFileSystem().getDirectoryEntries(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
-      final Set<String> names = new HashSet<String>();
-      while (iter.hasNext()) {
-         names.add(iter.next());
-      }
-      // Add any mounts that are logically present
-      final Iterator<String> submounts = vfs.getSubmounts(tokens);
-      while (submounts.hasNext()) {
-         names.add(submounts.next());
-      }
-      return names;
-   }
-
    /**
     * Get the children
     *
@@ -347,25 +351,31 @@
     * @throws IOException for any problem accessing the VFS
     * @throws IllegalArgumentException if the path is null
     */
-   public VirtualFile getChild(String path) throws IOException
+   public VirtualFile getChild(String path)
    {
       if (path == null)
          throw new IllegalArgumentException("Null path");
-
-      final List<String> newPathTokens = new ArrayList<String>();
-      newPathTokens.addAll(tokens);
-      PathTokenizer.getTokens(newPathTokens, path);
-      final List<String> childPath = PathTokenizer.applySpecialPaths(newPathTokens);
-      return new VirtualFile(vfs, newPathTokens, PathTokenizer.getRemainingPath(childPath, 0));
+      final List<String> pathParts = PathTokenizer.getTokens(path);
+      VirtualFile current = this;
+      for (String part : pathParts)
+      {
+         if (PathTokenizer.isCurrentToken(part)) {
+            continue;
+         } else if (PathTokenizer.isReverseToken(part)) {
+            final VirtualFile parent = current.parent;
+            current = parent == null ? current : parent;
+         } else {
+            current = new VirtualFile(vfs, part, current);
+         }
+      }
+      return current;
    }
 
-   @Override
    public String toString()
    {
-      return "Virtual file \"" + path + "\" for " + vfs;
+      return "Virtual file \"" + getPathName() + "\" for " + vfs;
    }
 
-   @Override
    public boolean equals(Object o)
    {
       if (this == o)
@@ -373,18 +383,21 @@
       if (! (o instanceof VirtualFile))
          return false;
       VirtualFile that = (VirtualFile) o;
-      if (! path.equals(that.path))
+      if (hashCode != that.hashCode)
          return false;
+      if (! name.equals(that.name))
+         return false;
       if (vfs != that.vfs)
          return false;
-      return true;
+      final VirtualFile parent = this.parent;
+      if (parent == null)
+         return that.parent == null;
+      else
+         return parent.equals(that.parent);
    }
 
-   @Override
    public int hashCode()
    {
-      int result = path.hashCode();
-      result = 31 * result + vfs.hashCode();
-      return result;
+      return hashCode;
    }
 }

Added: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterable.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterable.java	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterable.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -0,0 +1,41 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.virtual.spi;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+class EnumerationIterable<T> implements Iterable<T>
+{
+   private final Enumeration<T> entries;
+
+   public EnumerationIterable(Enumeration<T> entries)
+   {
+      this.entries = entries;
+   }
+
+   public Iterator<T> iterator()
+   {
+      return new EnumerationIterator<T>(entries);
+   }
+}

Added: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterator.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterator.java	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/EnumerationIterator.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.virtual.spi;
+
+import java.util.Iterator;
+import java.util.Enumeration;
+
+class EnumerationIterator<T> implements Iterator<T>
+{
+   private final Enumeration<T> entries;
+
+   public EnumerationIterator(Enumeration<T> entries)
+   {
+      this.entries = entries;
+   }
+
+   public boolean hasNext()
+   {
+      return entries.hasMoreElements();
+   }
+
+   public T next()
+   {
+      return entries.nextElement();
+   }
+
+   public void remove()
+   {
+      throw new UnsupportedOperationException();
+   }
+}

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/FileSystem.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/FileSystem.java	2009-07-14 02:44:23 UTC (rev 91184)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/FileSystem.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -22,6 +22,8 @@
 
 package org.jboss.virtual.spi;
 
+import org.jboss.virtual.VirtualFile;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.Closeable;
@@ -43,20 +45,22 @@
     * on the file type, the real path of the returned {@code File} may or may not bear a relationship to the virtual path
     * provided; if such a relationship is required, it must be negotiated at the time the filesystem is mounted.
     *
-    * @param pathComponents the relative path components
+    * @param mountPoint the mount point of the filesystem instance (guaranteed to be a parent of {@code target})
+    * @param target the virtual file to act upon
     * @return the file instance
     * @throws IOException if an I/O error occurs
     */
-   File getFile(List<String> pathComponents) throws IOException;
+   File getFile(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
    /**
     * Open an input stream for the file at the given relative path.
     *
-    * @param pathComponents the relative path components
+    * @param mountPoint the mount point of the filesystem instance (guaranteed to be a parent of {@code target})
+    * @param target the virtual file to act upon
     * @return the input stream
     * @throws IOException if an I/O error occurs
     */
-   InputStream openInputStream(List<String> pathComponents) throws IOException;
+   InputStream openInputStream(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
    /**
     * Determine whether this filesystem is read-only.  A read-only filesystem prohibits file modification or
@@ -67,24 +71,25 @@
     */
    boolean isReadOnly();
 
-   boolean delete(List<String> pathComponents) throws IOException;
+   boolean delete(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
-   long getSize(List<String> pathComponents) throws IOException;
+   long getSize(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
-   long getLastModified(List<String> pathComponents) throws IOException;
+   long getLastModified(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
-   boolean exists(List<String> pathComponents) throws IOException;
+   boolean exists(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
-   boolean isDirectory(List<String> pathComponents) throws IOException;
+   boolean isDirectory(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
    /**
     * Read a directory.  Returns all the simple path names (excluding "." and "..").
     *
-    * @param directoryPathComponents the relative path components for the directory
-    * @return the directory entries, or {@code null} if the specified path does not refer to a directory
+    * @param mountPoint the mount point of the filesystem instance (guaranteed to be a parent of {@code target})
+    * @param target the virtual file to act upon
+    * @return the collection of children names
     * @throws IOException if an I/O error occurs
     */
-   Iterator<String> getDirectoryEntries(List<String> directoryPathComponents) throws IOException;
+   List<String> getDirectoryEntries(VirtualFile mountPoint, VirtualFile target) throws IOException;
 
    /**
     * Destroy this filesystem instance.  After this method is called, the filesystem may not be used in any way.  This

Copied: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JZipFileSystem.java (from rev 91123, projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java)
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JZipFileSystem.java	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JZipFileSystem.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -0,0 +1,264 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.virtual.spi;
+
+import org.jboss.jzipfile.ZipEntry;
+import org.jboss.jzipfile.Zip;
+import org.jboss.jzipfile.ZipCatalog;
+import org.jboss.jzipfile.ZipEntryType;
+import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
+import org.jboss.virtual.VFSUtils;
+import org.jboss.virtual.TempFileProvider;
+import org.jboss.virtual.VirtualFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.BufferedOutputStream;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+/**
+ * {@inheritDoc}
+ * <p/>
+ * This implementation is backed by a zip file.  The provided file must be owned by this instance; otherwise, if the file
+ * disappears unexpectedly, the filesystem will malfunction.
+ */
+public final class JZipFileSystem implements FileSystem
+{
+   private final File zipFile;
+   private final long zipTime;
+   private final ZipNode rootNode;
+   private final TempFileProvider tempFileProvider;
+
+   /**
+    * Create a new instance.
+    *
+    * @param name the name of the source archive
+    * @param inputStream an input stream from the source archive
+    * @param tempFileProvider the temp file provider to use
+    * @throws java.io.IOException if an I/O error occurs
+    */
+   public JZipFileSystem(String name, InputStream inputStream, TempFileProvider tempFileProvider) throws IOException {
+      this(tempFileProvider.createTempFile(name, name.hashCode(), inputStream), tempFileProvider);
+   }
+
+   public JZipFileSystem(File zipFile, TempFileProvider tempFileProvider) throws IOException
+   {
+      zipTime = zipFile.lastModified();
+      this.tempFileProvider = tempFileProvider;
+      this.zipFile = zipFile;
+      final ZipCatalog catalog = Zip.readCatalog(zipFile);
+      final ZipNode rootNode = new ZipNode(new HashMap<String, ZipNode>(), "", null);
+      FILES: for (ZipEntry entry : catalog.allEntries())
+      {
+         final String name = entry.getName();
+         final boolean isDirectory = entry.getEntryType() == ZipEntryType.DIRECTORY;
+         final List<String> tokens = PathTokenizer.getTokens(name);
+         ZipNode node = rootNode;
+         final Iterator<String> it = tokens.iterator();
+         while (it.hasNext())
+         {
+            String token = it.next();
+            if (PathTokenizer.isCurrentToken(token) || PathTokenizer.isReverseToken(token)) {
+               // invalid file name
+               continue FILES;
+            }
+            final Map<String, ZipNode> children = node.children;
+            if (children == null) {
+               // todo - log bad zip entry
+               continue FILES;
+            }
+            ZipNode child = children.get(token.toLowerCase());
+            if (child == null)
+            {
+               child = it.hasNext() || isDirectory ? new ZipNode(new HashMap<String, ZipNode>(), token, null) : new ZipNode(null, token, entry);
+               children.put(token.toLowerCase(), child);
+            }
+            node = child;
+         }
+      }
+      this.rootNode = rootNode;
+   }
+
+   public File getFile(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+
+      // check if we have cached one already
+      File cachedFile = zipNode.cachedFile;
+      if (cachedFile != null) {
+         return cachedFile;
+      }
+      synchronized (zipNode) {
+         // double-check
+         cachedFile = zipNode.cachedFile;
+         if (cachedFile != null) {
+            return cachedFile;
+         }
+
+         // nope, create a cached temp
+         final ZipEntry entry = getNodeEntry(zipNode);
+         final String name = entry.getName();
+         cachedFile = tempFileProvider.createTempFile(name, entry.hashCode());
+         VFSUtils.copyStreamAndClose(Zip.openEntry(zipFile, entry), new BufferedOutputStream(new FileOutputStream(cachedFile)));
+         zipNode.cachedFile = cachedFile;
+         return cachedFile;
+      }
+   }
+
+   public InputStream openInputStream(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      if (cachedFile != null) {
+         return new FileInputStream(cachedFile);
+      }
+      final ZipEntry entry = zipNode.entry;
+      if (entry == null) {
+         throw new IOException("Not a file: \"" + target.getPathName() + "\"");
+      }
+      return Zip.openEntry(zipFile, entry);
+   }
+
+   public boolean delete(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      return cachedFile != null && cachedFile.delete();
+   }
+
+   public long getSize(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      final ZipEntry entry = zipNode.entry;
+      return cachedFile != null ? cachedFile.length() : entry == null ? 0L : entry.getSize();
+   }
+
+   public long getLastModified(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      final ZipEntry entry = zipNode.entry;
+      return cachedFile != null ? cachedFile.lastModified() : entry == null ? zipTime : entry.getModificationTime();
+   }
+
+   public boolean exists(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = rootNode.find(mountPoint, target);
+      if (zipNode == null) {
+         return false;
+      } else {
+         final File cachedFile = zipNode.cachedFile;
+         return cachedFile == null || cachedFile.exists();
+      }
+   }
+
+   public boolean isDirectory(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = rootNode.find(mountPoint, target);
+      return zipNode != null && zipNode.entry == null;
+   }
+
+   public List<String> getDirectoryEntries(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final Map<String, ZipNode> children = zipNode.children;
+      final Collection<ZipNode> values = children.values();
+      final List<String> names = new ArrayList<String>(values.size());
+      for (ZipNode node : values)
+      {
+         names.add(node.name);
+      }
+      return names;
+   }
+
+   private ZipEntry getNodeEntry(ZipNode zipNode)
+         throws IOException
+   {
+      final ZipEntry entry = zipNode.entry;
+      if (entry == null) {
+         throw new IOException("Cannot call this operation on a directory");
+      }
+      return entry;
+   }
+
+   private ZipNode getExistingZipNode(VirtualFile mountPoint, VirtualFile target)
+         throws FileNotFoundException
+   {
+      final ZipNode zipNode = rootNode.find(mountPoint, target);
+      if (zipNode == null) {
+         throw new FileNotFoundException(target.getPathName());
+      }
+      return zipNode;
+   }
+
+   public boolean isReadOnly()
+   {
+      return true;
+   }
+
+   public void close() throws IOException
+   {
+      tempFileProvider.close();
+   }
+
+   private static final class ZipNode {
+      // immutable child map
+      private final Map<String, ZipNode> children;
+      private final String name;
+      private final ZipEntry entry;
+      private volatile File cachedFile;
+
+      private ZipNode(Map<String, ZipNode> children, String name, ZipEntry entry)
+      {
+         this.children = children;
+         this.name = name;
+         this.entry = entry;
+      }
+
+      private ZipNode find(VirtualFile mountPoint, VirtualFile target) {
+         if (mountPoint.equals(target)) {
+            return this;
+         } else {
+            final ZipNode parent = find(mountPoint, target.getParent());
+            if (parent == null) {
+               return null;
+            }
+            final Map<String, ZipNode> children = parent.children;
+            if (children == null) {
+               return null;
+            }
+            return children.get(target.getLowerCaseName());
+         }
+      }
+   }
+}

Added: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JavaZipFileSystem.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JavaZipFileSystem.java	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/JavaZipFileSystem.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -0,0 +1,276 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.virtual.spi;
+
+import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
+import org.jboss.virtual.VFSUtils;
+import org.jboss.virtual.TempFileProvider;
+import org.jboss.virtual.VirtualFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.Closeable;
+import java.io.BufferedOutputStream;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Enumeration;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * {@inheritDoc}
+ * <p/>
+ * This implementation is backed by a zip file.  The provided file must be owned by this instance; otherwise, if the file
+ * disappears unexpectedly, the filesystem will malfunction.
+ */
+public final class JavaZipFileSystem implements FileSystem
+{
+   private final ZipFile zipFile;
+   private final long zipTime;
+   private final ZipNode rootNode;
+   private final TempFileProvider tempFileProvider;
+
+   /**
+    * Create a new instance.
+    *
+    * @param name the name of the source archive
+    * @param inputStream an input stream from the source archive
+    * @param tempFileProvider the temp file provider to use
+    * @throws java.io.IOException if an I/O error occurs
+    */
+   public JavaZipFileSystem(String name, InputStream inputStream, TempFileProvider tempFileProvider) throws IOException {
+      this(tempFileProvider.createTempFile(name, name.hashCode(), inputStream), tempFileProvider);
+   }
+
+   public JavaZipFileSystem(File file, TempFileProvider tempFileProvider) throws IOException
+   {
+      zipTime = file.lastModified();
+      final ZipFile zipFile;
+      this.zipFile = zipFile = new ZipFile(file);
+      this.tempFileProvider = tempFileProvider;
+      final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+      final ZipNode rootNode = new ZipNode(new HashMap<String, ZipNode>(), "", null);
+      FILES: for (ZipEntry entry : iter(entries))
+      {
+         final String name = entry.getName();
+         final boolean isDirectory = entry.isDirectory();
+         final List<String> tokens = PathTokenizer.getTokens(name);
+         ZipNode node = rootNode;
+         final Iterator<String> it = tokens.iterator();
+         while (it.hasNext())
+         {
+            String token = it.next();
+            if (PathTokenizer.isCurrentToken(token) || PathTokenizer.isReverseToken(token)) {
+               // invalid file name
+               continue FILES;
+            }
+            final Map<String, ZipNode> children = node.children;
+            if (children == null) {
+               // todo - log bad zip entry
+               continue FILES;
+            }
+            final String lcToken = token.toLowerCase();
+            ZipNode child = children.get(lcToken);
+            if (child == null)
+            {
+               child = it.hasNext() || isDirectory ? new ZipNode(new HashMap<String, ZipNode>(), token, null) : new ZipNode(null, token, entry);
+               children.put(lcToken, child);
+            }
+            node = child;
+         }
+      }
+      this.rootNode = rootNode;
+   }
+
+   private static <T> Iterable<T> iter(final Enumeration<T> entries)
+   {
+      return new EnumerationIterable<T>(entries);
+   }
+
+   public File getFile(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+
+      // check if we have cached one already
+      File cachedFile = zipNode.cachedFile;
+      if (cachedFile != null) {
+         return cachedFile;
+      }
+      synchronized (zipNode) {
+         // double-check
+         cachedFile = zipNode.cachedFile;
+         if (cachedFile != null) {
+            return cachedFile;
+         }
+
+         // nope, create a cached temp
+         final ZipEntry zipEntry = getNodeEntry(zipNode);
+         final String name = zipEntry.getName();
+         cachedFile = tempFileProvider.createTempFile(name, zipEntry.hashCode());
+         VFSUtils.copyStreamAndClose(zipFile.getInputStream(zipEntry), new BufferedOutputStream(new FileOutputStream(cachedFile)));
+         zipNode.cachedFile = cachedFile;
+         return cachedFile;
+      }
+   }
+
+   public InputStream openInputStream(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      if (cachedFile != null) {
+         return new FileInputStream(cachedFile);
+      }
+      final ZipEntry entry = zipNode.entry;
+      if (entry == null) {
+         throw new IOException("Not a file: \"" + target.getPathName() + "\"");
+      }
+      return zipFile.getInputStream(entry);
+   }
+
+   public boolean delete(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      return cachedFile != null && cachedFile.delete();
+   }
+
+   public long getSize(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      final ZipEntry entry = zipNode.entry;
+      return cachedFile != null ? cachedFile.length() : entry == null ? 0L : entry.getSize();
+   }
+
+   public long getLastModified(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final File cachedFile = zipNode.cachedFile;
+      final ZipEntry entry = zipNode.entry;
+      return cachedFile != null ? cachedFile.lastModified() : entry == null ? zipTime : entry.getTime();
+   }
+
+   public boolean exists(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = rootNode.find(mountPoint, target);
+      if (zipNode == null) {
+         return false;
+      } else {
+         final File cachedFile = zipNode.cachedFile;
+         return cachedFile == null || cachedFile.exists();
+      }
+   }
+
+   public boolean isDirectory(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = rootNode.find(mountPoint, target);
+      return zipNode != null && zipNode.entry == null;
+   }
+
+   public List<String> getDirectoryEntries(VirtualFile mountPoint, VirtualFile target) throws IOException
+   {
+      final ZipNode zipNode = getExistingZipNode(mountPoint, target);
+      final Map<String, ZipNode> children = zipNode.children;
+      final Collection<ZipNode> values = children.values();
+      final List<String> names = new ArrayList<String>(values.size());
+      for (ZipNode node : values)
+      {
+         names.add(node.name);
+      }
+      return names;
+   }
+
+   private ZipEntry getNodeEntry(ZipNode zipNode)
+         throws IOException
+   {
+      final ZipEntry entry = zipNode.entry;
+      if (entry == null) {
+         throw new IOException("Cannot call this operation on a directory");
+      }
+      return entry;
+   }
+
+   private ZipNode getExistingZipNode(VirtualFile mountPoint, VirtualFile target)
+         throws FileNotFoundException
+   {
+      final ZipNode zipNode = rootNode.find(mountPoint, target);
+      if (zipNode == null) {
+         throw new FileNotFoundException(target.getPathName());
+      }
+      return zipNode;
+   }
+
+   public boolean isReadOnly()
+   {
+      return true;
+   }
+
+   public void close() throws IOException
+   {
+      VFSUtils.safeClose(new Closeable() {
+         public void close() throws IOException {
+            zipFile.close();
+         }
+      });
+      tempFileProvider.close();
+   }
+
+   private static final class ZipNode {
+      // immutable child map
+      private final Map<String, ZipNode> children;
+      private final String name;
+      private final ZipEntry entry;
+      private volatile File cachedFile;
+
+      private ZipNode(Map<String, ZipNode> children, String name, ZipEntry entry)
+      {
+         this.children = children;
+         this.name = name;
+         this.entry = entry;
+      }
+
+      private ZipNode find(VirtualFile mountPoint, VirtualFile target) {
+         if (mountPoint.equals(target)) {
+            return this;
+         } else {
+            final ZipNode parent = find(mountPoint, target.getParent());
+            if (parent == null) {
+               return null;
+            }
+            final Map<String, ZipNode> children = parent.children;
+            if (children == null) {
+               return null;
+            }
+            return children.get(target.getLowerCaseName());
+         }
+      }
+   }
+}
\ No newline at end of file

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/RealFileSystem.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/RealFileSystem.java	2009-07-14 02:44:23 UTC (rev 91184)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/RealFileSystem.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -22,91 +22,75 @@
 
 package org.jboss.virtual.spi;
 
+import org.jboss.virtual.VirtualFile;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.FileInputStream;
 import java.util.List;
-import java.util.Iterator;
 import java.util.Arrays;
-import java.util.Collections;
 
 public final class RealFileSystem implements FileSystem
 {
-   public static final RealFileSystem ROOT_INSTANCE = new RealFileSystem(Collections.<String>emptyList());
+   public static final RealFileSystem ROOT_INSTANCE = new RealFileSystem(new File(""));
 
-   private final String base;
+   private final File realRoot;
 
-   private RealFileSystem(List<String> baseComponents)
+   private RealFileSystem(File realRoot)
    {
-      base = implode("", baseComponents) + File.separator;
+      this.realRoot = realRoot;
    }
 
-   public File getFile(List<String> pathComponents) throws IOException
+   public InputStream openInputStream(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return new File(implode(base, pathComponents));
+      return new FileInputStream(getFile(mountPoint, target));
    }
 
-   private static String implode(String base, List<String> pathComponents)
+   public boolean isReadOnly()
    {
-      int l = 0;
-      for (String s : pathComponents)
-      {
-         l += s.length() + 1;
-      }
-      if (l == 0) {
-         return base;
-      }
-      final StringBuilder builder = new StringBuilder(l + base.length());
-      builder.append(base);
-      for (String s : pathComponents)
-      {
-         builder.append(File.separatorChar);
-         builder.append(s);
-      }
-      return builder.toString();
+      return false;
    }
 
-   public InputStream openInputStream(List<String> pathComponents) throws IOException
+   public File getFile(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return new FileInputStream(getFile(pathComponents));
+      if (mountPoint == target) {
+         return realRoot;
+      } else {
+         return new File(getFile(mountPoint, target.getParent()), target.getName());
+      }
    }
 
-   public boolean isReadOnly()
+   public boolean delete(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return false;
+      return getFile(mountPoint, target).delete();
    }
 
-   public boolean delete(List<String> pathComponents) throws IOException
+   public long getSize(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return getFile(pathComponents).delete();
+      return getFile(mountPoint, target).length();
    }
 
-   public long getSize(List<String> pathComponents) throws IOException
+   public long getLastModified(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return getFile(pathComponents).length();
+      return getFile(mountPoint, target).lastModified();
    }
 
-   public long getLastModified(List<String> pathComponents) throws IOException
+   public boolean exists(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return getFile(pathComponents).lastModified();
+      return getFile(mountPoint, target).exists();
    }
 
-   public boolean exists(List<String> pathComponents) throws IOException
+   public boolean isDirectory(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return getFile(pathComponents).exists();
+      return getFile(mountPoint, target).isDirectory();
    }
 
-   public boolean isDirectory(List<String> pathComponents) throws IOException
+   public List<String> getDirectoryEntries(VirtualFile mountPoint, VirtualFile target) throws IOException
    {
-      return getFile(pathComponents).isDirectory();
+      return Arrays.asList(getFile(mountPoint, target).list());
    }
 
-   public Iterator<String> getDirectoryEntries(List<String> directoryPathComponents) throws IOException
-   {
-      return Arrays.asList(getFile(directoryPathComponents).list()).iterator();
-   }
-
    public void close() throws IOException
    {
       // no operation - the real FS can't be closed

Deleted: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java	2009-07-14 02:44:23 UTC (rev 91184)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java	2009-07-14 04:56:39 UTC (rev 91185)
@@ -1,278 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2009, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
-package org.jboss.virtual.spi;
-
-import org.jboss.jzipfile.ZipEntry;
-import org.jboss.jzipfile.Zip;
-import org.jboss.jzipfile.ZipCatalog;
-import org.jboss.jzipfile.ZipEntryType;
-import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
-import org.jboss.virtual.VFSUtils;
-import org.jboss.virtual.TempFileProvider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileInputStream;
-import java.util.List;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Collection;
-import java.util.HashMap;
-
-/**
- * {@inheritDoc}
- * <p/>
- * This implementation is backed by a zip file.  The provided file must be owned by this instance; otherwise, if the file
- * disappears unexpectedly, the filesystem will malfunction.
- */
-public final class ZipFileSystem implements FileSystem
-{
-   private final File zipFile;
-   private final ZipNode rootNode;
-   private final TempFileProvider tempFileProvider;
-
-   /**
-    * Create a new instance.
-    *
-    * @param name the name of the source archive
-    * @param inputStream an input stream from the source archive
-    * @param tempFileProvider the temp file provider to use
-    * @throws IOException if an I/O error occurs
-    */
-   public ZipFileSystem(String name, InputStream inputStream, TempFileProvider tempFileProvider) throws IOException {
-      this(tempFileProvider.createTempFile(name, name.hashCode(), inputStream), tempFileProvider);
-   }
-
-   public ZipFileSystem(File zipFile, TempFileProvider tempFileProvider) throws IOException
-   {
-      this.zipFile = zipFile;
-      this.tempFileProvider = tempFileProvider;
-      final ZipCatalog catalog = Zip.readCatalog(zipFile);
-      final Collection<ZipEntry> entries = catalog.allEntries();
-      final ZipNode rootNode = new ZipNode(new HashMap<String, ZipNode>(), null, "");
-      FILES: for (ZipEntry zipEntry : entries)
-      {
-         final List<String> tokens = PathTokenizer.getTokens(zipEntry.getName());
-         ZipNode node = rootNode;
-         final Iterator<String> it = tokens.iterator();
-         while (it.hasNext())
-         {
-            String token = it.next();
-            final Map<String, ZipNode> children = node.children;
-            if (children == null) {
-               // todo - log bad zip entry
-               continue FILES;
-            }
-            ZipNode child = children.get(token.toLowerCase());
-            if (child == null)
-            {
-               child = it.hasNext() || zipEntry.getEntryType() == ZipEntryType.DIRECTORY ? new ZipNode(new HashMap<String, ZipNode>(), null, token) : new ZipNode(null, zipEntry, token);
-               children.put(token.toLowerCase(), child);
-            }
-            node = child;
-         }
-      }
-      this.rootNode = rootNode;
-   }
-
-   public File getFile(List<String> pathComponents) throws IOException
-   {
-      final ZipNode zipNode = getExistingZipNode(pathComponents);
-
-      // check if we have cached one already
-      File cachedFile = zipNode.cachedFile;
-      if (cachedFile != null) {
-         return cachedFile;
-      }
-      synchronized (zipNode) {
-         // double-check
-         cachedFile = zipNode.cachedFile;
-         if (cachedFile != null) {
-            return cachedFile;
-         }
-
-         // nope, create a cached temp
-         final ZipEntry zipEntry = getNodeEntry(zipNode);
-         final String name = zipEntry.getName();
-         cachedFile = tempFileProvider.createTempFile(name, zipEntry.hashCode());
-         VFSUtils.copyStreamAndClose(Zip.openEntry(zipFile, zipEntry), new FileOutputStream(cachedFile));
-         zipNode.cachedFile = cachedFile;
-         return cachedFile;
-      }
-   }
-
-   public InputStream openInputStream(List<String> pathComponents) throws IOException
-   {
-      final ZipNode zipNode = getExistingZipNode(pathComponents);
-      final File cachedFile = zipNode.cachedFile;
-      if (cachedFile != null) {
-         return new FileInputStream(cachedFile);
-      }
-      final ZipEntry entry = getNodeEntry(zipNode);
-      return Zip.openEntry(zipFile, entry);
-   }
-
-   private ZipEntry getFileEntry(List<String> pathComponents)
-         throws IOException
-   {
-      final ZipNode zipNode = getExistingZipNode(pathComponents);
-      return getNodeEntry(zipNode);
-   }
-
-   private ZipEntry getNodeEntry(ZipNode zipNode)
-         throws IOException
-   {
-      final ZipEntry entry = zipNode.entry;
-      if (entry == null) {
-         throw new IOException("Cannot call this operation on a directory");
-      }
-      return entry;
-   }
-
-   private ZipNode getExistingZipNode(List<String> pathComponents)
-         throws FileNotFoundException
-   {
-      final ZipNode zipNode = rootNode.find(pathComponents.iterator());
-      if (zipNode == null) {
-         throw new FileNotFoundException(join(pathComponents));
-      }
-      return zipNode;
-   }
-
-   private static String join(List<String> pathComponents)
-   {
-      int l = 0;
-      for (String pathComponent : pathComponents)
-      {
-         l += pathComponent.length();
-      }
-      final StringBuilder sb = new StringBuilder(l);
-      for (String pathComponent : pathComponents)
-      {
-         sb.append('/');
-         sb.append(pathComponent);
-      }
-      return sb.toString();
-   }
-
-   public boolean isReadOnly()
-   {
-      return true;
-   }
-
-   public boolean delete(List<String> pathComponents) throws IOException
-   {
-      return false;
-   }
-
-   public long getSize(List<String> pathComponents) throws IOException
-   {
-      final ZipEntry entry = getFileEntry(pathComponents);
-      return entry.getSize();
-   }
-
-   public long getLastModified(List<String> pathComponents) throws IOException
-   {
-      final ZipNode zipNode = getExistingZipNode(pathComponents);
-      final ZipEntry entry = zipNode.entry;
-      if (entry != null) {
-         return entry.getModificationTime();
-      } else {
-         return 0L;
-      }
-   }
-
-   public boolean exists(List<String> pathComponents) throws IOException
-   {
-      return rootNode.find(pathComponents.iterator()) != null;
-   }
-
-   public boolean isDirectory(List<String> pathComponents) throws IOException
-   {
-      final ZipNode zipNode = rootNode.find(pathComponents.iterator());
-      return zipNode != null && zipNode.children != null;
-   }
-
-   public Iterator<String> getDirectoryEntries(List<String> directoryPathComponents) throws IOException
-   {
-      final ZipNode zipNode = rootNode.find(directoryPathComponents.iterator());
-      if (zipNode != null) {
-         final Map<String, ZipNode> children = zipNode.children;
-         if (children != null) {
-            final Iterator<ZipNode> it = children.values().iterator();
-            return new Iterator<String>()
-            {
-               public boolean hasNext()
-               {
-                  return it.hasNext();
-               }
-
-               public String next()
-               {
-                  return it.next().name;
-               }
-
-               public void remove()
-               {
-                  throw new UnsupportedOperationException();
-               }
-            };
-         }
-      }
-      throw new FileNotFoundException(join(directoryPathComponents));
-   }
-
-   public void close() throws IOException
-   {
-      tempFileProvider.close();
-   }
-
-   private static final class ZipNode {
-      private final Map<String, ZipNode> children;
-      private final ZipEntry entry;
-      private final String name;
-      private volatile File cachedFile;
-
-      private ZipNode(Map<String, ZipNode> children, ZipEntry entry, String name)
-      {
-         this.children = children;
-         this.entry = entry;
-         this.name = name;
-      }
-
-      private ZipNode find(Iterator<String> node) {
-         if (node.hasNext())
-         {
-            final ZipNode next = children.get(node.next().toLowerCase());
-            return next == null ? null : next.find(node);
-         }
-         else
-         {
-            return this;
-         }
-      }
-   }
-}




More information about the jboss-cvs-commits mailing list