* Added FileObject.copy(). * Renamed <v-copy> task attributes file -> srcfile, tofile -> destfile, todir -> destdir. * Moved provider instantiation out of DefaultFileSystemManager, and into myrmidon-aware VfsManager. Providers are instantiated using the TypeManager. The list of providers isn't configurable yet. * Some support for %nn encoded URI (not quite complete). * Zip file system now handles zip files from any file system, not just local files. Still read-only at this stage. Uses a truely dodgy and very temporary replication mechanism. * Zip file system now handles relative paths in URI (e.g. zip:relpath.zip), that are resolved against the base dir. * Fixed bug in resolving names against the root file of a file system. * Changed behaviour of FileName.resolveName( ".." ) for the root file of a file system. * Added more test cases. * A bucketload of other minor changes. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271803 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -557,6 +557,9 @@ Legal: | |||||
| <zip zipfile="${test.vfs.dir}/test.zip"> | <zip zipfile="${test.vfs.dir}/test.zip"> | ||||
| <fileset dir="${test.vfs.dir}" includes="basedir/**"/> | <fileset dir="${test.vfs.dir}" includes="basedir/**"/> | ||||
| </zip> | </zip> | ||||
| <zip zipfile="${test.vfs.dir}/nested.zip"> | |||||
| <fileset dir="${test.vfs.dir}" includes="test.zip"/> | |||||
| </zip> | |||||
| <!-- Prepare deployer tests --> | <!-- Prepare deployer tests --> | ||||
| <property name="test.deployer.dir" | <property name="test.deployer.dir" | ||||
| @@ -117,15 +117,22 @@ | |||||
| <blockquote> | <blockquote> | ||||
| <p>The VFS needs plenty of work:</p> | <p>The VFS needs plenty of work:</p> | ||||
| <ul> | <ul> | ||||
| <li>Move and copy files/folders.</li> | |||||
| <li>Move files/folders.</li> | |||||
| <li>Recursive folders copy.</li> | |||||
| <li>Search through a file hierarchy, using Ant-style wildcards.</li> | <li>Search through a file hierarchy, using Ant-style wildcards.</li> | ||||
| <li>Search through a file hierarchy, using a Selector interface.</li> | <li>Search through a file hierarchy, using a Selector interface.</li> | ||||
| <li>The in-memory caching mechanism is pretty rudimentary at this stage. | <li>The in-memory caching mechanism is pretty rudimentary at this stage. | ||||
| It needs work to make it size capped. In addition, some mechanism needs | It needs work to make it size capped. In addition, some mechanism needs | ||||
| to be provided to release and refresh cached info. | to be provided to release and refresh cached info. | ||||
| </li> | </li> | ||||
| <li>Convert files/folders into local files, for handing off | |||||
| to external commands, or legacy tasks.</li> | |||||
| <li>Refactor the replication mechanism out of ZipFileSystemProvder, | |||||
| and make more general pluggable.</li> | |||||
| <li>Capabilities discovery.</li> | <li>Capabilities discovery.</li> | ||||
| <li>Attributes and attribute schema.</li> | <li>Attributes and attribute schema.</li> | ||||
| <li>Handle file canonicalisation better (for cases like case-insensitive | |||||
| file systems, symbolic links, name encoding, etc).</li> | |||||
| <li>File system layering. That is, the ability for a file system to | <li>File system layering. That is, the ability for a file system to | ||||
| sit on top of another file system, or a file from another file system | sit on top of another file system, or a file from another file system | ||||
| (e.g. Zip/Jar/Tar file systems, gzip/encoding file systems, virtual file | (e.g. Zip/Jar/Tar file systems, gzip/encoding file systems, virtual file | ||||
| @@ -300,10 +307,6 @@ | |||||
| <p>A completely unordered list of items, big and small:</p> | <p>A completely unordered list of items, big and small:</p> | ||||
| <ul> | <ul> | ||||
| <li>Search through the code for 'TODO' items and fix them.</li> | <li>Search through the code for 'TODO' items and fix them.</li> | ||||
| <li>Tidy-up CLIMain so that it calls System.exit() with a non-zero exit code, | |||||
| if the build fails.</li> | |||||
| <li>Tidy-up the 'build failed' message, so that the stack trace is only | |||||
| printed out if the log level is verbose/debug.</li> | |||||
| <li>Allow service factories to be configured from the contents of the | <li>Allow service factories to be configured from the contents of the | ||||
| <code>ant-services.xml</code> descriptor.</li> | <code>ant-services.xml</code> descriptor.</li> | ||||
| <li>Route external process stdout and stderr through the logger.</li> | <li>Route external process stdout and stderr through the logger.</li> | ||||
| @@ -314,11 +317,10 @@ | |||||
| <li>Fire ProjectListener events projectStarted() and projectFinished() | <li>Fire ProjectListener events projectStarted() and projectFinished() | ||||
| events on start and finish of referenced projects, adding indicator methods | events on start and finish of referenced projects, adding indicator methods | ||||
| to ProjectEvent.</li> | to ProjectEvent.</li> | ||||
| <li>Convert PropertyUtil to a non-static PropertyResolver service.</li> | |||||
| <li>Validate project and target names in DefaultProjectBuilder - reject dodgy | <li>Validate project and target names in DefaultProjectBuilder - reject dodgy | ||||
| names like "," or "", or " ". Probably want to exclude names that start or | |||||
| names like "," or "", or " ". Probably want to reject names that start or | |||||
| end with white-space (though internal whitespace is probably fine). We also | end with white-space (though internal whitespace is probably fine). We also | ||||
| want to reserve certain punctuation characters like . , : ? [ ] { }, etc for | |||||
| want to reserve certain punctuation characters like , : ? $ [ ] { } < >, etc for | |||||
| future use.</li> | future use.</li> | ||||
| <li>Similarly, validate property names, using the same rules.</li> | <li>Similarly, validate property names, using the same rules.</li> | ||||
| <li>Detect duplicate type names.</li> | <li>Detect duplicate type names.</li> | ||||
| @@ -330,6 +332,7 @@ | |||||
| an antlib.</li> | an antlib.</li> | ||||
| <li>Split up <code><is-set></code> condition into is-set and is-true conditions.</li> | <li>Split up <code><is-set></code> condition into is-set and is-true conditions.</li> | ||||
| <li>Allow the <code><if></code> task to take any condition implementation.</li> | <li>Allow the <code><if></code> task to take any condition implementation.</li> | ||||
| <li>Add an else block to the <code><if></code> task.</li> | |||||
| <li>Unit tests.</li> | <li>Unit tests.</li> | ||||
| </ul> | </ul> | ||||
| </blockquote> | </blockquote> | ||||
| @@ -125,7 +125,8 @@ files are found in the <code>lib</code> directory:</p> | |||||
| <td bgcolor="#a0ddf0" colspan="" rowspan="" | <td bgcolor="#a0ddf0" colspan="" rowspan="" | ||||
| valign="top" align="left"> | valign="top" align="left"> | ||||
| <font color="#000000" size="-1" face="arial,helvetica,sanserif"> | <font color="#000000" size="-1" face="arial,helvetica,sanserif"> | ||||
| <a href="http://jcifs.samba.org">jcifs.samba.org</a> | |||||
| <a href="http://jcifs.samba.org">jcifs.samba.org</a>. | |||||
| <p>Note: there are problems using the 0.6.1 release. Try 0.6.0 instead.</p> | |||||
| </font> | </font> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -1,7 +1,7 @@ | |||||
| <project version="2.0"> | <project version="2.0"> | ||||
| <target name="copy"> | <target name="copy"> | ||||
| <v-fileset id="src-files" dir="src"/> | <v-fileset id="src-files" dir="src"/> | ||||
| <v-copy todir="dest"> | |||||
| <v-copy destdir="dest"> | |||||
| <v-fileset-ref id="src-files"/> | <v-fileset-ref id="src-files"/> | ||||
| </v-copy> | </v-copy> | ||||
| </target> | </target> | ||||
| @@ -7,8 +7,6 @@ | |||||
| */ | */ | ||||
| package org.apache.antlib.vfile; | package org.apache.antlib.vfile; | ||||
| import java.io.InputStream; | |||||
| import java.io.OutputStream; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.Iterator; | import java.util.Iterator; | ||||
| import org.apache.aut.vfs.FileObject; | import org.apache.aut.vfs.FileObject; | ||||
| @@ -41,7 +39,7 @@ public class CopyFilesTask | |||||
| /** | /** | ||||
| * Sets the source file. | * Sets the source file. | ||||
| */ | */ | ||||
| public void setFile( final FileObject file ) | |||||
| public void setSrcfile( final FileObject file ) | |||||
| { | { | ||||
| m_srcFile = file; | m_srcFile = file; | ||||
| } | } | ||||
| @@ -49,7 +47,7 @@ public class CopyFilesTask | |||||
| /** | /** | ||||
| * Sets the destination file. | * Sets the destination file. | ||||
| */ | */ | ||||
| public void setTofile( final FileObject file ) | |||||
| public void setDestfile( final FileObject file ) | |||||
| { | { | ||||
| m_destFile = file; | m_destFile = file; | ||||
| } | } | ||||
| @@ -57,11 +55,19 @@ public class CopyFilesTask | |||||
| /** | /** | ||||
| * Sets the destination directory. | * Sets the destination directory. | ||||
| */ | */ | ||||
| public void setTodir( final FileObject file ) | |||||
| public void setDestdir( final FileObject file ) | |||||
| { | { | ||||
| m_destDir = file; | m_destDir = file; | ||||
| } | } | ||||
| /** | |||||
| * Sets the source directory. | |||||
| */ | |||||
| public void setSrcdir( final FileObject dir ) | |||||
| { | |||||
| add( new DefaultFileSet( dir ) ); | |||||
| } | |||||
| /** | /** | ||||
| * Adds a source file set. | * Adds a source file set. | ||||
| */ | */ | ||||
| @@ -107,7 +113,8 @@ public class CopyFilesTask | |||||
| m_destFile = m_destDir.resolveFile( m_srcFile.getName().getBaseName() ); | m_destFile = m_destDir.resolveFile( m_srcFile.getName().getBaseName() ); | ||||
| } | } | ||||
| copyFile( m_srcFile, m_destFile ); | |||||
| getLogger().info( "copy " + m_srcFile + " to " + m_destFile ); | |||||
| m_destFile.copy( m_srcFile ); | |||||
| } | } | ||||
| // Copy the contents of the filesets across | // Copy the contents of the filesets across | ||||
| @@ -134,7 +141,8 @@ public class CopyFilesTask | |||||
| final FileObject destFile = m_destDir.resolveFile( path, NameScope.DESCENDENT ); | final FileObject destFile = m_destDir.resolveFile( path, NameScope.DESCENDENT ); | ||||
| // Copy the file across | // Copy the file across | ||||
| copyFile( srcFile, destFile ); | |||||
| getLogger().info( "copy " + srcFile + " to " + destFile ); | |||||
| destFile.copy( srcFile ); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -144,42 +152,4 @@ public class CopyFilesTask | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Copies a file. | |||||
| */ | |||||
| private void copyFile( final FileObject srcFile, final FileObject destFile ) | |||||
| throws TaskException | |||||
| { | |||||
| getLogger().info( "copy " + srcFile + " to " + destFile ); | |||||
| try | |||||
| { | |||||
| // TODO - move copy behind FileObject interface | |||||
| InputStream instr = srcFile.getContent().getInputStream(); | |||||
| try | |||||
| { | |||||
| OutputStream outstr = destFile.getContent().getOutputStream(); | |||||
| byte[] buffer = new byte[ 4096 ]; | |||||
| while( true ) | |||||
| { | |||||
| int nread = instr.read( buffer ); | |||||
| if( nread == -1 ) | |||||
| { | |||||
| break; | |||||
| } | |||||
| outstr.write( buffer, 0, nread ); | |||||
| } | |||||
| outstr.close(); | |||||
| } | |||||
| finally | |||||
| { | |||||
| instr.close(); | |||||
| } | |||||
| } | |||||
| catch( Exception exc ) | |||||
| { | |||||
| final String message = REZ.getString( "copyfilestask.copy-file.error", srcFile, destFile ); | |||||
| throw new TaskException( message, exc ); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -36,6 +36,15 @@ public class DefaultFileSet | |||||
| private FileObject m_dir; | private FileObject m_dir; | ||||
| private final AndFileSelector m_selector = new AndFileSelector(); | private final AndFileSelector m_selector = new AndFileSelector(); | ||||
| public DefaultFileSet() | |||||
| { | |||||
| } | |||||
| public DefaultFileSet( final FileObject dir ) | |||||
| { | |||||
| m_dir = dir; | |||||
| } | |||||
| /** | /** | ||||
| * Sets the root directory. | * Sets the root directory. | ||||
| */ | */ | ||||
| @@ -7,6 +7,7 @@ | |||||
| */ | */ | ||||
| package org.apache.antlib.vfile; | package org.apache.antlib.vfile; | ||||
| import java.util.ArrayList; | |||||
| import org.apache.aut.vfs.FileObject; | import org.apache.aut.vfs.FileObject; | ||||
| import org.apache.myrmidon.api.AbstractTask; | import org.apache.myrmidon.api.AbstractTask; | ||||
| import org.apache.myrmidon.api.TaskException; | import org.apache.myrmidon.api.TaskException; | ||||
| @@ -22,11 +23,11 @@ import org.apache.myrmidon.api.TaskException; | |||||
| public class ListFileSetTask | public class ListFileSetTask | ||||
| extends AbstractTask | extends AbstractTask | ||||
| { | { | ||||
| private FileSet m_fileSet; | |||||
| private final ArrayList m_fileSets = new ArrayList(); | |||||
| public void set( final FileSet fileSet ) | |||||
| public void add( final FileSet fileSet ) | |||||
| { | { | ||||
| m_fileSet = fileSet; | |||||
| m_fileSets.add( fileSet ); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -35,14 +36,19 @@ public class ListFileSetTask | |||||
| public void execute() | public void execute() | ||||
| throws TaskException | throws TaskException | ||||
| { | { | ||||
| FileSetResult result = m_fileSet.getResult( getContext() ); | |||||
| final FileObject[] files = result.getFiles(); | |||||
| final String[] paths = result.getPaths(); | |||||
| for( int i = 0; i < files.length; i++ ) | |||||
| final int count = m_fileSets.size(); | |||||
| for( int i = 0; i < count; i++ ) | |||||
| { | { | ||||
| final FileObject file = files[ i ]; | |||||
| final String path = paths[ i ]; | |||||
| getLogger().info( path + " = " + file ); | |||||
| final FileSet fileSet = (FileSet)m_fileSets.get(i ); | |||||
| FileSetResult result = fileSet.getResult( getContext() ); | |||||
| final FileObject[] files = result.getFiles(); | |||||
| final String[] paths = result.getPaths(); | |||||
| for( int j = 0; j < files.length; j++ ) | |||||
| { | |||||
| final FileObject file = files[ j ]; | |||||
| final String path = paths[ j ]; | |||||
| getLogger().info( path + " = " + file ); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -6,6 +6,5 @@ fileset.list-files.error=Could not list the files in folder "{0}". | |||||
| copyfilestask.no-source.error=No source files specified for {0} task. | copyfilestask.no-source.error=No source files specified for {0} task. | ||||
| copyfilestask.no-destination.error=No destination file or directory specified for {0} task. | copyfilestask.no-destination.error=No destination file or directory specified for {0} task. | ||||
| copyfilestask.no-destination.error=No destination directory specified for {0} task. | copyfilestask.no-destination.error=No destination directory specified for {0} task. | ||||
| copyfilestask.copy-file.error=Could not copy "{0}" to "{1}". | |||||
| filteredfilelist.no-selector.error=No filter criteria specified. | filteredfilelist.no-selector.error=No filter criteria specified. | ||||
| @@ -181,6 +181,20 @@ public interface FileObject | |||||
| */ | */ | ||||
| void create( FileType type ) throws FileSystemException; | void create( FileType type ) throws FileSystemException; | ||||
| /** | |||||
| * Copies the content of another file to this file. | |||||
| * | |||||
| * If this file does not exist, it is created. Its parent folder is also | |||||
| * created, if necessary. If this file does exist, its content is replaced. | |||||
| * | |||||
| * @param file The file to copy the content from. | |||||
| * | |||||
| * @throws FileSystemException | |||||
| * If this file is read-only, or is a folder, or if the supplied file | |||||
| * is not a file, or on error copying the content. | |||||
| */ | |||||
| void copy( FileObject file ) throws FileSystemException; | |||||
| /** | /** | ||||
| * Returns this file's content. The {@link FileContent} returned by this | * Returns this file's content. The {@link FileContent} returned by this | ||||
| * method can be used to read and write the content of the file. | * method can be used to read and write the content of the file. | ||||
| @@ -189,6 +203,12 @@ public interface FileObject | |||||
| * the returned {@link FileContent} can be used to create the file | * the returned {@link FileContent} can be used to create the file | ||||
| * by writing its content. | * by writing its content. | ||||
| * | * | ||||
| * @todo Do not throw an exception if this file is a folder. Instead, | |||||
| * throw the exceptions when (if) any methods on the returned object | |||||
| * are called. This is to hand 2 cases: when the folder is deleted | |||||
| * and recreated as a file, and to allow attributes of the folder | |||||
| * to be set (last modified time, permissions, etc). | |||||
| * | |||||
| * @return | * @return | ||||
| * This file's content. | * This file's content. | ||||
| * | * | ||||
| @@ -71,7 +71,8 @@ public interface FileSystemManager | |||||
| * @throws FileSystemException | * @throws FileSystemException | ||||
| * On error parsing the file name. | * On error parsing the file name. | ||||
| */ | */ | ||||
| FileObject resolveFile( String name ) throws FileSystemException; | |||||
| FileObject resolveFile( String name ) | |||||
| throws FileSystemException; | |||||
| /** | /** | ||||
| * Locates a file by name. The name is resolved as described | * Locates a file by name. The name is resolved as described | ||||
| @@ -90,7 +91,8 @@ public interface FileSystemManager | |||||
| * @throws FileSystemException | * @throws FileSystemException | ||||
| * On error parsing the file name. | * On error parsing the file name. | ||||
| */ | */ | ||||
| FileObject resolveFile( FileObject baseFile, String name ) throws FileSystemException; | |||||
| FileObject resolveFile( FileObject baseFile, String name ) | |||||
| throws FileSystemException; | |||||
| /** | /** | ||||
| * Locates a file by name. See {@link #resolveFile(FileObject, String)} | * Locates a file by name. See {@link #resolveFile(FileObject, String)} | ||||
| @@ -106,5 +108,39 @@ public interface FileSystemManager | |||||
| * On error parsing the file name. | * On error parsing the file name. | ||||
| * | * | ||||
| */ | */ | ||||
| FileObject resolveFile( File baseFile, String name ) throws FileSystemException; | |||||
| FileObject resolveFile( File baseFile, String name ) | |||||
| throws FileSystemException; | |||||
| /** | |||||
| * Converts a local file into a {@link FileObject}. | |||||
| * | |||||
| * @param file | |||||
| * The file to convert. | |||||
| * | |||||
| * @return | |||||
| * The {@link FileObject} that represents the local file. | |||||
| * | |||||
| * @throws FileSystemException | |||||
| * On error converting the file. | |||||
| */ | |||||
| FileObject convert( File file ) | |||||
| throws FileSystemException; | |||||
| /** | |||||
| * Creates a layered file system. A layered file system is a file system | |||||
| * that is created from the contents of another file file, such as a zip | |||||
| * or tar file. | |||||
| * | |||||
| * @param provider | |||||
| * The name of the file system provider to use. This name is | |||||
| * the same as the scheme used in URI to identify the provider. | |||||
| * | |||||
| * @param file | |||||
| * The file to use to create the file system. | |||||
| * | |||||
| * @throws FileSystemException | |||||
| * On error creating the file system. | |||||
| */ | |||||
| FileObject createFileSystem( String provider, FileObject file ) | |||||
| throws FileSystemException; | |||||
| } | } | ||||
| @@ -9,14 +9,11 @@ package org.apache.aut.vfs.impl; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.Iterator; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| import org.apache.aut.vfs.FileObject; | import org.apache.aut.vfs.FileObject; | ||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.FileSystemManager; | import org.apache.aut.vfs.FileSystemManager; | ||||
| import org.apache.aut.vfs.provider.FileSystem; | |||||
| import org.apache.aut.vfs.provider.FileSystemProvider; | import org.apache.aut.vfs.provider.FileSystemProvider; | ||||
| import org.apache.aut.vfs.provider.FileSystemProviderContext; | |||||
| import org.apache.aut.vfs.provider.UriParser; | import org.apache.aut.vfs.provider.UriParser; | ||||
| import org.apache.aut.vfs.provider.local.LocalFileSystemProvider; | import org.apache.aut.vfs.provider.local.LocalFileSystemProvider; | ||||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | import org.apache.avalon.excalibur.i18n.ResourceManager; | ||||
| @@ -25,7 +22,8 @@ import org.apache.avalon.excalibur.i18n.Resources; | |||||
| /** | /** | ||||
| * A default file system manager implementation. | * A default file system manager implementation. | ||||
| * | * | ||||
| * @author Adam Murdoch | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| * @version $Revision$ $Date$ | |||||
| */ | */ | ||||
| public class DefaultFileSystemManager | public class DefaultFileSystemManager | ||||
| implements FileSystemManager | implements FileSystemManager | ||||
| @@ -40,69 +38,55 @@ public class DefaultFileSystemManager | |||||
| private final Map m_providers = new HashMap(); | private final Map m_providers = new HashMap(); | ||||
| /** The provider context. */ | /** The provider context. */ | ||||
| private final ProviderContextImpl m_context = new ProviderContextImpl(); | |||||
| private final DefaultProviderContext m_context = new DefaultProviderContext( this ); | |||||
| /** The base file to use for relative URI. */ | /** The base file to use for relative URI. */ | ||||
| private FileObject m_baseFile; | private FileObject m_baseFile; | ||||
| /** | |||||
| * The cached file systems. This is a mapping from root URI to | |||||
| * FileSystem object. | |||||
| */ | |||||
| private final Map m_fileSystems = new HashMap(); | |||||
| public DefaultFileSystemManager() throws Exception | |||||
| public DefaultFileSystemManager() | |||||
| { | { | ||||
| // Create the local provider | // Create the local provider | ||||
| m_localFileProvider = new LocalFileSystemProvider(); | m_localFileProvider = new LocalFileSystemProvider(); | ||||
| m_providers.put( "file", m_localFileProvider ); | m_providers.put( "file", m_localFileProvider ); | ||||
| m_localFileProvider.setContext( m_context ); | |||||
| } | |||||
| // TODO - make this list configurable | |||||
| // Create the providers | |||||
| FileSystemProvider provider = createProvider( "org.apache.aut.vfs.provider.zip.ZipFileSystemProvider" ); | |||||
| if( provider != null ) | |||||
| { | |||||
| m_providers.put( "zip", provider ); | |||||
| m_providers.put( "jar", provider ); | |||||
| } | |||||
| provider = createProvider( "org.apache.aut.vfs.provider.smb.SmbFileSystemProvider" ); | |||||
| if( provider != null ) | |||||
| { | |||||
| m_providers.put( "smb", provider ); | |||||
| } | |||||
| provider = createProvider( "org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider" ); | |||||
| if( provider != null ) | |||||
| { | |||||
| m_providers.put( "ftp", provider ); | |||||
| } | |||||
| // Contextualise the providers | |||||
| for( Iterator iterator = m_providers.values().iterator(); iterator.hasNext(); ) | |||||
| { | |||||
| provider = (FileSystemProvider)iterator.next(); | |||||
| provider.setContext( m_context ); | |||||
| } | |||||
| /** | |||||
| * Registers a file system provider. | |||||
| */ | |||||
| public void addProvider( final String urlScheme, | |||||
| final FileSystemProvider provider ) | |||||
| throws FileSystemException | |||||
| { | |||||
| addProvider( new String[] { urlScheme }, provider ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Creates a provider instance, returns null if the provider class is | |||||
| * not found. | |||||
| * Registers a file system provider. | |||||
| */ | */ | ||||
| private FileSystemProvider createProvider( final String className ) | |||||
| throws Exception | |||||
| public void addProvider( final String[] urlSchemes, | |||||
| final FileSystemProvider provider ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| try | |||||
| // Check for duplicates | |||||
| for( int i = 0; i < urlSchemes.length; i++ ) | |||||
| { | { | ||||
| // TODO - wrap exceptions | |||||
| return (FileSystemProvider)Class.forName( className ).newInstance(); | |||||
| final String scheme = urlSchemes[i ]; | |||||
| if( m_providers.containsKey( scheme ) ) | |||||
| { | |||||
| final String message = REZ.getString( "multiple-providers-for-scheme.error", scheme ); | |||||
| throw new FileSystemException( message ); | |||||
| } | |||||
| } | } | ||||
| catch( ClassNotFoundException e ) | |||||
| // Contextualise | |||||
| provider.setContext( m_context ); | |||||
| // Add to map | |||||
| for( int i = 0; i < urlSchemes.length; i++ ) | |||||
| { | { | ||||
| // This is fine, for now | |||||
| return null; | |||||
| final String scheme = urlSchemes[ i ]; | |||||
| m_providers.put( scheme, provider ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -152,7 +136,7 @@ public class DefaultFileSystemManager | |||||
| public FileObject resolveFile( final File baseFile, final String uri ) | public FileObject resolveFile( final File baseFile, final String uri ) | ||||
| throws FileSystemException | throws FileSystemException | ||||
| { | { | ||||
| final FileObject baseFileObj = m_localFileProvider.findFileByLocalName( baseFile ); | |||||
| final FileObject baseFileObj = m_localFileProvider.findLocalFile( baseFile ); | |||||
| return resolveFile( baseFileObj, uri ); | return resolveFile( baseFileObj, uri ); | ||||
| } | } | ||||
| @@ -170,14 +154,17 @@ public class DefaultFileSystemManager | |||||
| final FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme ); | final FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme ); | ||||
| if( provider != null ) | if( provider != null ) | ||||
| { | { | ||||
| return provider.findFile( uri ); | |||||
| return provider.findFile( baseFile, uri ); | |||||
| } | } | ||||
| } | } | ||||
| // Decode the URI (remove %nn encodings) | |||||
| final String decodedUri = UriParser.decode( uri ); | |||||
| // Handle absolute file names | // Handle absolute file names | ||||
| if( m_localFileProvider.isAbsoluteLocalName( uri ) ) | |||||
| if( m_localFileProvider.isAbsoluteLocalName( decodedUri ) ) | |||||
| { | { | ||||
| return m_localFileProvider.findLocalFile( uri ); | |||||
| return m_localFileProvider.findLocalFile( decodedUri ); | |||||
| } | } | ||||
| // Assume a bad scheme | // Assume a bad scheme | ||||
| @@ -193,32 +180,31 @@ public class DefaultFileSystemManager | |||||
| final String message = REZ.getString( "find-rel-file.error", uri ); | final String message = REZ.getString( "find-rel-file.error", uri ); | ||||
| throw new FileSystemException( message ); | throw new FileSystemException( message ); | ||||
| } | } | ||||
| return baseFile.resolveFile( uri ); | |||||
| return baseFile.resolveFile( decodedUri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * A provider context implementation. | |||||
| * Converts a local file into a {@link FileObject}. | |||||
| */ | */ | ||||
| private final class ProviderContextImpl | |||||
| implements FileSystemProviderContext | |||||
| public FileObject convert( final File file ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| /** | |||||
| * Locates a cached file system by root URI. | |||||
| */ | |||||
| public FileSystem getFileSystem( final String rootURI ) | |||||
| { | |||||
| // TODO - need to have a per-fs uri comparator | |||||
| return (FileSystem)m_fileSystems.get( rootURI ); | |||||
| } | |||||
| return m_localFileProvider.findLocalFile( file ); | |||||
| } | |||||
| /** | |||||
| * Registers a file system for caching. | |||||
| */ | |||||
| public void putFileSystem( final String rootURI, final FileSystem fs ) | |||||
| throws FileSystemException | |||||
| /** | |||||
| * Creates a layered file system. | |||||
| */ | |||||
| public FileObject createFileSystem( final String scheme, | |||||
| final FileObject file ) | |||||
| throws FileSystemException | |||||
| { | |||||
| FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme ); | |||||
| if( provider == null ) | |||||
| { | { | ||||
| // TODO - should really check that there's not one already cached | |||||
| m_fileSystems.put( rootURI, fs ); | |||||
| final String message = REZ.getString( "unknown-provider.error", scheme ); | |||||
| throw new FileSystemException( message ); | |||||
| } | } | ||||
| return provider.createFileSystem( scheme, file ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,66 @@ | |||||
| /* | |||||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
| * | |||||
| * This software is published under the terms of the Apache Software License | |||||
| * version 1.1, a copy of which has been included with this distribution in | |||||
| * the LICENSE.txt file. | |||||
| */ | |||||
| package org.apache.aut.vfs.impl; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import org.apache.aut.vfs.FileObject; | |||||
| import org.apache.aut.vfs.FileSystemException; | |||||
| import org.apache.aut.vfs.provider.FileSystem; | |||||
| import org.apache.aut.vfs.provider.FileSystemProviderContext; | |||||
| /** | |||||
| * A provider context implementation. | |||||
| * | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| * @version $Revision$ $Date$ | |||||
| */ | |||||
| final class DefaultProviderContext | |||||
| implements FileSystemProviderContext | |||||
| { | |||||
| private final DefaultFileSystemManager m_manager; | |||||
| /** | |||||
| * The cached file systems. This is a mapping from root URI to | |||||
| * FileSystem object. | |||||
| */ | |||||
| private final Map m_fileSystems = new HashMap(); | |||||
| public DefaultProviderContext( final DefaultFileSystemManager manager ) | |||||
| { | |||||
| m_manager = manager; | |||||
| } | |||||
| /** | |||||
| * Locate a file by name. | |||||
| */ | |||||
| public FileObject resolveFile( final FileObject baseFile, final String name ) | |||||
| throws FileSystemException | |||||
| { | |||||
| return m_manager.resolveFile( baseFile, name ); | |||||
| } | |||||
| /** | |||||
| * Locates a cached file system by root URI. | |||||
| */ | |||||
| public FileSystem getFileSystem( final String rootURI ) | |||||
| { | |||||
| // TODO - need to have a per-fs uri comparator | |||||
| return (FileSystem)m_fileSystems.get( rootURI ); | |||||
| } | |||||
| /** | |||||
| * Registers a file system for caching. | |||||
| */ | |||||
| public void putFileSystem( final String rootURI, final FileSystem fs ) | |||||
| throws FileSystemException | |||||
| { | |||||
| // TODO - should really check that there's not one already cached | |||||
| m_fileSystems.put( rootURI, fs ); | |||||
| } | |||||
| } | |||||
| @@ -1,3 +1,5 @@ | |||||
| # DefaultFileSystemManager | # DefaultFileSystemManager | ||||
| unknown-scheme.error=Unknown scheme "{0}" in URI "{1}". | unknown-scheme.error=Unknown scheme "{0}" in URI "{1}". | ||||
| find-rel-file.error=Could not find file with URI "{0}" because it is a relative path, and no base URI was provided. | find-rel-file.error=Could not find file with URI "{0}" because it is a relative path, and no base URI was provided. | ||||
| multiple-providers-for-scheme.error=Multiple file system providers registered for URL scheme "{0}". | |||||
| unknown-provider.error=Unknown file system provider "{0}". | |||||
| @@ -21,6 +21,7 @@ import org.apache.aut.vfs.FileType; | |||||
| import org.apache.aut.vfs.NameScope; | import org.apache.aut.vfs.NameScope; | ||||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | import org.apache.avalon.excalibur.i18n.ResourceManager; | ||||
| import org.apache.avalon.excalibur.i18n.Resources; | import org.apache.avalon.excalibur.i18n.Resources; | ||||
| import org.apache.avalon.excalibur.io.IOUtil; | |||||
| /** | /** | ||||
| * A partial file object implementation. | * A partial file object implementation. | ||||
| @@ -349,7 +350,7 @@ public abstract class AbstractFileObject | |||||
| } | } | ||||
| // Update cached info | // Update cached info | ||||
| updateType( null ); | |||||
| updateType(); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -463,7 +464,41 @@ public abstract class AbstractFileObject | |||||
| } | } | ||||
| // Update cached info | // Update cached info | ||||
| updateType( type ); | |||||
| updateType(); | |||||
| } | |||||
| /** | |||||
| * Copies the content of another file to this file. | |||||
| */ | |||||
| public void copy( final FileObject file ) throws FileSystemException | |||||
| { | |||||
| try | |||||
| { | |||||
| final InputStream instr = file.getContent().getInputStream(); | |||||
| try | |||||
| { | |||||
| // Create the output strea via getContent(), to pick up the | |||||
| // validation it does | |||||
| final OutputStream outstr = getContent().getOutputStream(); | |||||
| try | |||||
| { | |||||
| IOUtil.copy( instr, outstr ); | |||||
| } | |||||
| finally | |||||
| { | |||||
| IOUtil.shutdownStream( outstr ); | |||||
| } | |||||
| } | |||||
| finally | |||||
| { | |||||
| IOUtil.shutdownStream( instr ); | |||||
| } | |||||
| } | |||||
| catch( final Exception exc ) | |||||
| { | |||||
| final String message = REZ.getString( "copy-file.error", file.getName(), m_name ); | |||||
| throw new FileSystemException( message, exc ); | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -598,14 +633,14 @@ public abstract class AbstractFileObject | |||||
| */ | */ | ||||
| public void endOutput() throws Exception | public void endOutput() throws Exception | ||||
| { | { | ||||
| updateType( FileType.FILE ); | |||||
| updateType(); | |||||
| doEndOutput(); | doEndOutput(); | ||||
| } | } | ||||
| /** | /** | ||||
| * Update cached info when this file's type changes. | * Update cached info when this file's type changes. | ||||
| */ | */ | ||||
| private void updateType( FileType type ) | |||||
| private void updateType() | |||||
| { | { | ||||
| // Notify parent that its child list may no longer be valid | // Notify parent that its child list may no longer be valid | ||||
| notifyParent(); | notifyParent(); | ||||
| @@ -23,13 +23,21 @@ public abstract class AbstractFileSystemProvider | |||||
| private final static Resources REZ = | private final static Resources REZ = | ||||
| ResourceManager.getPackageResources( AbstractFileSystemProvider.class ); | ResourceManager.getPackageResources( AbstractFileSystemProvider.class ); | ||||
| protected FileSystemProviderContext m_context; | |||||
| private FileSystemProviderContext m_context; | |||||
| /** | |||||
| * Returns the context for this provider. | |||||
| */ | |||||
| protected FileSystemProviderContext getContext() | |||||
| { | |||||
| return m_context; | |||||
| } | |||||
| /** | /** | ||||
| * Sets the context for this file system provider. This method is called | * Sets the context for this file system provider. This method is called | ||||
| * before any of the other provider methods. | * before any of the other provider methods. | ||||
| */ | */ | ||||
| public void setContext( FileSystemProviderContext context ) | |||||
| public void setContext( final FileSystemProviderContext context ) | |||||
| { | { | ||||
| m_context = context; | m_context = context; | ||||
| } | } | ||||
| @@ -40,13 +48,14 @@ public abstract class AbstractFileSystemProvider | |||||
| * @param uri | * @param uri | ||||
| * The absolute URI of the file to find. | * The absolute URI of the file to find. | ||||
| */ | */ | ||||
| public FileObject findFile( String uri ) throws FileSystemException | |||||
| public FileObject findFile( final FileObject baseFile, | |||||
| final String uri ) throws FileSystemException | |||||
| { | { | ||||
| // Parse the URI | // Parse the URI | ||||
| ParsedUri parsedURI = null; | |||||
| ParsedUri parsedUri = null; | |||||
| try | try | ||||
| { | { | ||||
| parsedURI = parseURI( uri ); | |||||
| parsedUri = parseUri( baseFile, uri ); | |||||
| } | } | ||||
| catch( FileSystemException exc ) | catch( FileSystemException exc ) | ||||
| { | { | ||||
| @@ -54,31 +63,70 @@ public abstract class AbstractFileSystemProvider | |||||
| throw new FileSystemException( message, exc ); | throw new FileSystemException( message, exc ); | ||||
| } | } | ||||
| // Locate the file | |||||
| return findFile( parsedUri ); | |||||
| } | |||||
| /** | |||||
| * Locates a file from its parsed URI. | |||||
| */ | |||||
| private FileObject findFile( final ParsedUri parsedUri ) | |||||
| throws FileSystemException | |||||
| { | |||||
| // Check in the cache for the file system | // Check in the cache for the file system | ||||
| FileSystem fs = m_context.getFileSystem( parsedURI.getRootURI() ); | |||||
| final String rootUri = parsedUri.getRootUri(); | |||||
| FileSystem fs = m_context.getFileSystem( rootUri ); | |||||
| if( fs == null ) | if( fs == null ) | ||||
| { | { | ||||
| // Need to create the file system | |||||
| fs = createFileSystem( parsedURI ); | |||||
| m_context.putFileSystem( parsedURI.getRootURI(), fs ); | |||||
| // Need to create the file system, and cache it | |||||
| fs = createFileSystem( parsedUri ); | |||||
| m_context.putFileSystem( rootUri, fs ); | |||||
| } | } | ||||
| // Locate the file | // Locate the file | ||||
| return fs.findFile( parsedURI.getPath() ); | |||||
| return fs.findFile( parsedUri.getPath() ); | |||||
| } | |||||
| /** | |||||
| * Creates a layered file system. | |||||
| */ | |||||
| public FileObject createFileSystem( final String scheme, final FileObject file ) | |||||
| throws FileSystemException | |||||
| { | |||||
| // TODO - this is a pretty shonky model for layered FS; need to revise | |||||
| // Build the URI | |||||
| final ParsedUri uri = buildUri( scheme, file ); | |||||
| // Locate the file | |||||
| return findFile( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Parses a URI into its components. The returned value is used to | * Parses a URI into its components. The returned value is used to | ||||
| * locate the file system in the cache (using the root prefix), and is | |||||
| * passed to {@link #createFileSystem} to create the file system. | |||||
| * locate the file system in the cache (using the root prefix). | |||||
| * | * | ||||
| * <p>The provider can annotate this object with any additional | * <p>The provider can annotate this object with any additional | ||||
| * information it requires to create a file system from the URI. | * information it requires to create a file system from the URI. | ||||
| */ | */ | ||||
| protected abstract ParsedUri parseURI( String uri ) throws FileSystemException; | |||||
| protected abstract ParsedUri parseUri( final FileObject baseFile, final String uri ) | |||||
| throws FileSystemException; | |||||
| /** | |||||
| * Builds the URI for the root of a layered file system. | |||||
| */ | |||||
| protected ParsedUri buildUri( final String scheme, | |||||
| final FileObject file ) | |||||
| throws FileSystemException | |||||
| { | |||||
| final String message = REZ.getString( "not-layered-fs.error" ); | |||||
| throw new FileSystemException( message ); | |||||
| } | |||||
| /** | /** | ||||
| * Creates the filesystem. | * Creates the filesystem. | ||||
| */ | */ | ||||
| protected abstract org.apache.aut.vfs.provider.FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException; | |||||
| protected abstract FileSystem createFileSystem( final ParsedUri uri ) | |||||
| throws FileSystemException; | |||||
| } | } | ||||
| @@ -12,6 +12,11 @@ import org.apache.aut.vfs.FileSystemException; | |||||
| /** | /** | ||||
| * A file system provider, or factory. | * A file system provider, or factory. | ||||
| * | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| * @version $Revision$ $Date$ | |||||
| * | |||||
| * @ant:role shorthand="file-system" | |||||
| */ | */ | ||||
| public interface FileSystemProvider | public interface FileSystemProvider | ||||
| { | { | ||||
| @@ -24,8 +29,16 @@ public interface FileSystemProvider | |||||
| /** | /** | ||||
| * Locates a file object, by absolute URI. | * Locates a file object, by absolute URI. | ||||
| * | * | ||||
| * @param baseFile | |||||
| * The base file to use for resolving the individual parts of | |||||
| * a compound URI. | |||||
| * @param uri | * @param uri | ||||
| * The absolute URI of the file to find. | * The absolute URI of the file to find. | ||||
| */ | */ | ||||
| FileObject findFile( String uri ) throws FileSystemException; | |||||
| FileObject findFile( FileObject baseFile, String uri ) throws FileSystemException; | |||||
| /** | |||||
| * Creates a layered file system. | |||||
| */ | |||||
| FileObject createFileSystem( String scheme, FileObject file ) throws FileSystemException; | |||||
| } | } | ||||
| @@ -7,16 +7,27 @@ | |||||
| */ | */ | ||||
| package org.apache.aut.vfs.provider; | package org.apache.aut.vfs.provider; | ||||
| import org.apache.aut.vfs.FileObject; | |||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.FileSystemManager; | |||||
| /** | /** | ||||
| * Used for a file system provider to access the services it needs, such | * Used for a file system provider to access the services it needs, such | ||||
| * as the file system cache or other file system providers. | * as the file system cache or other file system providers. | ||||
| * | * | ||||
| * @author Adam Murdoch | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| * @version $Revision$ $Date$ | |||||
| */ | */ | ||||
| public interface FileSystemProviderContext | public interface FileSystemProviderContext | ||||
| { | { | ||||
| /** | |||||
| * Locate a file by name. See | |||||
| * {@link FileSystemManager#resolveFile(FileObject, String)} for a | |||||
| * description of how this works. | |||||
| */ | |||||
| FileObject resolveFile( FileObject baseFile, String name ) | |||||
| throws FileSystemException; | |||||
| /** | /** | ||||
| * Locates a cached file system by root URI. | * Locates a cached file system by root URI. | ||||
| */ | */ | ||||
| @@ -34,13 +34,13 @@ public class ParsedUri | |||||
| } | } | ||||
| /** Returns the root URI, used to identify the file system. */ | /** Returns the root URI, used to identify the file system. */ | ||||
| public String getRootURI() | |||||
| public String getRootUri() | |||||
| { | { | ||||
| return m_rootURI; | return m_rootURI; | ||||
| } | } | ||||
| /** Sets the root URI. */ | /** Sets the root URI. */ | ||||
| public void setRootURI( String rootPrefix ) | |||||
| public void setRootUri( String rootPrefix ) | |||||
| { | { | ||||
| m_rootURI = rootPrefix; | m_rootURI = rootPrefix; | ||||
| } | } | ||||
| @@ -17,6 +17,7 @@ write-read-only.error=Could not write to "{0}" because it is read-only. | |||||
| write-folder.error=Could not write to "{0}" because it is a folder. | write-folder.error=Could not write to "{0}" because it is a folder. | ||||
| write-in-use.error=Could not write to "{0}" because it is already in use. | write-in-use.error=Could not write to "{0}" because it is already in use. | ||||
| write.error=Could not write to "{0}". | write.error=Could not write to "{0}". | ||||
| copy-file.error=Could not copy "{0}" to "{1}". | |||||
| # DefaultFileContent | # DefaultFileContent | ||||
| get-size-no-exist.error=Could not determine the size of file "{0}" because it does not exist. | get-size-no-exist.error=Could not determine the size of file "{0}" because it does not exist. | ||||
| @@ -30,6 +31,7 @@ close-outstr.error=Could not close file output stream. | |||||
| # AbstractFileSystemProvider | # AbstractFileSystemProvider | ||||
| invalid-absolute-uri.error=Invalid absolute URI "{0}". | invalid-absolute-uri.error=Invalid absolute URI "{0}". | ||||
| not-layered-fs.error=File system is not a layered file system. | |||||
| # UriParser | # UriParser | ||||
| missing-double-slashes.error=Expecting // to follow the scheme in URI "{0}". | missing-double-slashes.error=Expecting // to follow the scheme in URI "{0}". | ||||
| @@ -38,3 +40,5 @@ missing-port.error=Port number is missing from URI "{0}". | |||||
| missing-hostname-path-sep.error=Expecting / to follow the hostname in URI "{0}". | missing-hostname-path-sep.error=Expecting / to follow the hostname in URI "{0}". | ||||
| invalid-childname.error=Invalid file base-name "{0}". | invalid-childname.error=Invalid file base-name "{0}". | ||||
| invalid-descendent-name.error=Invalid descendent file name "{0}". | invalid-descendent-name.error=Invalid descendent file name "{0}". | ||||
| invalid-escape-sequence.error=Invalid URI escape sequence "{0}". | |||||
| invalid-relative-path.error=Invalid relative file name. | |||||
| @@ -84,14 +84,41 @@ public class UriParser | |||||
| * Parses an absolute URI, splitting it into its components. This | * Parses an absolute URI, splitting it into its components. This | ||||
| * implementation assumes a "generic URI", as defined by RFC 2396. See | * implementation assumes a "generic URI", as defined by RFC 2396. See | ||||
| * {@link #parseGenericUri} for more info. | * {@link #parseGenericUri} for more info. | ||||
| * | |||||
| * <p>Sub-classes should override this method. | |||||
| */ | */ | ||||
| public ParsedUri parseUri( final String uriStr ) throws FileSystemException | public ParsedUri parseUri( final String uriStr ) throws FileSystemException | ||||
| { | { | ||||
| final ParsedUri retval = new ParsedUri(); | |||||
| parseGenericUri( uriStr, retval ); | |||||
| return retval; | |||||
| // Parse the URI | |||||
| final ParsedUri uri = new ParsedUri(); | |||||
| parseGenericUri( uriStr, uri ); | |||||
| // Build the root URI | |||||
| final StringBuffer rootUri = new StringBuffer(); | |||||
| appendRootUri( uri, rootUri ); | |||||
| uri.setRootUri( rootUri.toString() ); | |||||
| return uri; | |||||
| } | |||||
| /** | |||||
| * Assembles a generic URI, appending to the supplied StringBuffer. | |||||
| */ | |||||
| protected void appendRootUri( final ParsedUri uri, final StringBuffer rootUri ) | |||||
| { | |||||
| rootUri.append( uri.getScheme() ); | |||||
| rootUri.append( "://" ); | |||||
| final String userInfo = uri.getUserInfo(); | |||||
| if( userInfo != null && userInfo.length() != 0 ) | |||||
| { | |||||
| rootUri.append( userInfo ); | |||||
| rootUri.append( "@" ); | |||||
| } | |||||
| rootUri.append( uri.getHostName() ); | |||||
| final String port = uri.getPort(); | |||||
| if( port != null && port.length() > 0 ) | |||||
| { | |||||
| rootUri.append( ":" ); | |||||
| rootUri.append( port ); | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -119,7 +146,8 @@ public class UriParser | |||||
| // Extract the scheme and authority parts | // Extract the scheme and authority parts | ||||
| extractToPath( uriStr, name, uri ); | extractToPath( uriStr, name, uri ); | ||||
| // Normalise the file name | |||||
| // Decode and normalise the file name | |||||
| decode( name, 0, name.length() ); | |||||
| normalisePath( name ); | normalisePath( name ); | ||||
| uri.setPath( name.toString() ); | uri.setPath( name.toString() ); | ||||
| @@ -128,7 +156,7 @@ public class UriParser | |||||
| rootUri.append( uri.getScheme() ); | rootUri.append( uri.getScheme() ); | ||||
| rootUri.append( "://" ); | rootUri.append( "://" ); | ||||
| rootUri.append( uri.getHostName() ); | rootUri.append( uri.getHostName() ); | ||||
| uri.setRootURI( rootUri.toString() ); | |||||
| uri.setRootUri( rootUri.toString() ); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -404,9 +432,9 @@ public class UriParser | |||||
| if( scope == NameScope.CHILD ) | if( scope == NameScope.CHILD ) | ||||
| { | { | ||||
| final int baseLen = baseFile.length(); | final int baseLen = baseFile.length(); | ||||
| if( ! resolvedPath.startsWith( baseFile ) | |||||
| if( !resolvedPath.startsWith( baseFile ) | |||||
| || resolvedPath.length() == baseLen | || resolvedPath.length() == baseLen | ||||
| || resolvedPath.charAt( baseLen ) != m_separatorChar | |||||
| || ( baseLen > 1 && resolvedPath.charAt( baseLen ) != m_separatorChar ) | |||||
| || resolvedPath.indexOf( m_separatorChar, baseLen + 1 ) != -1 ) | || resolvedPath.indexOf( m_separatorChar, baseLen + 1 ) != -1 ) | ||||
| { | { | ||||
| final String message = REZ.getString( "invalid-childname.error", path ); | final String message = REZ.getString( "invalid-childname.error", path ); | ||||
| @@ -416,9 +444,9 @@ public class UriParser | |||||
| else if( scope == NameScope.DESCENDENT ) | else if( scope == NameScope.DESCENDENT ) | ||||
| { | { | ||||
| final int baseLen = baseFile.length(); | final int baseLen = baseFile.length(); | ||||
| if( ! resolvedPath.startsWith( baseFile ) | |||||
| if( !resolvedPath.startsWith( baseFile ) | |||||
| || resolvedPath.length() == baseLen | || resolvedPath.length() == baseLen | ||||
| || resolvedPath.charAt( baseLen ) != m_separatorChar ) | |||||
| || ( baseLen > 1 && resolvedPath.charAt( baseLen ) != m_separatorChar ) ) | |||||
| { | { | ||||
| final String message = REZ.getString( "invalid-descendent-name.error", path ); | final String message = REZ.getString( "invalid-descendent-name.error", path ); | ||||
| throw new FileSystemException( message ); | throw new FileSystemException( message ); | ||||
| @@ -463,7 +491,8 @@ public class UriParser | |||||
| * <li>Removes trailing separator. | * <li>Removes trailing separator. | ||||
| * </ul> | * </ul> | ||||
| */ | */ | ||||
| public void normalisePath( final StringBuffer path ) throws FileSystemException | |||||
| public void normalisePath( final StringBuffer path ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| if( path.length() == 0 ) | if( path.length() == 0 ) | ||||
| { | { | ||||
| @@ -515,14 +544,20 @@ public class UriParser | |||||
| path.charAt( startElem + 1 ) == '.' ) | path.charAt( startElem + 1 ) == '.' ) | ||||
| { | { | ||||
| // A '..' element - remove the previous element | // A '..' element - remove the previous element | ||||
| if( startElem > startFirstElem ) | |||||
| if( startElem == startFirstElem ) | |||||
| { | { | ||||
| int pos = startElem - 2; | |||||
| for( ; pos >= 0 && path.charAt( pos ) != m_separatorChar; pos -- ) | |||||
| { | |||||
| } | |||||
| startElem = pos + 1; | |||||
| // Previous element is missing | |||||
| final String message = REZ.getString( "invalid-relative-path.error" ); | |||||
| throw new FileSystemException( message ); | |||||
| } | } | ||||
| // Find start of previous element | |||||
| int pos = startElem - 2; | |||||
| for( ; pos >= 0 && path.charAt( pos ) != m_separatorChar; pos-- ) | |||||
| { | |||||
| } | |||||
| startElem = pos + 1; | |||||
| path.delete( startElem, endElem + 1 ); | path.delete( startElem, endElem + 1 ); | ||||
| maxlen = path.length(); | maxlen = path.length(); | ||||
| continue; | continue; | ||||
| @@ -595,7 +630,8 @@ public class UriParser | |||||
| * @return | * @return | ||||
| * The scheme name. Returns null if there is no scheme. | * The scheme name. Returns null if there is no scheme. | ||||
| */ | */ | ||||
| protected static String extractScheme( final String uri, final StringBuffer buffer ) | |||||
| public static String extractScheme( final String uri, | |||||
| final StringBuffer buffer ) | |||||
| { | { | ||||
| if( buffer != null ) | if( buffer != null ) | ||||
| { | { | ||||
| @@ -642,4 +678,103 @@ public class UriParser | |||||
| // No scheme in URI | // No scheme in URI | ||||
| return null; | return null; | ||||
| } | } | ||||
| /** | |||||
| * Removes %nn encodings from a string. | |||||
| */ | |||||
| public static String decode( final String encodedStr ) | |||||
| throws FileSystemException | |||||
| { | |||||
| final StringBuffer buffer = new StringBuffer( encodedStr ); | |||||
| decode( buffer, 0, buffer.length() ); | |||||
| return buffer.toString(); | |||||
| } | |||||
| /** | |||||
| * Removes %nn encodings from a string. | |||||
| */ | |||||
| public static void decode( final StringBuffer buffer, | |||||
| final int offset, | |||||
| final int length ) | |||||
| throws FileSystemException | |||||
| { | |||||
| int index = offset; | |||||
| int count = length; | |||||
| for( ; count > 0; count--, index++ ) | |||||
| { | |||||
| final char ch = buffer.charAt( index ); | |||||
| if( ch != '%' ) | |||||
| { | |||||
| continue; | |||||
| } | |||||
| if( count < 3 ) | |||||
| { | |||||
| final String message = REZ.getString( "invalid-escape-sequence.error", buffer.substring( index, index + count ) ); | |||||
| throw new FileSystemException( message ); | |||||
| } | |||||
| // Decode | |||||
| int dig1 = Character.digit( buffer.charAt( index + 1 ), 16 ); | |||||
| int dig2 = Character.digit( buffer.charAt( index + 2 ), 16 ); | |||||
| if( dig1 == -1 || dig2 == -1 ) | |||||
| { | |||||
| final String message = REZ.getString( "invalid-escape-sequence.error", buffer.substring( index, index + 3 ) ); | |||||
| throw new FileSystemException( message ); | |||||
| } | |||||
| char value = (char)( dig1 << 4 | dig2 ); | |||||
| // Replace | |||||
| buffer.setCharAt( index, value ); | |||||
| buffer.delete( index + 1, index + 3 ); | |||||
| count -= 2; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Encodes and appends a string to a StringBuffer. | |||||
| */ | |||||
| public static void appendEncoded( final StringBuffer buffer, | |||||
| final String unencodedValue, | |||||
| final char[] reserved ) | |||||
| { | |||||
| final int offset = buffer.length(); | |||||
| buffer.append( unencodedValue ); | |||||
| encode( buffer, offset, unencodedValue.length(), reserved ); | |||||
| } | |||||
| /** | |||||
| * Encodes a set of reserved characters in a StringBuffer, using the URI | |||||
| * %nn encoding. Always encodes % characters. | |||||
| */ | |||||
| public static void encode( final StringBuffer buffer, | |||||
| final int offset, | |||||
| final int length, | |||||
| final char[] reserved ) | |||||
| { | |||||
| int index = offset; | |||||
| int count = length; | |||||
| for( ; count > 0; index++, count-- ) | |||||
| { | |||||
| final char ch = buffer.charAt( index ); | |||||
| boolean match = ( ch == '%' ); | |||||
| for( int i = 0; !match && i < reserved.length; i++ ) | |||||
| { | |||||
| if( ch == reserved[ i ] ) | |||||
| { | |||||
| match = true; | |||||
| } | |||||
| } | |||||
| if( match ) | |||||
| { | |||||
| // Encode | |||||
| char[] digits = { | |||||
| Character.forDigit( ( ( ch >> 4 ) & 0xF ), 16 ), | |||||
| Character.forDigit( ( ch & 0xF ), 16 ) | |||||
| }; | |||||
| buffer.setCharAt( index, '%' ); | |||||
| buffer.insert( index + 1, digits ); | |||||
| index += 2; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -8,7 +8,6 @@ | |||||
| package org.apache.aut.vfs.provider.ftp; | package org.apache.aut.vfs.provider.ftp; | ||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.provider.ParsedUri; | |||||
| import org.apache.aut.vfs.provider.UriParser; | import org.apache.aut.vfs.provider.UriParser; | ||||
| /** | /** | ||||
| @@ -21,15 +20,27 @@ public class FtpFileNameParser extends UriParser | |||||
| /** | /** | ||||
| * Parses an absolute URI, splitting it into its components. | * Parses an absolute URI, splitting it into its components. | ||||
| */ | */ | ||||
| public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
| public ParsedFtpUri parseFtpUri( final String uriStr ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| ParsedFtpUri uri = new ParsedFtpUri(); | |||||
| final ParsedFtpUri uri = new ParsedFtpUri(); | |||||
| // FTP URI are generic URI (as per RFC 2396) | // FTP URI are generic URI (as per RFC 2396) | ||||
| parseGenericUri( uriStr, uri ); | parseGenericUri( uriStr, uri ); | ||||
| // Adjust the hostname to lower-case | |||||
| final String hostname = uri.getHostName().toLowerCase(); | |||||
| uri.setHostName( hostname ); | |||||
| // Drop the port if it is 21 | |||||
| final String port = uri.getPort(); | |||||
| if( port != null && port.equals( "21" ) ) | |||||
| { | |||||
| uri.setPort( null ); | |||||
| } | |||||
| // Split up the userinfo into a username and password | // Split up the userinfo into a username and password | ||||
| String userInfo = uri.getUserInfo(); | |||||
| final String userInfo = uri.getUserInfo(); | |||||
| if( userInfo != null ) | if( userInfo != null ) | ||||
| { | { | ||||
| int idx = userInfo.indexOf( ':' ); | int idx = userInfo.indexOf( ':' ); | ||||
| @@ -46,6 +57,11 @@ public class FtpFileNameParser extends UriParser | |||||
| } | } | ||||
| } | } | ||||
| // Now build the root URI | |||||
| final StringBuffer rootUri = new StringBuffer(); | |||||
| appendRootUri( uri, rootUri ); | |||||
| uri.setRootUri( rootUri.toString() ); | |||||
| return uri; | return uri; | ||||
| } | } | ||||
| } | } | ||||
| @@ -8,39 +8,44 @@ | |||||
| package org.apache.aut.vfs.provider.ftp; | package org.apache.aut.vfs.provider.ftp; | ||||
| import org.apache.aut.vfs.FileName; | import org.apache.aut.vfs.FileName; | ||||
| import org.apache.aut.vfs.FileObject; | |||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | ||||
| import org.apache.aut.vfs.provider.DefaultFileName; | import org.apache.aut.vfs.provider.DefaultFileName; | ||||
| import org.apache.aut.vfs.provider.FileSystem; | import org.apache.aut.vfs.provider.FileSystem; | ||||
| import org.apache.aut.vfs.provider.ParsedUri; | import org.apache.aut.vfs.provider.ParsedUri; | ||||
| import org.apache.aut.vfs.provider.UriParser; | |||||
| /** | /** | ||||
| * A provider for FTP file systems. | * A provider for FTP file systems. | ||||
| * | * | ||||
| * @author Adam Murdoch | * @author Adam Murdoch | ||||
| * | |||||
| * @ant:type type="file-system" name="ftp" | |||||
| */ | */ | ||||
| public class FtpFileSystemProvider extends AbstractFileSystemProvider | public class FtpFileSystemProvider extends AbstractFileSystemProvider | ||||
| { | { | ||||
| private UriParser m_parser = new FtpFileNameParser(); | |||||
| private final FtpFileNameParser m_parser = new FtpFileNameParser(); | |||||
| /** | /** | ||||
| * Parses a URI into its components. | * Parses a URI into its components. | ||||
| */ | */ | ||||
| protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
| protected ParsedUri parseUri( final FileObject baseFile, | |||||
| final String uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| return m_parser.parseUri( uri ); | |||||
| return m_parser.parseFtpUri( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Creates the filesystem. | * Creates the filesystem. | ||||
| */ | */ | ||||
| protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| ParsedFtpUri ftpUri = (ParsedFtpUri)uri; | |||||
| final ParsedFtpUri ftpUri = (ParsedFtpUri)uri; | |||||
| // Build the root name | // Build the root name | ||||
| FileName rootName = new DefaultFileName( m_parser, ftpUri.getRootURI(), "/" ); | |||||
| final FileName rootName = new DefaultFileName( m_parser, ftpUri.getRootUri(), "/" ); | |||||
| // Determine the username and password to use | // Determine the username and password to use | ||||
| String username = ftpUri.getUserName(); | String username = ftpUri.getUserName(); | ||||
| @@ -50,21 +50,22 @@ abstract class LocalFileNameParser | |||||
| * | * | ||||
| * @param uriStr The URI. | * @param uriStr The URI. | ||||
| */ | */ | ||||
| public ParsedUri parseUri( final String uriStr ) | |||||
| public ParsedUri parseFileUri( final String uriStr ) | |||||
| throws FileSystemException | throws FileSystemException | ||||
| { | { | ||||
| StringBuffer name = new StringBuffer(); | |||||
| ParsedFileUri uri = new ParsedFileUri(); | |||||
| final StringBuffer name = new StringBuffer(); | |||||
| final ParsedFileUri uri = new ParsedFileUri(); | |||||
| // Extract the scheme | // Extract the scheme | ||||
| String scheme = extractScheme( uriStr, name ); | |||||
| final String scheme = extractScheme( uriStr, name ); | |||||
| uri.setScheme( scheme ); | uri.setScheme( scheme ); | ||||
| // Adjust the separators | |||||
| // Remove encoding, and adjust the separators | |||||
| decode( name, 0, name.length() ); | |||||
| fixSeparators( name ); | fixSeparators( name ); | ||||
| // Extract the root prefix | // Extract the root prefix | ||||
| String rootFile = extractRootPrefix( uriStr, name ); | |||||
| final String rootFile = extractRootPrefix( uriStr, name ); | |||||
| uri.setRootFile( rootFile ); | uri.setRootFile( rootFile ); | ||||
| // Normalise the path | // Normalise the path | ||||
| @@ -72,11 +73,11 @@ abstract class LocalFileNameParser | |||||
| uri.setPath( name.toString() ); | uri.setPath( name.toString() ); | ||||
| // Build the root URI | // Build the root URI | ||||
| StringBuffer rootUri = new StringBuffer(); | |||||
| final StringBuffer rootUri = new StringBuffer(); | |||||
| rootUri.append( scheme ); | rootUri.append( scheme ); | ||||
| rootUri.append( "://" ); | rootUri.append( "://" ); | ||||
| rootUri.append( rootFile ); | rootUri.append( rootFile ); | ||||
| uri.setRootURI( rootUri.toString() ); | |||||
| uri.setRootUri( rootUri.toString() ); | |||||
| return uri; | return uri; | ||||
| } | } | ||||
| @@ -51,20 +51,22 @@ public class LocalFileSystemProvider extends AbstractFileSystemProvider | |||||
| /** | /** | ||||
| * Finds a local file, from its local name. | * Finds a local file, from its local name. | ||||
| */ | */ | ||||
| public FileObject findLocalFile( final String name ) throws FileSystemException | |||||
| public FileObject findLocalFile( final String name ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| // TODO - tidy this up, no need to turn the name into an absolute URI, | // TODO - tidy this up, no need to turn the name into an absolute URI, | ||||
| // and then straight back again | // and then straight back again | ||||
| return findFile( "file:" + name ); | |||||
| return findFile( null, "file:" + name ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Finds a local file. | * Finds a local file. | ||||
| */ | */ | ||||
| public FileObject findFileByLocalName( final File file ) throws FileSystemException | |||||
| public FileObject findLocalFile( final File file ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| // TODO - tidy this up, should build file object straight from the file | // TODO - tidy this up, should build file object straight from the file | ||||
| return findFile( "file:" + file.getAbsolutePath() ); | |||||
| return findFile( null, "file:" + file.getAbsolutePath() ); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -75,22 +77,25 @@ public class LocalFileSystemProvider extends AbstractFileSystemProvider | |||||
| * <p>The provider can annotate this object with any additional | * <p>The provider can annotate this object with any additional | ||||
| * information it requires to create a file system from the URI. | * information it requires to create a file system from the URI. | ||||
| */ | */ | ||||
| protected ParsedUri parseURI( final String uri ) throws FileSystemException | |||||
| protected ParsedUri parseUri( final FileObject baseFile, | |||||
| final String uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| return m_parser.parseUri( uri ); | |||||
| return m_parser.parseFileUri( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Creates the filesystem. | * Creates the filesystem. | ||||
| */ | */ | ||||
| protected FileSystem createFileSystem( final ParsedUri uri ) throws FileSystemException | |||||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| // Build the name of the root file. | // Build the name of the root file. | ||||
| final ParsedFileUri fileUri = (ParsedFileUri)uri; | final ParsedFileUri fileUri = (ParsedFileUri)uri; | ||||
| final String rootFile = fileUri.getRootFile(); | final String rootFile = fileUri.getRootFile(); | ||||
| // Create the file system | // Create the file system | ||||
| final DefaultFileName rootName = new DefaultFileName( m_parser, fileUri.getRootURI(), "/" ); | |||||
| final DefaultFileName rootName = new DefaultFileName( m_parser, fileUri.getRootUri(), "/" ); | |||||
| return new LocalFileSystem( rootName, rootFile ); | return new LocalFileSystem( rootName, rootFile ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -27,19 +27,27 @@ public class SmbFileNameParser | |||||
| /** | /** | ||||
| * Parses an absolute URI, splitting it into its components. | * Parses an absolute URI, splitting it into its components. | ||||
| */ | */ | ||||
| public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
| public ParsedUri parseSmbUri( final String uriStr ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| ParsedSmbUri uri = new ParsedSmbUri(); | |||||
| StringBuffer name = new StringBuffer(); | |||||
| final ParsedSmbUri uri = new ParsedSmbUri(); | |||||
| final StringBuffer name = new StringBuffer(); | |||||
| // Extract the scheme and authority parts | // Extract the scheme and authority parts | ||||
| extractToPath( uriStr, name, uri ); | extractToPath( uriStr, name, uri ); | ||||
| // Normalise paths | |||||
| // Convert the hostname to lowercase | |||||
| final String hostname = uri.getHostName().toLowerCase(); | |||||
| uri.setHostName( hostname ); | |||||
| // TODO - drop the default port | |||||
| // Decode and adjust separators | |||||
| decode( name, 0, name.length() ); | |||||
| fixSeparators( name ); | fixSeparators( name ); | ||||
| // Extract the share | // Extract the share | ||||
| String share = extractFirstElement( name ); | |||||
| final String share = extractFirstElement( name ); | |||||
| if( share == null ) | if( share == null ) | ||||
| { | { | ||||
| final String message = REZ.getString( "missing-share-name.error", uriStr ); | final String message = REZ.getString( "missing-share-name.error", uriStr ); | ||||
| @@ -47,23 +55,18 @@ public class SmbFileNameParser | |||||
| } | } | ||||
| uri.setShare( share ); | uri.setShare( share ); | ||||
| // Normalise the path | |||||
| normalisePath( name ); | |||||
| // Set the path | // Set the path | ||||
| uri.setPath( name.toString() ); | uri.setPath( name.toString() ); | ||||
| // Set the root URI | // Set the root URI | ||||
| StringBuffer rootUri = new StringBuffer(); | StringBuffer rootUri = new StringBuffer(); | ||||
| rootUri.append( uri.getScheme() ); | |||||
| rootUri.append( "://" ); | |||||
| String userInfo = uri.getUserInfo(); | |||||
| if( userInfo != null ) | |||||
| { | |||||
| rootUri.append( userInfo ); | |||||
| rootUri.append( '@' ); | |||||
| } | |||||
| rootUri.append( uri.getHostName() ); | |||||
| appendRootUri( uri, rootUri ); | |||||
| rootUri.append( '/' ); | rootUri.append( '/' ); | ||||
| rootUri.append( share ); | rootUri.append( share ); | ||||
| uri.setRootURI( rootUri.toString() ); | |||||
| uri.setRootUri( rootUri.toString() ); | |||||
| return uri; | return uri; | ||||
| } | } | ||||
| @@ -8,6 +8,7 @@ | |||||
| package org.apache.aut.vfs.provider.smb; | package org.apache.aut.vfs.provider.smb; | ||||
| import org.apache.aut.vfs.FileName; | import org.apache.aut.vfs.FileName; | ||||
| import org.apache.aut.vfs.FileObject; | |||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | ||||
| import org.apache.aut.vfs.provider.DefaultFileName; | import org.apache.aut.vfs.provider.DefaultFileName; | ||||
| @@ -19,27 +20,31 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||||
| * A provider for SMB (Samba, Windows share) file systems. | * A provider for SMB (Samba, Windows share) file systems. | ||||
| * | * | ||||
| * @author Adam Murdoch | * @author Adam Murdoch | ||||
| * | |||||
| * @ant:type type="file-system" name="smb" | |||||
| */ | */ | ||||
| public class SmbFileSystemProvider extends AbstractFileSystemProvider implements FileSystemProvider | public class SmbFileSystemProvider extends AbstractFileSystemProvider implements FileSystemProvider | ||||
| { | { | ||||
| SmbFileNameParser m_parser = new SmbFileNameParser(); | |||||
| private final SmbFileNameParser m_parser = new SmbFileNameParser(); | |||||
| /** | /** | ||||
| * Parses a URI into its components. | * Parses a URI into its components. | ||||
| */ | */ | ||||
| protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
| protected ParsedUri parseUri( final FileObject baseFile, | |||||
| final String uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| return m_parser.parseUri( uri ); | |||||
| return m_parser.parseSmbUri( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Creates the filesystem. | * Creates the filesystem. | ||||
| */ | */ | ||||
| protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| ParsedSmbUri smbUri = (ParsedSmbUri)uri; | |||||
| FileName rootName = new DefaultFileName( m_parser, smbUri.getRootURI(), "/" ); | |||||
| final ParsedSmbUri smbUri = (ParsedSmbUri)uri; | |||||
| final FileName rootName = new DefaultFileName( m_parser, smbUri.getRootUri(), "/" ); | |||||
| return new SmbFileSystem( rootName ); | return new SmbFileSystem( rootName ); | ||||
| } | } | ||||
| } | } | ||||
| @@ -7,6 +7,7 @@ | |||||
| */ | */ | ||||
| package org.apache.aut.vfs.provider.zip; | package org.apache.aut.vfs.provider.zip; | ||||
| import org.apache.aut.vfs.FileObject; | |||||
| import org.apache.aut.vfs.provider.ParsedUri; | import org.apache.aut.vfs.provider.ParsedUri; | ||||
| /** | /** | ||||
| @@ -16,14 +17,25 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||||
| */ | */ | ||||
| public class ParsedZipUri extends ParsedUri | public class ParsedZipUri extends ParsedUri | ||||
| { | { | ||||
| private String m_zipFile; | |||||
| private String m_zipFileName; | |||||
| private FileObject m_zipFile; | |||||
| public String getZipFile() | |||||
| public String getZipFileName() | |||||
| { | |||||
| return m_zipFileName; | |||||
| } | |||||
| public void setZipFileName( final String zipFileName ) | |||||
| { | |||||
| m_zipFileName = zipFileName; | |||||
| } | |||||
| public FileObject getZipFile() | |||||
| { | { | ||||
| return m_zipFile; | return m_zipFile; | ||||
| } | } | ||||
| public void setZipFile( String zipFile ) | |||||
| public void setZipFile( final FileObject zipFile ) | |||||
| { | { | ||||
| m_zipFile = zipFile; | m_zipFile = zipFile; | ||||
| } | } | ||||
| @@ -8,7 +8,6 @@ | |||||
| package org.apache.aut.vfs.provider.zip; | package org.apache.aut.vfs.provider.zip; | ||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.provider.ParsedUri; | |||||
| import org.apache.aut.vfs.provider.UriParser; | import org.apache.aut.vfs.provider.UriParser; | ||||
| /** | /** | ||||
| @@ -16,66 +15,77 @@ import org.apache.aut.vfs.provider.UriParser; | |||||
| * | * | ||||
| * @author Adam Murdoch | * @author Adam Murdoch | ||||
| */ | */ | ||||
| public class ZipFileNameParser extends UriParser | |||||
| public class ZipFileNameParser | |||||
| extends UriParser | |||||
| { | { | ||||
| private static final char[] ZIP_URL_RESERVED_CHARS = { '!' }; | |||||
| /** | /** | ||||
| * Parses an absolute URI, splitting it into its components. | * Parses an absolute URI, splitting it into its components. | ||||
| * | * | ||||
| * @param name | |||||
| * @param uriStr | |||||
| * The URI. | * The URI. | ||||
| */ | */ | ||||
| public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
| public ParsedZipUri parseZipUri( final String uriStr ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| StringBuffer name = new StringBuffer(); | |||||
| ParsedZipUri uri = new ParsedZipUri(); | |||||
| final StringBuffer name = new StringBuffer(); | |||||
| final ParsedZipUri uri = new ParsedZipUri(); | |||||
| // Extract the scheme | // Extract the scheme | ||||
| String scheme = extractScheme( uriStr, name ); | |||||
| final String scheme = extractScheme( uriStr, name ); | |||||
| uri.setScheme( scheme ); | uri.setScheme( scheme ); | ||||
| // Extract the Zip file name | // Extract the Zip file name | ||||
| String zipName = extractZipName( name ); | |||||
| uri.setZipFile( zipName ); | |||||
| // Adjust the separators | |||||
| fixSeparators( name ); | |||||
| final String zipName = extractZipName( name ); | |||||
| uri.setZipFileName( zipName ); | |||||
| // Normalise the file name | |||||
| // Decode and normalise the file name | |||||
| decode( name, 0, name.length() ); | |||||
| normalisePath( name ); | normalisePath( name ); | ||||
| uri.setPath( name.toString() ); | uri.setPath( name.toString() ); | ||||
| // Build root URI | |||||
| StringBuffer rootUri = new StringBuffer(); | |||||
| rootUri.append( scheme ); | |||||
| return uri; | |||||
| } | |||||
| /** | |||||
| * Assembles a root URI from the components of a parsed URI. | |||||
| */ | |||||
| public String buildRootUri( final ParsedZipUri uri ) | |||||
| { | |||||
| final StringBuffer rootUri = new StringBuffer(); | |||||
| rootUri.append( uri.getScheme() ); | |||||
| rootUri.append( ":" ); | rootUri.append( ":" ); | ||||
| rootUri.append( zipName ); | |||||
| appendEncoded( rootUri, uri.getZipFile().getName().getURI(), ZIP_URL_RESERVED_CHARS ); | |||||
| rootUri.append( "!" ); | rootUri.append( "!" ); | ||||
| uri.setRootURI( rootUri.toString() ); | |||||
| return uri; | |||||
| return rootUri.toString(); | |||||
| } | } | ||||
| /** | /** | ||||
| * Pops the root prefix off a URI, which has had the scheme removed. | * Pops the root prefix off a URI, which has had the scheme removed. | ||||
| */ | */ | ||||
| protected String extractZipName( StringBuffer uri ) throws FileSystemException | |||||
| private String extractZipName( final StringBuffer uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| // Looking for <name>!<abspath> | // Looking for <name>!<abspath> | ||||
| // TODO - how does '!' in the file name get escaped? | |||||
| int maxlen = uri.length(); | int maxlen = uri.length(); | ||||
| for( int pos = 0; pos < maxlen; pos++ ) | |||||
| int pos = 0; | |||||
| for( ; pos < maxlen && uri.charAt( pos ) != '!'; pos++ ) | |||||
| { | |||||
| } | |||||
| // Extract the name | |||||
| String prefix = uri.substring( 0, pos ); | |||||
| if( pos < maxlen ) | |||||
| { | |||||
| uri.delete( 0, pos + 1 ); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| if( uri.charAt( pos ) == '!' ) | |||||
| { | |||||
| String prefix = uri.substring( 0, pos ); | |||||
| uri.delete( 0, pos + 1 ); | |||||
| return prefix; | |||||
| } | |||||
| uri.setLength( 0 ); | |||||
| } | } | ||||
| // Assume the URI is the Jar file name | |||||
| String prefix = uri.toString(); | |||||
| uri.setLength( 0 ); | |||||
| return prefix; | |||||
| // Decode the name | |||||
| return decode( prefix ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,6 +8,8 @@ | |||||
| package org.apache.aut.vfs.provider.zip; | package org.apache.aut.vfs.provider.zip; | ||||
| import java.io.File; | import java.io.File; | ||||
| import java.io.IOException; | |||||
| import org.apache.aut.vfs.FileObject; | |||||
| import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
| import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | ||||
| import org.apache.aut.vfs.provider.DefaultFileName; | import org.apache.aut.vfs.provider.DefaultFileName; | ||||
| @@ -20,31 +22,83 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||||
| * systems, for local Zip files only. | * systems, for local Zip files only. | ||||
| * | * | ||||
| * @author Adam Murdoch | * @author Adam Murdoch | ||||
| * | |||||
| * @ant:type type="file-system" name="zip" | |||||
| */ | */ | ||||
| public class ZipFileSystemProvider extends AbstractFileSystemProvider | |||||
| public class ZipFileSystemProvider | |||||
| extends AbstractFileSystemProvider | |||||
| implements FileSystemProvider | implements FileSystemProvider | ||||
| { | { | ||||
| private ZipFileNameParser m_parser = new ZipFileNameParser(); | |||||
| private final ZipFileNameParser m_parser = new ZipFileNameParser(); | |||||
| /** | /** | ||||
| * Parses a URI into its components. | * Parses a URI into its components. | ||||
| */ | */ | ||||
| protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
| protected ParsedUri parseUri( final FileObject baseFile, | |||||
| final String uriStr ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| return m_parser.parseUri( uri ); | |||||
| // Parse the URI | |||||
| final ParsedZipUri uri = m_parser.parseZipUri( uriStr ); | |||||
| // Make the URI canonical | |||||
| // Resolve the Zip file name | |||||
| final String fileName = uri.getZipFileName(); | |||||
| final FileObject file = getContext().resolveFile( baseFile, fileName ); | |||||
| uri.setZipFile( file ); | |||||
| // Rebuild the root URI | |||||
| final String rootUri = m_parser.buildRootUri( uri ); | |||||
| uri.setRootUri( rootUri ); | |||||
| return uri; | |||||
| } | |||||
| /** | |||||
| * Builds the URI for the root of a layered file system. | |||||
| */ | |||||
| protected ParsedUri buildUri( final String scheme, | |||||
| final FileObject file ) | |||||
| throws FileSystemException | |||||
| { | |||||
| ParsedZipUri uri = new ParsedZipUri(); | |||||
| uri.setScheme( scheme ); | |||||
| uri.setZipFile( file ); | |||||
| final String rootUri = m_parser.buildRootUri( uri ); | |||||
| uri.setRootUri( rootUri ); | |||||
| uri.setPath( "/" ); | |||||
| return uri; | |||||
| } | } | ||||
| /** | /** | ||||
| * Creates the filesystem. | * Creates the filesystem. | ||||
| */ | */ | ||||
| protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||||
| throws FileSystemException | |||||
| { | { | ||||
| // Locate the Zip file | |||||
| ParsedZipUri zipUri = (ParsedZipUri)uri; | |||||
| String fileName = zipUri.getZipFile(); | |||||
| // TODO - use the context to resolve zip file to a FileObject | |||||
| File file = new File( fileName ).getAbsoluteFile(); | |||||
| DefaultFileName name = new DefaultFileName( m_parser, zipUri.getRootURI(), "/" ); | |||||
| return new ZipFileSystem( name, file ); | |||||
| final ParsedZipUri zipUri = (ParsedZipUri)uri; | |||||
| final FileObject file = zipUri.getZipFile(); | |||||
| // TODO - temporary hack; need to use a converter | |||||
| File destFile = null; | |||||
| try | |||||
| { | |||||
| final File cacheDir = new File( "ant_vfs_cache" ); | |||||
| cacheDir.mkdirs(); | |||||
| destFile = File.createTempFile( "cache_", "_" + file.getName().getBaseName(), cacheDir ); | |||||
| destFile.deleteOnExit(); | |||||
| } | |||||
| catch( IOException e ) | |||||
| { | |||||
| throw new FileSystemException( "Could not replicate file", e ); | |||||
| } | |||||
| FileObject destFileObj = getContext().resolveFile( null, destFile.getAbsolutePath() ); | |||||
| destFileObj.copy( file ); | |||||
| // Create the file system | |||||
| DefaultFileName name = new DefaultFileName( m_parser, zipUri.getRootUri(), "/" ); | |||||
| return new ZipFileSystem( name, destFile ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,2 +1,3 @@ | |||||
| missing-home-dir.error=Cannot locate antRun scripts: Property 'myrmidon.home' not specified | missing-home-dir.error=Cannot locate antRun scripts: Property 'myrmidon.home' not specified | ||||
| create-vfs-manager.error=Could not create the VFS manager. | |||||
| create-vfs-manager.error=Could not create the VFS manager. | |||||
| create-provider.error=Could not create file system provider "{0}". | |||||
| @@ -0,0 +1,108 @@ | |||||
| /* | |||||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
| * | |||||
| * This software is published under the terms of the Apache Software License | |||||
| * version 1.1, a copy of which has been included with this distribution in | |||||
| * the LICENSE.txt file. | |||||
| */ | |||||
| package org.apache.myrmidon.framework.factories; | |||||
| import org.apache.aut.vfs.impl.DefaultFileSystemManager; | |||||
| import org.apache.aut.vfs.provider.FileSystemProvider; | |||||
| import org.apache.aut.vfs.FileSystemException; | |||||
| import org.apache.avalon.framework.activity.Disposable; | |||||
| import org.apache.avalon.framework.activity.Initializable; | |||||
| import org.apache.avalon.framework.service.Serviceable; | |||||
| import org.apache.avalon.framework.service.ServiceManager; | |||||
| import org.apache.avalon.framework.service.ServiceException; | |||||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
| import org.apache.avalon.excalibur.i18n.Resources; | |||||
| import org.apache.myrmidon.interfaces.type.TypeManager; | |||||
| import org.apache.myrmidon.interfaces.type.TypeFactory; | |||||
| import org.apache.myrmidon.interfaces.type.TypeException; | |||||
| /** | |||||
| * The myrmidon FileSystemManager implementation. | |||||
| * | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| * @version $Revision$ $Date$ | |||||
| */ | |||||
| public class VfsManager | |||||
| extends DefaultFileSystemManager | |||||
| implements Serviceable, Initializable, Disposable | |||||
| { | |||||
| private final static Resources REZ | |||||
| = ResourceManager.getPackageResources( VfsManager.class ); | |||||
| private TypeFactory m_typeFactory; | |||||
| /** | |||||
| * Locate the services used by this service. | |||||
| */ | |||||
| public void service( final ServiceManager serviceManager ) throws ServiceException | |||||
| { | |||||
| final TypeManager typeManager = (TypeManager)serviceManager.lookup( TypeManager.ROLE ); | |||||
| try | |||||
| { | |||||
| m_typeFactory = typeManager.getFactory( FileSystemProvider.class ); | |||||
| } | |||||
| catch( TypeException e ) | |||||
| { | |||||
| throw new ServiceException( e.getMessage(), e ); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Initialises this service. | |||||
| */ | |||||
| public void initialize() throws Exception | |||||
| { | |||||
| // TODO - make this list configurable | |||||
| // Required providers | |||||
| addProvider( new String[] { "zip", "jar" }, "zip", false ); | |||||
| // Optional providers | |||||
| addProvider( new String[] { "smb" }, "smb", true ); | |||||
| addProvider( new String[] { "ftp" }, "ftp", true ); | |||||
| } | |||||
| /** | |||||
| * Disposes this service. | |||||
| */ | |||||
| public void dispose() | |||||
| { | |||||
| // Clean-up | |||||
| close(); | |||||
| } | |||||
| /** | |||||
| * Registers a file system provider. | |||||
| */ | |||||
| public void addProvider( final String[] urlSchemes, | |||||
| final String providerName, | |||||
| final boolean ignoreIfNotPresent ) | |||||
| throws FileSystemException | |||||
| { | |||||
| // Create an instance | |||||
| if( ignoreIfNotPresent && ! m_typeFactory.canCreate( providerName ) ) | |||||
| { | |||||
| return; | |||||
| } | |||||
| final FileSystemProvider provider; | |||||
| try | |||||
| { | |||||
| provider = (FileSystemProvider)m_typeFactory.create( providerName ); | |||||
| } | |||||
| catch( Exception e ) | |||||
| { | |||||
| final String message = REZ.getString( "create-provider.error", providerName ); | |||||
| throw new FileSystemException( message, e ); | |||||
| } | |||||
| // Register the provider | |||||
| addProvider( urlSchemes, provider ); | |||||
| } | |||||
| } | |||||
| @@ -8,7 +8,6 @@ | |||||
| package org.apache.myrmidon.framework.factories; | package org.apache.myrmidon.framework.factories; | ||||
| import org.apache.aut.vfs.FileSystemManager; | import org.apache.aut.vfs.FileSystemManager; | ||||
| import org.apache.aut.vfs.impl.DefaultFileSystemManager; | |||||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | import org.apache.avalon.excalibur.i18n.ResourceManager; | ||||
| import org.apache.avalon.excalibur.i18n.Resources; | import org.apache.avalon.excalibur.i18n.Resources; | ||||
| import org.apache.myrmidon.interfaces.service.AntServiceException; | import org.apache.myrmidon.interfaces.service.AntServiceException; | ||||
| @@ -34,7 +33,7 @@ public class VfsManagerFactory | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| return new DefaultFileSystemManager(); | |||||
| return new VfsManager(); | |||||
| } | } | ||||
| catch( Exception e ) | catch( Exception e ) | ||||
| { | { | ||||
| @@ -1,9 +1,4 @@ | |||||
| <services version="1.0"> | <services version="1.0"> | ||||
| <exec-manager factory="org.apache.myrmidon.framework.factories.ExecManagerFactory"/> | <exec-manager factory="org.apache.myrmidon.framework.factories.ExecManagerFactory"/> | ||||
| <file-system-manager factory="org.apache.myrmidon.framework.factories.VfsManagerFactory"> | |||||
| <provider scheme="zip" classname="org.apache.aut.vfs.provider.zip.ZipFileSystemProvider"/> | |||||
| <provider scheme="jar" classname="org.apache.aut.vfs.provider.zip.ZipFileSystemProvider"/> | |||||
| <provider scheme="smb" classname="org.apache.aut.vfs.provider.smb.SmbFileSystemProvider"/> | |||||
| <provider scheme="ftp" classname="org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider"/> | |||||
| </file-system-manager> | |||||
| <file-system-manager factory="org.apache.myrmidon.framework.factories.VfsManagerFactory"/> | |||||
| </services> | </services> | ||||
| @@ -66,9 +66,9 @@ public abstract class AbstractFileSystemTest | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the base folder. | |||||
| * Returns the base folder to run the tests against. | |||||
| */ | */ | ||||
| protected abstract String getBaseFolderURI() throws Exception; | |||||
| protected abstract FileObject getBaseFolder() throws Exception; | |||||
| /** | /** | ||||
| * Sets up the test | * Sets up the test | ||||
| @@ -79,7 +79,10 @@ public abstract class AbstractFileSystemTest | |||||
| m_manager = new DefaultFileSystemManager(); | m_manager = new DefaultFileSystemManager(); | ||||
| // Locate the base folder | // Locate the base folder | ||||
| m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | |||||
| m_baseFolder = getBaseFolder(); | |||||
| // Make some assumptions absout the name | |||||
| assertTrue( ! m_baseFolder.getName().getPath().equals( "/" ) ); | |||||
| // Build the expected content of "file1.txt" | // Build the expected content of "file1.txt" | ||||
| final String eol = System.getProperty( "line.separator" ); | final String eol = System.getProperty( "line.separator" ); | ||||
| @@ -123,6 +126,66 @@ public abstract class AbstractFileSystemTest | |||||
| assertSame( "file object", m_baseFolder.getParent(), file ); | assertSame( "file object", m_baseFolder.getParent(), file ); | ||||
| } | } | ||||
| /** | |||||
| * Tests encoding of relative URI. | |||||
| */ | |||||
| public void testRelativeUriEncoding() throws Exception | |||||
| { | |||||
| // Build base dir | |||||
| m_manager.setBaseFile( m_baseFolder ); | |||||
| final String path = m_baseFolder.getName().getPath(); | |||||
| // Encode "some file" | |||||
| FileObject file = m_manager.resolveFile( "%73%6f%6d%65%20%66%69%6c%65" ); | |||||
| assertEquals( path + "/some file", file.getName().getPath() ); | |||||
| // Encode "." | |||||
| file = m_manager.resolveFile( "%2e" ); | |||||
| assertEquals( path, file.getName().getPath() ); | |||||
| // Encode '%' | |||||
| file = m_manager.resolveFile( "a%25" ); | |||||
| assertEquals( path + "/a%", file.getName().getPath() ); | |||||
| // Encode / | |||||
| file = m_manager.resolveFile( "dir%2fchild" ); | |||||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||||
| // Encode \ | |||||
| file = m_manager.resolveFile( "dir%5cchild" ); | |||||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||||
| // Use "%" literal | |||||
| try | |||||
| { | |||||
| m_manager.resolveFile( "%" ); | |||||
| fail(); | |||||
| } | |||||
| catch( FileSystemException e ) | |||||
| { | |||||
| } | |||||
| // Not enough digits in encoded char | |||||
| try | |||||
| { | |||||
| m_manager.resolveFile( "%5" ); | |||||
| fail(); | |||||
| } | |||||
| catch( FileSystemException e ) | |||||
| { | |||||
| } | |||||
| // Invalid digit in encoded char | |||||
| try | |||||
| { | |||||
| m_manager.resolveFile( "%q" ); | |||||
| fail(); | |||||
| } | |||||
| catch( FileSystemException e ) | |||||
| { | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Tests the root file name. | * Tests the root file name. | ||||
| */ | */ | ||||
| @@ -176,7 +239,7 @@ public abstract class AbstractFileSystemTest | |||||
| final NameScope scope ) | final NameScope scope ) | ||||
| throws Exception | throws Exception | ||||
| { | { | ||||
| // Make some assumptions about the name explicit | |||||
| // Make some assumptions about the name | |||||
| assertTrue( !name.getPath().equals( "/" ) ); | assertTrue( !name.getPath().equals( "/" ) ); | ||||
| assertTrue( !name.getPath().endsWith( "/a" ) ); | assertTrue( !name.getPath().endsWith( "/a" ) ); | ||||
| assertTrue( !name.getPath().endsWith( "/a/b" ) ); | assertTrue( !name.getPath().endsWith( "/a/b" ) ); | ||||
| @@ -329,6 +392,46 @@ public abstract class AbstractFileSystemTest | |||||
| checkDescendentNames( baseName, NameScope.DESCENDENT ); | checkDescendentNames( baseName, NameScope.DESCENDENT ); | ||||
| } | } | ||||
| /** | |||||
| * Tests resolution of absolute names. | |||||
| */ | |||||
| public void testAbsoluteNames() throws Exception | |||||
| { | |||||
| // Test against the base folder | |||||
| FileName name = m_baseFolder.getName(); | |||||
| checkAbsoluteNames( name ); | |||||
| // Test against the root | |||||
| name = m_baseFolder.getRoot().getName(); | |||||
| checkAbsoluteNames( name ); | |||||
| // Test against some unknown file | |||||
| name = name.resolveName( "a/b/unknown" ); | |||||
| checkAbsoluteNames( name ); | |||||
| } | |||||
| /** | |||||
| * Tests resolution of absolute names. | |||||
| */ | |||||
| private void checkAbsoluteNames( final FileName name ) throws Exception | |||||
| { | |||||
| // Root | |||||
| assertSameName( "/", name, "/" ); | |||||
| assertSameName( "/", name, "//" ); | |||||
| assertSameName( "/", name, "/." ); | |||||
| assertSameName( "/", name, "/some file/.." ); | |||||
| // Some absolute names | |||||
| assertSameName( "/a", name, "/a" ); | |||||
| assertSameName( "/a", name, "/./a" ); | |||||
| assertSameName( "/a", name, "/a/." ); | |||||
| assertSameName( "/a/b", name, "/a/b" ); | |||||
| // Some bad names | |||||
| assertBadName( name, "/..", NameScope.FILE_SYSTEM ); | |||||
| assertBadName( name, "/a/../..", NameScope.FILE_SYSTEM ); | |||||
| } | |||||
| /** | /** | ||||
| * Asserts that a particular relative name is invalid for a particular | * Asserts that a particular relative name is invalid for a particular | ||||
| * scope. | * scope. | ||||
| @@ -340,7 +443,7 @@ public abstract class AbstractFileSystemTest | |||||
| try | try | ||||
| { | { | ||||
| name.resolveName( relName, scope ); | name.resolveName( relName, scope ); | ||||
| fail(); | |||||
| fail( "expected failure" ); | |||||
| } | } | ||||
| catch( FileSystemException e ) | catch( FileSystemException e ) | ||||
| { | { | ||||
| @@ -16,7 +16,8 @@ import java.util.Set; | |||||
| * | * | ||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | ||||
| */ | */ | ||||
| public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemTest | |||||
| public abstract class AbstractWritableFileSystemTest | |||||
| extends AbstractFileSystemTest | |||||
| { | { | ||||
| public AbstractWritableFileSystemTest( String name ) | public AbstractWritableFileSystemTest( String name ) | ||||
| { | { | ||||
| @@ -26,14 +27,14 @@ public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemT | |||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected abstract String getWriteFolderURI() throws Exception; | |||||
| protected abstract FileObject getWriteFolder() throws Exception; | |||||
| /** | /** | ||||
| * Sets up a scratch folder for the test to use. | * Sets up a scratch folder for the test to use. | ||||
| */ | */ | ||||
| protected FileObject createScratchFolder() throws Exception | protected FileObject createScratchFolder() throws Exception | ||||
| { | { | ||||
| FileObject scratchFolder = m_manager.resolveFile( getWriteFolderURI() ); | |||||
| FileObject scratchFolder = getWriteFolder(); | |||||
| // Make sure the test folder is empty | // Make sure the test folder is empty | ||||
| scratchFolder.delete(); | scratchFolder.delete(); | ||||
| @@ -7,12 +7,15 @@ | |||||
| */ | */ | ||||
| package org.apache.aut.vfs; | package org.apache.aut.vfs; | ||||
| import org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider; | |||||
| /** | /** | ||||
| * Tests for FTP file systems. | * Tests for FTP file systems. | ||||
| * | * | ||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | ||||
| */ | */ | ||||
| public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||||
| public class FtpFileSystemTest | |||||
| extends AbstractWritableFileSystemTest | |||||
| { | { | ||||
| public FtpFileSystemTest( String name ) | public FtpFileSystemTest( String name ) | ||||
| { | { | ||||
| @@ -22,16 +25,19 @@ public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||||
| m_manager.addProvider( "ftp", new FtpFileSystemProvider() ); | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected String getWriteFolderURI() | |||||
| protected FileObject getWriteFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -24,21 +24,19 @@ public class LocalFileSystemTest extends AbstractWritableFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| throws Exception | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| final File testDir = getTestResource( "basedir" ); | final File testDir = getTestResource( "basedir" ); | ||||
| return testDir.toURL().toString(); | |||||
| return m_manager.convert( testDir ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected String getWriteFolderURI() | |||||
| throws Exception | |||||
| protected FileObject getWriteFolder() throws Exception | |||||
| { | { | ||||
| final File testDir = getTestResource( "write-tests" ); | final File testDir = getTestResource( "write-tests" ); | ||||
| return testDir.toURL().toString(); | |||||
| return m_manager.convert( testDir ); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -0,0 +1,41 @@ | |||||
| /* | |||||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
| * | |||||
| * This software is published under the terms of the Apache Software License | |||||
| * version 1.1, a copy of which has been included with this distribution in | |||||
| * the LICENSE.txt file. | |||||
| */ | |||||
| package org.apache.aut.vfs; | |||||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||||
| /** | |||||
| * Tests for the Zip file system, using a zip file nested inside another zip file. | |||||
| * | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| */ | |||||
| public class NestedZipFileSystemTest | |||||
| extends AbstractReadOnlyFileSystemTest | |||||
| { | |||||
| public NestedZipFileSystemTest( String name ) | |||||
| { | |||||
| super( name ); | |||||
| } | |||||
| /** | |||||
| * Returns the URI for the base folder. | |||||
| */ | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | |||||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||||
| // Locate the base Zip file | |||||
| final String zipFilePath = getTestResource( "nested.zip" ).getAbsolutePath(); | |||||
| String uri = "zip:" + zipFilePath + "!/test.zip"; | |||||
| final FileObject zipFile = m_manager.resolveFile( uri ); | |||||
| // Now build the nested file system | |||||
| final FileObject nestedFS = m_manager.createFileSystem( "zip", zipFile ); | |||||
| return nestedFS.resolveFile( "/basedir" ); | |||||
| } | |||||
| } | |||||
| @@ -7,6 +7,8 @@ | |||||
| */ | */ | ||||
| package org.apache.aut.vfs; | package org.apache.aut.vfs; | ||||
| import org.apache.aut.vfs.provider.smb.SmbFileSystemProvider; | |||||
| /** | /** | ||||
| * Tests for the SMB file system. | * Tests for the SMB file system. | ||||
| * | * | ||||
| @@ -22,16 +24,19 @@ public class SmbFileSystemTest extends AbstractWritableFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||||
| final String uri = System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||||
| m_manager.addProvider( "smb", new SmbFileSystemProvider() ); | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected String getWriteFolderURI() | |||||
| protected FileObject getWriteFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||||
| final String uri = System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,6 +8,7 @@ | |||||
| package org.apache.aut.vfs; | package org.apache.aut.vfs; | ||||
| import java.io.File; | import java.io.File; | ||||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||||
| /** | /** | ||||
| * Tests for the Zip file system. | * Tests for the Zip file system. | ||||
| @@ -24,10 +25,11 @@ public class ZipFileSystemTest extends AbstractReadOnlyFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| File zipFile = getTestResource( "test.zip" ); | File zipFile = getTestResource( "test.zip" ); | ||||
| String uri = "zip:" + zipFile + "!basedir"; | |||||
| return uri; | |||||
| String uri = "zip:" + zipFile.getAbsolutePath() + "!basedir"; | |||||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -66,9 +66,9 @@ public abstract class AbstractFileSystemTest | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the base folder. | |||||
| * Returns the base folder to run the tests against. | |||||
| */ | */ | ||||
| protected abstract String getBaseFolderURI() throws Exception; | |||||
| protected abstract FileObject getBaseFolder() throws Exception; | |||||
| /** | /** | ||||
| * Sets up the test | * Sets up the test | ||||
| @@ -79,7 +79,10 @@ public abstract class AbstractFileSystemTest | |||||
| m_manager = new DefaultFileSystemManager(); | m_manager = new DefaultFileSystemManager(); | ||||
| // Locate the base folder | // Locate the base folder | ||||
| m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | |||||
| m_baseFolder = getBaseFolder(); | |||||
| // Make some assumptions absout the name | |||||
| assertTrue( ! m_baseFolder.getName().getPath().equals( "/" ) ); | |||||
| // Build the expected content of "file1.txt" | // Build the expected content of "file1.txt" | ||||
| final String eol = System.getProperty( "line.separator" ); | final String eol = System.getProperty( "line.separator" ); | ||||
| @@ -123,6 +126,66 @@ public abstract class AbstractFileSystemTest | |||||
| assertSame( "file object", m_baseFolder.getParent(), file ); | assertSame( "file object", m_baseFolder.getParent(), file ); | ||||
| } | } | ||||
| /** | |||||
| * Tests encoding of relative URI. | |||||
| */ | |||||
| public void testRelativeUriEncoding() throws Exception | |||||
| { | |||||
| // Build base dir | |||||
| m_manager.setBaseFile( m_baseFolder ); | |||||
| final String path = m_baseFolder.getName().getPath(); | |||||
| // Encode "some file" | |||||
| FileObject file = m_manager.resolveFile( "%73%6f%6d%65%20%66%69%6c%65" ); | |||||
| assertEquals( path + "/some file", file.getName().getPath() ); | |||||
| // Encode "." | |||||
| file = m_manager.resolveFile( "%2e" ); | |||||
| assertEquals( path, file.getName().getPath() ); | |||||
| // Encode '%' | |||||
| file = m_manager.resolveFile( "a%25" ); | |||||
| assertEquals( path + "/a%", file.getName().getPath() ); | |||||
| // Encode / | |||||
| file = m_manager.resolveFile( "dir%2fchild" ); | |||||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||||
| // Encode \ | |||||
| file = m_manager.resolveFile( "dir%5cchild" ); | |||||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||||
| // Use "%" literal | |||||
| try | |||||
| { | |||||
| m_manager.resolveFile( "%" ); | |||||
| fail(); | |||||
| } | |||||
| catch( FileSystemException e ) | |||||
| { | |||||
| } | |||||
| // Not enough digits in encoded char | |||||
| try | |||||
| { | |||||
| m_manager.resolveFile( "%5" ); | |||||
| fail(); | |||||
| } | |||||
| catch( FileSystemException e ) | |||||
| { | |||||
| } | |||||
| // Invalid digit in encoded char | |||||
| try | |||||
| { | |||||
| m_manager.resolveFile( "%q" ); | |||||
| fail(); | |||||
| } | |||||
| catch( FileSystemException e ) | |||||
| { | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Tests the root file name. | * Tests the root file name. | ||||
| */ | */ | ||||
| @@ -176,7 +239,7 @@ public abstract class AbstractFileSystemTest | |||||
| final NameScope scope ) | final NameScope scope ) | ||||
| throws Exception | throws Exception | ||||
| { | { | ||||
| // Make some assumptions about the name explicit | |||||
| // Make some assumptions about the name | |||||
| assertTrue( !name.getPath().equals( "/" ) ); | assertTrue( !name.getPath().equals( "/" ) ); | ||||
| assertTrue( !name.getPath().endsWith( "/a" ) ); | assertTrue( !name.getPath().endsWith( "/a" ) ); | ||||
| assertTrue( !name.getPath().endsWith( "/a/b" ) ); | assertTrue( !name.getPath().endsWith( "/a/b" ) ); | ||||
| @@ -329,6 +392,46 @@ public abstract class AbstractFileSystemTest | |||||
| checkDescendentNames( baseName, NameScope.DESCENDENT ); | checkDescendentNames( baseName, NameScope.DESCENDENT ); | ||||
| } | } | ||||
| /** | |||||
| * Tests resolution of absolute names. | |||||
| */ | |||||
| public void testAbsoluteNames() throws Exception | |||||
| { | |||||
| // Test against the base folder | |||||
| FileName name = m_baseFolder.getName(); | |||||
| checkAbsoluteNames( name ); | |||||
| // Test against the root | |||||
| name = m_baseFolder.getRoot().getName(); | |||||
| checkAbsoluteNames( name ); | |||||
| // Test against some unknown file | |||||
| name = name.resolveName( "a/b/unknown" ); | |||||
| checkAbsoluteNames( name ); | |||||
| } | |||||
| /** | |||||
| * Tests resolution of absolute names. | |||||
| */ | |||||
| private void checkAbsoluteNames( final FileName name ) throws Exception | |||||
| { | |||||
| // Root | |||||
| assertSameName( "/", name, "/" ); | |||||
| assertSameName( "/", name, "//" ); | |||||
| assertSameName( "/", name, "/." ); | |||||
| assertSameName( "/", name, "/some file/.." ); | |||||
| // Some absolute names | |||||
| assertSameName( "/a", name, "/a" ); | |||||
| assertSameName( "/a", name, "/./a" ); | |||||
| assertSameName( "/a", name, "/a/." ); | |||||
| assertSameName( "/a/b", name, "/a/b" ); | |||||
| // Some bad names | |||||
| assertBadName( name, "/..", NameScope.FILE_SYSTEM ); | |||||
| assertBadName( name, "/a/../..", NameScope.FILE_SYSTEM ); | |||||
| } | |||||
| /** | /** | ||||
| * Asserts that a particular relative name is invalid for a particular | * Asserts that a particular relative name is invalid for a particular | ||||
| * scope. | * scope. | ||||
| @@ -340,7 +443,7 @@ public abstract class AbstractFileSystemTest | |||||
| try | try | ||||
| { | { | ||||
| name.resolveName( relName, scope ); | name.resolveName( relName, scope ); | ||||
| fail(); | |||||
| fail( "expected failure" ); | |||||
| } | } | ||||
| catch( FileSystemException e ) | catch( FileSystemException e ) | ||||
| { | { | ||||
| @@ -16,7 +16,8 @@ import java.util.Set; | |||||
| * | * | ||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | ||||
| */ | */ | ||||
| public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemTest | |||||
| public abstract class AbstractWritableFileSystemTest | |||||
| extends AbstractFileSystemTest | |||||
| { | { | ||||
| public AbstractWritableFileSystemTest( String name ) | public AbstractWritableFileSystemTest( String name ) | ||||
| { | { | ||||
| @@ -26,14 +27,14 @@ public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemT | |||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected abstract String getWriteFolderURI() throws Exception; | |||||
| protected abstract FileObject getWriteFolder() throws Exception; | |||||
| /** | /** | ||||
| * Sets up a scratch folder for the test to use. | * Sets up a scratch folder for the test to use. | ||||
| */ | */ | ||||
| protected FileObject createScratchFolder() throws Exception | protected FileObject createScratchFolder() throws Exception | ||||
| { | { | ||||
| FileObject scratchFolder = m_manager.resolveFile( getWriteFolderURI() ); | |||||
| FileObject scratchFolder = getWriteFolder(); | |||||
| // Make sure the test folder is empty | // Make sure the test folder is empty | ||||
| scratchFolder.delete(); | scratchFolder.delete(); | ||||
| @@ -7,12 +7,15 @@ | |||||
| */ | */ | ||||
| package org.apache.aut.vfs; | package org.apache.aut.vfs; | ||||
| import org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider; | |||||
| /** | /** | ||||
| * Tests for FTP file systems. | * Tests for FTP file systems. | ||||
| * | * | ||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | ||||
| */ | */ | ||||
| public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||||
| public class FtpFileSystemTest | |||||
| extends AbstractWritableFileSystemTest | |||||
| { | { | ||||
| public FtpFileSystemTest( String name ) | public FtpFileSystemTest( String name ) | ||||
| { | { | ||||
| @@ -22,16 +25,19 @@ public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||||
| m_manager.addProvider( "ftp", new FtpFileSystemProvider() ); | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected String getWriteFolderURI() | |||||
| protected FileObject getWriteFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -24,21 +24,19 @@ public class LocalFileSystemTest extends AbstractWritableFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| throws Exception | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| final File testDir = getTestResource( "basedir" ); | final File testDir = getTestResource( "basedir" ); | ||||
| return testDir.toURL().toString(); | |||||
| return m_manager.convert( testDir ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected String getWriteFolderURI() | |||||
| throws Exception | |||||
| protected FileObject getWriteFolder() throws Exception | |||||
| { | { | ||||
| final File testDir = getTestResource( "write-tests" ); | final File testDir = getTestResource( "write-tests" ); | ||||
| return testDir.toURL().toString(); | |||||
| return m_manager.convert( testDir ); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -0,0 +1,41 @@ | |||||
| /* | |||||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
| * | |||||
| * This software is published under the terms of the Apache Software License | |||||
| * version 1.1, a copy of which has been included with this distribution in | |||||
| * the LICENSE.txt file. | |||||
| */ | |||||
| package org.apache.aut.vfs; | |||||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||||
| /** | |||||
| * Tests for the Zip file system, using a zip file nested inside another zip file. | |||||
| * | |||||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||||
| */ | |||||
| public class NestedZipFileSystemTest | |||||
| extends AbstractReadOnlyFileSystemTest | |||||
| { | |||||
| public NestedZipFileSystemTest( String name ) | |||||
| { | |||||
| super( name ); | |||||
| } | |||||
| /** | |||||
| * Returns the URI for the base folder. | |||||
| */ | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | |||||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||||
| // Locate the base Zip file | |||||
| final String zipFilePath = getTestResource( "nested.zip" ).getAbsolutePath(); | |||||
| String uri = "zip:" + zipFilePath + "!/test.zip"; | |||||
| final FileObject zipFile = m_manager.resolveFile( uri ); | |||||
| // Now build the nested file system | |||||
| final FileObject nestedFS = m_manager.createFileSystem( "zip", zipFile ); | |||||
| return nestedFS.resolveFile( "/basedir" ); | |||||
| } | |||||
| } | |||||
| @@ -7,6 +7,8 @@ | |||||
| */ | */ | ||||
| package org.apache.aut.vfs; | package org.apache.aut.vfs; | ||||
| import org.apache.aut.vfs.provider.smb.SmbFileSystemProvider; | |||||
| /** | /** | ||||
| * Tests for the SMB file system. | * Tests for the SMB file system. | ||||
| * | * | ||||
| @@ -22,16 +24,19 @@ public class SmbFileSystemTest extends AbstractWritableFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||||
| final String uri = System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||||
| m_manager.addProvider( "smb", new SmbFileSystemProvider() ); | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| /** | /** | ||||
| * Returns the URI for the area to do tests in. | * Returns the URI for the area to do tests in. | ||||
| */ | */ | ||||
| protected String getWriteFolderURI() | |||||
| protected FileObject getWriteFolder() throws Exception | |||||
| { | { | ||||
| return System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||||
| final String uri = System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,6 +8,7 @@ | |||||
| package org.apache.aut.vfs; | package org.apache.aut.vfs; | ||||
| import java.io.File; | import java.io.File; | ||||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||||
| /** | /** | ||||
| * Tests for the Zip file system. | * Tests for the Zip file system. | ||||
| @@ -24,10 +25,11 @@ public class ZipFileSystemTest extends AbstractReadOnlyFileSystemTest | |||||
| /** | /** | ||||
| * Returns the URI for the base folder. | * Returns the URI for the base folder. | ||||
| */ | */ | ||||
| protected String getBaseFolderURI() | |||||
| protected FileObject getBaseFolder() throws Exception | |||||
| { | { | ||||
| File zipFile = getTestResource( "test.zip" ); | File zipFile = getTestResource( "test.zip" ); | ||||
| String uri = "zip:" + zipFile + "!basedir"; | |||||
| return uri; | |||||
| String uri = "zip:" + zipFile.getAbsolutePath() + "!basedir"; | |||||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||||
| return m_manager.resolveFile( uri ); | |||||
| } | } | ||||
| } | } | ||||
| @@ -37,15 +37,22 @@ | |||||
| <p>The VFS needs plenty of work:</p> | <p>The VFS needs plenty of work:</p> | ||||
| <ul> | <ul> | ||||
| <li>Move and copy files/folders.</li> | |||||
| <li>Move files/folders.</li> | |||||
| <li>Recursive folders copy.</li> | |||||
| <li>Search through a file hierarchy, using Ant-style wildcards.</li> | <li>Search through a file hierarchy, using Ant-style wildcards.</li> | ||||
| <li>Search through a file hierarchy, using a Selector interface.</li> | <li>Search through a file hierarchy, using a Selector interface.</li> | ||||
| <li>The in-memory caching mechanism is pretty rudimentary at this stage. | <li>The in-memory caching mechanism is pretty rudimentary at this stage. | ||||
| It needs work to make it size capped. In addition, some mechanism needs | It needs work to make it size capped. In addition, some mechanism needs | ||||
| to be provided to release and refresh cached info. | to be provided to release and refresh cached info. | ||||
| </li> | </li> | ||||
| <li>Convert files/folders into local files, for handing off | |||||
| to external commands, or legacy tasks.</li> | |||||
| <li>Refactor the replication mechanism out of ZipFileSystemProvder, | |||||
| and make more general pluggable.</li> | |||||
| <li>Capabilities discovery.</li> | <li>Capabilities discovery.</li> | ||||
| <li>Attributes and attribute schema.</li> | <li>Attributes and attribute schema.</li> | ||||
| <li>Handle file canonicalisation better (for cases like case-insensitive | |||||
| file systems, symbolic links, name encoding, etc).</li> | |||||
| <li>File system layering. That is, the ability for a file system to | <li>File system layering. That is, the ability for a file system to | ||||
| sit on top of another file system, or a file from another file system | sit on top of another file system, or a file from another file system | ||||
| (e.g. Zip/Jar/Tar file systems, gzip/encoding file systems, virtual file | (e.g. Zip/Jar/Tar file systems, gzip/encoding file systems, virtual file | ||||
| @@ -192,10 +199,6 @@ | |||||
| <ul> | <ul> | ||||
| <li>Search through the code for 'TODO' items and fix them.</li> | <li>Search through the code for 'TODO' items and fix them.</li> | ||||
| <li>Tidy-up CLIMain so that it calls System.exit() with a non-zero exit code, | |||||
| if the build fails.</li> | |||||
| <li>Tidy-up the 'build failed' message, so that the stack trace is only | |||||
| printed out if the log level is verbose/debug.</li> | |||||
| <li>Allow service factories to be configured from the contents of the | <li>Allow service factories to be configured from the contents of the | ||||
| <code>ant-services.xml</code> descriptor.</li> | <code>ant-services.xml</code> descriptor.</li> | ||||
| <li>Route external process stdout and stderr through the logger.</li> | <li>Route external process stdout and stderr through the logger.</li> | ||||
| @@ -206,11 +209,10 @@ | |||||
| <li>Fire ProjectListener events projectStarted() and projectFinished() | <li>Fire ProjectListener events projectStarted() and projectFinished() | ||||
| events on start and finish of referenced projects, adding indicator methods | events on start and finish of referenced projects, adding indicator methods | ||||
| to ProjectEvent.</li> | to ProjectEvent.</li> | ||||
| <li>Convert PropertyUtil to a non-static PropertyResolver service.</li> | |||||
| <li>Validate project and target names in DefaultProjectBuilder - reject dodgy | <li>Validate project and target names in DefaultProjectBuilder - reject dodgy | ||||
| names like "," or "", or " ". Probably want to exclude names that start or | |||||
| names like "," or "", or " ". Probably want to reject names that start or | |||||
| end with white-space (though internal whitespace is probably fine). We also | end with white-space (though internal whitespace is probably fine). We also | ||||
| want to reserve certain punctuation characters like . , : ? [ ] { }, etc for | |||||
| want to reserve certain punctuation characters like , : ? $ [ ] { } < >, etc for | |||||
| future use.</li> | future use.</li> | ||||
| <li>Similarly, validate property names, using the same rules.</li> | <li>Similarly, validate property names, using the same rules.</li> | ||||
| <li>Detect duplicate type names.</li> | <li>Detect duplicate type names.</li> | ||||
| @@ -222,6 +224,7 @@ | |||||
| an antlib.</li> | an antlib.</li> | ||||
| <li>Split up <code><is-set></code> condition into is-set and is-true conditions.</li> | <li>Split up <code><is-set></code> condition into is-set and is-true conditions.</li> | ||||
| <li>Allow the <code><if></code> task to take any condition implementation.</li> | <li>Allow the <code><if></code> task to take any condition implementation.</li> | ||||
| <li>Add an else block to the <code><if></code> task.</li> | |||||
| <li>Unit tests.</li> | <li>Unit tests.</li> | ||||
| </ul> | </ul> | ||||
| @@ -32,7 +32,8 @@ files are found in the <code>lib</code> directory:</p> | |||||
| <tr> | <tr> | ||||
| <td>SMB VFS support (Samba, Windows shares)</td> | <td>SMB VFS support (Samba, Windows shares)</td> | ||||
| <td>jcifs.jar</td> | <td>jcifs.jar</td> | ||||
| <td><a href="http://jcifs.samba.org">jcifs.samba.org</a></td> | |||||
| <td><a href="http://jcifs.samba.org">jcifs.samba.org</a>. | |||||
| <p>Note: there are problems using the 0.6.1 release. Try 0.6.0 instead.</p></td> | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td>FTP VFS support</td> | <td>FTP VFS support</td> | ||||