[jboss-cvs] JBossAS SVN: r90753 - in projects/vfs/branches/dml-zip-rework: src/main/java/org/jboss/virtual and 5 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Wed Jul 1 21:28:50 EDT 2009


Author: david.lloyd at jboss.com
Date: 2009-07-01 21:28:49 -0400 (Wed, 01 Jul 2009)
New Revision: 90753

Added:
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/LongestMatchComparator.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/TempFileProvider.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/
   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
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java
Removed:
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/AssembledDirectory.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/MemoryFileFactory.java
Modified:
   projects/vfs/branches/dml-zip-rework/pom.xml
   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/VFSUtils.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/plugins/vfs/helpers/PathTokenizer.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfs/Handler.java
   projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfsmemory/Handler.java
   projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/AssembledContextTestCase.java
   projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java
   projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/MemoryTestCase.java
Log:
Simplified implementation

Modified: projects/vfs/branches/dml-zip-rework/pom.xml
===================================================================
--- projects/vfs/branches/dml-zip-rework/pom.xml	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/pom.xml	2009-07-02 01:28:49 UTC (rev 90753)
@@ -8,7 +8,7 @@
   <groupId>org.jboss</groupId>
   <artifactId>jboss-vfs</artifactId>
   <packaging>jar</packaging>
-  <version>2.2.0-SNAPSHOT</version>
+  <version>3.0.0-SNAPSHOT</version>
   <name>JBoss VFS</name>
   <url>http://www.jboss.org</url>
   <description>A VFS library</description>

Deleted: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/AssembledDirectory.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/AssembledDirectory.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/AssembledDirectory.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -1,590 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2006, 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;
-
-import java.io.IOException;
-import java.net.URL;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.jboss.virtual.plugins.context.jar.JarUtils;
-import org.jboss.virtual.plugins.vfs.helpers.FilterVirtualFileVisitor;
-import org.jboss.virtual.plugins.vfs.helpers.SuffixesExcludeFilter;
-
-/**
- * Extension of VirtualFile that represents a virtual directory that can be composed of arbitrary files and resources
- * spread throughout the file system or embedded in jar files.
- *
- * @author <a href="bill at jboss.com">Bill Burke</a>
- * @author <a href="ales.justin at jboss.com">Ales Justin</a>
- * @version $Revision: 1.1 $
- */
-public class AssembledDirectory extends VirtualFile
-{
-   /** No jars file filter */
-   private static final VirtualFileFilter noJars = new SuffixesExcludeFilter(JarUtils.getSuffixes());
-   /** The directory */
-   private AssembledDirectoryHandler directory;
-
-   public AssembledDirectory(AssembledDirectoryHandler handler)
-   {
-      super(handler);
-      directory = handler;
-   }
-
-   /**
-    * Create assembled directory.
-    *
-    * @param name context's name
-    * @param rootName root name
-    * @return new assembled directory instance
-    * @throws IOException for any IO error
-    * @throws URISyntaxException for any URI error
-    */
-   public static AssembledDirectory createAssembledDirectory(String name, String rootName) throws IOException, URISyntaxException
-   {
-      AssembledContext context = new AssembledContext(name, rootName);
-      return context.getRoot().getVirtualFile();
-   }
-
-   /**
-    * Add files recursively from root, using the no jars filter.
-    *
-    * @param root the root
-    * @throws IOException for any error
-    */
-   public void addPath(VirtualFile root) throws IOException
-   {
-      addPath(root, noJars);
-   }
-
-   /**
-    * Add files recursively from root, using the filter.
-    *
-    * @param root the root
-    * @param recurseFilter the recurse filter
-    * @throws IOException for any error
-    */
-   public void addPath(VirtualFile root, VirtualFileFilter recurseFilter) throws IOException
-   {
-      final VisitorAttributes va = new VisitorAttributes();
-      va.setLeavesOnly(true);
-      va.setRecurseFilter(recurseFilter);
-
-      VirtualFileVisitor visitor = new VirtualFileVisitor()
-      {
-         public VisitorAttributes getAttributes()
-         {
-            return va;
-         }
-
-         public void visit(VirtualFile virtualFile)
-         {
-           mkdirs(virtualFile.getPathName()).addChild(virtualFile);
-         }
-      };
-      root.visit(visitor);
-   }
-
-   /**
-    * Find the underlying .class file representing this class and create it within this directory, along with
-    * its packages.
-    *
-    * So, if you added com.acme.Customer class, then a directory structure com/acme would be created
-    * and an entry in the acme directory would be the .class file.
-    *
-    * @param clazz the class
-    */
-   public void addClass(Class<?> clazz)
-   {
-      if (clazz == null)
-         throw new IllegalArgumentException("Null clazz");
-
-      addClass(clazz.getName(), clazz.getClassLoader());
-   }
-
-   /**
-    * Find the underlying .class file representing this class and create it within this directory, along with
-    * its packages.
-    *
-    * So, if you added com.acme.Customer class, then a directory structure com/acme would be created
-    * and an entry in the acme directory would be the .class file.
-    *
-    * @param className the class name
-    */
-   public void addClass(String className)
-   {
-      addClass(className, Thread.currentThread().getContextClassLoader());
-   }
-
-   /**
-    * Find the underlying .class file representing this class and create it within this directory, along with
-    * its packages.
-    *
-    * So, if you added com.acme.Customer class, then a directory structure com/acme would be created
-    * and an entry in the acme directory would be the .class file.
-    *
-    * @param className the class name
-    * @param loader ClassLoader to look for class resource
-    */
-   public void addClass(String className, ClassLoader loader)
-   {
-      if (className == null)
-         throw new IllegalArgumentException("Null className");
-      if (loader == null)
-         throw new IllegalArgumentException("Null loader");
-
-      String resource = className.replace('.', '/') + ".class";
-      URL url = loader.getResource(resource);
-      if (url == null)
-         throw new RuntimeException("Could not find resource: " + resource);
-
-      AssembledDirectory p = mkdirs(resource);
-      p.addResource(resource, loader);
-   }
-
-   /**
-    * Make any directories for the give path to a file.
-    *
-    * @param path must be a path to a file as last element in path does not have a directory created
-    * @return directory file will live in
-    */
-   public AssembledDirectory mkdirs(String path)
-   {
-      if (path == null)
-         throw new IllegalArgumentException("Null path");
-
-      String[] pkgs = path.split("/");
-      AssembledDirectoryHandler dir = directory;
-      for (int i = 0; i < pkgs.length - 1; i++)
-      {
-         AssembledDirectoryHandler next = (AssembledDirectoryHandler) dir.findChild(pkgs[i]);
-         if (next == null)
-         {
-            try
-            {
-               next = new AssembledDirectoryHandler(dir.getVFSContext(), dir, pkgs[i]);
-            }
-            catch (IOException e)
-            {
-               throw new RuntimeException(e);
-            }
-            dir.addChild(next);
-         }
-         dir = next;
-      }
-      return dir.getVirtualFile();
-   }
-
-   /**
-    * Locate the .class resource of baseResource.  From this resource, determine the base of the resource
-    * i.e. what jar or classpath directory it lives in.
-    *
-    * Once the base of the resource is found, scan all files recursively within the base using the include and exclude
-    * patterns.  A mirror file structure will be created within this AssembledDirectory. Ths is very useful when you
-    * want to create a virtual jar that contains a subset of .class files in your classpath.
-    *
-    * The include/exclude patterns follow the Ant file pattern matching syntax.  See ant.apache.org for more details.
-    *
-    * @param baseResource the base resource
-    * @param includes the includes
-    * @param excludes the excludes
-    */
-   public void addResources(Class<?> baseResource, String[] includes, String[] excludes)
-   {
-      if (baseResource == null)
-         throw new IllegalArgumentException("Null base resource");
-
-      String resource = baseResource.getName().replace('.', '/') + ".class";
-      addResources(resource, includes, excludes, baseResource.getClassLoader());
-   }
-
-   /**
-    * From the baseResource, determine the base of that resource
-    * i.e. what jar or classpath directory it lives in.  The Thread.currentThread().getContextClassloader() will be used
-    *
-    * Once the base of the resource is found, scan all files recursively within the base using the include and exclude
-    * patterns.  A mirror file structure will be created within this AssembledDirectory. Ths is very useful when you
-    * want to create a virtual jar that contains a subset of .class files in your classpath.
-    *
-    * The include/exclude patterns follow the Ant file pattern matching syntax.  See ant.apache.org for more details.
-    *
-    * @param baseResource the base resource
-    * @param includes the includes
-    * @param excludes the excludes
-    */
-   public void addResources(String baseResource, final String[] includes, final String[] excludes)
-   {
-      if (baseResource == null)
-         throw new IllegalArgumentException("Null base resource");
-
-      addResources(baseResource, includes, excludes, Thread.currentThread().getContextClassLoader());   
-   }
-
-   /**
-    * From the baseResource, determine the base of that resource
-    * i.e. what jar or classpath directory it lives in.  The loader parameter will be used to find the resource.
-    *
-    * Once the base of the resource is found, scan all files recursively within the base using the include and exclude
-    * patterns.  A mirror file structure will be created within this AssembledDirectory. Ths is very useful when you
-    * want to create a virtual jar that contains a subset of .class files in your classpath.
-    *
-    * The include/exclude patterns follow the Ant file pattern matching syntax.  See ant.apache.org for more details.
-    *
-    * @param baseResource the base resource
-    * @param includes the includes
-    * @param excludes the excludes
-    * @param loader the loader
-    */
-   public void addResources(String baseResource, final String[] includes, final String[] excludes, ClassLoader loader)
-   {
-      if (baseResource == null)
-         throw new IllegalArgumentException("Null baseResource");
-      if (loader == null)
-         throw new IllegalArgumentException("Null loader");
-
-      URL url = loader.getResource(baseResource);
-      if (url == null)
-         throw new RuntimeException("Could not find baseResource: " + baseResource);
-
-      String urlString = url.toString();
-      int idx = urlString.lastIndexOf(baseResource);
-      urlString = urlString.substring(0, idx);
-      try
-      {
-         url = new URL(urlString);
-         VirtualFile parent = VFS.getRoot(url);
-
-         VisitorAttributes va = new VisitorAttributes();
-         va.setLeavesOnly(true);
-         va.setRecurseFilter(noJars);
-
-         VirtualFileFilter filter = new VirtualFileFilter()
-         {
-            public boolean accepts(VirtualFile file)
-            {
-               boolean matched = false;
-               String path = file.getPathName();
-               for (String include : includes)
-               {
-                  if (antMatch(path, include))
-                  {
-                     matched = true;
-                     break;
-                  }
-               }
-               if (matched == false)
-                  return false;
-               if (excludes != null)
-               {
-                  for (String exclude : excludes)
-                  {
-                     if (antMatch(path, exclude))
-                        return false;
-                  }
-               }
-               return true;
-            }
-         };
-
-         FilterVirtualFileVisitor visitor = new FilterVirtualFileVisitor(filter, va);
-         parent.visit(visitor);
-         List<VirtualFile> files = visitor.getMatched();
-         for (VirtualFile vf : files)
-         {
-            mkdirs(vf.getPathName()).addChild(vf);
-         }
-      }
-      catch (IOException e)
-      {
-         throw new RuntimeException(e);
-      }
-   }
-
-   /**
-    * Create a regular expression pattern from an Ant file matching pattern
-    *
-    * @param matcher the matcher pattern
-    * @return the pattern instance
-    */
-   public static Pattern getPattern(String matcher)
-   {
-      if (matcher == null)
-         throw new IllegalArgumentException("Null matcher");
-
-      matcher = matcher.replace(".", "\\.");
-      matcher = matcher.replace("*", ".*");
-      matcher = matcher.replace("?", ".{1}");
-      return Pattern.compile(matcher);
-   }
-
-   /**
-    * Determine whether a given file path matches an Ant pattern.
-    *
-    * @param path the path
-    * @param expression the expression
-    * @return true if we match
-    */
-   public static boolean antMatch(String path, String expression)
-   {
-      if (path == null)
-         throw new IllegalArgumentException("Null path");
-      if (expression == null)
-         throw new IllegalArgumentException("Null expression");
-      if (path.startsWith("/")) path = path.substring(1);
-      if (expression.endsWith("/")) expression += "**";
-      String[] paths = path.split("/");
-      String[] expressions = expression.split("/");
-
-      int x = 0, p;
-      Pattern pattern = getPattern(expressions[0]);
-
-      for (p = 0; p < paths.length && x < expressions.length; p++)
-      {
-         if (expressions[x].equals("**"))
-         {
-            do
-            {
-               x++;
-            } while (x < expressions.length && expressions[x].equals("**"));
-            if (x == expressions.length)
-               return true; // "**" with nothing after it
-            pattern = getPattern(expressions[x]);
-         }
-         String element = paths[p];
-         if (pattern.matcher(element).matches())
-         {
-            x++;
-            if (x < expressions.length)
-            {
-               pattern = getPattern(expressions[x]);
-            }
-         }
-         else if (!(x > 0 && expressions[x - 1].equals("**"))) // our previous isn't "**"
-         {
-            return false;
-         }
-      }
-      if (p < paths.length)
-         return false;
-      if (x < expressions.length)
-         return false;
-      return true;
-   }
-
-   /**
-    * Add a VirtualFile as a child to this AssembledDirectory.
-    *
-    * @param vf the virtual file
-    * @return the file
-    */
-   public VirtualFile addChild(VirtualFile vf)
-   {
-      if (vf == null)
-         throw new IllegalArgumentException("Null virtual file");
-
-      return directory.addChild(vf.getHandler()).getVirtualFile();
-   }
-
-   /**
-    * Add a VirtualFile as a child to this AssembledDirectory.  This file will be added
-    * under a new aliased name.
-    *
-    * @param vf the virtual file
-    * @param newName the new name
-    * @return new file
-    */
-   public VirtualFile addChild(VirtualFile vf, String newName)
-   {
-      try
-      {
-         AssembledFileHandler handler = new AssembledFileHandler(directory.getVFSContext(), directory, newName, vf.getHandler());
-         directory.addChild(handler);
-         return handler.getVirtualFile();
-      }
-      catch (IOException e)
-      {
-         throw new RuntimeException(e);
-      }
-   }
-
-   /**
-    * Add a classloader found resource to as a child to this AssembledDirectory.  The base file name of the
-    * resource will be used as the child's name.
-    *
-    * Thread.currentThread.getCOntextClassLoader() will be used to load the resource.
-    *
-    * @param resource the resource
-    * @return the file
-    */
-   public VirtualFile addResource(String resource)
-   {
-      return addResource(resource, Thread.currentThread().getContextClassLoader());
-   }
-
-   /**
-    * Add a classloader found resource to as a child to this AssembledDirectory.  The newName parameter will be used
-    * as the name of the child.
-    *
-    * Thread.currentThread.getCOntextClassLoader() will be used to load the resource.
-    *
-    * @param resource the resource
-    * @param newName the new name
-    * @return the file
-    */
-   public VirtualFile addResource(String resource, String newName)
-   {
-      return addResource(resource, Thread.currentThread().getContextClassLoader(), newName);
-   }
-
-   /**
-    * Add a classloader found resource to as a child to this AssembledDirectory.  The base file name of the
-    * resource will be used as the child's name.
-    *
-    * The loader parameter will be used to load the resource.
-    *
-    * @param resource the resource
-    * @param loader the loader
-    * @return the file
-    */
-   public VirtualFile addResource(String resource, ClassLoader loader)
-   {
-      if (resource == null)
-         throw new IllegalArgumentException("Null resource");
-      if (loader == null)
-         throw new IllegalArgumentException("Null loader");
-
-      URL url = loader.getResource(resource);
-      if (url == null)
-         throw new RuntimeException("Could not find resource: " + resource);
-
-      return addResource(url);
-   }
-
-   /**
-    * Add a resource identified by the URL as a child to this AssembledDirectory.
-    *
-    * @param url the url
-    * @return the file
-    */
-   public VirtualFile addResource(URL url)
-   {
-      if (url == null)
-         throw new IllegalArgumentException("Null url");
-   
-      try
-      {
-         VirtualFile vf = VFS.getRoot(url);
-         return addChild(vf);
-      }
-      catch (IOException e)
-      {
-         throw new RuntimeException(e);
-      }
-   }
-
-   /**
-    * Add a classloader found resource to as a child to this AssembledDirectory.  The newName parameter will be used
-    * as the name of the child.
-    *
-    * The loader parameter will be used to load the resource.
-    *
-    * @param resource the resource
-    * @param loader the loader
-    * @param newName the new name
-    * @return the file
-    */
-   public VirtualFile addResource(String resource, ClassLoader loader, String newName)
-   {
-      if (resource == null)
-         throw new IllegalArgumentException("Null resource");
-      if (loader == null)
-         throw new IllegalArgumentException("Null loader");
-      if (newName == null)
-         throw new IllegalArgumentException("Null newName");
-
-      URL url = loader.getResource(resource);
-      if (url == null)
-         throw new RuntimeException("Could not find resource: " + resource);
-
-      try
-      {
-         VirtualFile vf = VFS.getRoot(url);
-         return addChild(vf, newName);
-      }
-      catch (IOException e)
-      {
-         throw new RuntimeException(e);
-      }
-   }
-
-   /**
-    * Add raw bytes as a file to this assembled directory
-    *
-    * @param bytes the bytes
-    * @param name the name
-    * @return the file
-    */
-   public VirtualFile addBytes(byte[] bytes, String name)
-   {
-      if (bytes == null)
-         throw new IllegalArgumentException("Null bytes");
-      if (name == null)
-         throw new IllegalArgumentException("Null name");
-
-      ByteArrayHandler handler;
-      try
-      {
-         handler = new ByteArrayHandler(directory.getVFSContext(), directory, name, bytes);
-      }
-      catch (IOException e)
-      {
-         throw new RuntimeException(e);
-      }
-      directory.addChild(handler);
-      return handler.getVirtualFile();
-   }
-
-   /**
-    * Create a directory within this directory.
-    *
-    * @param name the name
-    * @return the directory
-    */
-   public AssembledDirectory mkdir(String name)
-   {
-      if (name == null)
-         throw new IllegalArgumentException("Null name");
-
-      try
-      {
-         AssembledDirectoryHandler handler = new AssembledDirectoryHandler(directory.getVFSContext(), directory, name);
-         directory.addChild(handler);
-         return new AssembledDirectory(handler);
-      }
-      catch (IOException e)
-      {
-         throw new RuntimeException(e);
-      }
-   }
-}

Added: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/LongestMatchComparator.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/LongestMatchComparator.java	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/LongestMatchComparator.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.io.Serializable;
+
+/**
+ * A comparator which sorts longer values before their shorter initial sublists.
+ *
+ * @param <E> the element type
+ * @param <C> the collection type
+ */
+final class LongestMatchComparator<E, C extends Iterable<E>> implements Comparator<C>, Serializable
+{
+   private static final long serialVersionUID = 954089122568817323L;
+
+   private final Comparator<E> comparator;
+
+   private LongestMatchComparator(Comparator<E> comparator)
+   {
+      this.comparator = comparator;
+   }
+
+   /**
+    * Create a new instance.
+    *
+    * @param comparator the element comparator
+    * @param <E> the element type
+    * @param <C> the collection type
+    * @return the new collection comparator
+    */
+   static <E, C extends Iterable<E>> Comparator<C> create(Comparator<E> comparator) {
+      return new LongestMatchComparator<E, C>(comparator);
+   }
+
+   /**
+    * Create a new instance which uses the native ordering of the element type.
+    *
+    * @param <E> the element type
+    * @param <C> the collection type
+    * @return the new collection comparator
+    */
+   static <E extends Comparable<? super E>, C extends Iterable<E>> Comparator<C> create() {
+      return new LongestMatchComparator<E, C>(NativeComparator.<E>getInstance());
+   }
+
+   /** {@inheritDoc} */
+   public int compare(C o1, C o2)
+   {
+      final Comparator<E> comparator = this.comparator;
+      final Iterator<E> i1 = o1.iterator();
+      final Iterator<E> i2 = o2.iterator();
+      while (i1.hasNext() && i2.hasNext()) {
+         final E t1 = i1.next();
+         final E t2 = i2.next();
+         final int c = comparator.compare(t1, t2);
+         if (c != 0) {
+            return c;
+         }
+      }
+      return i1.hasNext() ? -1 : i2.hasNext() ? 1 : 0;
+   }
+
+   /**
+    * A native-order comparator.
+    *
+    * @param <E>
+    */
+   private static final class NativeComparator<E extends Comparable<? super E>> implements Comparator<E>, Serializable
+   {
+      private static final long serialVersionUID = -4198283451912738802L;
+
+      /** {@inheritDoc} */
+      public int compare(E o1, E o2)
+      {
+         return o1.compareTo(o2);
+      }
+
+      private static final NativeComparator INSTANCE = new NativeComparator();
+
+      @SuppressWarnings({ "unchecked" })
+      private static <E extends Comparable<? super E>> Comparator<E> getInstance() {
+         return INSTANCE;
+      }
+   }
+}

Deleted: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/MemoryFileFactory.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/MemoryFileFactory.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/MemoryFileFactory.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -1,115 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2006, 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;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URL;
-
-/**
- * Memory VFS API.
- *
- * @author <a href="mailto:ales.justin at jboss.com">Ales Justin</a>
- */
-public class MemoryFileFactory
-{
-   private static final MemoryContextFactory factory = MemoryContextFactory.getInstance();
-
-   /**
-    * Find host's VFS.
-    *
-    * @param host the host
-    * @return host's vfs
-    */
-   public static VFS find(String host)
-   {
-      return factory.find(host);
-   }
-
-   /**
-    * Create the memory root.
-    *
-    * @param uri the uri
-    * @return root's vfs
-    * @throws IOException for any error
-    */
-   public static VFS createRoot(URI uri) throws IOException
-   {
-      return createRoot(uri.toURL());
-   }
-
-   /**
-    * Create root vfs.
-    *
-    * @param url the url
-    * @return root's vfs
-    */
-   public static VFS createRoot(URL url)
-   {
-      return factory.createRoot(url).getVFS();
-   }
-
-   /**
-    * Create memory directory.
-    *
-    * @param url the url
-    * @return vfs directory
-    */
-   public static VirtualFile createDirectory(URL url)
-   {
-      return factory.createDirectory(url);
-   }
-
-   /**
-    * Put file.
-    *
-    * @param url the url
-    * @param contents the contents
-    * @return vfs file
-    */
-   public static VirtualFile putFile(URL url, byte[] contents)
-   {
-      return factory.putFile(url, contents);
-   }
-
-   /**
-    * Delete root.
-    *
-    * @param url the url
-    * @return true if deleted
-    */
-   public static boolean deleteRoot(URL url)
-   {
-      return factory.deleteRoot(url);
-   }
-
-   /**
-    * Delete.
-    *
-    * @param url the url
-    * @return true if deleted
-    */
-   public static boolean delete(URL url)
-   {
-      return factory.delete(url);
-   }
-}

Added: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/TempFileProvider.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/TempFileProvider.java	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/TempFileProvider.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A provider for temporary physical files and directories.
+ */
+public final class TempFileProvider implements Closeable
+{
+   private static final String TMP_DIR_PROPERTY = "jboss.server.temp.dir";
+   private static final File TMP_ROOT;
+
+   static {
+      final String configTmpDir = System.getProperty(TMP_DIR_PROPERTY);
+      try
+      {
+         TMP_ROOT = configTmpDir == null ? File.createTempFile("jboss-vfs-temp", "") : new File(configTmpDir, "vfs");
+         TMP_ROOT.mkdirs();
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException("Can't set up temp file provider", e);
+      }
+   }
+
+   /**
+    * Create a temporary file provider for a given type.
+    *
+    * @param providerType the provider type string (used as a prefix in the temp file dir name)
+    * @return the new provider
+    * @throws IOException if an I/O error occurs
+    */
+   public static TempFileProvider create(String providerType, ScheduledExecutorService executor) throws IOException
+   {
+      return new TempFileProvider(File.createTempFile(providerType, "", TMP_ROOT), 0, executor);
+   }
+
+   /**
+    * Create a temporary file provider for a given type and hash depth.  The hash depth is used to limit the number
+    * of files in a single directory.
+    *
+    * @param providerType the provider type string (used as a prefix in the temp file dir name)
+    * @param hashDepth the hash directory tree depth
+    * @return the new provider
+    * @throws IOException if an I/O error occurs
+    */
+   public static TempFileProvider create(String providerType, int hashDepth, ScheduledExecutorService executor) throws IOException
+   {
+      return new TempFileProvider(File.createTempFile(providerType, "", TMP_ROOT), hashDepth, executor);
+   }
+
+   private final File providerRoot;
+   private final int hashDepth;
+   private final ScheduledExecutorService executor;
+
+   private TempFileProvider(File providerRoot, int hashDepth, ScheduledExecutorService executor)
+   {
+      this.providerRoot = providerRoot;
+      this.executor = executor;
+      if (hashDepth < 0 || hashDepth > 4) {
+         throw new IllegalArgumentException("Bad hashDepth");
+      }
+      this.hashDepth = hashDepth;
+   }
+
+   /**
+    * Create a temp file within this provider.
+    *
+    * @param originalName the original file name
+    * @return the temporary file
+    * @throws IOException if an error occurs
+    */
+   public File createTempFile(String originalName) throws IOException {
+      return createTempFile(originalName, originalName.hashCode());
+   }
+
+   /**
+    * Create a temp file within this provider, using an alternate hash code.
+    *
+    * @param originalName the original file name
+    * @param hashCode the hash code to use
+    * @return the temporary file
+    * @throws IOException if an error occurs
+    */
+   public File createTempFile(String originalName, int hashCode) throws IOException {
+      File root = providerRoot;
+      for (int i = 0; i < hashDepth; i ++) {
+         final int dc = hashCode & 0x7f;
+         root = new File(root, dc < 16 ? "0" + Integer.toHexString(dc) : Integer.toHexString(dc));
+         root.mkdir();
+         hashCode >>= 7;
+      }
+      return File.createTempFile("", "-" + originalName, root);
+   }
+
+   /**
+    * Create a temp file within this provider, using an alternate hash code, and prepopulating the file from the given
+    * input stream.
+    *
+    * @param originalName the original file name
+    * @param hashCode the hash code to use
+    * @param sourceData the source input stream to use
+    * @return the temporary file
+    * @throws IOException if an error occurs
+    */
+   public File createTempFile(String originalName, int hashCode, InputStream sourceData) throws IOException {
+      final File tempFile = createTempFile(originalName, hashCode);
+      boolean ok = false;
+      try {
+         final FileOutputStream fos = new FileOutputStream(tempFile);
+         try {
+            VFSUtils.copyStream(sourceData, fos);
+            fos.close();
+            sourceData.close();
+            return tempFile;
+         } finally {
+            VFSUtils.safeClose(fos);
+         }
+      } finally {
+         VFSUtils.safeClose(sourceData);
+         if (! ok) {
+            tempFile.delete();
+         }
+      }
+   }
+
+   /**
+    * Close this provider and delete any temp files associated with it.
+    */
+   public void close() throws IOException
+   {
+      final Runnable task = new Runnable()
+      {
+         public void run()
+         {
+            if (! recursiveDelete(providerRoot)) {
+               executor.schedule(this, 30L, TimeUnit.SECONDS);
+            }
+         }
+      };
+      task.run();
+   }
+
+   private static boolean recursiveDelete(File root) {
+      boolean ok = true;
+      if (root.isDirectory()) {
+         final File[] files = root.listFiles();
+         for (File file : files)
+         {
+            ok &= recursiveDelete(file);
+         }
+         return ok && (root.delete() || ! root.exists());
+      } else {
+         ok &= root.delete() || ! root.exists();
+      }
+      return ok;
+   }
+}

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-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFS.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -22,12 +22,19 @@
 package org.jboss.virtual;
 
 import java.io.IOException;
+import java.io.Closeable;
 import java.net.URI;
 import java.net.URL;
 import java.util.List;
+import java.util.Collections;
+import java.util.Map;
 
 import org.jboss.logging.Logger;
-import org.jboss.virtual.plugins.vfs.helpers.WrappingVirtualFileHandlerVisitor;
+import org.jboss.util.collection.ConcurrentNavigableMap;
+import org.jboss.util.collection.ConcurrentSkipListMap;
+import org.jboss.virtual.spi.RealFileSystem;
+import org.jboss.virtual.spi.FileSystem;
+import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
 
 /**
  * Virtual File System
@@ -42,25 +49,20 @@
    /** The log */
    private static final Logger log = Logger.getLogger(VFS.class);
 
-   /** The VFS Context */
-   private final VFSContext context;
+   private final ConcurrentNavigableMap<List<String>, Mount> activeMounts = new ConcurrentSkipListMap<List<String>, Mount>(LongestMatchComparator.<String, List<String>>create());
+   private final VirtualFile rootVirtualFile;
 
    static
    {
       init();
    }
 
-   /**
-    * Create a new VFS.
-    *
-    * @param context the context
-    * @throws IllegalArgumentException for a null context
-    */
-   public VFS(VFSContext context)
+   public VFS()
    {
-      if (context == null)
-         throw new IllegalArgumentException("Null name");
-      this.context = context;
+      // By default, there's a root mount which points to the "real" FS
+      final List<String> emptyList = Collections.<String>emptyList();
+      activeMounts.put(emptyList, new Mount(RealFileSystem.ROOT_INSTANCE, emptyList));
+      rootVirtualFile = new VirtualFile(this, Collections.<String>emptyList(), "");
    }
 
    /**
@@ -80,255 +82,59 @@
          pkgs += "|org.jboss.virtual.protocol";
          System.setProperty("java.protocol.handler.pkgs", pkgs);
       }
-      org.jboss.virtual.plugins.context.VfsArchiveBrowserFactory factory = org.jboss.virtual.plugins.context.VfsArchiveBrowserFactory.INSTANCE;
-      // keep this until AOP and HEM uses VFS internally instead of the stupid ArchiveBrowser crap.
-      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfsfile", factory);
-      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfszip", factory);
-      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfsjar", factory);
-      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfs", factory);
+//      org.jboss.virtual.plugins.context.VfsArchiveBrowserFactory factory = org.jboss.virtual.plugins.context.VfsArchiveBrowserFactory.INSTANCE;
+//      // keep this until AOP and HEM uses VFS internally instead of the stupid ArchiveBrowser crap.
+//      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfsfile", factory);
+//      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfszip", factory);
+//      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfsjar", factory);
+//      org.jboss.util.file.ArchiveBrowser.factoryFinder.put("vfs", factory);
    }
 
    /**
-    * Get the vfs context.
+    * Mount a filesystem on a mount point in the VFS.  The mount point is any valid file name, existant or non-existant.
+    * If a relative path is given, it will be treated as relative to the VFS root.
     *
-    * This is package protected method.
-    * Same as VirtualFile::getHandler. 
-    *
-    * @return the vfs context
+    * @param mountPoint the mount point
+    * @param fileSystem the file system to mount
+    * @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
     */
-   VFSContext getContext()
-   {
-      return context;
-   }
-
-   /**
-    * Set exception handler.
-    *
-    * @param exceptionHandler the exception handler.
-    */
-   public void setExceptionHandler(ExceptionHandler exceptionHandler)
-   {
-      context.setExceptionHandler(exceptionHandler);
-   }
-
-   /**
-    * Cleanup any resources tied to this file.
-    * e.g. vfs cache
-    *
-    * @param file the file
-    */
-   static void cleanup(VirtualFile file)
-   {
-      VirtualFileHandler fileHandler = file.getHandler();
-      VFSContext context = fileHandler.getVFSContext();
-
-      try
-      {
-         context.cleanupTempInfo(fileHandler.getPathName());
+   public Closeable mount(String mountPoint, FileSystem fileSystem) throws IOException {
+      final List<String> realMountPoint = PathTokenizer.applySpecialPaths(PathTokenizer.getTokens(mountPoint));
+      final Mount mount = new Mount(fileSystem, realMountPoint);
+      if (activeMounts.putIfAbsent(realMountPoint, mount) == null) {
+         throw new IOException("Filsystem already mounted at mount point \"" + mountPoint + "\"");
       }
-      catch (Exception e)
-      {
-         log.debug("Exception cleaning temp info, file=" + file, e);
-      }
-
-      try
-      {
-         VirtualFileHandler contextHandler = context.getRoot();
-         // the file is the context root, hence possible registry candidate
-         if (fileHandler.equals(contextHandler))
-         {
-            VFSRegistry registry = VFSRegistry.getInstance();
-            registry.removeContext(context);
-         }
-      }
-      catch (Exception e)
-      {
-         log.debug("Exception removing cached context, file=" + file, e);
-      }
+      return mount;
    }
 
    /**
-    * Get the virtual file system for a root uri
-    * 
-    * @param rootURI the root URI
-    * @return the virtual file system
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL is null
-    */
-   public static VFS getVFS(URI rootURI) throws IOException
-   {
-      VFSContextFactory factory = VFSContextFactoryLocator.getFactory(rootURI);
-      if (factory == null)
-         throw new IOException("No context factory for " + rootURI);
-
-      VFSContext context = factory.getVFS(rootURI);
-      VFSRegistry.getInstance().addContext(context);
-      return context.getVFS();
-   }
-
-   /**
-    * Create new root
+    * Find a virtual file.
     *
-    * @param rootURI the root url
-    * @return the virtual file
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL
-    */
-   public static VirtualFile createNewRoot(URI rootURI) throws IOException
-   {
-      VFS vfs = getVFS(rootURI);
-      return vfs.getRoot();
-   }
-
-   /**
-    * Get the root virtual file
-    * 
-    * @param rootURI the root uri
-    * @return the virtual file
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL is null
-    */
-   public static VirtualFile getRoot(URI rootURI) throws IOException
-   {
-      VFSRegistry registry = VFSRegistry.getInstance();
-      VirtualFile file = registry.getFile(rootURI);
-      return (file != null) ? file : createNewRoot(rootURI);
-   }
-
-   /**
-    * Get a virtual file
-    * 
-    * @param rootURI the root uri
-    * @param name the path name
-    * @return the virtual file
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL or name is null
-    */
-   @SuppressWarnings("deprecation")
-   public static VirtualFile getVirtualFile(URI rootURI, String name) throws IOException
-   {
-      VirtualFile root = getRoot(rootURI);
-      return root.findChild(name);
-   }
-
-   /**
-    * Get the virtual file system for a root url
-    * 
-    * @param rootURL the root url
-    * @return the virtual file system
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL is null
-    */
-   public static VFS getVFS(URL rootURL) throws IOException
-   {
-      VFSContextFactory factory = VFSContextFactoryLocator.getFactory(rootURL);
-      if (factory == null)
-         throw new IOException("No context factory for " + rootURL);
-
-      VFSContext context = factory.getVFS(rootURL);
-      VFSRegistry.getInstance().addContext(context);
-      return context.getVFS();
-   }
-
-   /**
-    * Create new root
-    * 
-    * @param rootURL the root url
-    * @return the virtual file
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL
-    */
-   public static VirtualFile createNewRoot(URL rootURL) throws IOException
-   {
-      VFS vfs = getVFS(rootURL);
-      return vfs.getRoot();
-   }
-
-   /**
-    * Get the root virtual file
-    *
-    * @param rootURL the root url
-    * @return the virtual file
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL
-    */
-   public static VirtualFile getRoot(URL rootURL) throws IOException
-   {
-      VFSRegistry registry = VFSRegistry.getInstance();
-      VirtualFile file = registry.getFile(rootURL);
-      return (file != null) ? file : createNewRoot(rootURL);
-   }
-
-   /**
-    * Get a virtual file
-    * 
-    * @param rootURL the root url
-    * @param name the path name
-    * @return the virtual file
-    * @throws IOException if there is a problem accessing the VFS
-    * @throws IllegalArgumentException if the rootURL or name is null
-    */
-   @SuppressWarnings("deprecation")
-   public static VirtualFile getVirtualFile(URL rootURL, String name) throws IOException
-   {
-      VirtualFile root = getRoot(rootURL);
-      return root.findChild(name);
-   }
-
-   /**
-    * Get the root file of this VFS
-    * 
-    * @return the root
-    * @throws IOException for any problem accessing the VFS
-    */
-   public VirtualFile getRoot() throws IOException
-   {
-      VirtualFileHandler handler = context.getRoot();
-      return handler.getVirtualFile();
-   }
-   
-   /**
-    * Find a child from the root
-    *
     * @param path the child path
     * @return the child
-    * @throws IOException for any problem accessing the VFS (including the child does not exist)
+    * @throws IOException for any problem accessing the VFS
     * @throws IllegalArgumentException if the path is null
-    * @deprecated use getChild, and handle null if not found
     */
-   @Deprecated
-   public VirtualFile findChild(String path) throws IOException
+   public VirtualFile getChild(String path) throws IOException
    {
       if (path == null)
          throw new IllegalArgumentException("Null path");
-      
-      VirtualFileHandler handler = context.getRoot();
-      VirtualFileHandler result = context.getChild(handler, VFSUtils.fixName(path));
-      if (result == null)
-      {
-         List<VirtualFileHandler> children = handler.getChildren(true);
-         throw new IOException("Child not found " + path + " for " + handler + ", available children: " + children);
-      }
-      return result.getVirtualFile();
+      final List<String> realPath = PathTokenizer.applySpecialPaths(PathTokenizer.getTokens(path));
+      final String realPathString = PathTokenizer.getRemainingPath(realPath, 0);
+      return new VirtualFile(this, realPath, realPathString);
    }
-   
+
    /**
-   * Get a child
-   *
-   * @param path the child path
-   * @return the child or <code>null</code> if not found
-   * @throws IOException if a real problem occurs
-   */
-   public VirtualFile getChild(String path) throws IOException
+    * Get the root virtual file for this VFS instance.
+    *
+    * @return the root virtual file
+    */
+   public VirtualFile getRootVirtualFile()
    {
-      if (path == null)
-         throw new IllegalArgumentException("Null path");
-
-      VirtualFileHandler handler = context.getRoot();
-      VirtualFileHandler result = context.getChild(handler, VFSUtils.fixName(path));
-      return result != null ? result.getVirtualFile() : null;
+      return rootVirtualFile;
    }
-   
+
    /**
     * Get the children
     * 
@@ -337,7 +143,7 @@
     */
    public List<VirtualFile> getChildren() throws IOException
    {
-      return getRoot().getChildren(null);
+      return getRootVirtualFile().getChildren(null);
    }
 
    /**
@@ -349,7 +155,7 @@
     */
    public List<VirtualFile> getChildren(VirtualFileFilter filter) throws IOException
    {
-      return getRoot().getChildren(filter);
+      return getRootVirtualFile().getChildren(filter);
    }
    
    /**
@@ -362,7 +168,7 @@
     */
    public List<VirtualFile> getChildrenRecursively() throws IOException
    {
-      return getRoot().getChildrenRecursively(null);
+      return getRootVirtualFile().getChildrenRecursively(null);
    }
    
    /**
@@ -376,7 +182,7 @@
     */
    public List<VirtualFile> getChildrenRecursively(VirtualFileFilter filter) throws IOException
    {
-      return getRoot().getChildrenRecursively(filter);
+      return getRootVirtualFile().getChildrenRecursively(filter);
    }
    
    /**
@@ -388,12 +194,7 @@
     */
    public void visit(VirtualFileVisitor visitor) throws IOException
    {
-      VirtualFileHandler handler = context.getRoot();
-      if (handler.isLeaf() == false)
-      {
-         WrappingVirtualFileHandlerVisitor wrapper = new WrappingVirtualFileHandlerVisitor(visitor);
-         context.visit(handler, wrapper);
-      }
+      visitor.visit(rootVirtualFile);
    }
 
    /**
@@ -409,32 +210,52 @@
       if (file == null)
          throw new IllegalArgumentException("Null file");
 
-      VirtualFileHandler handler = file.getHandler();
-      WrappingVirtualFileHandlerVisitor wrapper = new WrappingVirtualFileHandlerVisitor(visitor);
-      VFSContext handlerContext = handler.getVFSContext();
-      handlerContext.visit(handler, wrapper);
+      if (file.getVFS() != this)
+         throw new IllegalArgumentException("Virtual file from foreign VFS");
+
+      visitor.visit(file);
    }
 
-   @Override
-   public String toString()
+   /**
+    * Get the enclosing mounted FileSystem for the given path.
+    *
+    * @param pathTokens the path tokens
+    * @return the filesystem
+    */
+   Mount getMount(List<String> pathTokens)
    {
-      return context.toString();
+      final Map.Entry<List<String>, Mount> entry = activeMounts.floorEntry(pathTokens);
+      return entry.getValue();
    }
 
-   @Override
-   public int hashCode()
-   {
-      return context.hashCode();
+   /**
+    * The mount representation.  This instance represents a binding between a position in the virtual filesystem and
+    * the backing filesystem implementation; the same {@code FileSystem} may be mounted in more than one place, however
+    * only one {@code FileSystem} may be bound to a specific path at a time.
+    */
+   final class Mount implements Closeable {
+      private final FileSystem fileSystem;
+      private final List<String> realMountPoint;
+
+      private Mount(FileSystem fileSystem, List<String> realMountPoint)
+      {
+         this.fileSystem = fileSystem;
+         this.realMountPoint = realMountPoint;
+      }
+
+      public void close() throws IOException
+      {
+         activeMounts.remove(realMountPoint, this);
+      }
+
+      FileSystem getFileSystem()
+      {
+         return fileSystem;
+      }
+
+      List<String> getRealMountPoint()
+      {
+         return realMountPoint;
+      }
    }
-   
-   @Override
-   public boolean equals(Object obj)
-   {
-      if (obj == this)
-         return true;
-      if (obj == null || obj instanceof VFS == false)
-         return false;
-      VFS other = (VFS) obj;
-      return context.equals(other.context);
-   }
 }

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFSUtils.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFSUtils.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VFSUtils.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -31,18 +31,15 @@
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLDecoder;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
 import org.jboss.logging.Logger;
-import org.jboss.util.StringPropertyReplacer;
 import org.jboss.util.collection.CollectionsFactory;
 
 /**
@@ -60,51 +57,7 @@
    /** The default encoding */
    private static final String DEFAULT_ENCODING = "UTF-8";
 
-   /** The link */
-   public static final String VFS_LINK_INFIX = ".vfslink";
-
-   /** The link properties */
-   public static final String VFS_LINK_PROPERTIES_SUFFIX = ".vfslink.properties";
-
-   /** The link name */
-   public static final String VFS_LINK_NAME = "vfs.link.name";
-   /** The link target */
-   public static final String VFS_LINK_TARGET = "vfs.link.target";
-
    /**
-    * The system no force copy key / query
-    */
-   public static final String FORCE_COPY_KEY = "jboss.vfs.forceCopy";
-   public static final String USE_COPY_QUERY = "useCopyJarHandler";
-
-   /**
-    * Key used to force fallback from vfszip (default) to vfsjar
-    */
-   public static final String FORCE_VFS_JAR_KEY = "jboss.vfs.forceVfsJar";
-
-   /**
-    * Key used to turn off reaper mode in vfszip - forcing synchronous (slower) handling of files
-    */
-   public static final String FORCE_NO_REAPER_KEY = "jboss.vfs.forceNoReaper";
-   public static final String NO_REAPER_QUERY = "noReaper";
-
-   /**
-    * Key used to force case sensitive path checking in vfsfile
-    */
-   public static final String FORCE_CASE_SENSITIVE_KEY = "jboss.vfs.forceCaseSensitive";
-   public static final String CASE_SENSITIVE_QUERY = "caseSensitive";
-
-   /**
-    * Key used to turn on memory optimizations - less cache use at the expense of performance
-    */
-   public static final String OPTIMIZE_FOR_MEMORY_KEY = "jboss.vfs.optimizeForMemory";
-
-   /**
-    * Key used to determine VFS Cache impl
-    */
-   public static final String VFS_CACHE_KEY = "jboss.vfs.cache";
-
-   /**
     * Constant representing the URL file protocol
     */
    public static final String FILE_PROTOCOL = "file";
@@ -112,15 +65,8 @@
    /** Standard separator for JAR URL */
    public static final String JAR_URL_SEPARATOR = "!/";
 
-   /** The temp marker flag */
-   public static final String IS_TEMP_FILE = "IS_TEMP_FILE";
-
-   /**
-    * Stop cache.
-    */
-   public static void stopCache()
+   private VFSUtils()
    {
-      VFSCacheFactory.getInstance().stop();
    }
 
    /**
@@ -274,24 +220,6 @@
    }
 
    /**
-     * Get a manifest from a virtual file system,
-     * assuming the root of the VFS is the root of an archive
-     *
-     * @param archive the vfs
-     * @return the manifest or null if not found
-     * @throws IOException if there is an error reading the manifest
-     * @throws IllegalArgumentException for a null archive
-     */
-    public static Manifest getManifest(VFS archive) throws IOException
-    {
-       if (archive == null)
-         throw new IllegalArgumentException("Null vfs archive");
-
-       VirtualFile root = archive.getRoot();
-       return getManifest(root);
-    }
-
-   /**
     * Fix a name (removes any trailing slash)
     *
     * @param name the name to fix
@@ -387,85 +315,6 @@
    }
 
    /**
-    * Does a vf name contain the VFS link prefix
-    * @param name - the name portion of a virtual file
-    * @return true if the name starts with VFS_LINK_PREFIX, false otherwise
-    */
-   public static boolean isLink(String name)
-   {
-      if (name == null)
-         throw new IllegalArgumentException("Null name");
-
-      return name.indexOf(VFS_LINK_INFIX) >= 0;
-   }
-
-   /**
-    * Read the link information from the stream based on the type as determined
-    * from the name suffix.
-    *
-    * @param is - input stream to the link file contents
-    * @param name - the name of the virtual file representing the link
-    * @param props the propertes
-    * @return a list of the links read from the stream
-    * @throws IOException on failure to read/parse the stream
-    * @throws URISyntaxException for an error parsing a URI
-    */
-   public static List<LinkInfo> readLinkInfo(InputStream is, String name, Properties props)
-      throws IOException, URISyntaxException
-   {
-      if (name == null)
-         throw new IllegalArgumentException("Null name");
-
-      if(name.endsWith(VFS_LINK_PROPERTIES_SUFFIX))
-      {
-         List<LinkInfo> info = new ArrayList<LinkInfo>();
-         parseLinkProperties(is, info, props);
-         return info;
-      }
-      else
-         throw new UnsupportedEncodingException("Unknown link format: " + name);
-   }
-
-   /**
-    * Parse a properties link file
-    *
-    * @param is - input stream to the link file contents
-    * @param info the link infos
-    * @param props the propertes
-    * @throws IOException on failure to read/parse the stream
-    * @throws URISyntaxException for an error parsing a URI
-    */
-   public static void parseLinkProperties(InputStream is, List<LinkInfo> info, Properties props)
-      throws IOException, URISyntaxException
-   {
-      if (is == null)
-         throw new IllegalArgumentException("Null input stream");
-      if (info == null)
-         throw new IllegalArgumentException("Null info");
-      if (props == null)
-         throw new IllegalArgumentException("Null properties");
-
-      props.load(is);
-      // Iterate over the property tuples
-      for(int n = 0; ; n ++)
-      {
-         String nameKey = VFS_LINK_NAME + "." + n;
-         String name = props.getProperty(nameKey);
-         String uriKey = VFS_LINK_TARGET + "." + n;
-         String uri = props.getProperty(uriKey);
-         // End when the value is null since a link may not have a name
-         if (uri == null)
-         {
-            break;
-         }
-         // Replace any system property references
-         uri = StringPropertyReplacer.replaceProperties(uri);
-         LinkInfo link = new LinkInfo(name, new URI(uri));
-         info.add(link);
-      }
-   }
-
-   /**
     * Deal with urls that may include spaces.
     *
     * @param url the url
@@ -505,374 +354,6 @@
    }
 
    /**
-    * Get the options for this file.
-    *
-    * @param file the file
-    * @return options map
-    */
-   private static Options getOptions(VirtualFile file)
-   {
-      if (file == null)
-         throw new IllegalArgumentException("Null file");
-
-      VirtualFileHandler handler = file.getHandler();
-      VFSContext context = handler.getVFSContext();
-      return context.getOptions();
-   }
-
-   /**
-    * Get the options for this vfs.
-    *
-    * @param vfs the vfs
-    * @return options map
-    */
-   private static Options getOptions(VFS vfs)
-   {
-      if (vfs == null)
-         throw new IllegalArgumentException("Null vfs");
-
-      VFSContext context = vfs.getContext();
-      return context.getOptions();
-   }
-
-   /**
-    * Get the option.
-    *
-    * @param file the file
-    * @param key the option key
-    * @return key's option
-    */
-   public static String getOption(VirtualFile file, String key)
-   {
-      Object option = getOption(file, key, Object.class);
-      return option != null ? option.toString() : null;
-   }
-
-   /**
-    * Get the option.
-    *
-    * @param vfs the vfs
-    * @param key the option key
-    * @return key's option
-    */
-   public static String getOption(VFS vfs, String key)
-   {
-      Object option = getOption(vfs, key, Object.class);
-      return option != null ? option.toString() : null;
-   }
-
-   /**
-    * Get the option.
-    *
-    * @param <T> exact type
-    * @param file the file
-    * @param key the option key
-    * @param exactType the exact type
-    * @return key's option
-    */
-   public static <T> T getOption(VirtualFile file, String key, Class<T> exactType)
-   {
-      Options options = getOptions(file);
-      return options != null ? options.getOption(key, exactType) : null;
-   }
-
-   /**
-    * Get the option.
-    *
-    * @param <T> exact type
-    * @param vfs the vfs
-    * @param key the option key
-    * @param exactType the exact type
-    * @return key's option
-    */
-   public static <T> T getOption(VFS vfs, String key, Class<T> exactType)
-   {
-      Options options = getOptions(vfs);
-      return options != null ? options.getOption(key, exactType) : null;
-   }
-
-   /**
-    * Enable option.
-    *
-    * @param file the file
-    * @param optionName option name
-    */
-   protected static void enableOption(VirtualFile file, String optionName)
-   {
-      Options options = getOptions(file);
-      if (options == null)
-         throw new IllegalArgumentException("Cannot enable " + optionName + " on null options: " + file);
-
-      options.addOption(optionName, Boolean.TRUE);
-   }
-
-   /**
-    * Disable option.
-    *
-    * @param file the file
-    * @param optionName option name
-    */
-   protected static void disableOption(VirtualFile file, String optionName)
-   {
-      Options options = getOptions(file);
-      if (options == null)
-         throw new IllegalArgumentException("Cannot disable " + optionName + " on null options: " + file);
-
-      options.removeOption(optionName);
-   }
-
-   /**
-    * Enable option.
-    *
-    * @param vfs the vfs
-    * @param optionName option name
-    */
-   protected static void enableOption(VFS vfs, String optionName)
-   {
-      Options options = getOptions(vfs);
-      if (options == null)
-         throw new IllegalArgumentException("Cannot enable " + optionName + " on null options: " + vfs);
-
-      options.addOption(optionName, Boolean.TRUE);
-   }
-
-   /**
-    * Disable option.
-    *
-    * @param vfs the vfs
-    * @param optionName option name
-    */
-   protected static void disableOption(VFS vfs, String optionName)
-   {
-      Options options = getOptions(vfs);
-      if (options == null)
-         throw new IllegalArgumentException("Cannot disable " + optionName + " on null options: " + vfs);
-
-      options.removeOption(optionName);
-   }
-
-   /**
-    * Enable copy for file param.
-    *
-    * @param file the file
-    */
-   public static void enableCopy(VirtualFile file)
-   {
-      enableOption(file, USE_COPY_QUERY);
-   }
-
-   /**
-    * Disable copy for file param.
-    *
-    * @param file the file
-    */
-   public static void disableCopy(VirtualFile file)
-   {
-      disableOption(file, USE_COPY_QUERY);
-   }
-
-   /**
-    * Enable copy for vfs param.
-    *
-    * @param vfs the vfs
-    */
-   public static void enableCopy(VFS vfs)
-   {
-      enableOption(vfs, USE_COPY_QUERY);
-   }
-
-   /**
-    * Disable copy for vfs param.
-    *
-    * @param vfs the vfs
-    */
-   public static void disableCopy(VFS vfs)
-   {
-      disableOption(vfs, USE_COPY_QUERY);
-   }
-
-   /**
-    * Enable reaper for file param.
-    *
-    * @param file the file
-    */
-   public static void enableNoReaper(VirtualFile file)
-   {
-      enableOption(file, NO_REAPER_QUERY);
-   }
-
-   /**
-    * Disable reaper for file param.
-    *
-    * @param file the file
-    */
-   public static void disableNoReaper(VirtualFile file)
-   {
-      disableOption(file, NO_REAPER_QUERY);
-   }
-
-   /**
-    * Enable reaper for vfs param.
-    *
-    * @param vfs the vfs
-    */
-   public static void enableNoReaper(VFS vfs)
-   {
-      enableOption(vfs, NO_REAPER_QUERY);
-   }
-
-   /**
-    * Disable reaper for vfs param.
-    *
-    * @param vfs the vfs
-    */
-   public static void disableNoReaper(VFS vfs)
-   {
-      disableOption(vfs, NO_REAPER_QUERY);
-   }
-
-   /**
-    * Enable case sensitive for file param.
-    *
-    * @param file the file
-    */
-   public static void enableCaseSensitive(VirtualFile file)
-   {
-      enableOption(file, CASE_SENSITIVE_QUERY);
-   }
-
-   /**
-    * Disable case sensitive for file param.
-    *
-    * @param file the file
-    */
-   public static void disableCaseSensitive(VirtualFile file)
-   {
-      disableOption(file, CASE_SENSITIVE_QUERY);
-   }
-
-   /**
-    * Enable case sensitive for vfs param.
-    *
-    * @param vfs the vfs
-    */
-   public static void enableCaseSensitive(VFS vfs)
-   {
-      enableOption(vfs, CASE_SENSITIVE_QUERY);
-   }
-
-   /**
-    * Disable case sensitive for vfs param.
-    *
-    * @param vfs the vfs
-    */
-   public static void disableCaseSensitive(VFS vfs)
-   {
-      disableOption(vfs, CASE_SENSITIVE_QUERY);
-   }
-
-   /**
-    * Is the virtual file temporary.
-    *
-    * @param file the file
-    * @return true if temporary, false otherwise
-    */
-   public static boolean isTemporaryFile(VirtualFile file)
-   {
-      Options options = getOptions(file);
-      return options.getBooleanOption(IS_TEMP_FILE);
-   }
-
-   /**
-    * Unpack the nested artifact under file param.
-    *
-    * @param file the file to unpack
-    * @return unpacked file
-    * @throws IOException for any io error
-    * @throws URISyntaxException for any uri error
-    */
-   public static VirtualFile unpack(VirtualFile file) throws IOException, URISyntaxException
-   {
-      log.warn("Using unpack modification is not yet fully supported - rewire-ing issues.");
-      return copy(file, UnpackCopyMechanism.INSTANCE);
-   }
-
-   /**
-    * Force explode.
-    * Explode archives or nested entries.
-    *
-    * @param file the file to explode
-    * @return exploded file
-    * @throws IOException for any io error
-    * @throws URISyntaxException for any uri error
-    */
-   public static VirtualFile explode(VirtualFile file) throws IOException, URISyntaxException
-   {
-      return copy(file, ExplodedCopyMechanism.INSTANCE);
-   }
-
-   /**
-    * Create temp.
-    *
-    * @param file the file to temp
-    * @return temp file
-    * @throws IOException for any io error
-    * @throws URISyntaxException for any uri error
-    */
-   public static VirtualFile temp(VirtualFile file) throws IOException, URISyntaxException
-   {
-      return copy(file, TempCopyMechanism.INSTANCE);
-   }
-
-   /**
-    * Unjar.
-    *
-    * @param file the file to unjar
-    * @return temp file
-    * @throws IOException for any io error
-    * @throws URISyntaxException for any uri error
-    */
-   public static VirtualFile unjar(VirtualFile file) throws IOException, URISyntaxException
-   {
-      return copy(file, UnjarCopyMechanism.INSTANCE);
-   }
-
-   /**
-    * Create temp.
-    *
-    * @param file the file to unpack/explode
-    * @param mechanism the copy mechanism
-    * @return temp file
-    * @throws IOException for any io error
-    * @throws URISyntaxException for any uri error
-    */
-   protected static VirtualFile copy(VirtualFile file, CopyMechanism mechanism) throws IOException, URISyntaxException
-   {
-      if (file == null)
-         throw new IllegalArgumentException("Null file");
-      if (mechanism == null)
-         throw new IllegalArgumentException("Null copy mechanism");
-
-      return mechanism.copy(file, file.getHandler());
-   }
-
-   /**
-    * Is file handle nested.
-    *
-    * @param file the file handle to check
-    * @return true if file/dir is nested otherwise false
-    * @throws IOException for any error
-    */
-   public static boolean isNestedFile(VirtualFile file) throws IOException
-   {
-      if (file == null)
-         throw new IllegalArgumentException("Null file");
-
-      VirtualFileHandler handler = file.getHandler();
-      return handler.isNested();
-   }
-
-   /**
     * Copy input stream to output stream and close them both
     *
     * @param is input stream
@@ -918,182 +399,59 @@
    }
 
    /**
-    * Get spec compatilbe url from virtual file.
+    * Get the virtual URL for a virtual file.  This URL can be used to access the virtual file; however, taking the
+    * file part of the URL and attempting to use it with the {@link java.io.File} class may fail if the file is not
+    * present on the physical filesystem, and in general should not be attempted.
     *
     * @param file the virtual file
-    * @return spec compatible url
-    * @throws Exception for any error
+    * @return the URL
+    * @throws MalformedURLException if the file cannot be coerced into a URL for some reason
     */
-   public static URL getCompatibleURL(VirtualFile file) throws Exception
+   public static URL getVirtualURL(VirtualFile file) throws MalformedURLException
    {
-      return getCompatibleResource(file, URL_CREATOR);
+      // todo: specify the URL handler directly as a minor optimization
+      return new URL("file", "", -1, file.getPathName());
    }
 
    /**
-    * Get spec compatilbe uri from virtual file.
+    * Get the virtual URI for a virtual file.
     *
     * @param file the virtual file
-    * @return spec compatible uri
-    * @throws Exception for any error
+    * @return the URI
+    * @throws URISyntaxException if the file cannot be coerced into a URI for some reason
     */
-   public static URI getCompatibleURI(VirtualFile file) throws Exception
+   public static URI getVirtualURI(VirtualFile file) throws URISyntaxException
    {
-      return getCompatibleResource(file, URI_CREATOR);
+      return new URI("file", "", file.getPathName(), null);
    }
 
    /**
-    * Create new compatible resource.
+    * Get a physical URL for a virtual file.  See the warnings on the {@link VirtualFile#getPhysicalFile()} method
+    * before using this method.
     *
     * @param file the virtual file
-    * @param creator resoruce creator
-    * @return new resource
-    * @throws Exception for any error
-    * @param <T> exact resource type
+    * @return the physical file URL
+    * @throws IOException if an I/O error occurs getting the physical file
     */
-   private static <T> T getCompatibleResource(VirtualFile file, ResourceCreator<T> creator) throws Exception
+   public static URL getPhysicalURL(VirtualFile file) throws IOException
    {
-      if (file == null)
-         throw new IllegalArgumentException("Null file");
-      if (creator == null)
-         throw new IllegalArgumentException("Null creator");
-
-      if (isNestedFile(file))
-      {
-         return creator.getNestedResource(file);
-      }
-      else
-      {
-         // is not nested, so direct VFS resource is not an option
-         return creator.getRealResource(file);
-      }
+      return getPhysicalURI(file).toURL();
    }
 
    /**
-    * @param <T> exact resource type
-    */
-   private static interface ResourceCreator<T>
-   {
-      /**
-       * Get resource from virtual file.
-       *
-       * @param file the virtual file
-       * @return resource instance from file
-       * @throws Exception for any error
-       */
-      T getNestedResource(VirtualFile file) throws Exception;
-
-      /**
-       * Get real resource from virtual file.
-       *
-       * @param file the virtual file
-       * @return resource instance from file
-       * @throws Exception for any error
-       */
-      T getRealResource(VirtualFile file) throws Exception;
-   }
-
-   private static final ResourceCreator<URL> URL_CREATOR = new ResourceCreator<URL>()
-   {
-      public URL getNestedResource(VirtualFile file) throws Exception
-      {
-         return file.toURL();
-      }
-
-      public URL getRealResource(VirtualFile file) throws Exception
-      {
-         return getRealURL(file);
-      }
-   };
-
-   private static final ResourceCreator<URI> URI_CREATOR = new ResourceCreator<URI>()
-   {
-      public URI getNestedResource(VirtualFile file) throws Exception
-      {
-         return file.toURI();
-      }
-
-      public URI getRealResource(VirtualFile file) throws Exception
-      {
-         return toURI(getRealURL(file));
-      }
-   };
-
-   /**
-    * Get real url.
-    * The closest thing that doesn't need the vfs url handlers.
+    * Get a physical URI for a virtual file.  See the warnings on the {@link VirtualFile#getPhysicalFile()} method
+    * before using this method.
     *
     * @param file the virtual file
-    * @return real url
-    * @throws IOException for any error
-    * @throws URISyntaxException for any uri syntac error
+    * @return the physical file URL
+    * @throws IOException if an I/O error occurs getting the physical file
     */
-   public static URL getRealURL(VirtualFile file) throws IOException, URISyntaxException
+   public static URI getPhysicalURI(VirtualFile file) throws IOException
    {
-      VirtualFileHandler handler = file.getHandler();
-      return handler.getRealURL();
+      return file.getPhysicalFile().toURI();
    }
 
    /**
-    * Get relative path.
-    *
-    * @param context the vfs context
-    * @param uri the uri
-    * @return uri's relative path to context's root
-    */
-   public static String getRelativePath(VFSContext context, URI uri)
-   {
-      String uriPath = stripProtocol(uri);
-      String contextKey = getKey(context);
-      return uriPath.substring(contextKey.length());
-   }
-
-   /**
-    * Strip protocol from url string.
-    *
-    * @param uri the uri
-    * @return uri's path string
-    */
-   public static String stripProtocol(URI uri)
-   {
-      String path = uri.getPath();
-      if(path == null)
-      {
-         String s = uri.toString();
-         if(s.startsWith("jar:file:"))
-            path = s.substring("jar:file:".length()).replaceFirst("!/", "/") + "/";
-      }
-      if (path != null && path.length() > 0)
-      {
-         StringBuilder sb = new StringBuilder(path);
-
-         if (sb.charAt(0) != '/')
-            sb.insert(0, '/');
-         if (sb.charAt(sb.length() - 1) != '/')
-            sb.append('/');
-
-         path = sb.toString();
-      }
-      else
-      {
-         path = "/";
-      }
-
-      return path;
-   }
-
-   /**
-    * Get path key.
-    *
-    * @param context the vfs context
-    * @return contex's root path w/o protocol
-    */
-   public static String getKey(VFSContext context)
-   {
-      URI uri = context.getRootURI();
-      return stripProtocol(uri);
-   }
-
-   /**
     * Safely close some resource without throwing an exception.  Any exception will be logged at TRACE level.
     *
     * @param c the resource

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-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/VirtualFile.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -19,26 +19,24 @@
   * 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 ;
+package org.jboss.virtual;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
+import java.io.File;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.ArrayList;
 
 import org.jboss.virtual.plugins.vfs.helpers.FilterVirtualFileVisitor;
 import org.jboss.virtual.plugins.vfs.helpers.MatchAllVirtualFileFilter;
-import org.jboss.util.collection.WeakSet;
+import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
 
 /**
- * A virtual file as seen by the user
+ * A virtual file.  This is a symbolic reference to a location in the virtual file system hierarchy.  Holding a
+ * {@code VirtualFile} instance gives no guarantees as to the presence or immutability of the referenced file or
+ * any of its parent path elements.
  *
  * @author Scott.Stark at jboss.org
  * @author adrian at jboss.org
@@ -48,126 +46,64 @@
 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 VFS vfs;
 
-   /** The virtual file handler */
-   private final VirtualFileHandler handler;
-
-   /** Whether we are closed */
-   private final AtomicBoolean closed = new AtomicBoolean(false);
-
-   /** The open streams */
-   private transient Set<InputStream> streams;
-
-   /**
-    * Create a new VirtualFile.
-    *
-    * @param handler the handler
-    * @throws IllegalArgumentException if the handler is null
-    */
-   public VirtualFile(VirtualFileHandler handler)
+   VirtualFile(VFS vfs, List<String> realPath, String realPathString)
    {
-      if (handler == null)
-         throw new IllegalArgumentException("Null handler");
-
-      this.handler = handler;
+      path = realPathString;
+      final int size = realPath.size();
+      name = size == 0 ? "" : realPath.get(size - 1);
+      tokens = realPath;
+      this.vfs = vfs;
    }
 
    /**
-    * Get the virtual file handler
-    *
-    * @return the handler
-    * @throws IllegalStateException if the file is closed or cleaned
-    */
-   VirtualFileHandler getHandler()
-   {
-      if (closed.get())
-         throw new IllegalStateException("The virtual file is closed");
-
-      return handler;
-   }
-
-   /**
     * Get the simple VF name (X.java)
     *
     * @return the simple file name
-    * @throws IllegalStateException if the file is closed
     */
    public String getName()
    {
-      return getHandler().getName();
+      return name;
    }
 
    /**
     * Get the VFS relative path name (org/jboss/X.java)
     *
     * @return the VFS relative path name
-    * @throws IllegalStateException if the file is closed
     */
    public String getPathName()
    {
-      return getHandler().getPathName();
+      return path;
    }
 
    /**
-    * Get the VF URL (file://root/org/jboss/X.java)
-    *
-    * @return the full URL to the VF in the VFS.
-    * @throws MalformedURLException if a url cannot be parsed
-    * @throws URISyntaxException if a uri cannot be parsed
-    * @throws IllegalStateException if the file is closed
-    */
-   public URL toURL() throws MalformedURLException, URISyntaxException
-   {
-      return getHandler().toVfsUrl();
-   }
-
-   /**
-    * Get the VF URI (file://root/org/jboss/X.java)
-    *
-    * @return the full URI to the VF in the VFS.
-    * @throws URISyntaxException if a uri cannot be parsed
-    * @throws IllegalStateException if the file is closed
-    * @throws MalformedURLException for a bad url
-    */
-   public URI toURI() throws MalformedURLException, URISyntaxException
-   {
-      return VFSUtils.toURI(toURL());
-   }
-
-   /**
     * When the file was last modified
     *
     * @return the last modified time
     * @throws IOException for any problem accessing the virtual file system
-    * @throws IllegalStateException if the file is closed
     */
    public long getLastModified() throws IOException
    {
-      return getHandler().getLastModified();
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().getLastModified(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
-    * Returns true if the file has been modified since this method was last called
-    * Last modified time is initialized at handler instantiation.
-    *
-    * @return true if modifed, false otherwise
-    * @throws IOException for any error
-    */
-   public boolean hasBeenModified() throws IOException
-   {
-      return getHandler().hasBeenModified();
-   }
-
-   /**
     * Get the size
     *
     * @return the size
     * @throws IOException for any problem accessing the virtual file system
-    * @throws IllegalStateException if the file is closed
     */
    public long getSize() throws IOException
    {
-      return getHandler().getSize();
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().getSize(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
@@ -177,7 +113,9 @@
     */
    public boolean exists() throws IOException
    {
-      return getHandler().exists();      
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().exists(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
@@ -186,34 +124,25 @@
     *
     * @return true if a simple file.
     * @throws IOException for any problem accessing the virtual file system
-    * @throws IllegalStateException if the file is closed
+    * @deprecated use {@link #isDirectory()} instead
     */
+   @Deprecated
    public boolean isLeaf() throws IOException
    {
-      return getHandler().isLeaf();
+      return ! isDirectory();
    }
 
    /**
-    * Is the file archive.
-    * 
-    * @return true if archive, false otherwise
-    * @throws IOException for any error
-    */
-   public boolean isArchive() throws IOException
-   {
-      return getHandler().isArchive();
-   }
-
-   /**
-    * Whether it is hidden
+    * Determine whether the named virtual file is a directory.
     *
-    * @return true when hidden
-    * @throws IOException for any problem accessing the virtual file system
-    * @throws IllegalStateException if the file is closed
+    * @return {@code true} if it is a directory, {@code false} otherwise
+    * @throws IOException if an I/O error occurs
     */
-   public boolean isHidden() throws IOException
+   public boolean isDirectory() throws IOException
    {
-      return getHandler().isHidden();
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().isDirectory(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
@@ -221,76 +150,15 @@
     *
     * @return an InputStream for the file contents.
     * @throws IOException for any error accessing the file system
-    * @throws IllegalStateException if the file is closed
     */
    public InputStream openStream() throws IOException
    {
-      InputStream result = getHandler().openStream();
-      checkStreams();
-      streams.add(result);
-      return result;
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().openInputStream(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
-    * Check if streams set exist.
-    */
-   @SuppressWarnings("unchecked")
-   protected void checkStreams()
-   {
-      if (streams == null)
-      {
-         synchronized (closed)
-         {
-            // double null check, so that possible
-            // waiting threads don't override streams
-            if (streams == null)
-               streams = Collections.synchronizedSet(new WeakSet());
-         }
-      }
-   }
-
-   /**
-    * Close the streams
-    */
-   public void closeStreams()
-   {
-      if (streams == null)
-         return;
-
-      // Close the streams
-      VFSUtils.safeClose(streams);
-      streams.clear();
-   }
-
-   /**
-    * Do file cleanup.
-    * e.g. delete temp files
-    */
-   public void cleanup()
-   {
-      try
-      {
-         getHandler().cleanup();
-      }
-      finally
-      {
-         VFS.cleanup(this);
-      }
-   }
-
-   /**
-    * Close the file resources (stream, etc.)
-    */
-   public void close()
-   {
-      if (closed.getAndSet(true) == false)
-      {
-         closeStreams();
-         handler.close();
-      }
-   }
-
-   /**
     * Delete this virtual file
     *
     * @return true if file was deleted
@@ -298,47 +166,54 @@
     */
    public boolean delete() throws IOException
    {
-      // gracePeriod of 2 seconds
-      return getHandler().delete(2000);
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().delete(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
-    * Delete this virtual file
+    * Get a physical file for this virtual file.  Depending on the underlying file system type, this may simply return
+    * an already-existing file; it may create a copy of a file; or it may reuse a preexisting copy of the file.  Furthermore,
+    * the retured file may or may not have any relationship to other files from the same or any other virtual
+    * directory.
     *
-    * @param gracePeriod max time to wait for any locks (in milliseconds)
-    * @return true if file was deleted
-    * @throws IOException if an error occurs
+    * @return the physical file
+    * @throws IOException if an I/O error occurs while producing the physical file
     */
-   public boolean delete(int gracePeriod) throws IOException
+   public File getPhysicalFile() throws IOException
    {
-      return getHandler().delete(gracePeriod);
+      final List<String> tokens = this.tokens;
+      final VFS.Mount mount = vfs.getMount(tokens);
+      return mount.getFileSystem().getFile(tokens.subList(mount.getRealMountPoint().size(), tokens.size()));
    }
 
    /**
     * Get the VFS instance for this virtual file
     *
     * @return the VFS
-    * @throws IllegalStateException if the file is closed
     */
    public VFS getVFS()
    {
-      VFSContext context = getHandler().getVFSContext();
-      return context.getVFS();
+      return vfs;
    }
 
    /**
-    * Get the parent
+    * Get a {@code VirtualFile} which represents the parent of this instance.
     *
-    * @return the parent or null if there is no parent
+    * @return the parent or {@code null} if there is no parent
     * @throws IOException for any problem accessing the virtual file system
-    * @throws IllegalStateException if the file is closed
     */
    public VirtualFile getParent() throws IOException
    {
-      VirtualFileHandler parent = getHandler().getParent();
-      if (parent != null)
-         return parent.getVirtualFile();
-      return null;
+      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('/')));
    }
 
    /**
@@ -346,7 +221,6 @@
     *
     * @return the children
     * @throws IOException for any problem accessing the virtual file system
-    * @throws IllegalStateException if the file is closed
     */
    public List<VirtualFile> getChildren() throws IOException
    {
@@ -363,7 +237,7 @@
     */
    public List<VirtualFile> getChildren(VirtualFileFilter filter) throws IOException
    {
-      if (isLeaf())
+      if (! isDirectory())
          return Collections.emptyList();
 
       if (filter == null)
@@ -399,7 +273,7 @@
     */
    public List<VirtualFile> getChildrenRecursively(VirtualFileFilter filter) throws IOException
    {
-      if (isLeaf())
+      if (! isDirectory())
          return Collections.emptyList();
 
       if (filter == null)
@@ -419,75 +293,56 @@
     */
    public void visit(VirtualFileVisitor visitor) throws IOException
    {
-      if (isLeaf() == false)
+      if (! isDirectory() == false)
          getVFS().visit(this, visitor);
    }
 
    /**
-    * Find a child
+    * Get a child virtual file.
     *
     * @param path the path
-    * @return the child
-    * @throws IOException for any problem accessing the VFS (including the child does not exist)
-    * @throws IllegalArgumentException if the path is null
-    * @throws IllegalStateException if the file is closed or it is a leaf node
-    * @deprecated use getChild, and handle null if not found
-    */
-   @Deprecated
-   public VirtualFile findChild(String path) throws IOException
-   {
-      if (path == null)
-         throw new IllegalArgumentException("Null path");
-
-      VirtualFileHandler handler = getHandler();      
-      VirtualFileHandler child = handler.getChild(VFSUtils.fixName(path));
-      if (child == null)
-      {
-         List<VirtualFileHandler> children = handler.getChildren(true);
-         throw new IOException("Child not found " + path + " for " + handler + ", available children: " + children);
-      }
-      return child.getVirtualFile();
-   }
-
-   /**
-    * Get a child
-    *
-    * @param path the path
-    * @return the child or <code>null</code> if not found
+    * @return the child or {@code null} if not found
     * @throws IOException for any problem accessing the VFS
     * @throws IllegalArgumentException if the path is null
-    * @throws IllegalStateException if the file is closed or it is a leaf node
     */
    public VirtualFile getChild(String path) throws IOException
    {
       if (path == null)
          throw new IllegalArgumentException("Null path");
 
-      VirtualFileHandler handler = getHandler();
-      VirtualFileHandler child = handler.getChild(VFSUtils.fixName(path));
-      return child != null ? child.getVirtualFile() : null;
+      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));
    }
 
    @Override
    public String toString()
    {
-      return handler.toString();
+      return "Virtual file \"" + path + "\" for " + vfs;
    }
 
    @Override
-   public int hashCode()
+   public boolean equals(Object o)
    {
-      return handler.hashCode();
+      if (this == o)
+         return true;
+      if (! (o instanceof VirtualFile))
+         return false;
+      VirtualFile that = (VirtualFile) o;
+      if (! path.equals(that.path))
+         return false;
+      if (vfs != that.vfs)
+         return false;
+      return true;
    }
 
    @Override
-   public boolean equals(Object obj)
+   public int hashCode()
    {
-      if (obj == this)
-         return true;
-      if (obj == null || obj instanceof VirtualFile == false)
-         return false;
-      VirtualFile other = (VirtualFile) obj;
-      return handler.equals(other.handler);
+      int result = path.hashCode();
+      result = 31 * result + vfs.hashCode();
+      return result;
    }
 }

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/plugins/vfs/helpers/PathTokenizer.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/plugins/vfs/helpers/PathTokenizer.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/plugins/vfs/helpers/PathTokenizer.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -22,7 +22,6 @@
 package org.jboss.virtual.plugins.vfs.helpers;
 
 import java.io.IOException;
-import java.security.Permission;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -43,12 +42,6 @@
    /** The reverse path const */
    private static final String REVERSE_PATH = "..";
 
-   /** Catch some suspicious tokens */
-   private static boolean errorOnSuspiciousTokens;
-
-   /** Flag permission */
-   private static Permission flagPermission = new RuntimePermission(PathTokenizer.class.getName() + ".setErrorOnSuspiciousTokens");
-
    /** Token states */
    private static final int STATE_INITIAL = 0;
    private static final int STATE_NORMAL = 1;
@@ -96,19 +89,29 @@
     * 
     * @param path the path
     * @return the tokens or null if the path is empty
-    * @throws IllegalArgumentException if the path is null, or if this class is configured to reject
-    *    so-called "suspicious" tokens (files that start with "." which are not one of the two special
-    *    directories "." or "..")
+    * @throws IllegalArgumentException if the path is null
     */
    public static List<String> getTokens(String path)
    {
       if (path == null)
          throw new IllegalArgumentException("Null path");
 
+      List<String> list = new ArrayList<String>();
+      getTokens(list, path);
+      return list;
+   }
+
+   /**
+    * Get the tokens that comprise this path and append them to the list.
+    *
+    * @param path the path
+    * @return the tokens or null if the path is empty
+    * @throws IllegalArgumentException if the path is null
+    */
+   public static void getTokens(List<String> list, String path)
+   {
       int start = -1, length = path.length(), state = STATE_INITIAL;
       char ch;
-      List<String> list = new ArrayList<String>();
-
       for (int index = 0; index < length; index ++) {
          ch = path.charAt(index);
          switch (ch) {
@@ -154,9 +157,6 @@
                   }
                   case STATE_MAYBE_REVERSE_PATH: {
                      // the third . in a row, guess it's just a weird path name
-                     if (errorOnSuspiciousTokens) {
-                        throw new IllegalArgumentException("Illegal suspicious token in path: " + path);
-                     }
                      state = STATE_NORMAL;
                      continue;
                   }
@@ -172,9 +172,6 @@
                   }
                   case STATE_MAYBE_CURRENT_PATH:
                   case STATE_MAYBE_REVERSE_PATH: {
-                     if (errorOnSuspiciousTokens) {
-                        throw new IllegalArgumentException("Illegal suspicious token in path: " + path);
-                     }
                      state = STATE_NORMAL;
                   }
                }
@@ -200,10 +197,9 @@
             break;
          }
       }
+      return;
+   }
 
-      return list;
-   }
-   
    /**
     * Get the remaining path from some tokens
     * 
@@ -252,6 +248,28 @@
    }
 
    /**
+    * Apply any . or .. paths in the pathTokens parameter, returning the minimal token list.
+    *
+    * @param pathTokens the path tokens
+    * @return the simple path tokens
+    * @throws IOException if reverse path goes over the top path
+    */
+   public static List<String> applySpecialPaths(List<String> pathTokens) throws IOException
+   {
+      final ArrayList<String> newTokens = new ArrayList<String>();
+      for (String pathToken : pathTokens)
+      {
+         if (isCurrentToken(pathToken))
+            continue;
+         else if (isReverseToken(pathToken))
+            newTokens.remove(newTokens.size() - 1);
+         else
+            newTokens.add(pathToken);
+      }
+      return newTokens;
+   }
+
+   /**
     * Is current token.
     *
     * @param token the token to check
@@ -272,18 +290,4 @@
    {
       return REVERSE_PATH == token;
    }
-
-   /**
-    * Set errorOnSuspiciousTokens flag.
-    *
-    * @param errorOnSuspiciousTokens the errorOnSuspiciousTokens flag
-    */
-   public static void setErrorOnSuspiciousTokens(boolean errorOnSuspiciousTokens)
-   {
-      SecurityManager sm = System.getSecurityManager();
-      if (sm != null)
-         sm.checkPermission(flagPermission);
-      
-      PathTokenizer.errorOnSuspiciousTokens = errorOnSuspiciousTokens;
-   }
 }

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfs/Handler.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfs/Handler.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfs/Handler.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -27,7 +27,6 @@
 import java.net.URLStreamHandler;
 
 import org.jboss.virtual.VirtualFile;
-import org.jboss.virtual.AssembledDirectory;
 import org.jboss.virtual.plugins.vfs.VirtualFileURLConnection;
 
 /**

Modified: projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfsmemory/Handler.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfsmemory/Handler.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/protocol/vfsmemory/Handler.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -26,7 +26,6 @@
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
 
-import org.jboss.virtual.MemoryFileFactory;
 import org.jboss.virtual.VFS;
 import org.jboss.virtual.VirtualFile;
 import org.jboss.virtual.plugins.vfs.VirtualFileURLConnection;

Added: 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	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/FileSystem.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -0,0 +1,97 @@
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.io.Closeable;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * A file system which is mounted in to the VFS.  This is the driver class for a given virtual file system type.  An
+ * instance of {@code FileSystem} will be mounted at some point on a VFS.  The specific instance is only called when
+ * a file from this filesystem is called upon.  The path components passed in to the operations are canonical, with
+ * no "." or ".." components.
+ */
+public interface FileSystem extends Closeable
+{
+   /**
+    * Get a real {@code File} for the given path within this filesystem.  Some filesystem types will need to make a copy
+    * in order to return this file; such copies should be cached and retained until the filesystem is closed.  Depending
+    * 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
+    * @return the file instance
+    * @throws IOException if an I/O error occurs
+    */
+   File getFile(List<String> pathComponents) throws IOException;
+
+   /**
+    * Open an input stream for the file at the given relative path.
+    *
+    * @param pathComponents the relative path components
+    * @return the input stream
+    * @throws IOException if an I/O error occurs
+    */
+   InputStream openInputStream(List<String> pathComponents) throws IOException;
+
+   /**
+    * Determine whether this filesystem is read-only.  A read-only filesystem prohibits file modification or
+    * deletion.  It is not an error to mount a read-write filesystem within a read-only filesystem however (this
+    * operation does not take place within the {@code FileSystem} implementation).
+    *
+    * @return {@code true} if the filesystem is read-only
+    */
+   boolean isReadOnly();
+
+   boolean delete(List<String> pathComponents) throws IOException;
+
+   long getSize(List<String> pathComponents) throws IOException;
+
+   long getLastModified(List<String> pathComponents) throws IOException;
+
+   boolean exists(List<String> pathComponents) throws IOException;
+
+   boolean isDirectory(List<String> pathComponents) 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
+    * @throws IOException if an I/O error occurs
+    */
+   Iterator<String> getDirectoryEntries(List<String> directoryPathComponents) throws IOException;
+
+   /**
+    * Destroy this filesystem instance.  After this method is called, the filesystem may not be used in any way.  This
+    * method should be called only after all mounts of this filesystem have been cleared; otherwise, VFS accesses may
+    * result in {@code IOException}s.
+    *
+    * @throws IOException if an I/O error occurs during close
+    */
+   void close() throws IOException;
+}

Added: 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	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/RealFileSystem.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -0,0 +1,114 @@
+/*
+ * 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.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());
+
+   private final String base;
+
+   private RealFileSystem(List<String> baseComponents)
+   {
+      base = implode("", baseComponents) + File.separator;
+   }
+
+   public File getFile(List<String> pathComponents) throws IOException
+   {
+      return new File(implode(base, pathComponents));
+   }
+
+   private static String implode(String base, List<String> pathComponents)
+   {
+      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();
+   }
+
+   public InputStream openInputStream(List<String> pathComponents) throws IOException
+   {
+      return new FileInputStream(getFile(pathComponents));
+   }
+
+   public boolean isReadOnly()
+   {
+      return false;
+   }
+
+   public boolean delete(List<String> pathComponents) throws IOException
+   {
+      return getFile(pathComponents).delete();
+   }
+
+   public long getSize(List<String> pathComponents) throws IOException
+   {
+      return getFile(pathComponents).length();
+   }
+
+   public long getLastModified(List<String> pathComponents) throws IOException
+   {
+      return getFile(pathComponents).lastModified();
+   }
+
+   public boolean exists(List<String> pathComponents) throws IOException
+   {
+      return getFile(pathComponents).exists();
+   }
+
+   public boolean isDirectory(List<String> pathComponents) throws IOException
+   {
+      return getFile(pathComponents).isDirectory();
+   }
+
+   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
+   }
+}

Added: 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	                        (rev 0)
+++ projects/vfs/branches/dml-zip-rework/src/main/java/org/jboss/virtual/spi/ZipFileSystem.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -0,0 +1,230 @@
+/*
+ * 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);
+      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;
+            ZipNode child = children.get(token);
+            if (child == null)
+            {
+               child = it.hasNext() || zipEntry.getEntryType() == ZipEntryType.DIRECTORY ? new ZipNode(new HashMap<String, ZipNode>(), null) : new ZipNode(null, zipEntry);
+            }
+         }
+      }
+      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();
+      }
+      return zipNode;
+   }
+
+   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
+   {
+      return null;
+   }
+
+   public void close() throws IOException
+   {
+      
+   }
+
+   private static final class ZipNode {
+      private final Map<String, ZipNode> children;
+      private final ZipEntry entry;
+      private volatile File cachedFile;
+
+      private ZipNode(Map<String, ZipNode> children, ZipEntry entry)
+      {
+         this.children = children;
+         this.entry = entry;
+      }
+
+      private ZipNode find(Iterator<String> node) {
+         if (node.hasNext())
+         {
+            final ZipNode next = children.get(node.next());
+            return next == null ? null : next.find(node);
+         }
+         else
+         {
+            return this;
+         }
+      }
+   }
+}

Modified: projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/AssembledContextTestCase.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/AssembledContextTestCase.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/AssembledContextTestCase.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -27,7 +27,6 @@
 
 import junit.framework.Test;
 import org.jboss.virtual.VirtualFile;
-import org.jboss.virtual.AssembledDirectory;
 
 /**
  * comment

Modified: projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -50,7 +50,6 @@
 import org.jboss.test.virtual.support.ClassPathIterator;
 import org.jboss.test.virtual.support.MetaDataMatchFilter;
 import org.jboss.test.virtual.support.ClassPathIterator.ClassPathEntry;
-import org.jboss.virtual.MemoryFileFactory;
 import org.jboss.virtual.VFS;
 import org.jboss.virtual.VFSUtils;
 import org.jboss.virtual.VirtualFile;

Modified: projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/MemoryTestCase.java
===================================================================
--- projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/MemoryTestCase.java	2009-07-01 22:28:40 UTC (rev 90752)
+++ projects/vfs/branches/dml-zip-rework/src/test/java/org/jboss/test/virtual/test/MemoryTestCase.java	2009-07-02 01:28:49 UTC (rev 90753)
@@ -33,7 +33,6 @@
 import junit.framework.Test;
 import org.jboss.util.collection.Iterators;
 import org.jboss.util.id.GUID;
-import org.jboss.virtual.MemoryFileFactory;
 import org.jboss.virtual.VFS;
 import org.jboss.virtual.VirtualFile;
 




More information about the jboss-cvs-commits mailing list