git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271053 13f79535-47bb-0310-9956-ffa450edef68master
@@ -59,7 +59,9 @@ Legal: | |||||
<property name="dist.lib" value="${dist.dir}/lib"/> | <property name="dist.lib" value="${dist.dir}/lib"/> | ||||
<property name="dist.ext" value="${dist.dir}/ext"/> | <property name="dist.ext" value="${dist.dir}/ext"/> | ||||
<property name="test.classes" value="${build.dir}/test/classes"/> | |||||
<property name="test.dir" value="${build.dir}/test"/> | |||||
<property name="test.working.dir" value="${test.dir}/testcases"/> | |||||
<property name="test.classes" value="${test.dir}/classes"/> | |||||
<property name="constants.file" value="org/apache/myrmidon/Constants.java"/> | <property name="constants.file" value="org/apache/myrmidon/Constants.java"/> | ||||
@@ -112,6 +114,9 @@ Legal: | |||||
<available property="netcomp.present" | <available property="netcomp.present" | ||||
classname="com.oroinc.net.ftp.FTPClient" | classname="com.oroinc.net.ftp.FTPClient" | ||||
classpathref="project.class.path" /> | classpathref="project.class.path" /> | ||||
<available property="jcifs.present" | |||||
classname="jcifs.smb.SmbFile" | |||||
classpathref="project.class.path" /> | |||||
<available property="starteam.present" | <available property="starteam.present" | ||||
classname="com.starbase.util.Platform" | classname="com.starbase.util.Platform" | ||||
classpathref="project.class.path" /> | classpathref="project.class.path" /> | ||||
@@ -187,6 +192,7 @@ Legal: | |||||
<property name="ant.package" value="org/apache/tools/ant"/> | <property name="ant.package" value="org/apache/tools/ant"/> | ||||
<property name="antlib.package" value="org/apache/antlib"/> | <property name="antlib.package" value="org/apache/antlib"/> | ||||
<property name="vfs.package" value="org/apache/aut/vfs"/> | |||||
<property name="optional.package" value="${ant.package}/taskdefs/optional"/> | <property name="optional.package" value="${ant.package}/taskdefs/optional"/> | ||||
<property name="optional.type.package" value="${ant.package}/types/optional"/> | <property name="optional.type.package" value="${ant.package}/types/optional"/> | ||||
<property name="util.package" value="${ant.package}/util"/> | <property name="util.package" value="${ant.package}/util"/> | ||||
@@ -221,6 +227,8 @@ Legal: | |||||
unless="jdk1.2+" /> | unless="jdk1.2+" /> | ||||
<exclude name="${ant.package}/listener/Log4jListener.java" | <exclude name="${ant.package}/listener/Log4jListener.java" | ||||
unless="log4j.present" /> | unless="log4j.present" /> | ||||
<exclude name="${vfs.package}/provider/ftp/**" unless="netcomp.present"/> | |||||
<exclude name="${vfs.package}/provider/smb/**" unless="jcifs.present"/> | |||||
<exclude name="${optional.package}/IContract.java" unless="icontract.present" /> | <exclude name="${optional.package}/IContract.java" unless="icontract.present" /> | ||||
<exclude name="${optional.package}/Script.java" unless="bsf.present" /> | <exclude name="${optional.package}/Script.java" unless="bsf.present" /> | ||||
@@ -278,7 +286,6 @@ Legal: | |||||
unless="jdk1.2+" /> | unless="jdk1.2+" /> | ||||
</javac> | </javac> | ||||
<copy todir="${build.classes}"> | <copy todir="${build.classes}"> | ||||
<fileset dir="${java.dir}"> | <fileset dir="${java.dir}"> | ||||
<include name="**/*.properties"/> | <include name="**/*.properties"/> | ||||
@@ -373,6 +380,18 @@ Legal: | |||||
</zipfileset> | </zipfileset> | ||||
</jar> | </jar> | ||||
<!-- | |||||
<jar jarfile="${build.lib}/vfile.atl" basedir="${build.classes}"> | |||||
<include name="org/apache/antlib/vfile/**" /> | |||||
<zipfileset dir="${manifest.dir}" fullpath="META-INF/ant-descriptor.xml"> | |||||
<include name="vfile-ant-descriptor.xml"/> | |||||
</zipfileset> | |||||
<zipfileset dir="${manifest.dir}" fullpath="META-INF/ant-roles.xml"> | |||||
<include name="vfile-ant-roles.xml"/> | |||||
</zipfileset> | |||||
</jar> | |||||
--> | |||||
<jar jarfile="${build.lib}/selftest.atl" | <jar jarfile="${build.lib}/selftest.atl" | ||||
basedir="${build.classes}" | basedir="${build.classes}" | ||||
manifest="${manifest.dir}/selftest.mf"> | manifest="${manifest.dir}/selftest.mf"> | ||||
@@ -436,6 +455,7 @@ Legal: | |||||
<!-- Compiles and runs the unit tests --> | <!-- Compiles and runs the unit tests --> | ||||
<target name="test" depends="compile" if="junit.present"> | <target name="test" depends="compile" if="junit.present"> | ||||
<!-- Compile the unit tests --> | |||||
<mkdir dir="${test.classes}"/> | <mkdir dir="${test.classes}"/> | ||||
<javac srcdir="src/testcases" | <javac srcdir="src/testcases" | ||||
destdir="${test.classes}" | destdir="${test.classes}" | ||||
@@ -444,13 +464,38 @@ Legal: | |||||
deprecation="${deprecation}"> | deprecation="${deprecation}"> | ||||
<classpath refid="project.class.path"/> | <classpath refid="project.class.path"/> | ||||
</javac> | </javac> | ||||
<property name="test.local.dir" location="${test.working.dir}/localfs"/> | |||||
<property name="test.zip.file" location="${test.working.dir}/zipfs/test.zip"/> | |||||
<!-- Prepare test files --> | |||||
<delete dir="${test.working.dir}"/> | |||||
<copy todir="${test.local.dir}/read-tests"> | |||||
<fileset dir="etc/testcases/org/apache/aut/vfs/basedir"/> | |||||
</copy> | |||||
<mkdir dir="${test.local.dir}/read-tests/emptydir"/> | |||||
<mkdir dir="${test.working.dir}/zipfs"/> | |||||
<zip zipfile="${test.zip.file}"> | |||||
<zipfileset dir="${test.local.dir}/read-tests" prefix="/basedir"/> | |||||
</zip> | |||||
<junit printsummary="on" | <junit printsummary="on" | ||||
fork="false"> | fork="false"> | ||||
<formatter type="brief" usefile="false"/> | <formatter type="brief" usefile="false"/> | ||||
<classpath refid="project.class.path"/> | <classpath refid="project.class.path"/> | ||||
<classpath location="${test.classes}"/> | <classpath location="${test.classes}"/> | ||||
<!-- Pass config to the tests --> | |||||
<sysproperty key="test.local.dir" value="${test.local.dir}"/> | |||||
<sysproperty key="test.zip.file" value="${test.zip.file}"/> | |||||
<sysproperty key="test.smb.uri" value="smb://${vfs.user}:${vfs.password}@${vfs.host}/${vfs.user}/vfs"/> | |||||
<sysproperty key="test.ftp.uri" value="ftp://${vfs.user}:${vfs.password}@${vfs.host}/home/${vfs.user}/vfs"/> | |||||
<batchtest> | <batchtest> | ||||
<fileset dir="${test.classes}" includes="**/*Test.class"/> | |||||
<fileset dir="${test.classes}" includes="**/*Test.class"> | |||||
<exclude name="**/SmbFileSystemTest.class" unless="test.smb"/> | |||||
<exclude name="**/FtpFileSystemTest.class" unless="test.ftp"/> | |||||
</fileset> | |||||
</batchtest> | </batchtest> | ||||
</junit> | </junit> | ||||
</target> | </target> | ||||
@@ -0,0 +1,2 @@ | |||||
This is a test file. | |||||
With 2 lines in it. |
@@ -0,0 +1,160 @@ | |||||
/* | |||||
* 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 java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import java.io.Reader; | |||||
import java.io.Writer; | |||||
import java.sql.Date; | |||||
/** | |||||
* This interface is used to access the data content of a file. | |||||
* | |||||
* <p>To read from a file, use the {@link #getInputStream} method. | |||||
* | |||||
* <p>To write to a file, use the {@link #getOutputStream} method. This | |||||
* method will create the file and the parent folder, if necessary. | |||||
* | |||||
* <p>To prevent concurrency problems, only a single <code>InputStream</code>, | |||||
* or <code>OutputStream</code> may be open at any time, for each file. | |||||
* | |||||
* <p>TODO - allow multiple input streams? | |||||
* | |||||
* @see FileObject#getContent | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public interface FileContent | |||||
{ | |||||
/** | |||||
* Returns the file which this is the content of. | |||||
*/ | |||||
FileObject getFile(); | |||||
/** | |||||
* Determines the size of the file, in bytes. | |||||
* | |||||
* @return | |||||
* The size of the file, in bytes. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file does not exist, or is being written to, or on error | |||||
* determining the size. | |||||
*/ | |||||
long getSize() throws FileSystemException; | |||||
/** | |||||
* Determines the last-modified timestamp of the file. | |||||
* | |||||
* @return | |||||
* The last-modified timestamp. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file does not exist, or is being written to, or on error | |||||
* determining the last-modified timestamp. | |||||
*/ | |||||
long getLastModifiedTime() throws FileSystemException; | |||||
/** | |||||
* Sets the last-modified timestamp of the file. Creates the file if | |||||
* it does not exist. | |||||
* | |||||
* @param modTime | |||||
* The time to set the last-modified timestamp to. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file is read-only, or is being read, or on error setting | |||||
* the last-modified timestamp. | |||||
*/ | |||||
void setLastModifiedTime( long modTime ) throws FileSystemException; | |||||
/** | |||||
* Gets the value of an attribute of the file's content. | |||||
* | |||||
* <p>TODO - change to <code>Map getAttributes()</code> instead? | |||||
* | |||||
* <p>TODO - define the standard attribute names, and define which attrs | |||||
* are guaranteed to be present. | |||||
* | |||||
* @param attrName | |||||
* The name of the attribute. | |||||
* | |||||
* @return | |||||
* The value of the attribute. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file does not exist, or is being written, or if the | |||||
* attribute is unknown. | |||||
*/ | |||||
Object getAttribute( String attrName ) throws FileSystemException; | |||||
/** | |||||
* Sets the value of an attribute of the file's content. Creates the | |||||
* file if it does not exist. | |||||
* | |||||
* @param attrName | |||||
* The name of the attribute. | |||||
* | |||||
* @param value | |||||
* The value of the attribute. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file is read-only, or is being read, or if the attribute | |||||
* is not supported, or on error setting the attribute. | |||||
*/ | |||||
void setAttribute( String attrName, Object value ) throws FileSystemException; | |||||
/** | |||||
* Returns an input stream for reading the file's content. | |||||
* | |||||
* <p>There may only be a single input or output stream open for the | |||||
* file at any time. | |||||
* | |||||
* @return | |||||
* An input stream to read the file's content from. The input | |||||
* stream is buffered, so there is no need to wrap it in a | |||||
* <code>BufferedInputStream</code>. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file does not exist, or is being read, or is being written, | |||||
* or on error opening the stream. | |||||
*/ | |||||
InputStream getInputStream() throws FileSystemException; | |||||
/** | |||||
* Returns an output stream for writing the file's content. | |||||
* | |||||
* If the file does not exist, this method creates it, and the parent | |||||
* folder, if necessary. If the file does exist, it is replaced with | |||||
* whatever is written to the output stream. | |||||
* | |||||
* <p>There may only be a single input or output stream open for the | |||||
* file at any time. | |||||
* | |||||
* @return | |||||
* An output stream to write the file's content to. The stream is | |||||
* buffered, so there is no need to wrap it in a | |||||
* <code>BufferedOutputStream</code>. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file is read-only, or is being read, or is being written, | |||||
* or on error opening the stream. | |||||
*/ | |||||
OutputStream getOutputStream() throws FileSystemException; | |||||
/** | |||||
* Closes all resources used by the content, including any open stream. | |||||
* Commits pending changes to the file. | |||||
* | |||||
* <p>This method is a hint to the implementation that it can release | |||||
* resources. This object can continue to be used after calling this | |||||
* method. | |||||
*/ | |||||
void close() throws FileSystemException; | |||||
} |
@@ -0,0 +1,87 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* The interface is used to perform operations on a file name. File names | |||||
* are immutable, and work correctly as keys in hash tables. | |||||
* | |||||
* @see FileObject | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public interface FileName | |||||
{ | |||||
/** | |||||
* Returns the base name of the file. The base name of a file is the | |||||
* last element of its name. For example the base name of | |||||
* <code>/somefolder/somefile</code> is <code>somefile</code>. | |||||
* | |||||
* <p>The root file of a file system has an empty base name. | |||||
*/ | |||||
String getBaseName(); | |||||
/** | |||||
* Returns the absolute path of the file, within its file system. This | |||||
* path is normalised, so that <code>.</code> and <code>..</code> elements | |||||
* have been removed. Also, the path only contains <code>/</code> as its | |||||
* separator character. The path always starts with <code>/</code> | |||||
* | |||||
* <p>The root of a file system has <code>/</code> as its path. | |||||
*/ | |||||
String getPath(); | |||||
/** | |||||
* Returns the absolute URI of the file. | |||||
*/ | |||||
String getURI(); | |||||
/** | |||||
* Returns the name of the parent of the file. The root of a file system | |||||
* has no parent. | |||||
* | |||||
* @return | |||||
* A {@link FileName} object representing the parent name. Returns | |||||
* null for the root of a file system. | |||||
*/ | |||||
FileName getParent(); | |||||
/** | |||||
* Resolves a name, relative to the file. Equivalent to calling | |||||
* <code>resolveName( path, NameScope.FILE_SYSTEM )</code>. | |||||
* | |||||
* @param path | |||||
* The path to resolve. | |||||
* | |||||
* @return | |||||
* A {@link FileName} object representing the resolved name. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the name is invalid. | |||||
*/ | |||||
FileName resolveName( String path ) throws FileSystemException; | |||||
/** | |||||
* Resolves a name, relative to the file. Refer to {@link NameScope#CHILD} | |||||
* and {@link NameScope#FILE_SYSTEM} for a description of how names are | |||||
* resolved. | |||||
* | |||||
* @param name | |||||
* The path to resolve. | |||||
* | |||||
* @param scope | |||||
* The scope to use when resolving the name. | |||||
* | |||||
* @return | |||||
* A {@link FileName} object representing the resolved name. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the name is invalid. | |||||
*/ | |||||
FileName resolveName( String name, NameScope scope ) throws FileSystemException; | |||||
} |
@@ -0,0 +1,214 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* This interface represents a file, and is used to access the content and | |||||
* structure of the file. | |||||
* | |||||
* <p>Files are arranged in a hierarchy. Each hierachy forms a | |||||
* <i>file system</i>. A file system represents things like a local OS | |||||
* file system, a windows share, an HTTP server, or the contents of a Zip file. | |||||
* | |||||
* <p>There are two types of files: <i>Folders</i>, which contain other files, | |||||
* and <i>normal files</i>, which contain data, or <i>content</i>. A folder may | |||||
* not have any content, and a normal file cannot contain other files. | |||||
* | |||||
* <h4>File Naming</h4> | |||||
* | |||||
* <p>TODO - write this. | |||||
* | |||||
* <h4>Reading and Writing a File</h4> | |||||
* | |||||
* <p>Reading and writing a file, and all other operations on the file's | |||||
* <i>content</i>, is done using the {@link FileContent} object returned | |||||
* by {@link #getContent}. | |||||
* | |||||
* <h4>Creating and Deleting a File</h4> | |||||
* | |||||
* <p>A file is created using either {@link #create}, or by writing to the | |||||
* file using one of the {@link FileContent} methods. | |||||
* | |||||
* <p>A file is deleted using {@link #delete}. Deletion is recursive, so | |||||
* that when a folder is deleted, so are all its child files. | |||||
* | |||||
* <h4>Finding Files</h4> | |||||
* | |||||
* <p>Other files in the <i>same</i> file system as this file can be found using: | |||||
* <ul> | |||||
* <li>{@link #resolveFile} to find another file relative to this file. | |||||
* <li>{@link #getChildren} to find the children of this file. | |||||
* <li>{@link #getParent} to find the folder containing this file. | |||||
* <li>{@link #getRoot} to find the root folder of the file system. | |||||
* </ul> | |||||
* | |||||
* <p>To find files in another file system, use a {@link FileSystemManager}. | |||||
* | |||||
* @see FileSystemManager | |||||
* @see FileContent | |||||
* @see FileName | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public interface FileObject | |||||
{ | |||||
/** | |||||
* Returns the name of this file. | |||||
*/ | |||||
FileName getName(); | |||||
/** | |||||
* Determines if this file exists. | |||||
* | |||||
* @return | |||||
* <code>true</code> if this file exists, <code>false</code> if not. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error determining if this file exists. | |||||
*/ | |||||
boolean exists() throws FileSystemException; | |||||
/** | |||||
* Returns this file's type. | |||||
* | |||||
* @return | |||||
* Either {@link FileType#FILE} or {@link FileType#FOLDER}. Never | |||||
* returns null. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file does not exist, or on error determining the file's type. | |||||
*/ | |||||
FileType getType() throws FileSystemException; | |||||
/** | |||||
* Returns the folder that contains this file. | |||||
* | |||||
* @return | |||||
* The folder that contains this file. Returns null if this file is | |||||
* the root of a file system. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error finding the file's parent. | |||||
*/ | |||||
FileObject getParent() throws FileSystemException; | |||||
/** | |||||
* Returns the root of the file system containing this file. | |||||
* | |||||
* @return | |||||
* The root of the file system. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error finding the root of the file system. | |||||
*/ | |||||
FileObject getRoot() throws FileSystemException; | |||||
/** | |||||
* Lists the children of this file. | |||||
* | |||||
* @return | |||||
* An array containing the children of this file. The array is | |||||
* unordered. If the file does not have any children, a zero-length | |||||
* array is returned. This method never returns null. | |||||
* | |||||
* @throws FileSystemException | |||||
* If this file does not exist, or is not a folder, or on error | |||||
* listing this file's children. | |||||
*/ | |||||
FileObject[] getChildren() throws FileSystemException; | |||||
/** | |||||
* Finds a file, relative to this file. Refer to {@link NameScope#CHILD} | |||||
* and {@link NameScope#FILE_SYSTEM} for a description of how names | |||||
* are resolved in the different scopes. | |||||
* | |||||
* @param name | |||||
* The name to resolve. | |||||
* | |||||
* @return | |||||
* The file. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error parsing the path, or on error finding the file. | |||||
*/ | |||||
FileObject resolveFile( String name, NameScope scope ) throws FileSystemException; | |||||
/** | |||||
* Finds a file, relative to this file. Equivalent to calling | |||||
* <code>resolveFile( path, NameScope.FILE_SYSTEM )</code>. | |||||
* | |||||
* @param path | |||||
* The path of the file to locate. Can either be a relative | |||||
* path or an absolute path. | |||||
* | |||||
* @return | |||||
* The file. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error parsing the path, or on error finding the file. | |||||
*/ | |||||
FileObject resolveFile( String path ) throws FileSystemException; | |||||
/** | |||||
* Deletes this file, and all children. Does nothing if the file | |||||
* does not exist. | |||||
* | |||||
* <p>This method is not transactional. If it fails and throws an | |||||
* exception, some of this file's descendents may have been deleted. | |||||
* | |||||
* @throws FileSystemException | |||||
* If this file or one of its descendents is read-only, or on error | |||||
* deleting this file or one of its descendents. | |||||
*/ | |||||
void delete() throws FileSystemException; | |||||
/** | |||||
* Creates this file, if it does not exist. Also creates any ancestor | |||||
* folders which do not exist. This method does nothing if the file | |||||
* already exists with the requested type. | |||||
* | |||||
* @param type | |||||
* The type of file to create. | |||||
* | |||||
* @throws FileSystemException | |||||
* If the file already exists with the wrong type, or the parent | |||||
* folder is read-only, or on error creating this file or one of | |||||
* its ancestors. | |||||
*/ | |||||
void create( FileType type ) throws FileSystemException; | |||||
/** | |||||
* Returns this file's content. The {@link FileContent} returned by this | |||||
* method can be used to read and write the content of the file. | |||||
* | |||||
* <p>This method can be called if the file does not exist, and | |||||
* the returned {@link FileContent} can be used to create the file | |||||
* by writing its content. | |||||
* | |||||
* @return | |||||
* This file's content. | |||||
* | |||||
* @throws FileSystemException | |||||
* If this file is a folder. | |||||
*/ | |||||
FileContent getContent() throws FileSystemException; | |||||
/** | |||||
* Closes this file, and its content. This method is a hint to the | |||||
* implementation that it can release any resources asociated with | |||||
* the file. | |||||
* | |||||
* <p>The file object can continue to be used after this method is called. | |||||
* | |||||
* @see FileContent#close | |||||
* | |||||
* @throws FileSystemEception | |||||
* On error closing the file. | |||||
*/ | |||||
void close() throws FileSystemException; | |||||
} |
@@ -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.avalon.framework.CascadingException; | |||||
/** | |||||
* Thrown for file system errors. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class FileSystemException extends CascadingException | |||||
{ | |||||
private Throwable m_cause; | |||||
/** | |||||
* Constructs exception with the specified detail message. | |||||
* | |||||
* @param msg the detail message. | |||||
*/ | |||||
public FileSystemException( String msg ) | |||||
{ | |||||
super( msg ); | |||||
} | |||||
/** | |||||
* Constructs exception with the specified detail message. | |||||
* | |||||
* @param msg the detail message. | |||||
* @param cause the cause. | |||||
*/ | |||||
public FileSystemException( String msg, Throwable cause ) | |||||
{ | |||||
super( msg, cause ); | |||||
} | |||||
} |
@@ -0,0 +1,94 @@ | |||||
/* | |||||
* 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.avalon.framework.component.Component; | |||||
/** | |||||
* A FileSystemManager is manages a set of file systems. This interface is | |||||
* used to locate a {@link FileObject} by name from one of those file systems. | |||||
* | |||||
* <p>To locate a {@link FileObject}, use one of the <code>resolveFile()</code> | |||||
* methods.</p> | |||||
* | |||||
* <h4><a name="naming">File Naming</a></h4> | |||||
* | |||||
* <p>A file system manager can recognise several types of file names: | |||||
* | |||||
* <ul> | |||||
* | |||||
* <li><p>Absolute URI. These must start with a scheme, such as | |||||
* <code>file:</code> or <code>ftp:</code>, followed by a scheme dependent | |||||
* file name. Some examples:</p> | |||||
* <pre> | |||||
* file:/c:/somefile | |||||
* ftp://somewhere.org/somefile | |||||
* </pre> | |||||
* | |||||
* <li><p>Absolute local file name. For example, | |||||
* <code>/home/someuser/a-file</code> or <code>c:\dir\somefile.html</code>. | |||||
* Elements in the name can be separated using any of the following | |||||
* characters: <code>/</code>, <code>\</code>, or the native file separator | |||||
* character. For example, the following file names are the same:</p> | |||||
* <pre> | |||||
* c:\somedir\somefile.xml | |||||
* c:/somedir/somefile.xml | |||||
* </pre> | |||||
* | |||||
* <li><p>Relative path. For example: <code>../somefile</code> or | |||||
* <code>somedir/file.txt</code>. The file system manager resolves relative | |||||
* paths against its <i>base file</i>. Elements in the relative path can be | |||||
* separated using <code>/</code>, <code>\</code>, or file system specific | |||||
* separator characters. Relative paths may also contain <code>..</code> and | |||||
* <code>.</code> elements. See {@link FileObject#resolveFile} for more details.</p> | |||||
* | |||||
* </ul> | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public interface FileSystemManager | |||||
extends Component | |||||
{ | |||||
String ROLE = "org.apache.aut.vfs.FileSystemManager"; | |||||
/** | |||||
* Returns the base file used to resolve relative paths. | |||||
*/ | |||||
FileObject getBaseFile(); | |||||
/** | |||||
* Locates a file by name. Equivalent to calling | |||||
* <code>resolveFile(uri, getBaseName())</code>. | |||||
* | |||||
* @param name | |||||
* The name of the file. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error parsing the file name. | |||||
*/ | |||||
FileObject resolveFile( String name ) throws FileSystemException; | |||||
/** | |||||
* Locates a file by name. The name is resolved as described | |||||
* <a href="#naming">above</a>. That is, the name can be either | |||||
* an absolute URI, an absolute file name, or a relative path to | |||||
* be resolved against <code>baseFile</code>. | |||||
* | |||||
* <p>Note that the file does not have to exist when this method is called. | |||||
* | |||||
* @param name | |||||
* The name of the file. | |||||
* | |||||
* @param baseFile | |||||
* The base file to use to resolve paths. | |||||
* | |||||
* @throws FileSystemException | |||||
* On error parsing the file name. | |||||
*/ | |||||
FileObject resolveFile( FileObject baseFile, String name ) throws FileSystemException; | |||||
} |
@@ -0,0 +1,52 @@ | |||||
/* | |||||
* 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. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
package org.apache.aut.vfs; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* An enumeration that represents a file's type. | |||||
*/ | |||||
public final class FileType | |||||
{ | |||||
private final static Resources REZ = | |||||
ResourceManager.getPackageResources( FileType.class ); | |||||
private String m_name; | |||||
private FileType( String name ) | |||||
{ | |||||
m_name = name; | |||||
} | |||||
/** Returns the name of the type. */ | |||||
public String toString() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** Returns the name of the type. */ | |||||
public String getName() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** | |||||
* A folder, which can contain other files, but does not have any data | |||||
* content. | |||||
*/ | |||||
public static final FileType FOLDER = new FileType( REZ.getString( "folder.name" ) ); | |||||
/** | |||||
* A regular file, which has data content, but cannot contain other files. | |||||
*/ | |||||
public static final FileType FILE = new FileType( REZ.getString( "file.name" ) ); | |||||
} |
@@ -0,0 +1,62 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* An enumerated type for file name scope, used when resolving a name relative | |||||
* to a file. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public final class NameScope | |||||
{ | |||||
private String m_name; | |||||
private NameScope( String name ) | |||||
{ | |||||
m_name = name; | |||||
} | |||||
/** Returns the name of the scope. */ | |||||
public String toString() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** Returns the name of the scope. */ | |||||
public String getName() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** | |||||
* Resolve against the children of the base file. | |||||
* | |||||
* <p>The supplied name must be a valid element name. That is, it may | |||||
* not be empty, or <code>.</code>, or <code>..</code>, or contain any | |||||
* separator characters. | |||||
*/ | |||||
public static final NameScope CHILD = new NameScope( "child" ); | |||||
/** | |||||
* Resolve against files in the same file system as the base file. | |||||
* | |||||
* <p>If the supplied name is an absolute path, then it is resolved | |||||
* relative to the root of the file system that the base file belongs to. | |||||
* If a relative name is supplied, then it is resolved relative to the base | |||||
* file. | |||||
* | |||||
* <p>The path may use any mix of <code>/</code>, <code>\</code>, or file | |||||
* system specific separators to separate elements in the path. It may | |||||
* also contain <code>.</code> and <code>..</code> elements. | |||||
* | |||||
* <p>A path is considered absolute if it starts with a separator character, | |||||
* and relative if it does not. | |||||
*/ | |||||
public static final NameScope FILE_SYSTEM = new NameScope( "filesystem" ); | |||||
} |
@@ -0,0 +1,2 @@ | |||||
folder.name=folder | |||||
file.name=file |
@@ -0,0 +1,10 @@ | |||||
<body> | |||||
<p>This package contains the interfaces used to access the VFS.</p> | |||||
<p>A {@link vfs.filesystem.FileSystemManager} is the starting point for | |||||
all file system access. It is used to locate a {@link vfs.filesystem.FileObject} | |||||
by name. Files are accessed using the {@link vfs.filesystem.FileObject} | |||||
interface. This interface allows a file's structure and content to be | |||||
accessed.</p> | |||||
</body> |
@@ -0,0 +1,644 @@ | |||||
/* | |||||
* 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.provider; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import java.util.ArrayList; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
import org.apache.aut.vfs.FileContent; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.FileType; | |||||
import org.apache.aut.vfs.NameScope; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A partial file object implementation. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class AbstractFileObject implements FileObject | |||||
{ | |||||
private static final Resources REZ = | |||||
ResourceManager.getPackageResources( AbstractFileObject.class ); | |||||
private FileName m_name; | |||||
private AbstractFileSystem m_fs; | |||||
private DefaultFileContent m_content; | |||||
private static final FileObject[] EMPTY_FILE_ARRAY = {}; | |||||
// Cached info | |||||
private boolean m_attached; | |||||
private AbstractFileObject m_parent; | |||||
private FileType m_type; | |||||
private FileObject[] m_children; | |||||
protected AbstractFileObject( FileName name, AbstractFileSystem fs ) | |||||
{ | |||||
m_name = name; | |||||
m_fs = fs; | |||||
} | |||||
/** | |||||
* Returns true if this file is read-only. | |||||
*/ | |||||
protected boolean isReadOnly() | |||||
{ | |||||
return false; | |||||
} | |||||
/** | |||||
* Attaches this file object to its file resource. This method is called | |||||
* before any of the doBlah() or onBlah() methods. Sub-classes can use | |||||
* this method to perform lazy initialisation. | |||||
*/ | |||||
protected void doAttach() throws Exception | |||||
{ | |||||
} | |||||
/** | |||||
* Detaches this file object from its file resource. | |||||
* | |||||
* <p>Called when this file is closed, or its type changes. Note that | |||||
* the file object may be reused later, so should be able to be reattached. | |||||
*/ | |||||
protected void doDetach() | |||||
{ | |||||
} | |||||
/** | |||||
* Determines the type of the file, returns null if the file does not | |||||
* exist. The return value of this method is cached, so the | |||||
* implementation can be expensive. | |||||
*/ | |||||
protected abstract FileType doGetType() throws Exception; | |||||
/** | |||||
* Lists the children of the file. Is only called if {@link #doGetType} | |||||
* returns {@link FileType#FOLDER}. The return value of this method | |||||
* is cached, so the implementation can be expensive. | |||||
*/ | |||||
protected abstract String[] doListChildren() throws Exception; | |||||
/** | |||||
* Deletes the file. Is only called when: | |||||
* <ul> | |||||
* <li>{@link #isReadOnly} returns false. | |||||
* <li>{@link #doGetType} does not return null. | |||||
* <li>If this file is a folder, it has no children. | |||||
* </ul> | |||||
*/ | |||||
protected void doDelete() throws Exception | |||||
{ | |||||
final String message = REZ.getString( "delete-not-supported.error" ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
/** | |||||
* Creates this file as a folder. Is only called when: | |||||
* <ul> | |||||
* <li>{@link #isReadOnly} returns false. | |||||
* <li>{@link #doGetType} returns null. | |||||
* <li>The parent folder exists or this file is the root of the file | |||||
* system. | |||||
* </ul> | |||||
*/ | |||||
protected void doCreateFolder() throws Exception | |||||
{ | |||||
final String message = REZ.getString( "create-folder-not-supported.error" ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
/** | |||||
* Called when the children of this file change. | |||||
*/ | |||||
protected void onChildrenChanged() | |||||
{ | |||||
} | |||||
/** | |||||
* Returns the size of the file content (in bytes). Is only called if | |||||
* {@link #doGetType} returns {@link FileType#FILE}. | |||||
*/ | |||||
protected abstract long doGetContentSize() throws Exception; | |||||
/** | |||||
* Creates an input stream to read the file content from. Is only called | |||||
* if {@link #doGetType} returns {@link FileType#FILE}. | |||||
* | |||||
* <p>There is guaranteed never to be more than one stream for this file | |||||
* (input or output) open at any given time. | |||||
* | |||||
* <p>The returned stream does not have to be buffered. | |||||
*/ | |||||
protected abstract InputStream doGetInputStream() throws Exception; | |||||
/** | |||||
* Creates an output stream to write the file content to. Is only | |||||
* called if: | |||||
* <ul> | |||||
* <li>This file is not read-only. | |||||
* <li>{@link #doGetType} returns {@link FileType#FILE}, or | |||||
* {@link #doGetType} returns null, and the file's parent exists | |||||
* and is a folder. | |||||
* </ul> | |||||
* | |||||
* <p>There is guaranteed never to be more than one stream for this file | |||||
* (input or output) open at any given time. | |||||
* | |||||
* <p>The returned stream does not have to be buffered. | |||||
*/ | |||||
protected OutputStream doGetOutputStream() throws Exception | |||||
{ | |||||
final String message = REZ.getString( "write-not-supported.error" ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
/** | |||||
* Notification of the output stream being closed. | |||||
* TODO - get rid of this. | |||||
*/ | |||||
protected void doEndOutput() throws Exception | |||||
{ | |||||
} | |||||
/** | |||||
* Notification of the input stream being closed. | |||||
* TODO - get rid of this. | |||||
*/ | |||||
protected void doEndInput() throws Exception | |||||
{ | |||||
} | |||||
/** | |||||
* Returns the URI of the file. | |||||
*/ | |||||
public String toString() | |||||
{ | |||||
return m_name.getURI(); | |||||
} | |||||
/** | |||||
* Returns the name of the file. | |||||
*/ | |||||
public FileName getName() | |||||
{ | |||||
return m_name; | |||||
} | |||||
/** | |||||
* Determines if the file exists. | |||||
*/ | |||||
public boolean exists() throws FileSystemException | |||||
{ | |||||
attach(); | |||||
return ( m_type != null ); | |||||
} | |||||
/** | |||||
* Returns the file's type. | |||||
*/ | |||||
public FileType getType() throws FileSystemException | |||||
{ | |||||
attach(); | |||||
if( m_type == null ) | |||||
{ | |||||
final String message = REZ.getString( "get-type-no-exist.error", m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
return m_type; | |||||
} | |||||
/** | |||||
* Returns the parent of the file. | |||||
*/ | |||||
public FileObject getParent() throws FileSystemException | |||||
{ | |||||
if( this == m_fs.getRoot() ) | |||||
{ | |||||
// Root file has no parent | |||||
return null; | |||||
} | |||||
// Locate the parent of this file | |||||
if( m_parent == null ) | |||||
{ | |||||
m_parent = (AbstractFileObject)m_fs.findFile( m_name.getParent() ); | |||||
} | |||||
return m_parent; | |||||
} | |||||
/** | |||||
* Returns the root of the file system containing the file. | |||||
*/ | |||||
public FileObject getRoot() throws FileSystemException | |||||
{ | |||||
return m_fs.getRoot(); | |||||
} | |||||
/** | |||||
* Returns the children of the file. | |||||
*/ | |||||
public FileObject[] getChildren() throws FileSystemException | |||||
{ | |||||
attach(); | |||||
if( m_type == null ) | |||||
{ | |||||
final String message = REZ.getString( "list-children-no-exist.error", m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
if( m_type != FileType.FOLDER ) | |||||
{ | |||||
final String message = REZ.getString( "list-children-not-folder.error", m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Use cached info, if present | |||||
if( m_children != null ) | |||||
{ | |||||
return m_children; | |||||
} | |||||
// List the children | |||||
String[] files; | |||||
try | |||||
{ | |||||
files = doListChildren(); | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "list-children.error", m_name ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
if( files == null || files.length == 0 ) | |||||
{ | |||||
// No children | |||||
m_children = EMPTY_FILE_ARRAY; | |||||
} | |||||
else | |||||
{ | |||||
// Create file objects for the children | |||||
m_children = new FileObject[ files.length ]; | |||||
for( int i = 0; i < files.length; i++ ) | |||||
{ | |||||
String file = files[ i ]; | |||||
m_children[ i ] = m_fs.findFile( m_name.resolveName( file, NameScope.CHILD ) ); | |||||
} | |||||
} | |||||
return m_children; | |||||
} | |||||
/** | |||||
* Returns a child by name. | |||||
*/ | |||||
public FileObject resolveFile( String name, NameScope scope ) throws FileSystemException | |||||
{ | |||||
// TODO - cache children (only if they exist) | |||||
return m_fs.findFile( m_name.resolveName( name, scope ) ); | |||||
} | |||||
/** | |||||
* Finds a file, relative to this file. | |||||
* | |||||
* @param path | |||||
* The path of the file to locate. Can either be a relative | |||||
* path, which is resolved relative to this file, or an | |||||
* absolute path, which is resolved relative to the file system | |||||
* that contains this file. | |||||
*/ | |||||
public FileObject resolveFile( String path ) throws FileSystemException | |||||
{ | |||||
FileName name = m_name.resolveName( path ); | |||||
return m_fs.findFile( name ); | |||||
} | |||||
/** | |||||
* Deletes this file, once all its children have been deleted | |||||
*/ | |||||
private void deleteSelf() throws FileSystemException | |||||
{ | |||||
if( isReadOnly() ) | |||||
{ | |||||
final String message = REZ.getString( "delete-read-only.error", m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Delete the file | |||||
try | |||||
{ | |||||
doDelete(); | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "delete.error", m_name ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
// Update cached info | |||||
updateType(null); | |||||
} | |||||
/** | |||||
* Deletes this file, and all children. | |||||
*/ | |||||
public void delete() throws FileSystemException | |||||
{ | |||||
attach(); | |||||
if( m_type == null ) | |||||
{ | |||||
// File does not exist | |||||
return; | |||||
} | |||||
// Recursively delete this file and all its children | |||||
List queue = new ArrayList(); | |||||
Set expanded = new HashSet(); | |||||
queue.add( this ); | |||||
// Recursively delete each file | |||||
// TODO - recover from errors | |||||
while( queue.size() > 0 ) | |||||
{ | |||||
AbstractFileObject file = (AbstractFileObject)queue.get( 0 ); | |||||
file.attach(); | |||||
if( file.m_type == null ) | |||||
{ | |||||
// Shouldn't happen | |||||
queue.remove( 0 ); | |||||
} | |||||
else if( file.m_type == FileType.FILE ) | |||||
{ | |||||
// Delete the file | |||||
file.deleteSelf(); | |||||
queue.remove( 0 ); | |||||
} | |||||
else if( expanded.contains( file ) ) | |||||
{ | |||||
// Have already deleted all the children of this folder - | |||||
// delete it | |||||
file.deleteSelf(); | |||||
queue.remove( 0 ); | |||||
} | |||||
else | |||||
{ | |||||
// Delete the folder's children | |||||
FileObject[] children = file.getChildren(); | |||||
for( int i = 0; i < children.length; i++ ) | |||||
{ | |||||
FileObject child = children[ i ]; | |||||
queue.add( 0, child ); | |||||
} | |||||
expanded.add( file ); | |||||
} | |||||
} | |||||
// Update parent's child list | |||||
notifyParent(); | |||||
} | |||||
/** | |||||
* Creates this file, if it does not exist. Also creates any ancestor | |||||
* files which do not exist. | |||||
*/ | |||||
public void create( FileType type ) throws FileSystemException | |||||
{ | |||||
attach(); | |||||
if( m_type == type ) | |||||
{ | |||||
// Already exists as correct type | |||||
return; | |||||
} | |||||
if( m_type != null ) | |||||
{ | |||||
final String message = REZ.getString( "create-mismatched-type.error", type, m_name, m_type ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
if( isReadOnly() ) | |||||
{ | |||||
final String message = REZ.getString( "create-read-only.error", type, m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Traverse up the heirarchy and make sure everything is a folder | |||||
FileObject parent = getParent(); | |||||
if( parent != null ) | |||||
{ | |||||
parent.create( FileType.FOLDER ); | |||||
} | |||||
// Create the folder | |||||
try | |||||
{ | |||||
if( type == FileType.FOLDER ) | |||||
{ | |||||
doCreateFolder(); | |||||
m_children = EMPTY_FILE_ARRAY; | |||||
} | |||||
else if( type == FileType.FILE ) | |||||
{ | |||||
OutputStream outStr = doGetOutputStream(); | |||||
outStr.close(); | |||||
endOutput(); | |||||
} | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "create.error", type, m_name ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
// Update cached info | |||||
updateType(type); | |||||
} | |||||
/** | |||||
* Returns the file's content. | |||||
*/ | |||||
public FileContent getContent() throws FileSystemException | |||||
{ | |||||
attach(); | |||||
if( m_type == FileType.FOLDER ) | |||||
{ | |||||
final String message = REZ.getString( "get-folder-content.error", m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
if( m_content == null ) | |||||
{ | |||||
m_content = new DefaultFileContent( this ); | |||||
} | |||||
return m_content; | |||||
} | |||||
/** | |||||
* Closes this file, and its content. | |||||
*/ | |||||
public void close() throws FileSystemException | |||||
{ | |||||
FileSystemException exc = null; | |||||
// Close the content | |||||
if( m_content != null ) | |||||
{ | |||||
try | |||||
{ | |||||
m_content.close(); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
exc = e; | |||||
} | |||||
} | |||||
// Detach from the file | |||||
if( m_attached ) | |||||
{ | |||||
doDetach(); | |||||
m_attached = false; | |||||
m_type = null; | |||||
m_children = null; | |||||
} | |||||
if( exc != null ) | |||||
{ | |||||
throw exc; | |||||
} | |||||
} | |||||
/** | |||||
* Prepares this file for writing. Makes sure it is either a file, | |||||
* or its parent folder exists. Returns an output stream to use to | |||||
* write the content of the file to. | |||||
*/ | |||||
public OutputStream getOutputStream() throws FileSystemException | |||||
{ | |||||
attach(); | |||||
if( isReadOnly() ) | |||||
{ | |||||
final String message = REZ.getString( "write-read-only.error", m_name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
if( m_type == FileType.FOLDER ) | |||||
{ | |||||
final String message = REZ.getString( "write-folder.error", m_name ); | |||||
} | |||||
if( m_type == null ) | |||||
{ | |||||
// Does not exist - make sure parent does | |||||
FileObject parent = getParent(); | |||||
if( parent != null ) | |||||
{ | |||||
parent.create( FileType.FOLDER ); | |||||
} | |||||
} | |||||
// Get the raw output stream | |||||
try | |||||
{ | |||||
return doGetOutputStream(); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
throw exc; | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "write.error", m_name ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
} | |||||
/** | |||||
* Attaches to the file. | |||||
*/ | |||||
private void attach() throws FileSystemException | |||||
{ | |||||
if( m_attached ) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
// Attach and determine the file type | |||||
doAttach(); | |||||
m_attached = true; | |||||
m_type = doGetType(); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
throw exc; | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "get-type.error", m_name ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
} | |||||
/** | |||||
* Called when the ouput stream for this file is closed. | |||||
*/ | |||||
public void endOutput() throws Exception | |||||
{ | |||||
updateType( FileType.FILE ); | |||||
doEndOutput(); | |||||
} | |||||
/** | |||||
* Update cached info when this file's type changes. | |||||
*/ | |||||
private void updateType(FileType type) | |||||
{ | |||||
// Notify parent that its child list may no longer be valid | |||||
notifyParent(); | |||||
// Detach | |||||
doDetach(); | |||||
m_attached = false; | |||||
m_type = null; | |||||
m_children = null; | |||||
} | |||||
/** | |||||
* Notify the parent of a change to its children, when a child is created | |||||
* or deleted. | |||||
*/ | |||||
private void notifyParent() | |||||
{ | |||||
if( m_parent == null ) | |||||
{ | |||||
// Locate the parent, if it is cached | |||||
m_parent = (AbstractFileObject)m_fs.getFile( m_name.getParent() ); | |||||
} | |||||
if( m_parent != null ) | |||||
{ | |||||
m_parent.invalidateChildren(); | |||||
} | |||||
} | |||||
/** | |||||
* Notifies a file that children have been created or deleted. | |||||
*/ | |||||
private void invalidateChildren() | |||||
{ | |||||
m_children = null; | |||||
onChildrenChanged(); | |||||
} | |||||
} |
@@ -0,0 +1,92 @@ | |||||
/* | |||||
* 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.provider; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
/** | |||||
* A partial file system implementation. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class AbstractFileSystem implements FileSystem | |||||
{ | |||||
private FileObject m_root; | |||||
private FileName m_rootName; | |||||
/** Map from absolute file path to FileObject. */ | |||||
private Map m_files = new HashMap(); | |||||
protected AbstractFileSystem( FileName rootName ) | |||||
{ | |||||
m_rootName = rootName; | |||||
} | |||||
/** | |||||
* Creates a file object. This method is called only if the requested | |||||
* file is not cached. | |||||
*/ | |||||
protected abstract FileObject createFile( FileName name ) throws FileSystemException; | |||||
/** | |||||
* Adds a file object to the cache. | |||||
*/ | |||||
protected void putFile( FileObject file ) | |||||
{ | |||||
m_files.put( file.getName().getPath(), file ); | |||||
} | |||||
/** | |||||
* Returns a cached file. | |||||
*/ | |||||
protected FileObject getFile( FileName name ) | |||||
{ | |||||
return (FileObject)m_files.get( name.getPath() ); | |||||
} | |||||
/** | |||||
* Returns the root file of this file system. | |||||
*/ | |||||
public FileObject getRoot() throws FileSystemException | |||||
{ | |||||
if( m_root == null ) | |||||
{ | |||||
m_root = findFile( m_rootName ); | |||||
} | |||||
return m_root; | |||||
} | |||||
/** | |||||
* Finds a file in this file system. | |||||
*/ | |||||
public FileObject findFile( String nameStr ) throws FileSystemException | |||||
{ | |||||
// Resolve the name, and create the file | |||||
FileName name = m_rootName.resolveName( nameStr ); | |||||
return findFile( name ); | |||||
} | |||||
/** | |||||
* Finds a file in this file system. | |||||
*/ | |||||
public FileObject findFile( FileName name ) throws FileSystemException | |||||
{ | |||||
// TODO - assert that name is from this file system | |||||
FileObject file = (FileObject)m_files.get( name.getPath() ); | |||||
if( file == null ) | |||||
{ | |||||
file = createFile( name ); | |||||
m_files.put( name.getPath(), file ); | |||||
} | |||||
return file; | |||||
} | |||||
} |
@@ -0,0 +1,83 @@ | |||||
/* | |||||
* 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.provider; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A partial file system provider implementation. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class AbstractFileSystemProvider implements FileSystemProvider | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( AbstractFileSystemProvider.class ); | |||||
protected FileSystemProviderContext m_context; | |||||
/** | |||||
* Sets the context for this file system provider. This method is called | |||||
* before any of the other provider methods. | |||||
*/ | |||||
public void setContext( FileSystemProviderContext context ) | |||||
{ | |||||
m_context = context; | |||||
} | |||||
/** | |||||
* Locates a file object, by absolute URI. | |||||
* | |||||
* @param uri | |||||
* The absolute URI of the file to find. | |||||
*/ | |||||
public FileObject findFile( String uri ) throws FileSystemException | |||||
{ | |||||
// Parse the URI | |||||
ParsedUri parsedURI = null; | |||||
try | |||||
{ | |||||
parsedURI = parseURI( uri ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
final String message = REZ.getString( "invalid-absolute-uri.error", uri ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
// Check in the cache for the file system | |||||
FileSystem fs = m_context.getFileSystem( parsedURI.getRootURI() ); | |||||
if( fs == null ) | |||||
{ | |||||
// Need to create the file system | |||||
fs = createFileSystem( parsedURI ); | |||||
m_context.putFileSystem( parsedURI.getRootURI(), fs ); | |||||
} | |||||
// Locate the file | |||||
return fs.findFile( parsedURI.getPath() ); | |||||
} | |||||
/** | |||||
* 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. | |||||
* | |||||
* <p>The provider can annotate this object with any additional | |||||
* information it requires to create a file system from the URI. | |||||
*/ | |||||
protected abstract ParsedUri parseURI( String uri ) throws FileSystemException; | |||||
/** | |||||
* Creates the filesystem. | |||||
*/ | |||||
protected abstract org.apache.aut.vfs.provider.FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException; | |||||
} |
@@ -0,0 +1,381 @@ | |||||
/* | |||||
* 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.provider; | |||||
import java.io.BufferedInputStream; | |||||
import java.io.BufferedOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.io.OutputStream; | |||||
import java.io.OutputStreamWriter; | |||||
import java.io.Reader; | |||||
import java.io.Writer; | |||||
import java.sql.Date; | |||||
import org.apache.aut.vfs.FileContent; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* The content of a file. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class DefaultFileContent implements FileContent | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( DefaultFileContent.class ); | |||||
private AbstractFileObject m_file; | |||||
private int _state = STATE_NONE; | |||||
private FileContentInputStream m_instr; | |||||
private FileContentOutputStream m_outstr; | |||||
private static final int STATE_NONE = 0; | |||||
private static final int STATE_READING = 1; | |||||
private static final int STATE_WRITING = 2; | |||||
public DefaultFileContent( AbstractFileObject file ) | |||||
{ | |||||
m_file = file; | |||||
} | |||||
/** | |||||
* Returns the file which this is the content of. | |||||
*/ | |||||
public FileObject getFile() | |||||
{ | |||||
return m_file; | |||||
} | |||||
/** | |||||
* Returns the size of the content (in bytes). | |||||
*/ | |||||
public long getSize() throws FileSystemException | |||||
{ | |||||
// Do some checking | |||||
if( !m_file.exists() ) | |||||
{ | |||||
final String message = REZ.getString( "get-size-no-exist.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
if( _state == STATE_WRITING ) | |||||
{ | |||||
final String message = REZ.getString( "get-size-write.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
try | |||||
{ | |||||
// Get the size | |||||
return m_file.doGetContentSize(); | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "get-size.error", m_file ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
} | |||||
/** | |||||
* Returns the last-modified timestamp. | |||||
*/ | |||||
public long getLastModifiedTime() throws FileSystemException | |||||
{ | |||||
// TODO - implement this | |||||
throw new FileSystemException( "Not implemented." ); | |||||
} | |||||
/** | |||||
* Sets the last-modified timestamp. | |||||
*/ | |||||
public void setLastModifiedTime( long modTime ) throws FileSystemException | |||||
{ | |||||
// TODO - implement this | |||||
throw new FileSystemException( "Not implemented." ); | |||||
} | |||||
/** | |||||
* Gets the value of an attribute. | |||||
*/ | |||||
public Object getAttribute( String attrName ) throws FileSystemException | |||||
{ | |||||
// TODO - implement this | |||||
throw new FileSystemException( "Not implemented." ); | |||||
} | |||||
/** | |||||
* Sets the value of an attribute. | |||||
*/ | |||||
public void setAttribute( String attrName, Object value ) throws FileSystemException | |||||
{ | |||||
// TODO - implement this | |||||
throw new FileSystemException( "Not implemented." ); | |||||
} | |||||
/** | |||||
* Returns an input stream for reading the content. | |||||
*/ | |||||
public InputStream getInputStream() throws FileSystemException | |||||
{ | |||||
if( !m_file.exists() ) | |||||
{ | |||||
final String message = REZ.getString( "read-no-exist.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
if( _state != STATE_NONE ) | |||||
{ | |||||
final String message = REZ.getString( "read-in-use.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Get the raw input stream | |||||
InputStream instr = null; | |||||
try | |||||
{ | |||||
instr = m_file.doGetInputStream(); | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
final String message = REZ.getString( "read.error", m_file ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
// TODO - reuse | |||||
m_instr = new FileContentInputStream( instr ); | |||||
_state = STATE_READING; | |||||
return m_instr; | |||||
} | |||||
/** | |||||
* Returns an output stream for writing the content. | |||||
*/ | |||||
public OutputStream getOutputStream() throws FileSystemException | |||||
{ | |||||
if( _state != STATE_NONE ) | |||||
{ | |||||
final String message = REZ.getString( "write-in-use.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Get the raw output stream | |||||
OutputStream outstr = m_file.getOutputStream(); | |||||
// Create wrapper | |||||
// TODO - reuse | |||||
m_outstr = new FileContentOutputStream( outstr ); | |||||
_state = STATE_WRITING; | |||||
return m_outstr; | |||||
} | |||||
/** | |||||
* Closes all resources used by the content, including all streams, readers | |||||
* and writers. | |||||
*/ | |||||
public void close() throws FileSystemException | |||||
{ | |||||
try | |||||
{ | |||||
// Close the input stream | |||||
if( m_instr != null ) | |||||
{ | |||||
try | |||||
{ | |||||
m_instr.close(); | |||||
} | |||||
catch( IOException ioe ) | |||||
{ | |||||
final String message = REZ.getString( "close-instr.error" ); | |||||
throw new FileSystemException( message, ioe ); | |||||
} | |||||
} | |||||
// Close the output stream | |||||
if( m_outstr != null ) | |||||
{ | |||||
try | |||||
{ | |||||
m_outstr.close(); | |||||
} | |||||
catch( IOException ioe ) | |||||
{ | |||||
final String message = REZ.getString( "close-outstr.error" ); | |||||
throw new FileSystemException( message, ioe ); | |||||
} | |||||
} | |||||
} | |||||
finally | |||||
{ | |||||
_state = STATE_NONE; | |||||
} | |||||
} | |||||
/** | |||||
* Handles the end of input stream. | |||||
*/ | |||||
private void endInput() throws Exception | |||||
{ | |||||
m_instr = null; | |||||
_state = STATE_NONE; | |||||
m_file.doEndInput(); | |||||
} | |||||
/** | |||||
* Handles the end of output stream. | |||||
*/ | |||||
private void endOutput() throws Exception | |||||
{ | |||||
m_outstr = null; | |||||
_state = STATE_NONE; | |||||
m_file.endOutput(); | |||||
} | |||||
/** | |||||
* An input stream for reading content. Provides buffering, and | |||||
* end-of-stream monitoring. | |||||
*/ | |||||
private final class FileContentInputStream extends BufferedInputStream | |||||
{ | |||||
boolean _finished; | |||||
FileContentInputStream( InputStream instr ) | |||||
{ | |||||
super( instr ); | |||||
} | |||||
/** | |||||
* Reads a character. | |||||
*/ | |||||
public int read() throws IOException | |||||
{ | |||||
if( _finished ) | |||||
{ | |||||
return -1; | |||||
} | |||||
int ch = super.read(); | |||||
if( ch != -1 ) | |||||
{ | |||||
return ch; | |||||
} | |||||
// End-of-stream | |||||
close(); | |||||
return -1; | |||||
} | |||||
/** | |||||
* Reads bytes from this input stream.error occurs. | |||||
*/ | |||||
public int read( byte b[], int off, int len ) | |||||
throws IOException | |||||
{ | |||||
if( _finished ) | |||||
{ | |||||
return -1; | |||||
} | |||||
int nread = super.read( b, off, len ); | |||||
if( nread != -1 ) | |||||
{ | |||||
return nread; | |||||
} | |||||
// End-of-stream | |||||
close(); | |||||
return -1; | |||||
} | |||||
/** | |||||
* Closes this input stream. | |||||
*/ | |||||
public void close() throws IOException | |||||
{ | |||||
if( _finished ) | |||||
{ | |||||
return; | |||||
} | |||||
// Close the stream | |||||
IOException exc = null; | |||||
try | |||||
{ | |||||
super.close(); | |||||
} | |||||
catch( IOException e ) | |||||
{ | |||||
exc = e; | |||||
} | |||||
// Notify the file object | |||||
try | |||||
{ | |||||
endInput(); | |||||
} | |||||
catch( Exception e ) | |||||
{ | |||||
exc = new IOException( e.getMessage() ); | |||||
} | |||||
_finished = true; | |||||
if( exc != null ) | |||||
{ | |||||
throw exc; | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* An output stream for writing content. | |||||
*/ | |||||
private final class FileContentOutputStream extends BufferedOutputStream | |||||
{ | |||||
FileContentOutputStream( OutputStream outstr ) | |||||
{ | |||||
super( outstr ); | |||||
} | |||||
/** | |||||
* Closes this output stream. | |||||
*/ | |||||
public void close() throws IOException | |||||
{ | |||||
IOException exc = null; | |||||
// Close the output stream | |||||
try | |||||
{ | |||||
super.close(); | |||||
} | |||||
catch( IOException e ) | |||||
{ | |||||
exc = e; | |||||
} | |||||
// Notify of end of output | |||||
try | |||||
{ | |||||
endOutput(); | |||||
} | |||||
catch( Exception e ) | |||||
{ | |||||
exc = new IOException( e.getMessage() ); | |||||
} | |||||
if( exc != null ) | |||||
{ | |||||
throw exc; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,123 @@ | |||||
/* | |||||
* 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.provider; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.NameScope; | |||||
/** | |||||
* A default file name implementation. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class DefaultFileName implements FileName | |||||
{ | |||||
private UriParser m_parser; | |||||
private String m_rootPrefix; | |||||
private String m_absPath; | |||||
// Cached stuff | |||||
private String m_uri; | |||||
private String m_baseName; | |||||
public DefaultFileName( UriParser parser, String rootPrefix, String absPath ) | |||||
{ | |||||
m_parser = parser; | |||||
m_rootPrefix = rootPrefix; | |||||
m_absPath = absPath; | |||||
} | |||||
// TODO - make these usable as hash keys | |||||
/** | |||||
* Returns the URI of the file. | |||||
*/ | |||||
public String toString() | |||||
{ | |||||
return getURI(); | |||||
} | |||||
/** | |||||
* Returns the base name of the file. | |||||
*/ | |||||
public String getBaseName() | |||||
{ | |||||
if( m_baseName == null ) | |||||
{ | |||||
m_baseName = m_parser.getBaseName( m_absPath ); | |||||
} | |||||
return m_baseName; | |||||
} | |||||
/** | |||||
* Returns the absolute path of the file, relative to the root of the | |||||
* file system that the file belongs to. | |||||
*/ | |||||
public String getPath() | |||||
{ | |||||
return m_absPath; | |||||
} | |||||
/** | |||||
* Returns the name of a child of the file. | |||||
*/ | |||||
public FileName resolveName( String name, NameScope scope ) throws FileSystemException | |||||
{ | |||||
if( scope == NameScope.CHILD ) | |||||
{ | |||||
String childPath = m_parser.getChildPath( m_absPath, name ); | |||||
return new DefaultFileName( m_parser, m_rootPrefix, childPath ); | |||||
} | |||||
else if( scope == NameScope.FILE_SYSTEM ) | |||||
{ | |||||
String absPath = m_parser.resolvePath( m_absPath, name ); | |||||
return new DefaultFileName( m_parser, m_rootPrefix, absPath ); | |||||
} | |||||
else | |||||
{ | |||||
throw new IllegalArgumentException(); | |||||
} | |||||
} | |||||
/** | |||||
* Returns the name of the parent of the file. | |||||
*/ | |||||
public FileName getParent() | |||||
{ | |||||
String parentPath = m_parser.getParentPath( m_absPath ); | |||||
if( parentPath == null ) | |||||
{ | |||||
return null; | |||||
} | |||||
return new DefaultFileName( m_parser, m_rootPrefix, parentPath ); | |||||
} | |||||
/** | |||||
* Resolves a name, relative to the file. If the supplied name is an | |||||
* absolute path, then it is resolved relative to the root of the | |||||
* file system that the file belongs to. If a relative name is supplied, | |||||
* then it is resolved relative to this file name. | |||||
*/ | |||||
public FileName resolveName( String path ) throws FileSystemException | |||||
{ | |||||
return resolveName( path, NameScope.FILE_SYSTEM ); | |||||
} | |||||
/** | |||||
* Returns the absolute URI of the file. | |||||
*/ | |||||
public String getURI() | |||||
{ | |||||
if( m_uri == null ) | |||||
{ | |||||
m_uri = m_parser.getUri( m_rootPrefix, m_absPath ); | |||||
} | |||||
return m_uri; | |||||
} | |||||
} |
@@ -0,0 +1,205 @@ | |||||
/* | |||||
* 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.provider; | |||||
import java.util.HashMap; | |||||
import java.util.Iterator; | |||||
import java.util.Map; | |||||
import java.io.File; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.FileSystemManager; | |||||
import org.apache.aut.vfs.provider.local.LocalFileSystemProvider; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A default file system manager implementation. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class DefaultFileSystemManager implements FileSystemManager | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( DefaultFileSystemManager.class ); | |||||
/** The default provider. */ | |||||
private LocalFileSystemProvider m_localFileProvider; | |||||
/** Mapping from URI scheme to FileSystemProvider. */ | |||||
private Map m_providers = new HashMap(); | |||||
/** The provider context. */ | |||||
private ProviderContextImpl m_context = new ProviderContextImpl(); | |||||
/** The base file to use for relative URI. */ | |||||
private FileObject m_baseFile; | |||||
/** | |||||
* The cached file systems. This is a mapping from root URI to | |||||
* FileSystem object. | |||||
*/ | |||||
private Map m_fileSystems = new HashMap(); | |||||
public DefaultFileSystemManager() throws Exception | |||||
{ | |||||
// Create the local provider | |||||
m_localFileProvider = new LocalFileSystemProvider(); | |||||
m_providers.put( "file", m_localFileProvider ); | |||||
// 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 ); | |||||
} | |||||
} | |||||
/** | |||||
* Creates a provider instance, returns null if the provider class is | |||||
* not found. | |||||
*/ | |||||
private FileSystemProvider createProvider( final String className ) throws Exception | |||||
{ | |||||
try | |||||
{ | |||||
// TODO - wrap exceptions | |||||
return (FileSystemProvider)Class.forName( className ).newInstance(); | |||||
} | |||||
catch( ClassNotFoundException e ) | |||||
{ | |||||
// This is fine, for now | |||||
return null; | |||||
} | |||||
} | |||||
/** | |||||
* Closes all file systems created by this file system manager. | |||||
*/ | |||||
public void close() | |||||
{ | |||||
// TODO - implement this | |||||
} | |||||
/** | |||||
* Sets the base file to use when resolving relative URI. | |||||
*/ | |||||
public void setBaseFile( FileObject baseFile ) throws FileSystemException | |||||
{ | |||||
m_baseFile = baseFile; | |||||
} | |||||
/** | |||||
* Sets the base file to use when resolving relative URI. | |||||
*/ | |||||
public void setBaseFile( File baseFile ) throws FileSystemException | |||||
{ | |||||
m_baseFile = m_localFileProvider.findFileByLocalName( baseFile.getAbsolutePath() ); | |||||
} | |||||
/** | |||||
* Returns the base file used to resolve relative URI. | |||||
*/ | |||||
public FileObject getBaseFile() | |||||
{ | |||||
return m_baseFile; | |||||
} | |||||
/** | |||||
* Locates a file by URI. | |||||
*/ | |||||
public FileObject resolveFile( String URI ) throws FileSystemException | |||||
{ | |||||
return resolveFile( m_baseFile, URI ); | |||||
} | |||||
/** | |||||
* Resolves a URI, relative to a base file. | |||||
*/ | |||||
public FileObject resolveFile( FileObject baseFile, String uri ) throws FileSystemException | |||||
{ | |||||
// Extract the scheme | |||||
String scheme = UriParser.extractScheme( uri ); | |||||
if( scheme != null ) | |||||
{ | |||||
// An absolute URI - locate the provider | |||||
FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme ); | |||||
if( provider != null ) | |||||
{ | |||||
return provider.findFile( uri ); | |||||
} | |||||
} | |||||
// Handle absolute file names | |||||
if( m_localFileProvider.isAbsoluteLocalName( uri ) ) | |||||
{ | |||||
return m_localFileProvider.findFileByLocalName( uri ); | |||||
} | |||||
// Assume a bad scheme | |||||
if( scheme != null ) | |||||
{ | |||||
final String message = REZ.getString( "unknown-scheme.error", scheme, uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Use the supplied base file | |||||
if( baseFile == null ) | |||||
{ | |||||
final String message = REZ.getString( "find-rel-file.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
return baseFile.resolveFile( uri ); | |||||
} | |||||
/** | |||||
* A provider context implementation. | |||||
*/ | |||||
private final class ProviderContextImpl implements FileSystemProviderContext | |||||
{ | |||||
/** | |||||
* Locates a cached file system by root URI. | |||||
*/ | |||||
public FileSystem getFileSystem( String rootURI ) | |||||
{ | |||||
// TODO - need to have a per-fs uri comparator | |||||
return (org.apache.aut.vfs.provider.FileSystem)m_fileSystems.get( rootURI ); | |||||
} | |||||
/** | |||||
* Registers a file system for caching. | |||||
*/ | |||||
public void putFileSystem( String rootURI, org.apache.aut.vfs.provider.FileSystem fs ) throws FileSystemException | |||||
{ | |||||
// TODO - should really check that there's not one already cached | |||||
m_fileSystems.put( rootURI, fs ); | |||||
} | |||||
} | |||||
} |
@@ -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.provider; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
/** | |||||
* A file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public interface FileSystem | |||||
{ | |||||
/** | |||||
* Returns the root of this file system. | |||||
*/ | |||||
FileObject getRoot() throws FileSystemException; | |||||
/** | |||||
* Finds a file in this file system. | |||||
* | |||||
* @param name | |||||
* The name of the file. | |||||
*/ | |||||
FileObject findFile( FileName name ) throws FileSystemException; | |||||
/** | |||||
* Finds a file in this file system. | |||||
* | |||||
* @param name | |||||
* The name of the file. This must be an absolute path. | |||||
*/ | |||||
FileObject findFile( String name ) throws FileSystemException; | |||||
} |
@@ -0,0 +1,31 @@ | |||||
/* | |||||
* 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.provider; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
/** | |||||
* A file system provider, or factory. | |||||
*/ | |||||
public interface FileSystemProvider | |||||
{ | |||||
/** | |||||
* Sets the context for this file system provider. This method is called | |||||
* before any of the other provider methods. | |||||
*/ | |||||
void setContext( FileSystemProviderContext context ); | |||||
/** | |||||
* Locates a file object, by absolute URI. | |||||
* | |||||
* @param uri | |||||
* The absolute URI of the file to find. | |||||
*/ | |||||
FileObject findFile( String uri ) throws FileSystemException; | |||||
} |
@@ -0,0 +1,29 @@ | |||||
/* | |||||
* 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.provider; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
/** | |||||
* Used for a file system provider to access the services it needs, such | |||||
* as the file system cache or other file system providers. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public interface FileSystemProviderContext | |||||
{ | |||||
/** | |||||
* Locates a cached file system by root URI. | |||||
*/ | |||||
FileSystem getFileSystem( String rootURI ); | |||||
/** | |||||
* Registers a file system for caching. | |||||
*/ | |||||
void putFileSystem( String rootURI, FileSystem fs ) throws FileSystemException; | |||||
} |
@@ -0,0 +1,95 @@ | |||||
/* | |||||
* 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.provider; | |||||
/** | |||||
* A data container for information parsed from an absolute URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ParsedUri | |||||
{ | |||||
private String m_scheme; | |||||
private String m_rootURI; | |||||
private String m_path; | |||||
private String m_userInfo; | |||||
private String m_hostName; | |||||
private String m_port; | |||||
/** Returns the scheme. */ | |||||
public String getScheme() | |||||
{ | |||||
return m_scheme; | |||||
} | |||||
/** Sets the scheme. */ | |||||
public void setScheme( String scheme ) | |||||
{ | |||||
m_scheme = scheme; | |||||
} | |||||
/** Returns the root URI, used to identify the file system. */ | |||||
public String getRootURI() | |||||
{ | |||||
return m_rootURI; | |||||
} | |||||
/** Sets the root URI. */ | |||||
public void setRootURI( String rootPrefix ) | |||||
{ | |||||
m_rootURI = rootPrefix; | |||||
} | |||||
/** Returns the user info part of the URI. */ | |||||
public String getUserInfo() | |||||
{ | |||||
return m_userInfo; | |||||
} | |||||
/** Sets the user info part of the URI. */ | |||||
public void setUserInfo( String userInfo ) | |||||
{ | |||||
m_userInfo = userInfo; | |||||
} | |||||
/** Returns the host name part of the URI. */ | |||||
public String getHostName() | |||||
{ | |||||
return m_hostName; | |||||
} | |||||
/** Sets the host name part of the URI. */ | |||||
public void setHostName( String hostName ) | |||||
{ | |||||
m_hostName = hostName; | |||||
} | |||||
/** Returns the port part of the URI. */ | |||||
public String getPort() | |||||
{ | |||||
return m_port; | |||||
} | |||||
/** Sets the port part of the URI. */ | |||||
public void setPort( String port ) | |||||
{ | |||||
m_port = port; | |||||
} | |||||
/** Returns the path part of the URI. */ | |||||
public String getPath() | |||||
{ | |||||
return m_path; | |||||
} | |||||
/** Sets the path part of the URI. */ | |||||
public void setPath( String absolutePath ) | |||||
{ | |||||
m_path = absolutePath; | |||||
} | |||||
} |
@@ -0,0 +1,43 @@ | |||||
# AbstractFileObject | |||||
delete-not-supported.error=This file type does not support delete. | |||||
create-folder-not-supported.error=This file type does not support folder creation. | |||||
write-not-supported.error=This file type cannot be written to. | |||||
get-type-no-exist.error=Could not determine the type of file "{0}" because it does not exist. | |||||
get-type.error=Could not determine the type of file "{0}". | |||||
list-children-no-exist.error=Could not list the contents of folder "{0}" because it does not exist. | |||||
list-children-not-folder.error=Could not list the contents of "{0}" because it is not a folder. | |||||
list-children.error=Could not list the contents of folder "{0}". | |||||
delete-read-only.error=Could not delete "{0}" because it is read-only. | |||||
delete.error=Could not delete "{0}". | |||||
create-mismatched-type.error=Could not create {0} "{1}" because it already exists and is a {2}. | |||||
create-read-only.error=Could not create {0} "{1}" because the file system is read-only. | |||||
create.error=Could not create {0} "{1}". | |||||
get-folder-content.error=Could not get the content of "{0}" because it is a folder. | |||||
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-in-use.error=Could not write to "{0}" because it is already in use. | |||||
write.error=Could not write to "{0}". | |||||
# DefaultFileContent | |||||
get-size-no-exist.error=Could not determine the size of file "{0}" because it does not exist. | |||||
get-size-write.error=Could not determine the size of file "{0}" because it is being written to. | |||||
get-size.error=Could not determine the size of file "{0}". | |||||
read-no-exist.error=Could not read file "{0}" because it does not exist. | |||||
read-in-use.error=Could not read file "{0}" because it is already being used. | |||||
read.error=Could not read file "{0}". | |||||
close-instr.error=Could not close file input stream. | |||||
close-outstr.error=Could not close file output stream. | |||||
# AbstractFileSystemProvider | |||||
invalid-absolute-uri.error=Invalid absolute URI "{0}". | |||||
# DefaultFileSystemManager | |||||
unknown-scheme.error=Unknown scheme "{0}" in URI "{0}". | |||||
find-rel-file.error=Could not find file with URI "{0}" because it is a relative path, and no base URI was provided. | |||||
# UriParser | |||||
missing-double-slashes.error=Expecting // to follow the scheme in URI "{0}". | |||||
missing-hostname.error=Hostname missing from URI "{0}". | |||||
missing-port.error=Port number is missing from URI "{0}". | |||||
missing-hostname-path-sep.error=Expecting / to follow the hostname in URI "{0}". | |||||
invalid-childname.error=Invalid file base-name "{0}". |
@@ -0,0 +1,630 @@ | |||||
/* | |||||
* 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.provider; | |||||
import java.util.HashSet; | |||||
import java.util.Iterator; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A name parser which parses absolute URIs. See RFC 2396 for details. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class UriParser | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( UriParser.class ); | |||||
/** The normalised separator to use. */ | |||||
private char m_separatorChar; | |||||
private String m_separator; | |||||
/** | |||||
* The set of valid separators. These are all converted to the normalised one. | |||||
* Does <i>not</i> contain the normalised separator | |||||
*/ | |||||
private char[] m_separators; | |||||
/** | |||||
* Creates a parser, using '/' and '\' as the path separators. | |||||
*/ | |||||
public UriParser() | |||||
{ | |||||
this( null ); | |||||
} | |||||
/** | |||||
* Creates a parser, using '/' and '\' as the path separators, along with | |||||
* a provider-specific set of separators. | |||||
* | |||||
* @param separators | |||||
* Additional legal separator characters. Any occurrences of | |||||
* these in paths are replaced with the separator char. | |||||
*/ | |||||
protected UriParser( char[] separators ) | |||||
{ | |||||
m_separatorChar = '/'; | |||||
// Remove the separator char from the separators array | |||||
HashSet set = new HashSet(); | |||||
set.add( new Character( '\\' ) ); | |||||
if( separators != null ) | |||||
{ | |||||
for( int i = 0; i < separators.length; i++ ) | |||||
{ | |||||
char separator = separators[ i ]; | |||||
if( separator == m_separatorChar ) | |||||
{ | |||||
continue; | |||||
} | |||||
set.add( new Character( separator ) ); | |||||
} | |||||
} | |||||
m_separators = new char[ set.size() ]; | |||||
Iterator iter = set.iterator(); | |||||
for( int i = 0; i < m_separators.length; i++ ) | |||||
{ | |||||
Character ch = (Character)iter.next(); | |||||
m_separators[ i ] = ch.charValue(); | |||||
} | |||||
m_separator = String.valueOf( m_separatorChar ); | |||||
} | |||||
/** | |||||
* Parses an absolute URI, splitting it into its components. This | |||||
* implementation assumes a "generic URI", as defined by RFC 2396. See | |||||
* {@link #parseGenericUri} for more info. | |||||
* | |||||
* <p>Sub-classes should override this method. | |||||
*/ | |||||
public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
{ | |||||
ParsedUri retval = new ParsedUri(); | |||||
parseGenericUri( uriStr, retval ); | |||||
return retval; | |||||
} | |||||
/** | |||||
* Parses a generic URI, as defined by RFC 2396. Briefly, a generic URI | |||||
* looks like: | |||||
* | |||||
* <pre> | |||||
* <scheme> '://' [ <userinfo> '@' ] <hostname> [ ':' <port> ] '/' <path> | |||||
* </pre> | |||||
* | |||||
* <p>This method differs from the RFC, in that either / or \ is allowed | |||||
* as a path separator. | |||||
* | |||||
* @param uriStr | |||||
* The URI to parse. | |||||
* @param uri | |||||
* Used to return the parsed components of the URI. | |||||
*/ | |||||
protected void parseGenericUri( String uriStr, ParsedUri uri ) throws FileSystemException | |||||
{ | |||||
StringBuffer name = new StringBuffer(); | |||||
// Extract the scheme and authority parts | |||||
extractToPath( uriStr, name, uri ); | |||||
// Normalise the file name | |||||
normalisePath( name ); | |||||
uri.setPath( name.toString() ); | |||||
// Build the root uri | |||||
StringBuffer rootUri = new StringBuffer(); | |||||
rootUri.append( uri.getScheme() ); | |||||
rootUri.append( "://" ); | |||||
rootUri.append( uri.getHostName() ); | |||||
uri.setRootURI( rootUri.toString() ); | |||||
} | |||||
/** | |||||
* Extracts the scheme, userinfo, hostname and port components of an | |||||
* absolute "generic URI". | |||||
* | |||||
* @param uri | |||||
* The absolute URI to parse. | |||||
* | |||||
* @param name | |||||
* Used to return the remainder of the URI. | |||||
* | |||||
* @parsedUri | |||||
* Used to return the extracted components. | |||||
*/ | |||||
protected void extractToPath( String uri, StringBuffer name, ParsedUri parsedUri ) | |||||
throws FileSystemException | |||||
{ | |||||
// Extract the scheme | |||||
String scheme = extractScheme( uri, name ); | |||||
parsedUri.setScheme( scheme ); | |||||
// Expecting "//" | |||||
if( name.length() < 2 || name.charAt( 0 ) != '/' || name.charAt( 1 ) != '/' ) | |||||
{ | |||||
final String message = REZ.getString( "missing-double-slashes.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
name.delete( 0, 2 ); | |||||
// Extract userinfo | |||||
String userInfo = extractUserInfo( name ); | |||||
parsedUri.setUserInfo( userInfo ); | |||||
// Extract hostname | |||||
String hostName = extractHostName( name ); | |||||
if( hostName == null ) | |||||
{ | |||||
final String message = REZ.getString( "missing-hostname.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
parsedUri.setHostName( hostName ); | |||||
// Extract port | |||||
String port = extractPort( name ); | |||||
if( port != null && port.length() == 0 ) | |||||
{ | |||||
final String message = REZ.getString( "missing-port.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
parsedUri.setPort( port ); | |||||
// Expecting '/' or empty name | |||||
if( name.length() > 0 && name.charAt( 0 ) != '/' ) | |||||
{ | |||||
final String message = REZ.getString( "missing-hostname-path-sep.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
/** | |||||
* Extracts the user info from a URI. The <scheme>:// part has been removed | |||||
* already. | |||||
*/ | |||||
protected String extractUserInfo( StringBuffer name ) | |||||
{ | |||||
int maxlen = name.length(); | |||||
for( int pos = 0; pos < maxlen; pos++ ) | |||||
{ | |||||
char ch = name.charAt( pos ); | |||||
if( ch == '@' ) | |||||
{ | |||||
// Found the end of the user info | |||||
String userInfo = name.substring( 0, pos ); | |||||
name.delete( 0, pos + 1 ); | |||||
return userInfo; | |||||
} | |||||
if( ch == '/' || ch == '?' ) | |||||
{ | |||||
// Not allowed in user info | |||||
break; | |||||
} | |||||
} | |||||
// Not found | |||||
return null; | |||||
} | |||||
/** | |||||
* Extracts the hostname from a URI. The <scheme>://<userinfo>@ part has | |||||
* been removed. | |||||
*/ | |||||
protected String extractHostName( StringBuffer name ) | |||||
{ | |||||
int maxlen = name.length(); | |||||
int pos = 0; | |||||
for( ; pos < maxlen; pos++ ) | |||||
{ | |||||
char ch = name.charAt( pos ); | |||||
if( ch == '/' || ch == ';' || ch == '?' || ch == ':' | |||||
|| ch == '@' || ch == '&' || ch == '=' || ch == '+' | |||||
|| ch == '$' || ch == ',' ) | |||||
{ | |||||
break; | |||||
} | |||||
} | |||||
if( pos == 0 ) | |||||
{ | |||||
return null; | |||||
} | |||||
String hostname = name.substring( 0, pos ); | |||||
name.delete( 0, pos ); | |||||
return hostname; | |||||
} | |||||
/** | |||||
* Extracts the port from a URI. The <scheme>://<userinfo>@<hostname> | |||||
* part has been removed. | |||||
*/ | |||||
protected String extractPort( StringBuffer name ) | |||||
{ | |||||
if( name.length() < 1 || name.charAt( 0 ) != ':' ) | |||||
{ | |||||
return null; | |||||
} | |||||
int maxlen = name.length(); | |||||
int pos = 1; | |||||
for( ; pos < maxlen; pos++ ) | |||||
{ | |||||
char ch = name.charAt( pos ); | |||||
if( ch < '0' || ch > '9' ) | |||||
{ | |||||
break; | |||||
} | |||||
} | |||||
String port = name.substring( 1, pos ); | |||||
name.delete( 0, pos ); | |||||
return port; | |||||
} | |||||
/** | |||||
* Extracts the first element of a path. | |||||
*/ | |||||
protected String extractFirstElement( StringBuffer name ) | |||||
{ | |||||
int len = name.length(); | |||||
if( len < 1 ) | |||||
{ | |||||
return null; | |||||
} | |||||
int startPos = 0; | |||||
if( name.charAt( 0 ) == m_separatorChar ) | |||||
{ | |||||
startPos = 1; | |||||
} | |||||
for( int pos = startPos; pos < len; pos++ ) | |||||
{ | |||||
if( name.charAt( pos ) == m_separatorChar ) | |||||
{ | |||||
// Found a separator | |||||
String elem = name.substring( startPos, pos ); | |||||
name.delete( startPos, pos + 1 ); | |||||
return elem; | |||||
} | |||||
} | |||||
// No separator | |||||
String elem = name.substring( startPos ); | |||||
name.setLength( 0 ); | |||||
return elem; | |||||
} | |||||
/** | |||||
* Builds a URI from a root URI and path. | |||||
* | |||||
* @param rootUri | |||||
* The root URI. | |||||
* | |||||
* @param path | |||||
* A <i>normalised</i> path. | |||||
*/ | |||||
public String getUri( String rootUri, String path ) | |||||
{ | |||||
StringBuffer uri = new StringBuffer( rootUri ); | |||||
int len = uri.length(); | |||||
if( uri.charAt( len - 1 ) == m_separatorChar ) | |||||
{ | |||||
uri.delete( len - 1, len ); | |||||
} | |||||
if( !path.startsWith( m_separator ) ) | |||||
{ | |||||
uri.append( m_separatorChar ); | |||||
} | |||||
uri.append( path ); | |||||
return uri.toString(); | |||||
} | |||||
/** | |||||
* Returns the base name of a path. | |||||
* | |||||
* @param path | |||||
* A <i>normalised</i> path. | |||||
*/ | |||||
public String getBaseName( String path ) | |||||
{ | |||||
int idx = path.lastIndexOf( m_separatorChar ); | |||||
if( idx == -1 ) | |||||
{ | |||||
return path; | |||||
} | |||||
return path.substring( idx + 1 ); | |||||
} | |||||
/** | |||||
* Resolves a path, relative to a base path. If the supplied path | |||||
* is an absolute path, it is normalised and returned. If the supplied | |||||
* path is a relative path, it is resolved relative to the base path. | |||||
* | |||||
* @param basePath | |||||
* A <i>normalised</i> path. | |||||
* | |||||
* @param path | |||||
* The path to resolve. Does not need to be normalised, but | |||||
* does need to be a path (i.e. not an absolute URI). | |||||
* | |||||
*/ | |||||
public String resolvePath( String basePath, String path ) throws FileSystemException | |||||
{ | |||||
StringBuffer buffer = new StringBuffer( path ); | |||||
// Adjust separators | |||||
fixSeparators( buffer ); | |||||
// Determine whether to prepend the base path | |||||
if( path.length() == 0 || path.charAt( 0 ) != m_separatorChar ) | |||||
{ | |||||
// Supplied path is not absolute | |||||
buffer.insert( 0, m_separatorChar ); | |||||
buffer.insert( 0, basePath ); | |||||
} | |||||
// Normalise the path | |||||
normalisePath( buffer ); | |||||
return buffer.toString(); | |||||
} | |||||
/** | |||||
* Returns a child path. | |||||
* | |||||
* @param parent | |||||
* A <i>normalised</i> path. | |||||
* | |||||
* @param name | |||||
* The child name. Must be a valid element name (i.e. no separators, etc). | |||||
*/ | |||||
public String getChildPath( String parent, String name ) throws FileSystemException | |||||
{ | |||||
// Validate the child name | |||||
if( name.length() == 0 | |||||
|| name.equals( "." ) | |||||
|| name.equals( ".." ) ) | |||||
{ | |||||
final String message = REZ.getString( "invalid-childname.error", name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Check for separators | |||||
if( name.indexOf( m_separatorChar ) != -1 ) | |||||
{ | |||||
final String message = REZ.getString( "invalid-childname.error", name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
for( int i = 0; i < m_separators.length; i++ ) | |||||
{ | |||||
char separator = m_separators[ i ]; | |||||
if( name.indexOf( separator ) != -1 ) | |||||
{ | |||||
final String message = REZ.getString( "invalid-childname.error", name ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
if( parent.endsWith( m_separator ) ) | |||||
{ | |||||
// Either root, or the parent name already ends with the separator | |||||
return parent + name; | |||||
} | |||||
return parent + m_separatorChar + name; | |||||
} | |||||
/** | |||||
* Returns a parent path, or null if the path has no parent. | |||||
* | |||||
* @param path | |||||
* A <i>normalised</i> path. | |||||
*/ | |||||
public String getParentPath( String path ) | |||||
{ | |||||
int idx = path.lastIndexOf( m_separatorChar ); | |||||
if( idx == -1 || idx == path.length() - 1 ) | |||||
{ | |||||
// No parent | |||||
return null; | |||||
} | |||||
if( idx == 0 ) | |||||
{ | |||||
// Root is the parent | |||||
return m_separator; | |||||
} | |||||
return path.substring( 0, idx ); | |||||
} | |||||
/** | |||||
* Normalises a path. Does the following: | |||||
* <ul> | |||||
* <li>Normalises separators, where more than one can be used. | |||||
* <li>Removes empty path elements. | |||||
* <li>Handles '.' and '..' elements. | |||||
* <li>Removes trailing separator. | |||||
* </ul> | |||||
*/ | |||||
public void normalisePath( StringBuffer path ) throws FileSystemException | |||||
{ | |||||
if( path.length() == 0 ) | |||||
{ | |||||
return; | |||||
} | |||||
// Adjust separators | |||||
fixSeparators( path ); | |||||
// Determine the start of the first element | |||||
int startFirstElem = 0; | |||||
if( path.charAt( 0 ) == m_separatorChar ) | |||||
{ | |||||
if( path.length() == 1 ) | |||||
{ | |||||
return; | |||||
} | |||||
startFirstElem = 1; | |||||
} | |||||
// Iterate over each element | |||||
int startElem = startFirstElem; | |||||
int maxlen = path.length(); | |||||
while( startElem < maxlen ) | |||||
{ | |||||
// Find the end of the element | |||||
int endElem = startElem; | |||||
for( ; endElem < maxlen && path.charAt( endElem ) != m_separatorChar; endElem++ ) | |||||
{ | |||||
} | |||||
int elemLen = endElem - startElem; | |||||
if( elemLen == 0 ) | |||||
{ | |||||
// An empty element - axe it | |||||
path.delete( endElem, endElem + 1 ); | |||||
maxlen = path.length(); | |||||
continue; | |||||
} | |||||
if( elemLen == 1 && path.charAt( startElem ) == '.' ) | |||||
{ | |||||
// A '.' element - axe it | |||||
path.delete( startElem, endElem + 1 ); | |||||
maxlen = path.length(); | |||||
continue; | |||||
} | |||||
if( elemLen == 2 | |||||
&& path.charAt( startElem ) == '.' | |||||
&& path.charAt( startElem + 1 ) == '.' ) | |||||
{ | |||||
// A '..' element - remove the previous element | |||||
if( startElem > startFirstElem ) | |||||
{ | |||||
int pos = startElem - 2; | |||||
for( ; path.charAt( pos ) != m_separatorChar; pos-- ) | |||||
{ | |||||
} | |||||
startElem = pos + 1; | |||||
} | |||||
path.delete( startElem, endElem + 1 ); | |||||
maxlen = path.length(); | |||||
continue; | |||||
} | |||||
// A regular element | |||||
startElem = endElem + 1; | |||||
} | |||||
// Remove trailing separator | |||||
if( path.charAt( maxlen - 1 ) == m_separatorChar && maxlen > 1 ) | |||||
{ | |||||
path.delete( maxlen - 1, maxlen ); | |||||
} | |||||
} | |||||
/** | |||||
* Adjusts the separators in a name. | |||||
*/ | |||||
protected boolean fixSeparators( StringBuffer name ) | |||||
{ | |||||
if( m_separators.length == 0 ) | |||||
{ | |||||
// Only one valid separator, so don't need to do anything | |||||
return false; | |||||
} | |||||
boolean changed = false; | |||||
int maxlen = name.length(); | |||||
for( int i = 0; i < maxlen; i++ ) | |||||
{ | |||||
char ch = name.charAt( i ); | |||||
for( int j = 0; j < m_separators.length; j++ ) | |||||
{ | |||||
char separator = m_separators[ j ]; | |||||
if( ch == separator ) | |||||
{ | |||||
name.setCharAt( i, m_separatorChar ); | |||||
changed = true; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return changed; | |||||
} | |||||
/** | |||||
* Extracts the scheme from a URI. | |||||
* | |||||
* @param uri | |||||
* The URI. | |||||
* | |||||
* @return | |||||
* The scheme name. Returns null if there is no scheme. | |||||
*/ | |||||
public static String extractScheme( String uri ) | |||||
{ | |||||
return extractScheme( uri, null ); | |||||
} | |||||
/** | |||||
* Extracts the scheme from a URI. | |||||
* | |||||
* @param uri | |||||
* The URI. | |||||
* | |||||
* @param buffer | |||||
* Returns the remainder of the URI. | |||||
* | |||||
* @return | |||||
* The scheme name. Returns null if there is no scheme. | |||||
*/ | |||||
protected static String extractScheme( String uri, StringBuffer buffer ) | |||||
{ | |||||
if( buffer != null ) | |||||
{ | |||||
buffer.setLength( 0 ); | |||||
buffer.append( uri ); | |||||
} | |||||
int maxPos = uri.length(); | |||||
for( int pos = 0; pos < maxPos; pos++ ) | |||||
{ | |||||
char ch = uri.charAt( pos ); | |||||
if( ch == ':' ) | |||||
{ | |||||
// Found the end of the scheme | |||||
String scheme = uri.substring( 0, pos ); | |||||
if( buffer != null ) | |||||
{ | |||||
buffer.delete( 0, pos + 1 ); | |||||
} | |||||
return scheme; | |||||
} | |||||
if( ( ch >= 'a' && ch <= 'z' ) | |||||
|| ( ch >= 'A' && ch <= 'Z' ) ) | |||||
{ | |||||
// A scheme character | |||||
continue; | |||||
} | |||||
if( pos > 0 && | |||||
( ( ch >= '0' && ch <= '9' ) | |||||
|| ch == '+' || ch == '-' || ch == '.' ) ) | |||||
{ | |||||
// A scheme character (these are not allowed as the first | |||||
// character of the scheme, but can be used as subsequent | |||||
// characters. | |||||
continue; | |||||
} | |||||
// Not a scheme character | |||||
break; | |||||
} | |||||
// No scheme in URI | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,51 @@ | |||||
/* | |||||
* 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.provider.ftp; | |||||
import org.apache.aut.vfs.provider.UriParser; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
/** | |||||
* A parser for FTP URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class FtpFileNameParser extends UriParser | |||||
{ | |||||
/** | |||||
* Parses an absolute URI, splitting it into its components. | |||||
*/ | |||||
public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
{ | |||||
ParsedFtpUri uri = new ParsedFtpUri(); | |||||
// FTP URI are generic URI (as per RFC 2396) | |||||
parseGenericUri( uriStr, uri ); | |||||
// Split up the userinfo into a username and password | |||||
String userInfo = uri.getUserInfo(); | |||||
if( userInfo != null ) | |||||
{ | |||||
int idx = userInfo.indexOf(':'); | |||||
if( idx == -1 ) | |||||
{ | |||||
uri.setUserName( userInfo ); | |||||
} | |||||
else | |||||
{ | |||||
String userName = userInfo.substring(0, idx); | |||||
String password = userInfo.substring(idx+1); | |||||
uri.setUserName( userName ); | |||||
uri.setPassword( password ); | |||||
} | |||||
} | |||||
return uri; | |||||
} | |||||
} |
@@ -0,0 +1,225 @@ | |||||
/* | |||||
* 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.provider.ftp; | |||||
import com.oroinc.net.ftp.FTPFile; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.FileType; | |||||
import org.apache.aut.vfs.provider.AbstractFileObject; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* An FTP file. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
class FtpFileObject extends AbstractFileObject | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( FtpFileObject.class ); | |||||
private FtpFileSystem m_ftpFs; | |||||
// Cached info | |||||
private FTPFile m_fileInfo; | |||||
private FTPFile[] m_children; | |||||
private static final FTPFile[] EMPTY_FTP_FILE_ARRAY = {}; | |||||
public FtpFileObject( FileName name, FtpFileSystem fs ) | |||||
{ | |||||
super( name, fs ); | |||||
m_ftpFs = fs; | |||||
} | |||||
/** | |||||
* Called by child file objects, to locate their ftp file info. | |||||
*/ | |||||
private FTPFile getChildFile( String name ) throws Exception | |||||
{ | |||||
if( m_children == null ) | |||||
{ | |||||
// List the children of this file | |||||
m_children = m_ftpFs.getClient().listFiles( getName().getPath() ); | |||||
if( m_children == null ) | |||||
{ | |||||
m_children = EMPTY_FTP_FILE_ARRAY; | |||||
} | |||||
} | |||||
// Look for the requested child | |||||
// TODO - use hash table | |||||
for( int i = 0; i < m_children.length; i++ ) | |||||
{ | |||||
FTPFile child = m_children[ i ]; | |||||
if( child.getName().equals( name ) ) | |||||
{ | |||||
// TODO - should be using something else to compare names | |||||
return child; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* Attaches this file object to its file resource. | |||||
*/ | |||||
protected void doAttach() throws Exception | |||||
{ | |||||
// Get the parent folder to find the info for this file | |||||
FtpFileObject parent = (FtpFileObject)getParent(); | |||||
m_fileInfo = parent.getChildFile( getName().getBaseName() ); | |||||
if( m_fileInfo == null || !m_fileInfo.isDirectory() ) | |||||
{ | |||||
m_children = EMPTY_FTP_FILE_ARRAY; | |||||
} | |||||
} | |||||
/** | |||||
* Detaches this file object from its file resource. | |||||
*/ | |||||
protected void doDetach() | |||||
{ | |||||
m_fileInfo = null; | |||||
m_children = null; | |||||
} | |||||
/** | |||||
* Called when the children of this file change. | |||||
*/ | |||||
protected void onChildrenChanged() | |||||
{ | |||||
m_children = null; | |||||
} | |||||
/** | |||||
* Determines the type of the file, returns null if the file does not | |||||
* exist. | |||||
*/ | |||||
protected FileType doGetType() throws Exception | |||||
{ | |||||
if( m_fileInfo == null ) | |||||
{ | |||||
// Does not exist | |||||
return null; | |||||
} | |||||
if( m_fileInfo.isDirectory() ) | |||||
{ | |||||
return FileType.FOLDER; | |||||
} | |||||
if( m_fileInfo.isFile() ) | |||||
{ | |||||
return FileType.FILE; | |||||
} | |||||
final String message = REZ.getString( "get-type.error", getName() ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
/** | |||||
* Lists the children of the file. | |||||
*/ | |||||
protected String[] doListChildren() throws Exception | |||||
{ | |||||
if( m_children == null ) | |||||
{ | |||||
// List the children of this file | |||||
m_children = m_ftpFs.getClient().listFiles( getName().getPath() ); | |||||
if( m_children == null ) | |||||
{ | |||||
m_children = EMPTY_FTP_FILE_ARRAY; | |||||
} | |||||
} | |||||
String[] children = new String[ m_children.length ]; | |||||
for( int i = 0; i < m_children.length; i++ ) | |||||
{ | |||||
FTPFile child = m_children[ i ]; | |||||
children[ i ] = child.getName(); | |||||
} | |||||
return children; | |||||
} | |||||
/** | |||||
* Deletes the file. | |||||
*/ | |||||
protected void doDelete() throws Exception | |||||
{ | |||||
if( !m_ftpFs.getClient().deleteFile( getName().getPath() ) ) | |||||
{ | |||||
final String message = REZ.getString( "delete-file.error", getName() ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
/** | |||||
* Creates this file as a folder. | |||||
*/ | |||||
protected void doCreateFolder() throws Exception | |||||
{ | |||||
if( !m_ftpFs.getClient().makeDirectory( getName().getPath() ) ) | |||||
{ | |||||
final String message = REZ.getString( "create-folder.error", getName() ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
/** | |||||
* Returns the size of the file content (in bytes). | |||||
*/ | |||||
protected long doGetContentSize() throws Exception | |||||
{ | |||||
return m_fileInfo.getSize(); | |||||
} | |||||
/** | |||||
* Creates an input stream to read the file content from. | |||||
*/ | |||||
protected InputStream doGetInputStream() throws Exception | |||||
{ | |||||
return m_ftpFs.getClient().retrieveFileStream( getName().getPath() ); | |||||
} | |||||
/** | |||||
* Notification of the input stream being closed. | |||||
*/ | |||||
protected void doEndInput() throws Exception | |||||
{ | |||||
if( !m_ftpFs.getClient().completePendingCommand() ) | |||||
{ | |||||
final String message = REZ.getString( "finish-get.error", getName() ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
/** | |||||
* Creates an output stream to write the file content to. | |||||
*/ | |||||
protected OutputStream doGetOutputStream() throws Exception | |||||
{ | |||||
return m_ftpFs.getClient().storeFileStream( getName().getPath() ); | |||||
} | |||||
/** | |||||
* Notification of the output stream being closed. | |||||
*/ | |||||
protected void doEndOutput() throws Exception | |||||
{ | |||||
if( !m_ftpFs.getClient().completePendingCommand() ) | |||||
{ | |||||
final String message = REZ.getString( "finish-put.error", getName() ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,102 @@ | |||||
/* | |||||
* 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.provider.ftp; | |||||
import com.oroinc.net.ftp.FTP; | |||||
import com.oroinc.net.ftp.FTPClient; | |||||
import com.oroinc.net.ftp.FTPReply; | |||||
import java.io.IOException; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystem; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* An FTP file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
class FtpFileSystem extends AbstractFileSystem | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( FtpFileSystem.class ); | |||||
private FTPClient m_client; | |||||
public FtpFileSystem( FileName rootName, | |||||
String hostname, | |||||
String username, | |||||
String password ) throws FileSystemException | |||||
{ | |||||
super( rootName ); | |||||
try | |||||
{ | |||||
m_client = new FTPClient(); | |||||
m_client.connect( hostname ); | |||||
int reply = m_client.getReplyCode(); | |||||
if( !FTPReply.isPositiveCompletion( reply ) ) | |||||
{ | |||||
final String message = REZ.getString( "connect-rejected.error", hostname ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Login | |||||
if( !m_client.login( username, password ) ) | |||||
{ | |||||
final String message = REZ.getString( "login.error", hostname, username ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Set binary mode | |||||
if( !m_client.setFileType( FTP.BINARY_FILE_TYPE ) ) | |||||
{ | |||||
final String message = REZ.getString( "set-binary.error", hostname ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
catch( Exception exc ) | |||||
{ | |||||
try | |||||
{ | |||||
// Clean up | |||||
if( m_client.isConnected() ) | |||||
{ | |||||
m_client.disconnect(); | |||||
} | |||||
} | |||||
catch( IOException e ) | |||||
{ | |||||
// Ignore | |||||
} | |||||
final String message = REZ.getString( "connect.error", hostname ); | |||||
throw new FileSystemException( message, exc ); | |||||
} | |||||
// TODO - close connection | |||||
} | |||||
/** | |||||
* Returns an FTP client to use. | |||||
*/ | |||||
public FTPClient getClient() | |||||
{ | |||||
return m_client; | |||||
} | |||||
/** | |||||
* Creates a file object. | |||||
*/ | |||||
protected FileObject createFile( FileName name ) throws FileSystemException | |||||
{ | |||||
return new FtpFileObject( name, this ); | |||||
} | |||||
} |
@@ -0,0 +1,60 @@ | |||||
/* | |||||
* 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.provider.ftp; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||||
import org.apache.aut.vfs.provider.DefaultFileName; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
import org.apache.aut.vfs.provider.UriParser; | |||||
/** | |||||
* A provider for FTP file systems. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class FtpFileSystemProvider extends AbstractFileSystemProvider | |||||
{ | |||||
private UriParser m_parser = new FtpFileNameParser(); | |||||
/** | |||||
* Parses a URI into its components. | |||||
*/ | |||||
protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
{ | |||||
return m_parser.parseUri( uri ); | |||||
} | |||||
/** | |||||
* Creates the filesystem. | |||||
*/ | |||||
protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||||
{ | |||||
ParsedFtpUri ftpUri = (ParsedFtpUri)uri; | |||||
// Build the root name | |||||
FileName rootName = new DefaultFileName( m_parser, ftpUri.getRootURI(), "/" ); | |||||
// Determine the username and password to use | |||||
String username = ftpUri.getUserName(); | |||||
if( username == null ) | |||||
{ | |||||
username = "anonymous"; | |||||
} | |||||
String password = ftpUri.getPassword(); | |||||
if( password == null ) | |||||
{ | |||||
password = "anonymous"; | |||||
} | |||||
// Create the file system | |||||
return new FtpFileSystem( rootName, ftpUri.getHostName(), username, password ); | |||||
} | |||||
} |
@@ -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.provider.ftp; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A parsed FTP URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ParsedFtpUri extends ParsedUri | |||||
{ | |||||
private String m_userName; | |||||
private String m_password; | |||||
public String getUserName() | |||||
{ | |||||
return m_userName; | |||||
} | |||||
public void setUserName( String userName ) | |||||
{ | |||||
m_userName = userName; | |||||
} | |||||
public String getPassword() | |||||
{ | |||||
return m_password; | |||||
} | |||||
public void setPassword( String password ) | |||||
{ | |||||
m_password = password; | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
get-type.error=Could not determine the file type of "{0}". | |||||
delete-file.error=Could not delete FTP file "{0}". | |||||
create-folder.error=Could not create FTP directory "{0}". | |||||
finish-get.error=Could not get FTP file "{0}". | |||||
finish-put.error=Could not put FTP file "{0}". | |||||
connect-rejected.error=Connection to FTP server on "{0}" rejected. | |||||
login.error=Could not login to FTP server on "{0}" as user "{1}". | |||||
set-binary.error=Could not switch to binary transfer mode. | |||||
connect.error=Could not connect to FTP server on "{0}". |
@@ -0,0 +1,133 @@ | |||||
/* | |||||
* 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.provider.local; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.FileOutputStream; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.FileType; | |||||
import org.apache.aut.vfs.provider.AbstractFileObject; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A file object implementation which uses direct file access. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
final class LocalFile extends AbstractFileObject implements FileObject | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( LocalFile.class ); | |||||
private File m_file; | |||||
private String m_fileName; | |||||
/** | |||||
* Creates a non-root file. | |||||
*/ | |||||
public LocalFile( LocalFileSystem fs, String fileName, FileName name ) | |||||
{ | |||||
super( name, fs ); | |||||
m_fileName = fileName; | |||||
} | |||||
/** | |||||
* Attaches this file object to its file resource. | |||||
*/ | |||||
protected void doAttach() throws Exception | |||||
{ | |||||
if( m_file == null ) | |||||
{ | |||||
m_file = new File( m_fileName ); | |||||
} | |||||
} | |||||
/** | |||||
* Returns the file's type. | |||||
*/ | |||||
protected FileType doGetType() throws Exception | |||||
{ | |||||
if( !m_file.exists() ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( m_file.isDirectory() ) | |||||
{ | |||||
return FileType.FOLDER; | |||||
} | |||||
if( m_file.isFile() ) | |||||
{ | |||||
return FileType.FILE; | |||||
} | |||||
final String message = REZ.getString( "get-type.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
/** | |||||
* Returns the children of the file. | |||||
*/ | |||||
protected String[] doListChildren() throws Exception | |||||
{ | |||||
return m_file.list(); | |||||
} | |||||
/** | |||||
* Deletes this file, and all children. | |||||
*/ | |||||
public void doDelete() throws Exception | |||||
{ | |||||
if( !m_file.delete() ) | |||||
{ | |||||
final String message = REZ.getString( "delete-file.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
/** | |||||
* Creates this folder. | |||||
*/ | |||||
protected void doCreateFolder() throws Exception | |||||
{ | |||||
if( !m_file.mkdir() ) | |||||
{ | |||||
final String message = REZ.getString( "create-folder.error", m_file ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
} | |||||
/** | |||||
* Creates an input stream to read the content from. | |||||
*/ | |||||
protected InputStream doGetInputStream() throws Exception | |||||
{ | |||||
return new FileInputStream( m_file ); | |||||
} | |||||
/** | |||||
* Creates an output stream to write the file content to. | |||||
*/ | |||||
protected OutputStream doGetOutputStream() throws Exception | |||||
{ | |||||
return new FileOutputStream( m_file ); | |||||
} | |||||
/** | |||||
* Returns the size of the file content (in bytes). | |||||
*/ | |||||
protected long doGetContentSize() throws Exception | |||||
{ | |||||
return m_file.length(); | |||||
} | |||||
} |
@@ -0,0 +1,228 @@ | |||||
/* | |||||
* 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.provider.local; | |||||
import java.io.File; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
import org.apache.aut.vfs.provider.UriParser; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A name parser. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
class LocalFileNameParser extends UriParser | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( LocalFileNameParser.class ); | |||||
private boolean m_windowsNames; | |||||
public LocalFileNameParser() | |||||
{ | |||||
super( new char[]{File.separatorChar, '/', '\\'} ); | |||||
m_windowsNames = ( System.getProperty( "os.name" ).toLowerCase().indexOf( "windows" ) != -1 ); | |||||
} | |||||
/** | |||||
* Determines if a name is an absolute file name. | |||||
*/ | |||||
public boolean isAbsoluteName( String name ) | |||||
{ | |||||
// TODO - this is yucky | |||||
StringBuffer b = new StringBuffer( name ); | |||||
try | |||||
{ | |||||
fixSeparators( b ); | |||||
extractRootPrefix( name, b ); | |||||
return true; | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
/** | |||||
* Parses an absolute URI, splitting it into its components. | |||||
* | |||||
* @param name | |||||
* The URI. | |||||
*/ | |||||
public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
{ | |||||
StringBuffer name = new StringBuffer(); | |||||
ParsedFileUri uri = new ParsedFileUri(); | |||||
// Extract the scheme | |||||
String scheme = extractScheme( uriStr, name ); | |||||
uri.setScheme( scheme ); | |||||
// Adjust the separators | |||||
fixSeparators( name ); | |||||
// Extract the root prefix | |||||
String rootFile = extractRootPrefix( uriStr, name ); | |||||
uri.setRootFile( rootFile ); | |||||
// Normalise the path | |||||
normalisePath( name ); | |||||
uri.setPath( name.toString() ); | |||||
// Build the root URI | |||||
StringBuffer rootUri = new StringBuffer(); | |||||
rootUri.append( scheme ); | |||||
rootUri.append( "://" ); | |||||
rootUri.append( rootFile ); | |||||
uri.setRootURI( rootUri.toString() ); | |||||
return uri; | |||||
} | |||||
/** | |||||
* Pops the root prefix off a URI, which has had the scheme removed. | |||||
*/ | |||||
private String extractRootPrefix( String uri, | |||||
StringBuffer name ) | |||||
throws FileSystemException | |||||
{ | |||||
// TODO - split this into sub-classes | |||||
if( m_windowsNames ) | |||||
{ | |||||
return extractWindowsRootPrefix( uri, name ); | |||||
} | |||||
else | |||||
{ | |||||
// Looking for <sep> | |||||
if( name.length() == 0 || name.charAt( 0 ) != '/' ) | |||||
{ | |||||
final String message = REZ.getString( "not-absolute-file-name.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// TODO - this isn't always true | |||||
return "/"; | |||||
} | |||||
} | |||||
/** | |||||
* Extracts a Windows root prefix from a name. | |||||
*/ | |||||
private String extractWindowsRootPrefix( String uri, | |||||
StringBuffer name ) | |||||
throws FileSystemException | |||||
{ | |||||
// Looking for: | |||||
// ('/'){0, 3} <letter> ':' '/' | |||||
// ['/'] '//' <name> '/' <name> ( '/' | <end> ) | |||||
// Skip over first 3 leading '/' chars | |||||
int startPos = 0; | |||||
int maxlen = Math.min( 3, name.length() ); | |||||
for( ; startPos < maxlen && name.charAt( startPos ) == '/'; startPos++ ) | |||||
{ | |||||
} | |||||
if( startPos == maxlen ) | |||||
{ | |||||
// Too many '/' | |||||
final String message = REZ.getString( "not-absolute-file-name.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
name.delete( 0, startPos ); | |||||
// Look for drive name | |||||
String driveName = extractDrivePrefix( name ); | |||||
if( driveName != null ) | |||||
{ | |||||
return driveName; | |||||
} | |||||
// Look for UNC name | |||||
if( startPos < 2 ) | |||||
{ | |||||
final String message = REZ.getString( "not-absolute-file-name.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
return "//" + extractUNCPrefix( uri, name ); | |||||
} | |||||
/** | |||||
* Extracts a drive prefix from a path. Leading '/' chars have been removed. | |||||
*/ | |||||
private String extractDrivePrefix( StringBuffer name ) throws FileSystemException | |||||
{ | |||||
// Looking for <letter> ':' '/' | |||||
if( name.length() < 3 ) | |||||
{ | |||||
// Too short | |||||
return null; | |||||
} | |||||
char ch = name.charAt( 0 ); | |||||
if( ch == '/' || ch == ':' ) | |||||
{ | |||||
// Missing drive letter | |||||
return null; | |||||
} | |||||
if( name.charAt( 1 ) != ':' ) | |||||
{ | |||||
// Missing ':' | |||||
return null; | |||||
} | |||||
if( name.charAt( 2 ) != '/' ) | |||||
{ | |||||
// Missing separator | |||||
return null; | |||||
} | |||||
String prefix = name.substring( 0, 2 ); | |||||
name.delete( 0, 2 ); | |||||
return prefix; | |||||
} | |||||
/** | |||||
* Extracts a UNC name from a path. Leading '/' chars have been removed. | |||||
*/ | |||||
private String extractUNCPrefix( String uri, | |||||
StringBuffer name ) throws FileSystemException | |||||
{ | |||||
// Looking for <name> '/' <name> ( '/' | <end> ) | |||||
// Look for first separator | |||||
int maxpos = name.length(); | |||||
int pos = 0; | |||||
for( ; pos < maxpos && name.charAt( pos ) != '/'; pos++ ) | |||||
{ | |||||
} | |||||
pos++; | |||||
if( pos >= maxpos ) | |||||
{ | |||||
final String message = REZ.getString( "missing-share-name.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Now have <name> '/' | |||||
int startShareName = pos; | |||||
for( ; pos < maxpos && name.charAt( pos ) != '/'; pos++ ) | |||||
{ | |||||
} | |||||
if( pos == startShareName ) | |||||
{ | |||||
final String message = REZ.getString( "missing-share-name.error", uri ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
// Now have <name> '/' <name> ( '/' | <end> ) | |||||
String prefix = name.substring( 0, pos ); | |||||
name.delete( 0, pos ); | |||||
return prefix; | |||||
} | |||||
} |
@@ -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.provider.local; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystem; | |||||
import org.apache.aut.vfs.provider.DefaultFileName; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
/** | |||||
* A local file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
class LocalFileSystem extends AbstractFileSystem implements FileSystem | |||||
{ | |||||
private String m_rootFile; | |||||
public LocalFileSystem( DefaultFileName rootName, String rootFile ) | |||||
{ | |||||
super( rootName ); | |||||
m_rootFile = rootFile; | |||||
} | |||||
/** | |||||
* Creates a file object. | |||||
*/ | |||||
protected FileObject createFile( FileName name ) throws FileSystemException | |||||
{ | |||||
// Create the file | |||||
String fileName = m_rootFile + name.getPath(); | |||||
return new LocalFile( this, fileName, name ); | |||||
} | |||||
} |
@@ -0,0 +1,72 @@ | |||||
/* | |||||
* 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.provider.local; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||||
import org.apache.aut.vfs.provider.DefaultFileName; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
import org.apache.aut.vfs.provider.FileSystemProvider; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A file system provider, which uses direct file access. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class LocalFileSystemProvider extends AbstractFileSystemProvider | |||||
implements FileSystemProvider | |||||
{ | |||||
private LocalFileNameParser m_parser = new LocalFileNameParser(); | |||||
/** | |||||
* Determines if a name is an absolute file name. | |||||
*/ | |||||
public boolean isAbsoluteLocalName( String name ) | |||||
{ | |||||
return m_parser.isAbsoluteName( name ); | |||||
} | |||||
/** | |||||
* Finds a file by local file name. | |||||
*/ | |||||
public FileObject findFileByLocalName( String name ) throws FileSystemException | |||||
{ | |||||
// TODO - tidy this up, no need to turn the name into an absolute URI, | |||||
// and then straight back again | |||||
return findFile( "file:" + name ); | |||||
} | |||||
/** | |||||
* 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. | |||||
* | |||||
* <p>The provider can annotate this object with any additional | |||||
* information it requires to create a file system from the URI. | |||||
*/ | |||||
protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
{ | |||||
return m_parser.parseUri( uri ); | |||||
} | |||||
/** | |||||
* Creates the filesystem. | |||||
*/ | |||||
protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||||
{ | |||||
// Build the name of the root file. | |||||
ParsedFileUri fileUri = (ParsedFileUri)uri; | |||||
String rootFile = fileUri.getRootFile(); | |||||
// Create the file system | |||||
DefaultFileName rootName = new DefaultFileName( m_parser, fileUri.getRootURI(), "/" ); | |||||
return new LocalFileSystem( rootName, rootFile ); | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
/* | |||||
* 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.provider.local; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A parsed file URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ParsedFileUri extends ParsedUri | |||||
{ | |||||
private String m_rootFile; | |||||
public String getRootFile() | |||||
{ | |||||
return m_rootFile; | |||||
} | |||||
public void setRootFile( String rootPrefix ) | |||||
{ | |||||
m_rootFile = rootPrefix; | |||||
} | |||||
} |
@@ -0,0 +1,5 @@ | |||||
get-type.error=Could not determine the type of "{0}". | |||||
delete-file.error=Could not delete "{0}". | |||||
create-folder.error=Could not create directory "{0}". | |||||
not-absolute-file-name.error=URI "{0}" is not an absolute file name. | |||||
missing-share-name.error=Share name missing from UNC file name "{0}". |
@@ -0,0 +1,30 @@ | |||||
/* | |||||
* 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.provider.smb; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A parsed SMB URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ParsedSmbUri extends ParsedUri | |||||
{ | |||||
private String m_share; | |||||
public String getShare() | |||||
{ | |||||
return m_share; | |||||
} | |||||
public void setShare( String share ) | |||||
{ | |||||
m_share = share; | |||||
} | |||||
} |
@@ -0,0 +1,2 @@ | |||||
missing-share-name.error=The share name is missing from URI "{0}". | |||||
get-type.error=Could not detemine the type of "{0}". |
@@ -0,0 +1,69 @@ | |||||
/* | |||||
* 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.provider.smb; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
import org.apache.aut.vfs.provider.UriParser; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A parser for SMB URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class SmbFileNameParser extends UriParser | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( SmbFileNameParser.class ); | |||||
/** | |||||
* Parses an absolute URI, splitting it into its components. | |||||
*/ | |||||
public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
{ | |||||
ParsedSmbUri uri = new ParsedSmbUri(); | |||||
StringBuffer name = new StringBuffer(); | |||||
// Extract the scheme and authority parts | |||||
extractToPath( uriStr, name, uri ); | |||||
// Normalise paths | |||||
fixSeparators( name ); | |||||
// Extract the share | |||||
String share = extractFirstElement( name ); | |||||
if( share == null ) | |||||
{ | |||||
final String message = REZ.getString( "missing-share-name.error", uriStr ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
uri.setShare( share ); | |||||
// Set the path | |||||
uri.setPath( name.toString() ); | |||||
// Set the root URI | |||||
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() ); | |||||
rootUri.append( '/' ); | |||||
rootUri.append( share ); | |||||
uri.setRootURI( rootUri.toString() ); | |||||
return uri; | |||||
} | |||||
} |
@@ -0,0 +1,144 @@ | |||||
/* | |||||
* 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.provider.smb; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import jcifs.smb.SmbFile; | |||||
import jcifs.smb.SmbFileInputStream; | |||||
import jcifs.smb.SmbFileOutputStream; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.FileType; | |||||
import org.apache.aut.vfs.provider.AbstractFileObject; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
/** | |||||
* A file in an SMB file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class SmbFileObject extends AbstractFileObject implements FileObject | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( SmbFileObject.class ); | |||||
private String m_fileName; | |||||
private SmbFile m_file; | |||||
protected SmbFileObject( String fileName, FileName name, SmbFileSystem fs ) | |||||
{ | |||||
super( name, fs ); | |||||
m_fileName = fileName; | |||||
} | |||||
/** | |||||
* Attaches this file object to its file resource. | |||||
*/ | |||||
protected void doAttach() throws Exception | |||||
{ | |||||
// Defer creation of the SmbFile to here | |||||
if( m_file == null ) | |||||
{ | |||||
m_file = new SmbFile( m_fileName ); | |||||
} | |||||
} | |||||
/** | |||||
* Detaches this file object from its file resource. | |||||
*/ | |||||
protected void doDetach() | |||||
{ | |||||
// Need to throw away the file when the file's type changes, because | |||||
// the SmbFile caches the type | |||||
m_file = null; | |||||
} | |||||
/** | |||||
* Determines the type of the file, returns null if the file does not | |||||
* exist. | |||||
*/ | |||||
protected FileType doGetType() throws Exception | |||||
{ | |||||
// Need to check whether parent exists or not, because SmbFile.exists() | |||||
// throws an exception if it does not | |||||
// TODO - patch jCIFS? | |||||
FileObject parent = getParent(); | |||||
if( parent != null && !parent.exists() ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( !m_file.exists() ) | |||||
{ | |||||
return null; | |||||
} | |||||
if( m_file.isDirectory() ) | |||||
{ | |||||
return FileType.FOLDER; | |||||
} | |||||
if( m_file.isFile() ) | |||||
{ | |||||
return FileType.FILE; | |||||
} | |||||
final String message = REZ.getString( "get-type.error", getName() ); | |||||
throw new FileSystemException( message ); | |||||
} | |||||
/** | |||||
* Lists the children of the file. Is only called if {@link #doGetType} | |||||
* returns {@link FileType#FOLDER}. | |||||
*/ | |||||
protected String[] doListChildren() throws Exception | |||||
{ | |||||
return m_file.list(); | |||||
} | |||||
/** | |||||
* Deletes the file. | |||||
*/ | |||||
protected void doDelete() throws Exception | |||||
{ | |||||
m_file.delete(); | |||||
} | |||||
/** | |||||
* Creates this file as a folder. | |||||
*/ | |||||
protected void doCreateFolder() throws Exception | |||||
{ | |||||
m_file.mkdir(); | |||||
} | |||||
/** | |||||
* Returns the size of the file content (in bytes). | |||||
*/ | |||||
protected long doGetContentSize() throws Exception | |||||
{ | |||||
return m_file.length(); | |||||
} | |||||
/** | |||||
* Creates an input stream to read the file content from. | |||||
*/ | |||||
protected InputStream doGetInputStream() throws Exception | |||||
{ | |||||
return new SmbFileInputStream( m_file ); | |||||
} | |||||
/** | |||||
* Creates an output stream to write the file content to. | |||||
*/ | |||||
protected OutputStream doGetOutputStream() throws Exception | |||||
{ | |||||
return new SmbFileOutputStream( m_file ); | |||||
} | |||||
} |
@@ -0,0 +1,36 @@ | |||||
/* | |||||
* 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.provider.smb; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystem; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
/** | |||||
* A SMB file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class SmbFileSystem extends AbstractFileSystem implements FileSystem | |||||
{ | |||||
public SmbFileSystem( FileName rootName ) | |||||
{ | |||||
super( rootName ); | |||||
} | |||||
/** | |||||
* Creates a file object. | |||||
*/ | |||||
protected FileObject createFile( FileName name ) throws FileSystemException | |||||
{ | |||||
String fileName = name.getURI(); | |||||
return new SmbFileObject( fileName, name, this ); | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
/* | |||||
* 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.provider.smb; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||||
import org.apache.aut.vfs.provider.DefaultFileName; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
import org.apache.aut.vfs.provider.FileSystemProvider; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A provider for SMB (Samba, Windows share) file systems. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class SmbFileSystemProvider extends AbstractFileSystemProvider implements FileSystemProvider | |||||
{ | |||||
SmbFileNameParser m_parser = new SmbFileNameParser(); | |||||
/** | |||||
* Parses a URI into its components. | |||||
*/ | |||||
protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
{ | |||||
return m_parser.parseUri( uri ); | |||||
} | |||||
/** | |||||
* Creates the filesystem. | |||||
*/ | |||||
protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||||
{ | |||||
ParsedSmbUri smbUri = (ParsedSmbUri)uri; | |||||
FileName rootName = new DefaultFileName( m_parser, smbUri.getRootURI(), "/" ); | |||||
return new SmbFileSystem( rootName ); | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
/* | |||||
* 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.provider.zip; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A parsed Zip URI. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ParsedZipUri extends ParsedUri | |||||
{ | |||||
private String m_zipFile; | |||||
public String getZipFile() | |||||
{ | |||||
return m_zipFile; | |||||
} | |||||
public void setZipFile( String zipFile ) | |||||
{ | |||||
m_zipFile = zipFile; | |||||
} | |||||
} |
@@ -0,0 +1 @@ | |||||
open-zip-file.error=Could not open Zip file "{0}". |
@@ -0,0 +1,81 @@ | |||||
/* | |||||
* 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.provider.zip; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
import org.apache.aut.vfs.provider.UriParser; | |||||
/** | |||||
* A parser for Zip file names. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ZipFileNameParser extends UriParser | |||||
{ | |||||
/** | |||||
* Parses an absolute URI, splitting it into its components. | |||||
* | |||||
* @param name | |||||
* The URI. | |||||
*/ | |||||
public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||||
{ | |||||
StringBuffer name = new StringBuffer(); | |||||
ParsedZipUri uri = new ParsedZipUri(); | |||||
// Extract the scheme | |||||
String scheme = extractScheme( uriStr, name ); | |||||
uri.setScheme( scheme ); | |||||
// Extract the Zip file name | |||||
String zipName = extractZipName( name ); | |||||
uri.setZipFile( zipName ); | |||||
// Adjust the separators | |||||
fixSeparators( name ); | |||||
// Normalise the file name | |||||
normalisePath( name ); | |||||
uri.setPath( name.toString() ); | |||||
// Build root URI | |||||
StringBuffer rootUri = new StringBuffer(); | |||||
rootUri.append( scheme ); | |||||
rootUri.append( ":" ); | |||||
rootUri.append( zipName ); | |||||
rootUri.append( "!" ); | |||||
uri.setRootURI( rootUri.toString() ); | |||||
return uri; | |||||
} | |||||
/** | |||||
* Pops the root prefix off a URI, which has had the scheme removed. | |||||
*/ | |||||
protected String extractZipName( StringBuffer uri ) throws FileSystemException | |||||
{ | |||||
// Looking for <name>!<abspath> | |||||
// TODO - how does '!' in the file name get escaped? | |||||
int maxlen = uri.length(); | |||||
for( int pos = 0; pos < maxlen; pos++ ) | |||||
{ | |||||
if( uri.charAt( pos ) == '!' ) | |||||
{ | |||||
String prefix = uri.substring( 0, pos ); | |||||
uri.delete( 0, pos + 1 ); | |||||
return prefix; | |||||
} | |||||
} | |||||
// Assume the URI is the Jar file name | |||||
String prefix = uri.toString(); | |||||
uri.setLength( 0 ); | |||||
return prefix; | |||||
} | |||||
} |
@@ -0,0 +1,103 @@ | |||||
/* | |||||
* 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.provider.zip; | |||||
import java.io.InputStream; | |||||
import java.util.HashSet; | |||||
import java.util.zip.ZipEntry; | |||||
import java.util.zip.ZipFile; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileType; | |||||
import org.apache.aut.vfs.provider.AbstractFileObject; | |||||
/** | |||||
* A file in a Zip file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
class ZipFileObject extends AbstractFileObject implements FileObject | |||||
{ | |||||
private ZipEntry m_entry; | |||||
private ZipFile m_file; | |||||
private FileType m_type; | |||||
private HashSet m_children = new HashSet(); | |||||
public ZipFileObject( FileName name, | |||||
ZipEntry entry, | |||||
ZipFile zipFile, | |||||
ZipFileSystem fs ) | |||||
{ | |||||
super( name, fs ); | |||||
m_type = FileType.FILE; | |||||
m_entry = entry; | |||||
m_file = zipFile; | |||||
} | |||||
public ZipFileObject( FileName name, boolean exists, ZipFileSystem fs ) | |||||
{ | |||||
super( name, fs ); | |||||
if( exists ) | |||||
{ | |||||
m_type = FileType.FOLDER; | |||||
} | |||||
// else _type = null | |||||
} | |||||
/** | |||||
* Attaches a child | |||||
*/ | |||||
public void attachChild( FileName childName ) | |||||
{ | |||||
m_children.add( childName.getBaseName() ); | |||||
} | |||||
/** | |||||
* Returns true if this file is read-only. | |||||
*/ | |||||
protected boolean isReadOnly() | |||||
{ | |||||
return true; | |||||
} | |||||
/** | |||||
* Returns the file's type. | |||||
*/ | |||||
protected FileType doGetType() | |||||
{ | |||||
return m_type; | |||||
} | |||||
/** | |||||
* Lists the children of the file. | |||||
*/ | |||||
protected String[] doListChildren() | |||||
{ | |||||
return (String[])m_children.toArray( new String[ m_children.size() ] ); | |||||
} | |||||
/** | |||||
* Returns the size of the file content (in bytes). Is only called if | |||||
* {@link #doGetType} returns {@link FileType#FILE}. | |||||
*/ | |||||
protected long doGetContentSize() | |||||
{ | |||||
return m_entry.getSize(); | |||||
} | |||||
/** | |||||
* Creates an input stream to read the file content from. Is only called | |||||
* if {@link #doGetType} returns {@link FileType#FILE}. The input stream | |||||
* returned by this method is guaranteed to be closed before this | |||||
* method is called again. | |||||
*/ | |||||
protected InputStream doGetInputStream() throws Exception | |||||
{ | |||||
return m_file.getInputStream( m_entry ); | |||||
} | |||||
} |
@@ -0,0 +1,112 @@ | |||||
/* | |||||
* 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.provider.zip; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.Enumeration; | |||||
import java.util.zip.ZipEntry; | |||||
import java.util.zip.ZipFile; | |||||
import org.apache.aut.vfs.FileName; | |||||
import org.apache.aut.vfs.FileObject; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystem; | |||||
import org.apache.aut.vfs.provider.DefaultFileName; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
/** | |||||
* A read-only file system for Zip/Jar files. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ZipFileSystem extends AbstractFileSystem implements FileSystem | |||||
{ | |||||
private static final Resources REZ | |||||
= ResourceManager.getPackageResources( ZipFileSystem.class ); | |||||
private File m_file; | |||||
private ZipFile m_zipFile; | |||||
public ZipFileSystem( DefaultFileName rootName, File file ) throws FileSystemException | |||||
{ | |||||
super( rootName ); | |||||
m_file = file; | |||||
// Open the Zip file | |||||
if( !file.exists() ) | |||||
{ | |||||
// Don't need to do anything | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
m_zipFile = new ZipFile( m_file ); | |||||
} | |||||
catch( IOException ioe ) | |||||
{ | |||||
final String message = REZ.getString( "open-zip-file.error", m_file ); | |||||
throw new FileSystemException( message, ioe ); | |||||
} | |||||
// Build the index | |||||
Enumeration entries = m_zipFile.entries(); | |||||
while( entries.hasMoreElements() ) | |||||
{ | |||||
ZipEntry entry = (ZipEntry)entries.nextElement(); | |||||
FileName name = rootName.resolveName( entry.getName() ); | |||||
// Create the file | |||||
ZipFileObject fileObj; | |||||
if( entry.isDirectory() ) | |||||
{ | |||||
if( getFile( name ) != null ) | |||||
{ | |||||
// Already created implicitly | |||||
continue; | |||||
} | |||||
fileObj = new ZipFileObject( name, true, this ); | |||||
} | |||||
else | |||||
{ | |||||
fileObj = new ZipFileObject( name, entry, m_zipFile, this ); | |||||
} | |||||
putFile( fileObj ); | |||||
// Make sure all ancestors exist | |||||
// TODO - create these on demand | |||||
ZipFileObject parent = null; | |||||
for( FileName parentName = name.getParent(); | |||||
parentName != null; | |||||
fileObj = parent, parentName = parentName.getParent() ) | |||||
{ | |||||
// Locate the parent | |||||
parent = (ZipFileObject)getFile( parentName ); | |||||
if( parent == null ) | |||||
{ | |||||
parent = new ZipFileObject( parentName, true, this ); | |||||
putFile( parent ); | |||||
} | |||||
// Attach child to parent | |||||
parent.attachChild( fileObj.getName() ); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Creates a file object. | |||||
*/ | |||||
protected FileObject createFile( FileName name ) throws FileSystemException | |||||
{ | |||||
// This is only called for files which do not exist in the Zip file | |||||
return new ZipFileObject( name, false, this ); | |||||
} | |||||
} |
@@ -0,0 +1,50 @@ | |||||
/* | |||||
* 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.provider.zip; | |||||
import java.io.File; | |||||
import org.apache.aut.vfs.FileSystemException; | |||||
import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||||
import org.apache.aut.vfs.provider.DefaultFileName; | |||||
import org.apache.aut.vfs.provider.FileSystem; | |||||
import org.apache.aut.vfs.provider.FileSystemProvider; | |||||
import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | |||||
* A file system provider for Zip/Jar files. Provides read-only file | |||||
* systems, for local Zip files only. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ZipFileSystemProvider extends AbstractFileSystemProvider | |||||
implements FileSystemProvider | |||||
{ | |||||
private ZipFileNameParser m_parser = new ZipFileNameParser(); | |||||
/** | |||||
* Parses a URI into its components. | |||||
*/ | |||||
protected ParsedUri parseURI( String uri ) throws FileSystemException | |||||
{ | |||||
return m_parser.parseUri( uri ); | |||||
} | |||||
/** | |||||
* Creates the filesystem. | |||||
*/ | |||||
protected FileSystem createFileSystem( 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 ); | |||||
} | |||||
} |
@@ -0,0 +1,587 @@ | |||||
/* | |||||
* 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 java.io.ByteArrayOutputStream; | |||||
import java.io.InputStream; | |||||
import java.io.Reader; | |||||
import java.io.StringWriter; | |||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import junit.framework.TestCase; | |||||
import org.apache.aut.vfs.provider.DefaultFileSystemManager; | |||||
/** | |||||
* File system test cases, which verifies the structure and naming | |||||
* functionality. | |||||
* | |||||
* Works from a base folder, and assumes a particular structure under | |||||
* that base folder. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class BasicFileSystemTestBase extends TestCase | |||||
{ | |||||
protected FileObject m_baseFolder; | |||||
protected DefaultFileSystemManager m_manager; | |||||
// Contents of "file1.txt" | |||||
private String m_charContent; | |||||
public BasicFileSystemTestBase( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Builds the expected folder structure. | |||||
*/ | |||||
private FileInfo buildExpectedStructure() | |||||
{ | |||||
// Build the expected structure | |||||
FileInfo base = new FileInfo( "test", FileType.FOLDER ); | |||||
base.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | |||||
base.addChild( new FileInfo( "empty.txt", FileType.FILE ) ); | |||||
base.addChild( new FileInfo( "emptydir", FileType.FOLDER ) ); | |||||
FileInfo dir = new FileInfo( "dir1", FileType.FOLDER ); | |||||
base.addChild( dir ); | |||||
dir.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | |||||
dir.addChild( new FileInfo( "file2.txt", FileType.FILE ) ); | |||||
dir.addChild( new FileInfo( "file3.txt", FileType.FILE ) ); | |||||
return base; | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected abstract String getBaseFolderURI(); | |||||
/** | |||||
* Sets up the test | |||||
*/ | |||||
protected void setUp() throws Exception | |||||
{ | |||||
// Create the file system manager | |||||
m_manager = new DefaultFileSystemManager(); | |||||
// Locate the base folder | |||||
m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | |||||
// Build the expected content of "file1.txt" | |||||
String eol = System.getProperty( "line.separator" ); | |||||
m_charContent = "This is a test file." + eol + "With 2 lines in it." + eol; | |||||
} | |||||
/** | |||||
* Tests resolution of absolute URI. | |||||
*/ | |||||
public void testAbsoluteURI() throws Exception | |||||
{ | |||||
// Try fetching base folder again by its URI | |||||
String uri = m_baseFolder.getName().getURI(); | |||||
FileObject file = m_manager.resolveFile( uri ); | |||||
assertSame( "file object", m_baseFolder, file ); | |||||
} | |||||
/** | |||||
* Tests resolution of relative file names via the FS manager | |||||
*/ | |||||
public void testRelativeURI() throws Exception | |||||
{ | |||||
// Build base dir | |||||
m_manager.setBaseFile( m_baseFolder ); | |||||
// Locate the base dir | |||||
FileObject file = m_manager.resolveFile( "." ); | |||||
assertSame( "file object", m_baseFolder, file ); | |||||
// Locate a child | |||||
file = m_manager.resolveFile( "some-child" ); | |||||
assertSame( "file object", m_baseFolder, file.getParent() ); | |||||
// Locate a descendent | |||||
file = m_manager.resolveFile( "some-folder/some-file" ); | |||||
assertSame( "file object", m_baseFolder, file.getParent().getParent() ); | |||||
// Locate parent | |||||
file = m_manager.resolveFile( ".." ); | |||||
assertSame( "file object", m_baseFolder.getParent(), file ); | |||||
} | |||||
/** | |||||
* Tests the root file name. | |||||
*/ | |||||
public void testRootFileName() throws Exception | |||||
{ | |||||
// Locate the root file | |||||
FileName rootName = m_baseFolder.getRoot().getName(); | |||||
// Test that the root path is "/" | |||||
assertEquals( "root path", "/", rootName.getPath() ); | |||||
// Test that the root basname is "" | |||||
assertEquals( "root base name", "", rootName.getBaseName() ); | |||||
// Test that the root name has no parent | |||||
assertNull( "root parent", rootName.getParent() ); | |||||
} | |||||
/** | |||||
* Tests child file names. | |||||
*/ | |||||
public void testChildName() throws Exception | |||||
{ | |||||
FileName baseName = m_baseFolder.getName(); | |||||
String basePath = baseName.getPath(); | |||||
FileName name = baseName.resolveName( "some-child", NameScope.CHILD ); | |||||
// Test path is absolute | |||||
assertTrue( "is absolute", basePath.startsWith( "/" ) ); | |||||
// Test base name | |||||
assertEquals( "base name", "some-child", name.getBaseName() ); | |||||
// Test absolute path | |||||
assertEquals( "absolute path", basePath + "/some-child", name.getPath() ); | |||||
// Test parent path | |||||
assertEquals( "parent absolute path", basePath, name.getParent().getPath() ); | |||||
// Try using a compound name to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( "a/b", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try using a empty name to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( "", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try using '.' to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( ".", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try using '..' to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( "..", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
} | |||||
/** | |||||
* Checks that a relative name resolves to the expected absolute path. | |||||
*/ | |||||
private void assertSameName(String expectedPath, | |||||
FileName baseName, | |||||
String relName ) throws Exception | |||||
{ | |||||
FileName name = baseName.resolveName(relName); | |||||
assertEquals( expectedPath, name.getPath() ); | |||||
// Replace the separators | |||||
relName.replace('\\', '/'); | |||||
name = baseName.resolveName(relName); | |||||
assertEquals( expectedPath, name.getPath() ); | |||||
// And again | |||||
relName.replace('/', '\\'); | |||||
name = baseName.resolveName(relName); | |||||
assertEquals( expectedPath, name.getPath() ); | |||||
} | |||||
/** | |||||
* Tests relative name resolution, relative to the base folder. | |||||
*/ | |||||
public void testNameResolution() throws Exception | |||||
{ | |||||
FileName baseName = m_baseFolder.getName(); | |||||
String parentPath = baseName.getParent().getPath(); | |||||
String path = baseName.getPath(); | |||||
String childPath = path + "/some-child"; | |||||
// Test empty relative path | |||||
assertSameName( path, baseName, "" ); | |||||
// Test . relative path | |||||
assertSameName( path, baseName, "." ); | |||||
// Test ./ relative path | |||||
assertSameName( path, baseName, "./" ); | |||||
// Test .// relative path | |||||
assertSameName( path, baseName, ".//" ); | |||||
// Test .///.///. relative path | |||||
assertSameName( path, baseName, ".///.///." ); | |||||
assertSameName( path, baseName, "./\\/.\\//." ); | |||||
// Test <elem>/.. relative path | |||||
assertSameName( path, baseName, "a/.." ); | |||||
// Test .. relative path | |||||
assertSameName( parentPath, baseName, ".." ); | |||||
// Test ../ relative path | |||||
assertSameName( parentPath, baseName, "../" ); | |||||
// Test ..//./ relative path | |||||
assertSameName( parentPath, baseName, "..//./" ); | |||||
assertSameName( parentPath, baseName, "..//.\\" ); | |||||
// Test <elem>/../.. relative path | |||||
assertSameName( parentPath, baseName, "a/../.." ); | |||||
// Test <elem> relative path | |||||
assertSameName( childPath, baseName, "some-child" ); | |||||
// Test ./<elem> relative path | |||||
assertSameName( childPath, baseName, "./some-child" ); | |||||
// Test ./<elem>/ relative path | |||||
assertSameName( childPath, baseName, "./some-child/" ); | |||||
// Test <elem>/././././ relative path | |||||
assertSameName( childPath, baseName, "./some-child/././././" ); | |||||
// Test <elem>/../<elem> relative path | |||||
assertSameName( childPath, baseName, "a/../some-child" ); | |||||
// Test <elem>/<elem>/../../<elem> relative path | |||||
assertSameName( childPath, baseName, "a/b/../../some-child" ); | |||||
} | |||||
/** | |||||
* Tests relative name resolution, relative to the root file. | |||||
*/ | |||||
public void testNameResolutionRoot() throws Exception | |||||
{ | |||||
FileName rootName = m_baseFolder.getRoot().getName(); | |||||
} | |||||
/** | |||||
* Walks the folder structure, asserting it contains exactly the | |||||
* expected files and folders. | |||||
*/ | |||||
public void testStructure() throws Exception | |||||
{ | |||||
// Setup the structure | |||||
List queueExpected = new ArrayList(); | |||||
FileInfo baseInfo = buildExpectedStructure(); | |||||
queueExpected.add( baseInfo ); | |||||
List queueActual = new ArrayList(); | |||||
queueActual.add( m_baseFolder ); | |||||
while( queueActual.size() > 0 ) | |||||
{ | |||||
FileObject file = (FileObject)queueActual.remove( 0 ); | |||||
FileInfo info = (FileInfo)queueExpected.remove( 0 ); | |||||
// Check the type is correct | |||||
assertSame( file.getType(), info._type ); | |||||
if( info._type == FileType.FILE ) | |||||
{ | |||||
continue; | |||||
} | |||||
// Check children | |||||
FileObject[] children = file.getChildren(); | |||||
// Make sure all children were found | |||||
assertNotNull( children ); | |||||
assertEquals( "count children of \"" + file.getName() + "\"", info._children.size(), children.length ); | |||||
// Recursively check each child | |||||
for( int i = 0; i < children.length; i++ ) | |||||
{ | |||||
FileObject child = children[ i ]; | |||||
FileInfo childInfo = (FileInfo)info._children.get( child.getName().getBaseName() ); | |||||
// Make sure the child is expected | |||||
assertNotNull( childInfo ); | |||||
// Add to the queue of files to check | |||||
queueExpected.add( childInfo ); | |||||
queueActual.add( child ); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Tests existence determination. | |||||
*/ | |||||
public void testExists() throws Exception | |||||
{ | |||||
// Test a file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertTrue( "file exists", file.exists() ); | |||||
// Test a folder | |||||
file = m_baseFolder.resolveFile( "dir1" ); | |||||
assertTrue( "folder exists", file.exists() ); | |||||
// Test an unknown file | |||||
file = m_baseFolder.resolveFile( "unknown-child" ); | |||||
assertTrue( "unknown file does not exist", !file.exists() ); | |||||
// Test an unknown file in an unknown folder | |||||
file = m_baseFolder.resolveFile( "unknown-folder/unknown-child" ); | |||||
assertTrue( "unknown file does not exist", !file.exists() ); | |||||
} | |||||
/** | |||||
* Tests type determination. | |||||
*/ | |||||
public void testType() throws Exception | |||||
{ | |||||
// Test a file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertSame( FileType.FILE, file.getType() ); | |||||
// Test a folder | |||||
file = m_baseFolder.resolveFile( "dir1" ); | |||||
assertSame( FileType.FOLDER, file.getType() ); | |||||
// Test an unknown file | |||||
file = m_baseFolder.resolveFile( "unknown-child" ); | |||||
FileSystemException exc = null; | |||||
try | |||||
{ | |||||
file.getType(); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
exc = e; | |||||
} | |||||
assertNotNull( exc ); | |||||
} | |||||
/** | |||||
* Tests parent identity | |||||
*/ | |||||
public void testParent() throws FileSystemException | |||||
{ | |||||
// Test when both exist | |||||
FileObject folder = m_baseFolder.resolveFile( "dir1" ); | |||||
FileObject child = folder.resolveFile( "file3.txt" ); | |||||
assertTrue( "folder exists", folder.exists() ); | |||||
assertTrue( "child exists", child.exists() ); | |||||
assertSame( folder, child.getParent() ); | |||||
// Test when file does not exist | |||||
child = folder.resolveFile( "unknown-file" ); | |||||
assertTrue( "folder exists", folder.exists() ); | |||||
assertTrue( "child does not exist", !child.exists() ); | |||||
assertSame( folder, child.getParent() ); | |||||
// Test when neither exists | |||||
folder = m_baseFolder.resolveFile( "unknown-folder" ); | |||||
child = folder.resolveFile( "unknown-file" ); | |||||
assertTrue( "folder does not exist", !folder.exists() ); | |||||
assertTrue( "child does not exist", !child.exists() ); | |||||
assertSame( folder, child.getParent() ); | |||||
// Test root of the file system has no parent | |||||
FileObject root = m_baseFolder.getRoot(); | |||||
assertNull( "root has null parent", root.getParent() ); | |||||
} | |||||
/** | |||||
* Tests that children cannot be listed for non-folders. | |||||
*/ | |||||
public void testChildren() throws FileSystemException | |||||
{ | |||||
// Check for file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertSame( FileType.FILE, file.getType() ); | |||||
try | |||||
{ | |||||
file.getChildren(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Should be able to get child by name | |||||
file = file.resolveFile( "some-child" ); | |||||
assertNotNull( file ); | |||||
// Check for unknown file | |||||
file = m_baseFolder.resolveFile( "unknown-file" ); | |||||
assertTrue( !file.exists() ); | |||||
try | |||||
{ | |||||
file.getChildren(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Should be able to get child by name | |||||
FileObject child = file.resolveFile( "some-child" ); | |||||
assertNotNull( child ); | |||||
} | |||||
/** | |||||
* Tests content. | |||||
*/ | |||||
public void testContent() throws Exception | |||||
{ | |||||
// Test non-empty file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
FileContent content = file.getContent(); | |||||
assertSameContent(m_charContent, content); | |||||
// Test empty file | |||||
file = m_baseFolder.resolveFile( "empty.txt" ); | |||||
content = file.getContent(); | |||||
assertSameContent("", content); | |||||
} | |||||
/** | |||||
* Asserts that the content of a file is the same as expected. Checks the | |||||
* length reported by getSize() is correct, then reads the content as | |||||
* a byte stream, and as a char stream, and compares the result with | |||||
* the expected content. Assumes files are encoded using UTF-8. | |||||
*/ | |||||
public void assertSameContent( String expected, FileContent content ) throws Exception | |||||
{ | |||||
// Get file content as a binary stream | |||||
byte[] expectedBin = expected.getBytes( "utf-8" ); | |||||
// Check lengths | |||||
assertEquals( "same content length", expectedBin.length, content.getSize() ); | |||||
// Read content into byte array | |||||
InputStream instr = content.getInputStream(); | |||||
ByteArrayOutputStream outstr = new ByteArrayOutputStream(); | |||||
byte[] buffer = new byte[ 256 ]; | |||||
int nread = 0; | |||||
while( nread >= 0 ) | |||||
{ | |||||
outstr.write( buffer, 0, nread ); | |||||
nread = instr.read( buffer ); | |||||
} | |||||
// Compare | |||||
assertTrue( "same binary content", Arrays.equals( expectedBin, outstr.toByteArray() ) ); | |||||
} | |||||
/** | |||||
* Tests that folders and unknown files have no content. | |||||
*/ | |||||
public void testNoContent() throws Exception | |||||
{ | |||||
// Try getting the content of a folder | |||||
FileObject folder = m_baseFolder.resolveFile( "dir1" ); | |||||
try | |||||
{ | |||||
folder.getContent(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try getting the content of an unknown file | |||||
FileObject unknownFile = m_baseFolder.resolveFile( "unknown-file" ); | |||||
FileContent content = unknownFile.getContent(); | |||||
try | |||||
{ | |||||
content.getInputStream(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
try | |||||
{ | |||||
content.getSize(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
} | |||||
/** | |||||
* Tests that content and file objects are usable after being closed. | |||||
*/ | |||||
public void testReuse() throws Exception | |||||
{ | |||||
// Get the test file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertEquals( FileType.FILE, file.getType() ); | |||||
// Get the file content | |||||
FileContent content = file.getContent(); | |||||
assertSameContent( m_charContent, content ); | |||||
// Read the content again | |||||
content = file.getContent(); | |||||
assertSameContent( m_charContent, content ); | |||||
// Close the content + file | |||||
content.close(); | |||||
file.close(); | |||||
// Read the content again | |||||
content = file.getContent(); | |||||
assertSameContent( m_charContent, content ); | |||||
} | |||||
/** | |||||
* Info about a file. | |||||
*/ | |||||
private static final class FileInfo | |||||
{ | |||||
String _baseName; | |||||
FileType _type; | |||||
Map _children = new HashMap(); | |||||
public FileInfo( String name, FileType type ) | |||||
{ | |||||
_baseName = name; | |||||
_type = type; | |||||
} | |||||
/** Adds a child. */ | |||||
public void addChild( FileInfo child ) | |||||
{ | |||||
_children.put( child._baseName, child ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* Tests for FTP file systems. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class FtpFileSystemTest extends WritableFileSystemTestBase | |||||
{ | |||||
public FtpFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
return System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected String getWriteFolderURI() | |||||
{ | |||||
return System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||||
} | |||||
} |
@@ -0,0 +1,64 @@ | |||||
/* | |||||
* 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 java.io.File; | |||||
/** | |||||
* Tests for the local file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class LocalFileSystemTest extends WritableFileSystemTestBase | |||||
{ | |||||
private File m_baseDir; | |||||
public LocalFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
String baseDir = System.getProperty( "test.local.dir" ); | |||||
m_baseDir = new File( baseDir ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
String testDir = new File( m_baseDir, "read-tests" ).getAbsolutePath(); | |||||
String uri = "file:/" + testDir; | |||||
return uri; | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected String getWriteFolderURI() | |||||
{ | |||||
String testDir = new File( m_baseDir, "write-tests" ).getAbsolutePath(); | |||||
String uri = "file:/" + testDir; | |||||
return uri; | |||||
} | |||||
/** | |||||
* Tests resolution of an absolute file name. | |||||
*/ | |||||
public void testAbsoluteFileName() throws Exception | |||||
{ | |||||
// Locate file by absolute file name | |||||
String fileName = new File( "testdir" ).getAbsolutePath(); | |||||
FileObject absFile = m_manager.resolveFile( fileName ); | |||||
// Locate file by URI | |||||
String uri = "file://" + fileName.replace( File.separatorChar, '/' ); | |||||
FileObject uriFile = m_manager.resolveFile( uri ); | |||||
assertSame( "file object", absFile, uriFile ); | |||||
} | |||||
} |
@@ -0,0 +1,22 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* File system tests which check that a read-only file system cannot be | |||||
* changed. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class ReadOnlyFileSystemTestBase extends BasicFileSystemTestBase | |||||
{ | |||||
public ReadOnlyFileSystemTestBase( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* Tests for the SMB file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class SmbFileSystemTest extends WritableFileSystemTestBase | |||||
{ | |||||
public SmbFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
return System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected String getWriteFolderURI() | |||||
{ | |||||
return System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||||
} | |||||
} |
@@ -0,0 +1,255 @@ | |||||
/* | |||||
* 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 java.io.Writer; | |||||
import java.io.OutputStream; | |||||
import java.util.HashSet; | |||||
import java.util.Set; | |||||
/** | |||||
* File system test that check that a file system can be modified. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class WritableFileSystemTestBase extends BasicFileSystemTestBase | |||||
{ | |||||
public WritableFileSystemTestBase( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected abstract String getWriteFolderURI(); | |||||
/** | |||||
* Sets up a scratch folder for the test to use. | |||||
*/ | |||||
protected FileObject createScratchFolder() throws Exception | |||||
{ | |||||
FileObject scratchFolder = m_manager.resolveFile( getWriteFolderURI() ); | |||||
// Make sure the test folder is empty | |||||
scratchFolder.delete(); | |||||
scratchFolder.create( FileType.FOLDER ); | |||||
return scratchFolder; | |||||
} | |||||
/** | |||||
* Tests folder creation. | |||||
*/ | |||||
public void testFolderCreate() throws Exception | |||||
{ | |||||
FileObject scratchFolder = createScratchFolder(); | |||||
// Create direct child of the test folder | |||||
FileObject folder = scratchFolder.resolveFile( "dir1" ); | |||||
assertTrue( !folder.exists() ); | |||||
folder.create( FileType.FOLDER ); | |||||
assertTrue( folder.exists() ); | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
// Create a descendant, where the intermediate folders don't exist | |||||
folder = scratchFolder.resolveFile( "dir2/dir1/dir1" ); | |||||
assertTrue( !folder.exists() ); | |||||
assertTrue( !folder.getParent().exists() ); | |||||
assertTrue( !folder.getParent().getParent().exists() ); | |||||
folder.create( FileType.FOLDER ); | |||||
assertTrue( folder.exists() ); | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
assertTrue( folder.getParent().exists() ); | |||||
assertTrue( folder.getParent().getParent().exists() ); | |||||
// Test creating a folder that already exists | |||||
folder.create( FileType.FOLDER ); | |||||
} | |||||
/** | |||||
* Tests file creation | |||||
*/ | |||||
public void testFileCreate() throws Exception | |||||
{ | |||||
FileObject scratchFolder = createScratchFolder(); | |||||
// Create direct child of the test folder | |||||
FileObject file = scratchFolder.resolveFile( "file1.txt" ); | |||||
assertTrue( !file.exists() ); | |||||
file.create( FileType.FILE ); | |||||
assertTrue( file.exists() ); | |||||
assertEquals( 0, file.getContent().getSize() ); | |||||
// Create a descendant, where the intermediate folders don't exist | |||||
file = scratchFolder.resolveFile( "dir1/dir1/file1.txt" ); | |||||
assertTrue( !file.exists() ); | |||||
assertTrue( !file.getParent().exists() ); | |||||
assertTrue( !file.getParent().getParent().exists() ); | |||||
file.create( FileType.FILE ); | |||||
assertTrue( file.exists() ); | |||||
assertEquals( 0, file.getContent().getSize() ); | |||||
assertTrue( file.getParent().exists() ); | |||||
assertTrue( file.getParent().getParent().exists() ); | |||||
// Test creating a file that already exists | |||||
file.create( FileType.FILE ); | |||||
} | |||||
/** | |||||
* Tests file/folder creation with mismatched types. | |||||
*/ | |||||
public void testFileCreateMismatched() throws Exception | |||||
{ | |||||
FileObject scratchFolder = createScratchFolder(); | |||||
// Create a test file and folder | |||||
FileObject file = scratchFolder.resolveFile( "dir1/file1.txt" ); | |||||
file.create( FileType.FILE ); | |||||
assertEquals( FileType.FILE, file.getType() ); | |||||
FileObject folder = scratchFolder.resolveFile( "dir1/dir2" ); | |||||
folder.create( FileType.FOLDER ); | |||||
assertEquals( FileType.FOLDER, folder.getType() ); | |||||
// Attempt to create a file that already exists as a folder | |||||
try | |||||
{ | |||||
folder.create( FileType.FILE ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
} | |||||
// Attempt to create a folder that already exists as a file | |||||
try | |||||
{ | |||||
file.create( FileType.FOLDER ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
} | |||||
// Attempt to create a folder as a child of a file | |||||
FileObject folder2 = file.resolveFile( "some-child" ); | |||||
try | |||||
{ | |||||
folder2.create( FileType.FOLDER ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
} | |||||
} | |||||
/** | |||||
* Tests deletion | |||||
*/ | |||||
public void testDelete() throws Exception | |||||
{ | |||||
// Set-up the test structure | |||||
FileObject folder = createScratchFolder(); | |||||
folder.resolveFile( "file1.txt" ).create( FileType.FILE ); | |||||
folder.resolveFile( "emptydir" ).create( FileType.FOLDER ); | |||||
folder.resolveFile( "dir1/file1.txt" ).create( FileType.FILE ); | |||||
folder.resolveFile( "dir1/dir2/file2.txt" ).create( FileType.FILE ); | |||||
// Delete a file | |||||
FileObject file = folder.resolveFile( "file1.txt" ); | |||||
assertTrue( file.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
// Delete an empty folder | |||||
file = folder.resolveFile( "emptydir" ); | |||||
assertTrue( file.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
// Recursive delete | |||||
file = folder.resolveFile( "dir1" ); | |||||
FileObject file2 = file.resolveFile( "dir2/file2.txt" ); | |||||
assertTrue( file.exists() ); | |||||
assertTrue( file2.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
assertTrue( !file2.exists() ); | |||||
// Delete a file that does not exist | |||||
file = folder.resolveFile( "some-folder/some-file" ); | |||||
assertTrue( !file.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
} | |||||
/** | |||||
* Test that children are handled correctly by create and delete. | |||||
*/ | |||||
public void testListChildren() throws Exception | |||||
{ | |||||
FileObject folder = createScratchFolder(); | |||||
HashSet names = new HashSet(); | |||||
// Make sure the folder is empty | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
// Create a child folder | |||||
folder.resolveFile( "dir1" ).create( FileType.FOLDER ); | |||||
names.add( "dir1" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Create a child file | |||||
folder.resolveFile( "file1.html" ).create( FileType.FILE ); | |||||
names.add( "file1.html" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Create a descendent | |||||
folder.resolveFile( "dir2/file1.txt" ).create( FileType.FILE ); | |||||
names.add( "dir2" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Create a child file via an output stream | |||||
OutputStream outstr = folder.resolveFile( "file2.txt" ).getContent().getOutputStream(); | |||||
outstr.close(); | |||||
names.add( "file2.txt" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Delete a child folder | |||||
folder.resolveFile( "dir1" ).delete(); | |||||
names.remove( "dir1" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Delete a child file | |||||
folder.resolveFile( "file1.html" ).delete(); | |||||
names.remove( "file1.html" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Recreate the folder | |||||
folder.delete(); | |||||
folder.create( FileType.FOLDER ); | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
} | |||||
/** | |||||
* Ensures the names of a set of files match an expected set. | |||||
*/ | |||||
private void assertSameFileSet( Set names, FileObject[] files ) | |||||
{ | |||||
// Make sure the sets are the same length | |||||
assertEquals( names.size(), files.length ); | |||||
// Check for unexpected names | |||||
for( int i = 0; i < files.length; i++ ) | |||||
{ | |||||
FileObject file = files[ i ]; | |||||
assertTrue( names.contains( file.getName().getBaseName() ) ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,34 @@ | |||||
/* | |||||
* 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 java.io.File; | |||||
/** | |||||
* Tests for the Zip file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ZipFileSystemTest extends ReadOnlyFileSystemTestBase | |||||
{ | |||||
public ZipFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
String zipFileName = System.getProperty( "test.zip.file" ); | |||||
String zipFile = new File( zipFileName ).getAbsolutePath(); | |||||
String uri = "zip:" + zipFile + "!basedir"; | |||||
return uri; | |||||
} | |||||
} |
@@ -0,0 +1,587 @@ | |||||
/* | |||||
* 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 java.io.ByteArrayOutputStream; | |||||
import java.io.InputStream; | |||||
import java.io.Reader; | |||||
import java.io.StringWriter; | |||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import junit.framework.TestCase; | |||||
import org.apache.aut.vfs.provider.DefaultFileSystemManager; | |||||
/** | |||||
* File system test cases, which verifies the structure and naming | |||||
* functionality. | |||||
* | |||||
* Works from a base folder, and assumes a particular structure under | |||||
* that base folder. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class BasicFileSystemTestBase extends TestCase | |||||
{ | |||||
protected FileObject m_baseFolder; | |||||
protected DefaultFileSystemManager m_manager; | |||||
// Contents of "file1.txt" | |||||
private String m_charContent; | |||||
public BasicFileSystemTestBase( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Builds the expected folder structure. | |||||
*/ | |||||
private FileInfo buildExpectedStructure() | |||||
{ | |||||
// Build the expected structure | |||||
FileInfo base = new FileInfo( "test", FileType.FOLDER ); | |||||
base.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | |||||
base.addChild( new FileInfo( "empty.txt", FileType.FILE ) ); | |||||
base.addChild( new FileInfo( "emptydir", FileType.FOLDER ) ); | |||||
FileInfo dir = new FileInfo( "dir1", FileType.FOLDER ); | |||||
base.addChild( dir ); | |||||
dir.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | |||||
dir.addChild( new FileInfo( "file2.txt", FileType.FILE ) ); | |||||
dir.addChild( new FileInfo( "file3.txt", FileType.FILE ) ); | |||||
return base; | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected abstract String getBaseFolderURI(); | |||||
/** | |||||
* Sets up the test | |||||
*/ | |||||
protected void setUp() throws Exception | |||||
{ | |||||
// Create the file system manager | |||||
m_manager = new DefaultFileSystemManager(); | |||||
// Locate the base folder | |||||
m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | |||||
// Build the expected content of "file1.txt" | |||||
String eol = System.getProperty( "line.separator" ); | |||||
m_charContent = "This is a test file." + eol + "With 2 lines in it." + eol; | |||||
} | |||||
/** | |||||
* Tests resolution of absolute URI. | |||||
*/ | |||||
public void testAbsoluteURI() throws Exception | |||||
{ | |||||
// Try fetching base folder again by its URI | |||||
String uri = m_baseFolder.getName().getURI(); | |||||
FileObject file = m_manager.resolveFile( uri ); | |||||
assertSame( "file object", m_baseFolder, file ); | |||||
} | |||||
/** | |||||
* Tests resolution of relative file names via the FS manager | |||||
*/ | |||||
public void testRelativeURI() throws Exception | |||||
{ | |||||
// Build base dir | |||||
m_manager.setBaseFile( m_baseFolder ); | |||||
// Locate the base dir | |||||
FileObject file = m_manager.resolveFile( "." ); | |||||
assertSame( "file object", m_baseFolder, file ); | |||||
// Locate a child | |||||
file = m_manager.resolveFile( "some-child" ); | |||||
assertSame( "file object", m_baseFolder, file.getParent() ); | |||||
// Locate a descendent | |||||
file = m_manager.resolveFile( "some-folder/some-file" ); | |||||
assertSame( "file object", m_baseFolder, file.getParent().getParent() ); | |||||
// Locate parent | |||||
file = m_manager.resolveFile( ".." ); | |||||
assertSame( "file object", m_baseFolder.getParent(), file ); | |||||
} | |||||
/** | |||||
* Tests the root file name. | |||||
*/ | |||||
public void testRootFileName() throws Exception | |||||
{ | |||||
// Locate the root file | |||||
FileName rootName = m_baseFolder.getRoot().getName(); | |||||
// Test that the root path is "/" | |||||
assertEquals( "root path", "/", rootName.getPath() ); | |||||
// Test that the root basname is "" | |||||
assertEquals( "root base name", "", rootName.getBaseName() ); | |||||
// Test that the root name has no parent | |||||
assertNull( "root parent", rootName.getParent() ); | |||||
} | |||||
/** | |||||
* Tests child file names. | |||||
*/ | |||||
public void testChildName() throws Exception | |||||
{ | |||||
FileName baseName = m_baseFolder.getName(); | |||||
String basePath = baseName.getPath(); | |||||
FileName name = baseName.resolveName( "some-child", NameScope.CHILD ); | |||||
// Test path is absolute | |||||
assertTrue( "is absolute", basePath.startsWith( "/" ) ); | |||||
// Test base name | |||||
assertEquals( "base name", "some-child", name.getBaseName() ); | |||||
// Test absolute path | |||||
assertEquals( "absolute path", basePath + "/some-child", name.getPath() ); | |||||
// Test parent path | |||||
assertEquals( "parent absolute path", basePath, name.getParent().getPath() ); | |||||
// Try using a compound name to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( "a/b", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try using a empty name to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( "", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try using '.' to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( ".", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try using '..' to find a child | |||||
try | |||||
{ | |||||
FileName name2 = name.resolveName( "..", NameScope.CHILD ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
} | |||||
/** | |||||
* Checks that a relative name resolves to the expected absolute path. | |||||
*/ | |||||
private void assertSameName(String expectedPath, | |||||
FileName baseName, | |||||
String relName ) throws Exception | |||||
{ | |||||
FileName name = baseName.resolveName(relName); | |||||
assertEquals( expectedPath, name.getPath() ); | |||||
// Replace the separators | |||||
relName.replace('\\', '/'); | |||||
name = baseName.resolveName(relName); | |||||
assertEquals( expectedPath, name.getPath() ); | |||||
// And again | |||||
relName.replace('/', '\\'); | |||||
name = baseName.resolveName(relName); | |||||
assertEquals( expectedPath, name.getPath() ); | |||||
} | |||||
/** | |||||
* Tests relative name resolution, relative to the base folder. | |||||
*/ | |||||
public void testNameResolution() throws Exception | |||||
{ | |||||
FileName baseName = m_baseFolder.getName(); | |||||
String parentPath = baseName.getParent().getPath(); | |||||
String path = baseName.getPath(); | |||||
String childPath = path + "/some-child"; | |||||
// Test empty relative path | |||||
assertSameName( path, baseName, "" ); | |||||
// Test . relative path | |||||
assertSameName( path, baseName, "." ); | |||||
// Test ./ relative path | |||||
assertSameName( path, baseName, "./" ); | |||||
// Test .// relative path | |||||
assertSameName( path, baseName, ".//" ); | |||||
// Test .///.///. relative path | |||||
assertSameName( path, baseName, ".///.///." ); | |||||
assertSameName( path, baseName, "./\\/.\\//." ); | |||||
// Test <elem>/.. relative path | |||||
assertSameName( path, baseName, "a/.." ); | |||||
// Test .. relative path | |||||
assertSameName( parentPath, baseName, ".." ); | |||||
// Test ../ relative path | |||||
assertSameName( parentPath, baseName, "../" ); | |||||
// Test ..//./ relative path | |||||
assertSameName( parentPath, baseName, "..//./" ); | |||||
assertSameName( parentPath, baseName, "..//.\\" ); | |||||
// Test <elem>/../.. relative path | |||||
assertSameName( parentPath, baseName, "a/../.." ); | |||||
// Test <elem> relative path | |||||
assertSameName( childPath, baseName, "some-child" ); | |||||
// Test ./<elem> relative path | |||||
assertSameName( childPath, baseName, "./some-child" ); | |||||
// Test ./<elem>/ relative path | |||||
assertSameName( childPath, baseName, "./some-child/" ); | |||||
// Test <elem>/././././ relative path | |||||
assertSameName( childPath, baseName, "./some-child/././././" ); | |||||
// Test <elem>/../<elem> relative path | |||||
assertSameName( childPath, baseName, "a/../some-child" ); | |||||
// Test <elem>/<elem>/../../<elem> relative path | |||||
assertSameName( childPath, baseName, "a/b/../../some-child" ); | |||||
} | |||||
/** | |||||
* Tests relative name resolution, relative to the root file. | |||||
*/ | |||||
public void testNameResolutionRoot() throws Exception | |||||
{ | |||||
FileName rootName = m_baseFolder.getRoot().getName(); | |||||
} | |||||
/** | |||||
* Walks the folder structure, asserting it contains exactly the | |||||
* expected files and folders. | |||||
*/ | |||||
public void testStructure() throws Exception | |||||
{ | |||||
// Setup the structure | |||||
List queueExpected = new ArrayList(); | |||||
FileInfo baseInfo = buildExpectedStructure(); | |||||
queueExpected.add( baseInfo ); | |||||
List queueActual = new ArrayList(); | |||||
queueActual.add( m_baseFolder ); | |||||
while( queueActual.size() > 0 ) | |||||
{ | |||||
FileObject file = (FileObject)queueActual.remove( 0 ); | |||||
FileInfo info = (FileInfo)queueExpected.remove( 0 ); | |||||
// Check the type is correct | |||||
assertSame( file.getType(), info._type ); | |||||
if( info._type == FileType.FILE ) | |||||
{ | |||||
continue; | |||||
} | |||||
// Check children | |||||
FileObject[] children = file.getChildren(); | |||||
// Make sure all children were found | |||||
assertNotNull( children ); | |||||
assertEquals( "count children of \"" + file.getName() + "\"", info._children.size(), children.length ); | |||||
// Recursively check each child | |||||
for( int i = 0; i < children.length; i++ ) | |||||
{ | |||||
FileObject child = children[ i ]; | |||||
FileInfo childInfo = (FileInfo)info._children.get( child.getName().getBaseName() ); | |||||
// Make sure the child is expected | |||||
assertNotNull( childInfo ); | |||||
// Add to the queue of files to check | |||||
queueExpected.add( childInfo ); | |||||
queueActual.add( child ); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Tests existence determination. | |||||
*/ | |||||
public void testExists() throws Exception | |||||
{ | |||||
// Test a file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertTrue( "file exists", file.exists() ); | |||||
// Test a folder | |||||
file = m_baseFolder.resolveFile( "dir1" ); | |||||
assertTrue( "folder exists", file.exists() ); | |||||
// Test an unknown file | |||||
file = m_baseFolder.resolveFile( "unknown-child" ); | |||||
assertTrue( "unknown file does not exist", !file.exists() ); | |||||
// Test an unknown file in an unknown folder | |||||
file = m_baseFolder.resolveFile( "unknown-folder/unknown-child" ); | |||||
assertTrue( "unknown file does not exist", !file.exists() ); | |||||
} | |||||
/** | |||||
* Tests type determination. | |||||
*/ | |||||
public void testType() throws Exception | |||||
{ | |||||
// Test a file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertSame( FileType.FILE, file.getType() ); | |||||
// Test a folder | |||||
file = m_baseFolder.resolveFile( "dir1" ); | |||||
assertSame( FileType.FOLDER, file.getType() ); | |||||
// Test an unknown file | |||||
file = m_baseFolder.resolveFile( "unknown-child" ); | |||||
FileSystemException exc = null; | |||||
try | |||||
{ | |||||
file.getType(); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
exc = e; | |||||
} | |||||
assertNotNull( exc ); | |||||
} | |||||
/** | |||||
* Tests parent identity | |||||
*/ | |||||
public void testParent() throws FileSystemException | |||||
{ | |||||
// Test when both exist | |||||
FileObject folder = m_baseFolder.resolveFile( "dir1" ); | |||||
FileObject child = folder.resolveFile( "file3.txt" ); | |||||
assertTrue( "folder exists", folder.exists() ); | |||||
assertTrue( "child exists", child.exists() ); | |||||
assertSame( folder, child.getParent() ); | |||||
// Test when file does not exist | |||||
child = folder.resolveFile( "unknown-file" ); | |||||
assertTrue( "folder exists", folder.exists() ); | |||||
assertTrue( "child does not exist", !child.exists() ); | |||||
assertSame( folder, child.getParent() ); | |||||
// Test when neither exists | |||||
folder = m_baseFolder.resolveFile( "unknown-folder" ); | |||||
child = folder.resolveFile( "unknown-file" ); | |||||
assertTrue( "folder does not exist", !folder.exists() ); | |||||
assertTrue( "child does not exist", !child.exists() ); | |||||
assertSame( folder, child.getParent() ); | |||||
// Test root of the file system has no parent | |||||
FileObject root = m_baseFolder.getRoot(); | |||||
assertNull( "root has null parent", root.getParent() ); | |||||
} | |||||
/** | |||||
* Tests that children cannot be listed for non-folders. | |||||
*/ | |||||
public void testChildren() throws FileSystemException | |||||
{ | |||||
// Check for file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertSame( FileType.FILE, file.getType() ); | |||||
try | |||||
{ | |||||
file.getChildren(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Should be able to get child by name | |||||
file = file.resolveFile( "some-child" ); | |||||
assertNotNull( file ); | |||||
// Check for unknown file | |||||
file = m_baseFolder.resolveFile( "unknown-file" ); | |||||
assertTrue( !file.exists() ); | |||||
try | |||||
{ | |||||
file.getChildren(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Should be able to get child by name | |||||
FileObject child = file.resolveFile( "some-child" ); | |||||
assertNotNull( child ); | |||||
} | |||||
/** | |||||
* Tests content. | |||||
*/ | |||||
public void testContent() throws Exception | |||||
{ | |||||
// Test non-empty file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
FileContent content = file.getContent(); | |||||
assertSameContent(m_charContent, content); | |||||
// Test empty file | |||||
file = m_baseFolder.resolveFile( "empty.txt" ); | |||||
content = file.getContent(); | |||||
assertSameContent("", content); | |||||
} | |||||
/** | |||||
* Asserts that the content of a file is the same as expected. Checks the | |||||
* length reported by getSize() is correct, then reads the content as | |||||
* a byte stream, and as a char stream, and compares the result with | |||||
* the expected content. Assumes files are encoded using UTF-8. | |||||
*/ | |||||
public void assertSameContent( String expected, FileContent content ) throws Exception | |||||
{ | |||||
// Get file content as a binary stream | |||||
byte[] expectedBin = expected.getBytes( "utf-8" ); | |||||
// Check lengths | |||||
assertEquals( "same content length", expectedBin.length, content.getSize() ); | |||||
// Read content into byte array | |||||
InputStream instr = content.getInputStream(); | |||||
ByteArrayOutputStream outstr = new ByteArrayOutputStream(); | |||||
byte[] buffer = new byte[ 256 ]; | |||||
int nread = 0; | |||||
while( nread >= 0 ) | |||||
{ | |||||
outstr.write( buffer, 0, nread ); | |||||
nread = instr.read( buffer ); | |||||
} | |||||
// Compare | |||||
assertTrue( "same binary content", Arrays.equals( expectedBin, outstr.toByteArray() ) ); | |||||
} | |||||
/** | |||||
* Tests that folders and unknown files have no content. | |||||
*/ | |||||
public void testNoContent() throws Exception | |||||
{ | |||||
// Try getting the content of a folder | |||||
FileObject folder = m_baseFolder.resolveFile( "dir1" ); | |||||
try | |||||
{ | |||||
folder.getContent(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
// Try getting the content of an unknown file | |||||
FileObject unknownFile = m_baseFolder.resolveFile( "unknown-file" ); | |||||
FileContent content = unknownFile.getContent(); | |||||
try | |||||
{ | |||||
content.getInputStream(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
try | |||||
{ | |||||
content.getSize(); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException e ) | |||||
{ | |||||
} | |||||
} | |||||
/** | |||||
* Tests that content and file objects are usable after being closed. | |||||
*/ | |||||
public void testReuse() throws Exception | |||||
{ | |||||
// Get the test file | |||||
FileObject file = m_baseFolder.resolveFile( "file1.txt" ); | |||||
assertEquals( FileType.FILE, file.getType() ); | |||||
// Get the file content | |||||
FileContent content = file.getContent(); | |||||
assertSameContent( m_charContent, content ); | |||||
// Read the content again | |||||
content = file.getContent(); | |||||
assertSameContent( m_charContent, content ); | |||||
// Close the content + file | |||||
content.close(); | |||||
file.close(); | |||||
// Read the content again | |||||
content = file.getContent(); | |||||
assertSameContent( m_charContent, content ); | |||||
} | |||||
/** | |||||
* Info about a file. | |||||
*/ | |||||
private static final class FileInfo | |||||
{ | |||||
String _baseName; | |||||
FileType _type; | |||||
Map _children = new HashMap(); | |||||
public FileInfo( String name, FileType type ) | |||||
{ | |||||
_baseName = name; | |||||
_type = type; | |||||
} | |||||
/** Adds a child. */ | |||||
public void addChild( FileInfo child ) | |||||
{ | |||||
_children.put( child._baseName, child ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* Tests for FTP file systems. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class FtpFileSystemTest extends WritableFileSystemTestBase | |||||
{ | |||||
public FtpFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
return System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected String getWriteFolderURI() | |||||
{ | |||||
return System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||||
} | |||||
} |
@@ -0,0 +1,64 @@ | |||||
/* | |||||
* 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 java.io.File; | |||||
/** | |||||
* Tests for the local file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class LocalFileSystemTest extends WritableFileSystemTestBase | |||||
{ | |||||
private File m_baseDir; | |||||
public LocalFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
String baseDir = System.getProperty( "test.local.dir" ); | |||||
m_baseDir = new File( baseDir ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
String testDir = new File( m_baseDir, "read-tests" ).getAbsolutePath(); | |||||
String uri = "file:/" + testDir; | |||||
return uri; | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected String getWriteFolderURI() | |||||
{ | |||||
String testDir = new File( m_baseDir, "write-tests" ).getAbsolutePath(); | |||||
String uri = "file:/" + testDir; | |||||
return uri; | |||||
} | |||||
/** | |||||
* Tests resolution of an absolute file name. | |||||
*/ | |||||
public void testAbsoluteFileName() throws Exception | |||||
{ | |||||
// Locate file by absolute file name | |||||
String fileName = new File( "testdir" ).getAbsolutePath(); | |||||
FileObject absFile = m_manager.resolveFile( fileName ); | |||||
// Locate file by URI | |||||
String uri = "file://" + fileName.replace( File.separatorChar, '/' ); | |||||
FileObject uriFile = m_manager.resolveFile( uri ); | |||||
assertSame( "file object", absFile, uriFile ); | |||||
} | |||||
} |
@@ -0,0 +1,22 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* File system tests which check that a read-only file system cannot be | |||||
* changed. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class ReadOnlyFileSystemTestBase extends BasicFileSystemTestBase | |||||
{ | |||||
public ReadOnlyFileSystemTestBase( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* Tests for the SMB file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class SmbFileSystemTest extends WritableFileSystemTestBase | |||||
{ | |||||
public SmbFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
return System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected String getWriteFolderURI() | |||||
{ | |||||
return System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||||
} | |||||
} |
@@ -0,0 +1,255 @@ | |||||
/* | |||||
* 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 java.io.Writer; | |||||
import java.io.OutputStream; | |||||
import java.util.HashSet; | |||||
import java.util.Set; | |||||
/** | |||||
* File system test that check that a file system can be modified. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public abstract class WritableFileSystemTestBase extends BasicFileSystemTestBase | |||||
{ | |||||
public WritableFileSystemTestBase( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the area to do tests in. | |||||
*/ | |||||
protected abstract String getWriteFolderURI(); | |||||
/** | |||||
* Sets up a scratch folder for the test to use. | |||||
*/ | |||||
protected FileObject createScratchFolder() throws Exception | |||||
{ | |||||
FileObject scratchFolder = m_manager.resolveFile( getWriteFolderURI() ); | |||||
// Make sure the test folder is empty | |||||
scratchFolder.delete(); | |||||
scratchFolder.create( FileType.FOLDER ); | |||||
return scratchFolder; | |||||
} | |||||
/** | |||||
* Tests folder creation. | |||||
*/ | |||||
public void testFolderCreate() throws Exception | |||||
{ | |||||
FileObject scratchFolder = createScratchFolder(); | |||||
// Create direct child of the test folder | |||||
FileObject folder = scratchFolder.resolveFile( "dir1" ); | |||||
assertTrue( !folder.exists() ); | |||||
folder.create( FileType.FOLDER ); | |||||
assertTrue( folder.exists() ); | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
// Create a descendant, where the intermediate folders don't exist | |||||
folder = scratchFolder.resolveFile( "dir2/dir1/dir1" ); | |||||
assertTrue( !folder.exists() ); | |||||
assertTrue( !folder.getParent().exists() ); | |||||
assertTrue( !folder.getParent().getParent().exists() ); | |||||
folder.create( FileType.FOLDER ); | |||||
assertTrue( folder.exists() ); | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
assertTrue( folder.getParent().exists() ); | |||||
assertTrue( folder.getParent().getParent().exists() ); | |||||
// Test creating a folder that already exists | |||||
folder.create( FileType.FOLDER ); | |||||
} | |||||
/** | |||||
* Tests file creation | |||||
*/ | |||||
public void testFileCreate() throws Exception | |||||
{ | |||||
FileObject scratchFolder = createScratchFolder(); | |||||
// Create direct child of the test folder | |||||
FileObject file = scratchFolder.resolveFile( "file1.txt" ); | |||||
assertTrue( !file.exists() ); | |||||
file.create( FileType.FILE ); | |||||
assertTrue( file.exists() ); | |||||
assertEquals( 0, file.getContent().getSize() ); | |||||
// Create a descendant, where the intermediate folders don't exist | |||||
file = scratchFolder.resolveFile( "dir1/dir1/file1.txt" ); | |||||
assertTrue( !file.exists() ); | |||||
assertTrue( !file.getParent().exists() ); | |||||
assertTrue( !file.getParent().getParent().exists() ); | |||||
file.create( FileType.FILE ); | |||||
assertTrue( file.exists() ); | |||||
assertEquals( 0, file.getContent().getSize() ); | |||||
assertTrue( file.getParent().exists() ); | |||||
assertTrue( file.getParent().getParent().exists() ); | |||||
// Test creating a file that already exists | |||||
file.create( FileType.FILE ); | |||||
} | |||||
/** | |||||
* Tests file/folder creation with mismatched types. | |||||
*/ | |||||
public void testFileCreateMismatched() throws Exception | |||||
{ | |||||
FileObject scratchFolder = createScratchFolder(); | |||||
// Create a test file and folder | |||||
FileObject file = scratchFolder.resolveFile( "dir1/file1.txt" ); | |||||
file.create( FileType.FILE ); | |||||
assertEquals( FileType.FILE, file.getType() ); | |||||
FileObject folder = scratchFolder.resolveFile( "dir1/dir2" ); | |||||
folder.create( FileType.FOLDER ); | |||||
assertEquals( FileType.FOLDER, folder.getType() ); | |||||
// Attempt to create a file that already exists as a folder | |||||
try | |||||
{ | |||||
folder.create( FileType.FILE ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
} | |||||
// Attempt to create a folder that already exists as a file | |||||
try | |||||
{ | |||||
file.create( FileType.FOLDER ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
} | |||||
// Attempt to create a folder as a child of a file | |||||
FileObject folder2 = file.resolveFile( "some-child" ); | |||||
try | |||||
{ | |||||
folder2.create( FileType.FOLDER ); | |||||
assertTrue( false ); | |||||
} | |||||
catch( FileSystemException exc ) | |||||
{ | |||||
} | |||||
} | |||||
/** | |||||
* Tests deletion | |||||
*/ | |||||
public void testDelete() throws Exception | |||||
{ | |||||
// Set-up the test structure | |||||
FileObject folder = createScratchFolder(); | |||||
folder.resolveFile( "file1.txt" ).create( FileType.FILE ); | |||||
folder.resolveFile( "emptydir" ).create( FileType.FOLDER ); | |||||
folder.resolveFile( "dir1/file1.txt" ).create( FileType.FILE ); | |||||
folder.resolveFile( "dir1/dir2/file2.txt" ).create( FileType.FILE ); | |||||
// Delete a file | |||||
FileObject file = folder.resolveFile( "file1.txt" ); | |||||
assertTrue( file.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
// Delete an empty folder | |||||
file = folder.resolveFile( "emptydir" ); | |||||
assertTrue( file.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
// Recursive delete | |||||
file = folder.resolveFile( "dir1" ); | |||||
FileObject file2 = file.resolveFile( "dir2/file2.txt" ); | |||||
assertTrue( file.exists() ); | |||||
assertTrue( file2.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
assertTrue( !file2.exists() ); | |||||
// Delete a file that does not exist | |||||
file = folder.resolveFile( "some-folder/some-file" ); | |||||
assertTrue( !file.exists() ); | |||||
file.delete(); | |||||
assertTrue( !file.exists() ); | |||||
} | |||||
/** | |||||
* Test that children are handled correctly by create and delete. | |||||
*/ | |||||
public void testListChildren() throws Exception | |||||
{ | |||||
FileObject folder = createScratchFolder(); | |||||
HashSet names = new HashSet(); | |||||
// Make sure the folder is empty | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
// Create a child folder | |||||
folder.resolveFile( "dir1" ).create( FileType.FOLDER ); | |||||
names.add( "dir1" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Create a child file | |||||
folder.resolveFile( "file1.html" ).create( FileType.FILE ); | |||||
names.add( "file1.html" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Create a descendent | |||||
folder.resolveFile( "dir2/file1.txt" ).create( FileType.FILE ); | |||||
names.add( "dir2" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Create a child file via an output stream | |||||
OutputStream outstr = folder.resolveFile( "file2.txt" ).getContent().getOutputStream(); | |||||
outstr.close(); | |||||
names.add( "file2.txt" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Delete a child folder | |||||
folder.resolveFile( "dir1" ).delete(); | |||||
names.remove( "dir1" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Delete a child file | |||||
folder.resolveFile( "file1.html" ).delete(); | |||||
names.remove( "file1.html" ); | |||||
assertSameFileSet( names, folder.getChildren() ); | |||||
// Recreate the folder | |||||
folder.delete(); | |||||
folder.create( FileType.FOLDER ); | |||||
assertEquals( 0, folder.getChildren().length ); | |||||
} | |||||
/** | |||||
* Ensures the names of a set of files match an expected set. | |||||
*/ | |||||
private void assertSameFileSet( Set names, FileObject[] files ) | |||||
{ | |||||
// Make sure the sets are the same length | |||||
assertEquals( names.size(), files.length ); | |||||
// Check for unexpected names | |||||
for( int i = 0; i < files.length; i++ ) | |||||
{ | |||||
FileObject file = files[ i ]; | |||||
assertTrue( names.contains( file.getName().getBaseName() ) ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,34 @@ | |||||
/* | |||||
* 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 java.io.File; | |||||
/** | |||||
* Tests for the Zip file system. | |||||
* | |||||
* @author Adam Murdoch | |||||
*/ | |||||
public class ZipFileSystemTest extends ReadOnlyFileSystemTestBase | |||||
{ | |||||
public ZipFileSystemTest( String name ) | |||||
{ | |||||
super( name ); | |||||
} | |||||
/** | |||||
* Returns the URI for the base folder. | |||||
*/ | |||||
protected String getBaseFolderURI() | |||||
{ | |||||
String zipFileName = System.getProperty( "test.zip.file" ); | |||||
String zipFile = new File( zipFileName ).getAbsolutePath(); | |||||
String uri = "zip:" + zipFile + "!basedir"; | |||||
return uri; | |||||
} | |||||
} |