Package org.jboss.virtual

Source Code of org.jboss.virtual.VFSUtils

/*
* 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.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
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;
import org.jboss.virtual.plugins.copy.CopyMechanism;
import org.jboss.virtual.plugins.copy.ExplodedCopyMechanism;
import org.jboss.virtual.plugins.copy.TempCopyMechanism;
import org.jboss.virtual.plugins.copy.UnjarCopyMechanism;
import org.jboss.virtual.plugins.copy.UnpackCopyMechanism;
import org.jboss.virtual.plugins.vfs.helpers.PathTokenizer;
import org.jboss.virtual.spi.LinkInfo;
import org.jboss.virtual.spi.Options;
import org.jboss.virtual.spi.VFSContext;
import org.jboss.virtual.spi.VirtualFileHandler;
import org.jboss.virtual.spi.cache.VFSCacheFactory;

/**
* VFS Utilities
*
* @author <a href="adrian@jboss.com">Adrian Brock</a>
* @author <a href="ales.justin@jboss.com">Ales Justin</a>
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
* @version $Revision: 1.1 $
*/
public class VFSUtils
{
   /** The log */
   private static final Logger log = Logger.getLogger(VFSUtils.class);

   /** 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 force canonical lookup
    */
   public static final String FORCE_CANONICAL = "jboss.vfs.forceCanonical";

   /**
    * 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";
  
   /** 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()
   {
      VFSCacheFactory.getInstance().stop();
   }

   /**
    * Get the paths string for a collection of virtual files
    *
    * @param paths the paths
    * @return the string
    * @throws IllegalArgumentException for null paths
    */
   public static String getPathsString(Collection<VirtualFile> paths)
   {
      if (paths == null)
         throw new IllegalArgumentException("Null paths");

      StringBuilder buffer = new StringBuilder();
      boolean first = true;
      for (VirtualFile path : paths)
      {
         if (path == null)
            throw new IllegalArgumentException("Null path in " + paths);
         if (first == false)
            buffer.append(':');
         else
            first = false;
         buffer.append(path.getPathName());
      }

      if (first == true)
         buffer.append("<empty>");

      return buffer.toString();
   }

   /**
    * Add manifest paths
    *
    * @param file the file
    * @param paths the paths to add to
    * @throws IOException if there is an error reading the manifest or the
    *         virtual file is closed
    * @throws IllegalStateException if the file has no parent
    * @throws IllegalArgumentException for a null file or paths
    */
   public static void addManifestLocations(VirtualFile file, List<VirtualFile> paths) throws IOException
   {
      if (file == null)
         throw new IllegalArgumentException("Null file");
      if (paths == null)
         throw new IllegalArgumentException("Null paths");

      boolean trace = log.isTraceEnabled();
     
      Manifest manifest = getManifest(file);
      if (manifest == null)
         return;

      Attributes mainAttributes = manifest.getMainAttributes();
      String classPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH);

      if (classPath == null)
      {
         if (trace)
            log.trace("Manifest has no Class-Path for " + file.getPathName());
         return;
      }

      VirtualFile parent = file.getParent();
      if (parent == null)
      {
         log.debug(file + " has no parent.");
         return;
      }

      if (trace)
         log.trace("Parsing Class-Path: " + classPath + " for " + file.getName() + " parent=" + parent.getName());
     
      StringTokenizer tokenizer = new StringTokenizer(classPath);
      while (tokenizer.hasMoreTokens())
      {
         String path = tokenizer.nextToken();
         try
         {
            VirtualFile vf = parent.getChild(path);
            if(vf != null)
            {
               if(paths.contains(vf) == false)
               {
                  paths.add(vf);
                  // Recursively process the jar
                  addManifestLocations(vf, paths);
               }
               else if (trace)
                  log.trace(vf.getName() + " from manifiest is already in the classpath " + paths);
            }
            else if (trace)
               log.trace("Unable to find " + path + " from " + parent.getName());
         }
         catch (IOException e)
         {
            log.debug("Manifest Class-Path entry " + path + " ignored for " + file.getPathName() + " reason=" + e);
         }
      }
   }

   /**
    * Get a manifest from a virtual file,
    * assuming the virtual file is the root of an archive
    *
    * @param archive the root the archive
    * @return the manifest or null if not found
    * @throws IOException if there is an error reading the manifest or the
    *         virtual file is closed
    * @throws IllegalArgumentException for a null archive
    */
   public static Manifest getManifest(VirtualFile archive) throws IOException
   {
      if (archive == null)
         throw new IllegalArgumentException("Null archive");

      VirtualFile manifest = archive.getChild(JarFile.MANIFEST_NAME);
      if (manifest == null)
      {
         if (log.isTraceEnabled())
            log.trace("Can't find manifest for " + archive.getPathName());
         return null;
      }
      return readManifest(manifest);
   }

   /**
    * Read the manifest from given manifest VirtualFile.
    *
    * @param manifest the VF to read from
    * @return JAR's manifest
    * @throws IOException if problems while opening VF stream occur
    */
   public static Manifest readManifest(VirtualFile manifest) throws IOException
   {
      if (manifest == null)
         throw new IllegalArgumentException("Null manifest file");

      InputStream stream = manifest.openStream();
      try
      {
         return new Manifest(stream);
      }
      finally
      {
         try
         {
            stream.close();
         }
         catch (IOException ignored)
         {
         }
      }
   }

   /**
     * 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
    * @return the fixed name
    * @throws IllegalArgumentException for a null name
    */
   public static String fixName(String name)
   {
      if (name == null)
         throw new IllegalArgumentException("Null name");

      int length = name.length();
      if (length <= 1)
         return name;
      if (name.charAt(length-1) == '/')
         return name.substring(0, length-1);

      return name;
   }

   /**
    * Get the name.
    *
    * @param uri the uri
    * @return name from uri's path
    */
   public static String getName(URI uri)
   {
      if (uri == null)
         throw new IllegalArgumentException("Null uri");

      String name = uri.getPath();
      if( name != null )
      {
         // TODO: Not correct for certain uris like jar:...!/
         int lastSlash = name.lastIndexOf('/');
         if( lastSlash > 0 )
            name = name.substring(lastSlash+1);
      }
      return name;
   }

   /**
    * Take a URL.getQuery string and parse it into name=value pairs
    *
    * @param query Possibly empty/null url query string
    * @return String[] for the name/value pairs in the query. May be empty but never null.
    */
   public static Map<String, String> parseURLQuery(String query)
   {
     Map<String, String> pairsMap = CollectionsFactory.createLazyMap();
      if(query != null)
      {
        StringTokenizer tokenizer = new StringTokenizer(query, "=&");
        while(tokenizer.hasMoreTokens())
        {
          String name = tokenizer.nextToken();
          String value = tokenizer.nextToken();
          pairsMap.put(name, value);
        }
      }
     return pairsMap;
   }

   /**
    * 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
    * @return uri the uri
    * @throws URISyntaxException for any error
    */
   public static URI toURI(URL url) throws URISyntaxException
   {
      if (url == null)
         throw new IllegalArgumentException("Null url");

      try
      {
         return url.toURI();
      }
      catch (URISyntaxException e)
      {
         String urispec = url.toExternalForm();
         // Escape percent sign and spaces
         urispec = urispec.replaceAll("%", "%25");
         urispec = urispec.replaceAll(" ", "%20");
         return new URI(urispec);
      }
   }

   /**
    * Ensure the url is convertible to URI by encoding spaces and percent characters if necessary
    *
    * @param url to be sanitized
    * @return sanitized URL
    * @throws URISyntaxException if URI conversion can't be fixed
    * @throws MalformedURLException if an error occurs
    */
   public static URL sanitizeURL(URL url) throws URISyntaxException, MalformedURLException
   {
      return toURI(url).toURL();
   }

   /**
    * 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
    * @param os output stream
    * @throws IOException for any error
    */
   public static void copyStreamAndClose(InputStream is, OutputStream os) throws IOException
   {
      try
      {
         copyStream(is, os);
      }
      finally
      {
         if (is != null)
         {
            try
            {
               is.close();
            }
            catch(IOException ignored)
            {
            }
         }
         if (os != null)
            os.close();
      }
   }

   /**
    * Copy input stream to output stream without closing streams.
    * Flushes output stream when done.
    *
    * @param is input stream
    * @param os output stream
    * @throws IOException for any error
    */
   public static void copyStream(InputStream is, OutputStream os) throws IOException
   {
      if (is == null)
         throw new IllegalArgumentException("input stream is null");
      if (os == null)
         throw new IllegalArgumentException("output stream is null");

      try
      {
         byte [] buff = new byte[65536];
         int rc = is.read(buff);
         while (rc != -1)
         {
            os.write(buff, 0, rc);
            rc = is.read(buff);
         }
      }
      finally
      {
         os.flush();
      }
   }

   /**
    * Get spec compatilbe url from virtual file.
    *
    * @param file the virtual file
    * @return spec compatible url
    * @throws Exception for any error
    */
   public static URL getCompatibleURL(VirtualFile file) throws Exception
   {
      return getCompatibleResource(file, URL_CREATOR);
   }

   /**
    * Get spec compatilbe uri from virtual file.
    *
    * @param file the virtual file
    * @return spec compatible uri
    * @throws Exception for any error
    */
   public static URI getCompatibleURI(VirtualFile file) throws Exception
   {
      return getCompatibleResource(file, URI_CREATOR);
   }

   /**
    * Create new compatible resource.
    *
    * @param file the virtual file
    * @param creator resoruce creator
    * @return new resource
    * @throws Exception for any error
    * @param <T> exact resource type
    */
   private static <T> T getCompatibleResource(VirtualFile file, ResourceCreator<T> creator) throws Exception
   {
      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);
      }
   }

   /**
    * @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.
    *
    * @param file the virtual file
    * @return real url
    * @throws IOException for any error
    * @throws URISyntaxException for any uri syntac error
    */
   public static URL getRealURL(VirtualFile file) throws IOException, URISyntaxException
   {
      VirtualFileHandler handler = file.getHandler();
      return handler.getRealURL();
   }

   /**
    * 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());
   }

   /**
    * Get path.
    * Apply Carlo's fix as well.
    *
    * @param uri the uri
    * @return uri's path
    */
   protected static String getPath(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("!/", "/") + "/";
      }
      return path;
   }

   /**
    * Strip protocol from url string, return tokens.
    *
    * @param uri the uri
    * @return uri's path string tokens
    */
   public static List<String> stripProtocolToTokens(URI uri)
   {
      String path = getPath(uri);
      try
      {
         return PathTokenizer.applySpecialPathsToTokens(path);
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
   }

   /**
    * Strip protocol from url string.
    *
    * @param uri the uri
    * @return uri's path string
    */
   public static String stripProtocol(URI uri)
   {
      List<String> tokens = stripProtocolToTokens(uri);
      if (tokens.isEmpty() == false)
      {
         StringBuilder builder = new StringBuilder();
         for (String token : tokens)
         {
            builder.append("/").append(token);
         }
         builder.append("/");
         return builder.toString();
      }
      else
      {
         return "/";
      }
   }

   /**
    * 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);
   }
  
   /**
    * Returns a string of the contents of the virtual file, showing the names of all
    * the nested directories and names, useful for debugging
    *
    * @param file the virtual file to display the contents of
    * @return A string containing the contents of the virtual file
    * @throws RuntimeException if an error happened
    */
   public static String outputContents(VirtualFile file)
   {
      StringWriter writer = new StringWriter();
      outputContents(file, writer);
      return writer.toString();
   }

   /**
    * Output contents to writer.
    *
    * @param file the file
    * @param writer the writer
    */
   public static void outputContents(VirtualFile file, Writer writer)
   {
      if (writer == null)
         throw new IllegalArgumentException("Null writer.");

      try
      {
         //writer.write("Contents of " + file.toURI() + "\n");
         VirtualFileOutputter.visit(file, writer);
      }
      catch (Exception e)
      {
         throw new RuntimeException("ERROR displaying the contents of " + file.getName(), e);
      }
   }

   /**
    * Output visitor.
    */
   private static class VirtualFileOutputter implements VirtualFileVisitor
   {
      private Writer writer;
      private Map<VirtualFile, Integer> levels;

      private VirtualFileOutputter(Writer writer, Map<VirtualFile, Integer> levels)
      {
         this.writer = writer;
         // prepare levels
         this.levels = levels;
      }

      static void visit(VirtualFile file, Writer writer) throws IOException
      {
         Map<VirtualFile, Integer> levels = new HashMap<VirtualFile, Integer>();
         levels.put(file.getParent(), 0);
         VirtualFileOutputter visitor = new VirtualFileOutputter(writer, levels);
         file.visit(visitor);
      }

      public VisitorAttributes getAttributes()
      {
         VisitorAttributes attributes = new VisitorAttributes();
         attributes.setIncludeRoot(true);
         attributes.setRecurseFilter(VisitorAttributes.RECURSE_ALL);
         return attributes;
      }

      public void visit(VirtualFile file)
      {
         try
         {
            VirtualFile parent = file.getParent();
            int level = levels.get(parent);
            String suffix = "";
            if (file.isLeaf() == false)
            {
               suffix = "/";
               levels.put(file, level + 1);
            }
            String string = file.getName() + suffix;
            writeToBuffer(level, string);
         }
         catch (IOException e)
         {
            throw new RuntimeException("Cannot handle file: " + file, e);
         }
      }
     
      private void writeToBuffer(int level, String string) throws IOException
      {
         for (int i = 0 ; i < level ; i++)
            writer.append("  ");
        
         writer.append(string);
         writer.append("\n");
      }
   }
}
TOP

Related Classes of org.jboss.virtual.VFSUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.