diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java
index 2c1f58686..a265c0f4b 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java
@@ -67,9 +67,8 @@ public interface FileName
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.
+ * Resolves a name, relative to the file. Refer to {@link NameScope}
+ * for a description of how names are resolved.
*
* @param name
* The path to resolve.
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java
index 46545b84f..fc5a720ef 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java
@@ -123,9 +123,8 @@ public interface FileObject
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.
+ * Finds a file, relative to this file. Refer to {@link NameScope}
+ * for a description of how names are resolved in the different scopes.
*
* @param name
* The name to resolve.
@@ -207,7 +206,7 @@ public interface FileObject
*
* @see FileContent#close
*
- * @throws FileSystemEception
+ * @throws FileSystemException
* On error closing the file.
*/
void close() throws FileSystemException;
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java
index 3e339f18c..ffd61771d 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java
@@ -16,14 +16,19 @@ package org.apache.aut.vfs;
public final class NameScope
{
/**
- * Resolve against the children of the base file.
- *
- *
The supplied name must be a valid element name. That is, it may
- * not be empty, or .
, or ..
, or contain any
- * separator characters.
+ * Resolve against the children of the base file. The name is resolved
+ * as described by {@link #FILE_SYSTEM}. However, an exception is
+ * thrown if the resolved file is not a direct child of the base file.
*/
public final static NameScope CHILD = new NameScope( "child" );
+ /**
+ * Resolve against the descendents of the base file. The name is resolved
+ * as described by {@link #FILE_SYSTEM}. However, an exception is thrown
+ * if the resolved file is not a descendent of the base file.
+ */
+ public final static NameScope DESCENDENT = new NameScope( "descendent" );
+
/**
* Resolve against files in the same file system as the base file.
*
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java
index 6f169f142..abb9a174b 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java
@@ -18,22 +18,39 @@ import org.apache.aut.vfs.NameScope;
*/
public class DefaultFileName implements FileName
{
- private UriParser m_parser;
- private String m_rootPrefix;
- private String m_absPath;
+ private final UriParser m_parser;
+ private final String m_rootPrefix;
+ private final String m_absPath;
// Cached stuff
private String m_uri;
private String m_baseName;
- public DefaultFileName( UriParser parser, String rootPrefix, String absPath )
+ public DefaultFileName( final UriParser parser,
+ final String rootPrefix,
+ final String absPath )
{
m_parser = parser;
m_rootPrefix = rootPrefix;
m_absPath = absPath;
}
- // TODO - make these usable as hash keys
+ /**
+ * Returns the hashcode for this name.
+ */
+ public int hashCode()
+ {
+ return ( m_rootPrefix.hashCode() ^ m_absPath.hashCode() );
+ }
+
+ /**
+ * Determines if this object is equal to another.
+ */
+ public boolean equals( final Object obj )
+ {
+ final DefaultFileName name = (DefaultFileName)obj;
+ return ( m_rootPrefix.equals( name.m_rootPrefix ) && m_absPath.equals( m_absPath ) );
+ }
/**
* Returns the URI of the file.
@@ -67,22 +84,12 @@ public class DefaultFileName implements FileName
/**
* Returns the name of a child of the file.
*/
- public FileName resolveName( String name, NameScope scope ) throws FileSystemException
+ public FileName resolveName( final String name,
+ final 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();
- }
+ final String absPath = m_parser.resolvePath( m_absPath, name, scope );
+ return new DefaultFileName( m_parser, m_rootPrefix, absPath );
}
/**
@@ -90,7 +97,7 @@ public class DefaultFileName implements FileName
*/
public FileName getParent()
{
- String parentPath = m_parser.getParentPath( m_absPath );
+ final String parentPath = m_parser.getParentPath( m_absPath );
if( parentPath == null )
{
return null;
@@ -104,7 +111,7 @@ public class DefaultFileName implements FileName
* 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
+ public FileName resolveName( final String path ) throws FileSystemException
{
return resolveName( path, NameScope.FILE_SYSTEM );
}
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties
index 1d2d95d7b..82b2cd6dd 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties
@@ -37,3 +37,4 @@ 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}".
+invalid-descendent-name.error=Invalid descendent file name "{0}".
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java
index fbba9e791..a45cba92c 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java
@@ -10,6 +10,7 @@ package org.apache.aut.vfs.provider;
import java.util.HashSet;
import java.util.Iterator;
import org.apache.aut.vfs.FileSystemException;
+import org.apache.aut.vfs.NameScope;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
@@ -24,14 +25,14 @@ public class UriParser
ResourceManager.getPackageResources( UriParser.class );
/** The normalised separator to use. */
- private char m_separatorChar;
- private String m_separator;
+ private final char m_separatorChar;
+ private final String m_separator;
/**
* The set of valid separators. These are all converted to the normalised one.
* Does not contain the normalised separator
*/
- private char[] m_separators;
+ private final char[] m_separators;
/**
* Creates a parser, using '/' and '\' as the path separators.
@@ -49,12 +50,12 @@ public class UriParser
* Additional legal separator characters. Any occurrences of
* these in paths are replaced with the separator char.
*/
- protected UriParser( char[] separators )
+ protected UriParser( final char[] separators )
{
m_separatorChar = '/';
// Remove the separator char from the separators array
- HashSet set = new HashSet();
+ final HashSet set = new HashSet();
set.add( new Character( '\\' ) );
if( separators != null )
{
@@ -69,10 +70,10 @@ public class UriParser
}
}
m_separators = new char[ set.size() ];
- Iterator iter = set.iterator();
+ final Iterator iter = set.iterator();
for( int i = 0; i < m_separators.length; i++ )
{
- Character ch = (Character)iter.next();
+ final Character ch = (Character)iter.next();
m_separators[ i ] = ch.charValue();
}
@@ -86,9 +87,9 @@ public class UriParser
*
*
Sub-classes should override this method.
*/
- public ParsedUri parseUri( String uriStr ) throws FileSystemException
+ public ParsedUri parseUri( final String uriStr ) throws FileSystemException
{
- ParsedUri retval = new ParsedUri();
+ final ParsedUri retval = new ParsedUri();
parseGenericUri( uriStr, retval );
return retval;
}
@@ -109,9 +110,11 @@ public class UriParser
* @param uri
* Used to return the parsed components of the URI.
*/
- protected void parseGenericUri( String uriStr, ParsedUri uri ) throws FileSystemException
+ protected void parseGenericUri( final String uriStr,
+ final ParsedUri uri )
+ throws FileSystemException
{
- StringBuffer name = new StringBuffer();
+ final StringBuffer name = new StringBuffer();
// Extract the scheme and authority parts
extractToPath( uriStr, name, uri );
@@ -121,7 +124,7 @@ public class UriParser
uri.setPath( name.toString() );
// Build the root uri
- StringBuffer rootUri = new StringBuffer();
+ final StringBuffer rootUri = new StringBuffer();
rootUri.append( uri.getScheme() );
rootUri.append( "://" );
rootUri.append( uri.getHostName() );
@@ -141,11 +144,13 @@ public class UriParser
* @parsedUri
* Used to return the extracted components.
*/
- protected void extractToPath( String uri, StringBuffer name, ParsedUri parsedUri )
+ protected void extractToPath( final String uri,
+ final StringBuffer name,
+ final ParsedUri parsedUri )
throws FileSystemException
{
// Extract the scheme
- String scheme = extractScheme( uri, name );
+ final String scheme = extractScheme( uri, name );
parsedUri.setScheme( scheme );
// Expecting "//"
@@ -157,11 +162,11 @@ public class UriParser
name.delete( 0, 2 );
// Extract userinfo
- String userInfo = extractUserInfo( name );
+ final String userInfo = extractUserInfo( name );
parsedUri.setUserInfo( userInfo );
// Extract hostname
- String hostName = extractHostName( name );
+ final String hostName = extractHostName( name );
if( hostName == null )
{
final String message = REZ.getString( "missing-hostname.error", uri );
@@ -170,7 +175,7 @@ public class UriParser
parsedUri.setHostName( hostName );
// Extract port
- String port = extractPort( name );
+ final String port = extractPort( name );
if( port != null && port.length() == 0 )
{
final String message = REZ.getString( "missing-port.error", uri );
@@ -190,12 +195,12 @@ public class UriParser
* Extracts the user info from a URI. The :// part has been removed
* already.
*/
- protected String extractUserInfo( StringBuffer name )
+ protected String extractUserInfo( final StringBuffer name )
{
- int maxlen = name.length();
+ final int maxlen = name.length();
for( int pos = 0; pos < maxlen; pos++ )
{
- char ch = name.charAt( pos );
+ final char ch = name.charAt( pos );
if( ch == '@' )
{
// Found the end of the user info
@@ -218,13 +223,13 @@ public class UriParser
* Extracts the hostname from a URI. The ://@ part has
* been removed.
*/
- protected String extractHostName( StringBuffer name )
+ protected String extractHostName( final StringBuffer name )
{
- int maxlen = name.length();
+ final int maxlen = name.length();
int pos = 0;
for( ; pos < maxlen; pos++ )
{
- char ch = name.charAt( pos );
+ final char ch = name.charAt( pos );
if( ch == '/' || ch == ';' || ch == '?' || ch == ':'
|| ch == '@' || ch == '&' || ch == '=' || ch == '+'
|| ch == '$' || ch == ',' )
@@ -237,7 +242,7 @@ public class UriParser
return null;
}
- String hostname = name.substring( 0, pos );
+ final String hostname = name.substring( 0, pos );
name.delete( 0, pos );
return hostname;
}
@@ -246,23 +251,25 @@ public class UriParser
* Extracts the port from a URI. The ://@
* part has been removed.
*/
- protected String extractPort( StringBuffer name )
+ protected String extractPort( final StringBuffer name )
{
if( name.length() < 1 || name.charAt( 0 ) != ':' )
{
return null;
}
- int maxlen = name.length();
+
+ final int maxlen = name.length();
int pos = 1;
for( ; pos < maxlen; pos++ )
{
- char ch = name.charAt( pos );
+ final char ch = name.charAt( pos );
if( ch < '0' || ch > '9' )
{
break;
}
}
- String port = name.substring( 1, pos );
+
+ final String port = name.substring( 1, pos );
name.delete( 0, pos );
return port;
}
@@ -270,9 +277,9 @@ public class UriParser
/**
* Extracts the first element of a path.
*/
- protected String extractFirstElement( StringBuffer name )
+ protected String extractFirstElement( final StringBuffer name )
{
- int len = name.length();
+ final int len = name.length();
if( len < 1 )
{
return null;
@@ -287,14 +294,14 @@ public class UriParser
if( name.charAt( pos ) == m_separatorChar )
{
// Found a separator
- String elem = name.substring( startPos, pos );
+ final String elem = name.substring( startPos, pos );
name.delete( startPos, pos + 1 );
return elem;
}
}
// No separator
- String elem = name.substring( startPos );
+ final String elem = name.substring( startPos );
name.setLength( 0 );
return elem;
}
@@ -308,10 +315,11 @@ public class UriParser
* @param path
* A normalised path.
*/
- public String getUri( String rootUri, String path )
+ public String getUri( final String rootUri,
+ final String path )
{
- StringBuffer uri = new StringBuffer( rootUri );
- int len = uri.length();
+ final StringBuffer uri = new StringBuffer( rootUri );
+ final int len = uri.length();
if( uri.charAt( len - 1 ) == m_separatorChar )
{
uri.delete( len - 1, len );
@@ -330,9 +338,9 @@ public class UriParser
* @param path
* A normalised path.
*/
- public String getBaseName( String path )
+ public String getBaseName( final String path )
{
- int idx = path.lastIndexOf( m_separatorChar );
+ final int idx = path.lastIndexOf( m_separatorChar );
if( idx == -1 )
{
return path;
@@ -353,9 +361,11 @@ public class UriParser
* does need to be a path (i.e. not an absolute URI).
*
*/
- public String resolvePath( String basePath, String path ) throws FileSystemException
+ public String resolvePath( final String basePath,
+ final String path )
+ throws FileSystemException
{
- StringBuffer buffer = new StringBuffer( path );
+ final StringBuffer buffer = new StringBuffer( path );
// Adjust separators
fixSeparators( buffer );
@@ -374,47 +384,52 @@ public class UriParser
}
/**
- * Returns a child path.
+ * Resolved a name, relative to a base file.
*
- * @param parent
+ * @param baseFile
* A normalised path.
*
- * @param name
- * The child name. Must be a valid element name (i.e. no separators, etc).
+ * @param path
+ * The path to resolve.
+ *
+ * @param scope
+ * The scope to resolve and validate the name in.
*/
- public String getChildPath( String parent, String name ) throws FileSystemException
+ public String resolvePath( final String baseFile,
+ final String path,
+ final NameScope scope )
+ 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 );
+ final String resolvedPath = resolvePath( baseFile, path );
+ if( scope == NameScope.CHILD )
+ {
+ final int baseLen = baseFile.length();
+ if( ! resolvedPath.startsWith( baseFile )
+ || resolvedPath.length() == baseLen
+ || resolvedPath.charAt( baseLen ) != m_separatorChar
+ || resolvedPath.indexOf( m_separatorChar, baseLen + 1 ) != -1 )
+ {
+ final String message = REZ.getString( "invalid-childname.error", path );
+ throw new FileSystemException( message );
+ }
}
- for( int i = 0; i < m_separators.length; i++ )
+ else if( scope == NameScope.DESCENDENT )
{
- char separator = m_separators[ i ];
- if( name.indexOf( separator ) != -1 )
+ final int baseLen = baseFile.length();
+ if( ! resolvedPath.startsWith( baseFile )
+ || resolvedPath.length() == baseLen
+ || resolvedPath.charAt( baseLen ) != m_separatorChar )
{
- final String message = REZ.getString( "invalid-childname.error", name );
+ final String message = REZ.getString( "invalid-descendent-name.error", path );
throw new FileSystemException( message );
}
}
-
- if( parent.endsWith( m_separator ) )
+ else if( scope != NameScope.FILE_SYSTEM )
{
- // Either root, or the parent name already ends with the separator
- return parent + name;
+ throw new IllegalArgumentException();
}
- return parent + m_separatorChar + name;
+
+ return resolvedPath;
}
/**
@@ -423,9 +438,9 @@ public class UriParser
* @param path
* A normalised path.
*/
- public String getParentPath( String path )
+ public String getParentPath( final String path )
{
- int idx = path.lastIndexOf( m_separatorChar );
+ final int idx = path.lastIndexOf( m_separatorChar );
if( idx == -1 || idx == path.length() - 1 )
{
// No parent
@@ -448,7 +463,7 @@ public class UriParser
* Removes trailing separator.
*
*/
- public void normalisePath( StringBuffer path ) throws FileSystemException
+ public void normalisePath( final StringBuffer path ) throws FileSystemException
{
if( path.length() == 0 )
{
@@ -480,7 +495,7 @@ public class UriParser
{
}
- int elemLen = endElem - startElem;
+ final int elemLen = endElem - startElem;
if( elemLen == 0 )
{
// An empty element - axe it
@@ -503,11 +518,8 @@ public class UriParser
if( startElem > startFirstElem )
{
int pos = startElem - 2;
- char ch = path.charAt( pos );
- while( ch != m_separatorChar )
+ for( ; pos >= 0 && path.charAt( pos ) != m_separatorChar; pos -- )
{
- pos--;
- ch = path.charAt( pos );
}
startElem = pos + 1;
}
@@ -521,7 +533,7 @@ public class UriParser
}
// Remove trailing separator
- if( path.charAt( maxlen - 1 ) == m_separatorChar && maxlen > 1 )
+ if( maxlen > 0 && path.charAt( maxlen - 1 ) == m_separatorChar && maxlen > 1 )
{
path.delete( maxlen - 1, maxlen );
}
@@ -530,7 +542,7 @@ public class UriParser
/**
* Adjusts the separators in a name.
*/
- protected boolean fixSeparators( StringBuffer name )
+ protected boolean fixSeparators( final StringBuffer name )
{
if( m_separators.length == 0 )
{
@@ -539,10 +551,10 @@ public class UriParser
}
boolean changed = false;
- int maxlen = name.length();
+ final int maxlen = name.length();
for( int i = 0; i < maxlen; i++ )
{
- char ch = name.charAt( i );
+ final char ch = name.charAt( i );
for( int j = 0; j < m_separators.length; j++ )
{
char separator = m_separators[ j ];
@@ -566,7 +578,7 @@ public class UriParser
* @return
* The scheme name. Returns null if there is no scheme.
*/
- public static String extractScheme( String uri )
+ public static String extractScheme( final String uri )
{
return extractScheme( uri, null );
}
@@ -583,7 +595,7 @@ public class UriParser
* @return
* The scheme name. Returns null if there is no scheme.
*/
- protected static String extractScheme( String uri, StringBuffer buffer )
+ protected static String extractScheme( final String uri, final StringBuffer buffer )
{
if( buffer != null )
{
@@ -591,15 +603,15 @@ public class UriParser
buffer.append( uri );
}
- int maxPos = uri.length();
+ final int maxPos = uri.length();
for( int pos = 0; pos < maxPos; pos++ )
{
- char ch = uri.charAt( pos );
+ final char ch = uri.charAt( pos );
if( ch == ':' )
{
// Found the end of the scheme
- String scheme = uri.substring( 0, pos );
+ final String scheme = uri.substring( 0, pos );
if( buffer != null )
{
buffer.delete( 0, pos + 1 );
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/GenericFileNameParser.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/GenericFileNameParser.java
new file mode 100644
index 000000000..042c2451e
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/GenericFileNameParser.java
@@ -0,0 +1,44 @@
+/*
+* 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.FileSystemException;
+import org.apache.avalon.excalibur.i18n.ResourceManager;
+import org.apache.avalon.excalibur.i18n.Resources;
+
+/**
+ * A general-purpose file name parser.
+ *
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
+ */
+class GenericFileNameParser
+ extends LocalFileNameParser
+{
+ private final static Resources REZ
+ = ResourceManager.getPackageResources( GenericFileNameParser.class );
+
+ /**
+ * Pops the root prefix off a URI, which has had the scheme removed.
+ */
+ protected String extractRootPrefix( final String uri,
+ final StringBuffer name )
+ throws FileSystemException
+ {
+ // TODO - this class isn't generic at all. Need to fix this
+
+ // Looking for
+ if( name.length() == 0 || name.charAt( 0 ) != '/' )
+ {
+ final String message = REZ.getString( "not-absolute-file-name.error", uri );
+ throw new FileSystemException( message );
+ }
+
+ return "/";
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java
index 3a59556be..f9fe1c4b0 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java
@@ -23,7 +23,8 @@ import org.apache.avalon.excalibur.i18n.Resources;
/**
* A file object implementation which uses direct file access.
*
- * @author Adam Murdoch
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
*/
final class LocalFile
extends AbstractFileObject
@@ -33,7 +34,7 @@ final class LocalFile
ResourceManager.getPackageResources( LocalFile.class );
private File m_file;
- private String m_fileName;
+ private final String m_fileName;
/**
* Creates a non-root file.
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java
index dfa5417d0..d55db0f27 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java
@@ -11,26 +11,19 @@ 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.ResourceManager;
-import org.apache.avalon.excalibur.i18n.Resources;
/**
* A name parser.
*
- * @author Adam Murdoch
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
*/
-class LocalFileNameParser
+abstract class LocalFileNameParser
extends UriParser
{
- private final static 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 );
}
/**
@@ -91,141 +84,8 @@ class LocalFileNameParser
/**
* Pops the root prefix off a URI, which has had the scheme removed.
*/
- private String extractRootPrefix( final String uri,
- final StringBuffer name )
- throws FileSystemException
- {
- // TODO - split this into sub-classes
- if( m_windowsNames )
- {
- return extractWindowsRootPrefix( uri, name );
- }
- else
- {
- // Looking for
- 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( final String uri,
- final StringBuffer name )
- throws FileSystemException
- {
- // Looking for:
- // ('/'){0, 3} ':' '/'
- // ['/'] '//' '/' ( '/' | )
+ protected abstract String extractRootPrefix( final String uri,
+ final StringBuffer name )
+ throws FileSystemException;
- // 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( final StringBuffer name )
- throws FileSystemException
- {
- // Looking for ':' '/'
- 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( final String uri,
- final StringBuffer name )
- throws FileSystemException
- {
- // Looking for '/' ( '/' | )
-
- // 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 '/'
- 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 '/' ( '/' | )
- String prefix = name.substring( 0, pos );
- name.delete( 0, pos );
- return prefix;
- }
}
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java
index 79ffc4bfc..2c8712f0a 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java
@@ -17,7 +17,8 @@ import org.apache.aut.vfs.provider.FileSystem;
/**
* A local file system.
*
- * @author Adam Murdoch
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
*/
class LocalFileSystem extends AbstractFileSystem implements FileSystem
{
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java
index 7cd1ef5ff..694f3a0a8 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java
@@ -8,6 +8,7 @@
package org.apache.aut.vfs.provider.local;
import java.io.File;
+import org.apache.aut.nativelib.Os;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystemProvider;
@@ -19,12 +20,25 @@ import org.apache.aut.vfs.provider.ParsedUri;
/**
* A file system provider, which uses direct file access.
*
- * @author Adam Murdoch
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
*/
public class LocalFileSystemProvider extends AbstractFileSystemProvider
implements FileSystemProvider
{
- private final LocalFileNameParser m_parser = new LocalFileNameParser();
+ private final LocalFileNameParser m_parser;
+
+ public LocalFileSystemProvider()
+ {
+ if( Os.isFamily( Os.OS_FAMILY_WINDOWS ) )
+ {
+ m_parser = new WindowsFileNameParser();
+ }
+ else
+ {
+ m_parser = new GenericFileNameParser();
+ }
+ }
/**
* Determines if a name is an absolute file name.
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java
index 90df6d2bf..7a3be01a1 100644
--- a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java
@@ -12,9 +12,10 @@ import org.apache.aut.vfs.provider.ParsedUri;
/**
* A parsed file URI.
*
- * @author Adam Murdoch
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
*/
-public class ParsedFileUri extends ParsedUri
+class ParsedFileUri extends ParsedUri
{
private String m_rootFile;
@@ -23,7 +24,7 @@ public class ParsedFileUri extends ParsedUri
return m_rootFile;
}
- public void setRootFile( String rootPrefix )
+ public void setRootFile( final String rootPrefix )
{
m_rootFile = rootPrefix;
}
diff --git a/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/WindowsFileNameParser.java b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/WindowsFileNameParser.java
new file mode 100644
index 000000000..1d2931b08
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/WindowsFileNameParser.java
@@ -0,0 +1,150 @@
+/*
+ * 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.FileSystemException;
+import org.apache.avalon.excalibur.i18n.ResourceManager;
+import org.apache.avalon.excalibur.i18n.Resources;
+
+/**
+ * A parser for Windows file names.
+ *
+ * @author Adam Murdoch
+ * @version $Revision$ $Date$
+ */
+class WindowsFileNameParser
+ extends LocalFileNameParser
+{
+ private final static Resources REZ
+ = ResourceManager.getPackageResources( WindowsFileNameParser.class );
+
+ /**
+ * Pops the root prefix off a URI, which has had the scheme removed.
+ */
+ protected String extractRootPrefix( final String uri,
+ final StringBuffer name )
+ throws FileSystemException
+ {
+ return extractWindowsRootPrefix( uri, name );
+ }
+
+ /**
+ * Extracts a Windows root prefix from a name.
+ */
+ private String extractWindowsRootPrefix( final String uri,
+ final StringBuffer name )
+ throws FileSystemException
+ {
+ // Looking for:
+ // ('/'){0, 3} ':' '/'
+ // ['/'] '//' '/' ( '/' | )
+
+ // 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( final StringBuffer name )
+ throws FileSystemException
+ {
+ // Looking for ':' '/'
+ 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( final String uri,
+ final StringBuffer name )
+ throws FileSystemException
+ {
+ // Looking for '/' ( '/' | )
+
+ // 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 '/'
+ 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 '/' ( '/' | )
+ String prefix = name.substring( 0, pos );
+ name.delete( 0, pos );
+ return prefix;
+ }
+}
diff --git a/proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java b/proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java
index 5660f5afc..a48f7a139 100644
--- a/proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java
+++ b/proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java
@@ -15,6 +15,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.aut.vfs.impl.DefaultFileSystemManager;
+import org.apache.aut.vfs.provider.AbstractFileObject;
+import org.apache.avalon.excalibur.i18n.ResourceManager;
+import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.myrmidon.AbstractMyrmidonTest;
/**
@@ -29,6 +32,9 @@ import org.apache.myrmidon.AbstractMyrmidonTest;
public abstract class AbstractFileSystemTest
extends AbstractMyrmidonTest
{
+ private final static Resources REZ
+ = ResourceManager.getPackageResources( AbstractFileObject.class );
+
protected FileObject m_baseFolder;
protected DefaultFileSystemManager m_manager;
@@ -46,12 +52,12 @@ public abstract class AbstractFileSystemTest
private FileInfo buildExpectedStructure()
{
// Build the expected structure
- FileInfo base = new FileInfo( "test", FileType.FOLDER );
+ final 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 );
+ final 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 ) );
@@ -76,7 +82,7 @@ public abstract class AbstractFileSystemTest
m_baseFolder = m_manager.resolveFile( getBaseFolderURI() );
// Build the expected content of "file1.txt"
- String eol = System.getProperty( "line.separator" );
+ final String eol = System.getProperty( "line.separator" );
m_charContent = "This is a test file." + eol + "With 2 lines in it." + eol;
}
@@ -86,8 +92,8 @@ public abstract class AbstractFileSystemTest
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 );
+ final String uri = m_baseFolder.getName().getURI();
+ final FileObject file = m_manager.resolveFile( uri );
assertSame( "file object", m_baseFolder, file );
}
@@ -123,7 +129,7 @@ public abstract class AbstractFileSystemTest
public void testRootFileName() throws Exception
{
// Locate the root file
- FileName rootName = m_baseFolder.getRoot().getName();
+ final FileName rootName = m_baseFolder.getRoot().getName();
// Test that the root path is "/"
assertEquals( "root path", "/", rootName.getPath() );
@@ -140,9 +146,9 @@ public abstract class AbstractFileSystemTest
*/
public void testChildName() throws Exception
{
- FileName baseName = m_baseFolder.getName();
- String basePath = baseName.getPath();
- FileName name = baseName.resolveName( "some-child", NameScope.CHILD );
+ final FileName baseName = m_baseFolder.getName();
+ final String basePath = baseName.getPath();
+ final FileName name = baseName.resolveName( "some-child", NameScope.CHILD );
// Test path is absolute
assertTrue( "is absolute", basePath.startsWith( "/" ) );
@@ -157,76 +163,95 @@ public abstract class AbstractFileSystemTest
assertEquals( "parent absolute path", basePath, name.getParent().getPath() );
// Try using a compound name to find a child
- try
- {
- name.resolveName( "a/b", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
+ assertBadName( name, "a/b", NameScope.CHILD );
- // Try using a empty name to find a child
- try
- {
- name.resolveName( "", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
-
- // Try using '.' to find a child
- try
- {
- name.resolveName( ".", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
+ // Check other invalid names
+ checkDescendentNames( name, NameScope.CHILD );
+ }
- // Try using '..' to find a child
- try
- {
- name.resolveName( "..", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
+ /**
+ * Name resolution tests that are common for CHILD or DESCENDENT scope.
+ */
+ private void checkDescendentNames( final FileName name,
+ final NameScope scope )
+ throws Exception
+ {
+ // Make some assumptions about the name explicit
+ assertTrue( !name.getPath().equals( "/" ) );
+ assertTrue( !name.getPath().endsWith( "/a" ) );
+ assertTrue( !name.getPath().endsWith( "/a/b" ) );
+
+ // Test names with the same prefix
+ String path = name.getPath() + "/a";
+ assertSameName( path, name, path, scope );
+ assertSameName( path, name, "../" + name.getBaseName() + "/a", scope );
+
+ // Test an empty name
+ assertBadName( name, "", scope );
+
+ // Test . name
+ assertBadName( name, ".", scope );
+ assertBadName( name, "./", scope );
+
+ // Test ancestor names
+ assertBadName( name, "..", scope );
+ assertBadName( name, "../a", scope );
+ assertBadName( name, "../" + name.getBaseName() + "a", scope );
+ assertBadName( name, "a/..", scope );
+
+ // Test absolute names
+ assertBadName( name, "/", scope );
+ assertBadName( name, "/a", scope );
+ assertBadName( name, "/a/b", scope );
+ assertBadName( name, name.getPath(), scope );
+ assertBadName( name, name.getPath() + "a", scope );
}
/**
* Checks that a relative name resolves to the expected absolute path.
+ * Tests both forward and back slashes.
*/
- private void assertSameName( String expectedPath,
- FileName baseName,
- String relName ) throws Exception
+ private void assertSameName( final String expectedPath,
+ final FileName baseName,
+ final String relName,
+ final NameScope scope )
+ throws Exception
{
- FileName name = baseName.resolveName( relName );
+ // Try the supplied name
+ FileName name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
// Replace the separators
relName.replace( '\\', '/' );
- name = baseName.resolveName( relName );
+ name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
// And again
relName.replace( '/', '\\' );
- name = baseName.resolveName( relName );
+ name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
}
+ /**
+ * Checks that a relative name resolves to the expected absolute path.
+ * Tests both forward and back slashes.
+ */
+ private void assertSameName( String expectedPath,
+ FileName baseName,
+ String relName ) throws Exception
+ {
+ assertSameName( expectedPath, baseName, relName, NameScope.FILE_SYSTEM );
+ }
+
/**
* 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";
+ final FileName baseName = m_baseFolder.getName();
+ final String parentPath = baseName.getParent().getPath();
+ final String path = baseName.getPath();
+ final String childPath = path + "/some-child";
// Test empty relative path
assertSameName( path, baseName, "" );
@@ -280,23 +305,78 @@ public abstract class AbstractFileSystemTest
}
/**
- * Walks the folder structure, asserting it contains exactly the
+ * Tests descendent name resolution.
+ */
+ public void testDescendentName()
+ throws Exception
+ {
+ final FileName baseName = m_baseFolder.getName();
+
+ // Test direct child
+ String path = baseName.getPath() + "/some-child";
+ assertSameName( path, baseName, "some-child", NameScope.DESCENDENT );
+
+ // Test compound name
+ path = path + "/grand-child";
+ assertSameName( path, baseName, "some-child/grand-child", NameScope.DESCENDENT );
+
+ // Test relative names
+ assertSameName( path, baseName, "./some-child/grand-child", NameScope.DESCENDENT );
+ assertSameName( path, baseName, "./nada/../some-child/grand-child", NameScope.DESCENDENT );
+ assertSameName( path, baseName, "some-child/./grand-child", NameScope.DESCENDENT );
+
+ // Test badly formed descendent names
+ checkDescendentNames( baseName, NameScope.DESCENDENT );
+ }
+
+ /**
+ * Asserts that a particular relative name is invalid for a particular
+ * scope.
+ */
+ private void assertBadName( final FileName name,
+ final String relName,
+ final NameScope scope )
+ {
+ try
+ {
+ name.resolveName( relName, scope );
+ fail();
+ }
+ catch( FileSystemException e )
+ {
+ // TODO - should check error message
+ }
+ }
+
+ /**
+ * Walks the base folder structure, asserting it contains exactly the
* expected files and folders.
*/
public void testStructure() throws Exception
+ {
+ final FileInfo baseInfo = buildExpectedStructure();
+ assertSameStructure( m_baseFolder, baseInfo );
+ }
+
+ /**
+ * Walks a folder structure, asserting it contains exactly the
+ * expected files and folders.
+ */
+ protected void assertSameStructure( final FileObject folder,
+ final FileInfo expected )
+ throws Exception
{
// Setup the structure
- List queueExpected = new ArrayList();
- FileInfo baseInfo = buildExpectedStructure();
- queueExpected.add( baseInfo );
+ final List queueExpected = new ArrayList();
+ queueExpected.add( expected );
- List queueActual = new ArrayList();
- queueActual.add( m_baseFolder );
+ final List queueActual = new ArrayList();
+ queueActual.add( folder );
while( queueActual.size() > 0 )
{
- FileObject file = (FileObject)queueActual.remove( 0 );
- FileInfo info = (FileInfo)queueExpected.remove( 0 );
+ final FileObject file = (FileObject)queueActual.remove( 0 );
+ final FileInfo info = (FileInfo)queueExpected.remove( 0 );
// Check the type is correct
assertSame( file.getType(), info._type );
@@ -307,7 +387,7 @@ public abstract class AbstractFileSystemTest
}
// Check children
- FileObject[] children = file.getChildren();
+ final FileObject[] children = file.getChildren();
// Make sure all children were found
assertNotNull( children );
@@ -316,8 +396,8 @@ public abstract class AbstractFileSystemTest
// 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() );
+ final FileObject child = children[ i ];
+ final FileInfo childInfo = (FileInfo)info._children.get( child.getName().getBaseName() );
// Make sure the child is expected
assertNotNull( childInfo );
@@ -366,16 +446,16 @@ public abstract class AbstractFileSystemTest
// Test an unknown file
file = m_baseFolder.resolveFile( "unknown-child" );
- FileSystemException exc = null;
try
{
file.getType();
+ fail();
}
catch( FileSystemException e )
{
- exc = e;
+ final String message = REZ.getString( "get-type-no-exist.error", file );
+ assertSameMessage( message, e );
}
- assertNotNull( exc );
}
/**
@@ -419,10 +499,12 @@ public abstract class AbstractFileSystemTest
try
{
file.getChildren();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "list-children-not-folder.error", file );
+ assertSameMessage( message, e );
}
// Should be able to get child by name
@@ -435,10 +517,12 @@ public abstract class AbstractFileSystemTest
try
{
file.getChildren();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "list-children-no-exist.error", file );
+ assertSameMessage( message, e );
}
// Should be able to get child by name
@@ -468,23 +552,33 @@ public abstract class AbstractFileSystemTest
* 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
+ protected void assertSameContent( final String expected,
+ final FileContent content )
+ throws Exception
{
// Get file content as a binary stream
- byte[] expectedBin = expected.getBytes( "utf-8" );
+ final 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 )
+ final InputStream instr = content.getInputStream();
+ final ByteArrayOutputStream outstr;
+ try
+ {
+ outstr = new ByteArrayOutputStream();
+ final byte[] buffer = new byte[ 256 ];
+ int nread = 0;
+ while( nread >= 0 )
+ {
+ outstr.write( buffer, 0, nread );
+ nread = instr.read( buffer );
+ }
+ }
+ finally
{
- outstr.write( buffer, 0, nread );
- nread = instr.read( buffer );
+ instr.close();
}
// Compare
@@ -501,10 +595,12 @@ public abstract class AbstractFileSystemTest
try
{
folder.getContent();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "get-folder-content.error", folder );
+ assertSameMessage( message, e );
}
// Try getting the content of an unknown file
@@ -513,18 +609,22 @@ public abstract class AbstractFileSystemTest
try
{
content.getInputStream();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "read-no-exist.error", unknownFile );
+ assertSameMessage( message, e );
}
try
{
content.getSize();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "get-size-no-exist.error", unknownFile );
+ assertSameMessage( message, e );
}
}
@@ -557,7 +657,7 @@ public abstract class AbstractFileSystemTest
/**
* Info about a file.
*/
- private static final class FileInfo
+ protected static final class FileInfo
{
String _baseName;
FileType _type;
diff --git a/proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java b/proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java
index 5660f5afc..a48f7a139 100644
--- a/proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java
+++ b/proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java
@@ -15,6 +15,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.aut.vfs.impl.DefaultFileSystemManager;
+import org.apache.aut.vfs.provider.AbstractFileObject;
+import org.apache.avalon.excalibur.i18n.ResourceManager;
+import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.myrmidon.AbstractMyrmidonTest;
/**
@@ -29,6 +32,9 @@ import org.apache.myrmidon.AbstractMyrmidonTest;
public abstract class AbstractFileSystemTest
extends AbstractMyrmidonTest
{
+ private final static Resources REZ
+ = ResourceManager.getPackageResources( AbstractFileObject.class );
+
protected FileObject m_baseFolder;
protected DefaultFileSystemManager m_manager;
@@ -46,12 +52,12 @@ public abstract class AbstractFileSystemTest
private FileInfo buildExpectedStructure()
{
// Build the expected structure
- FileInfo base = new FileInfo( "test", FileType.FOLDER );
+ final 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 );
+ final 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 ) );
@@ -76,7 +82,7 @@ public abstract class AbstractFileSystemTest
m_baseFolder = m_manager.resolveFile( getBaseFolderURI() );
// Build the expected content of "file1.txt"
- String eol = System.getProperty( "line.separator" );
+ final String eol = System.getProperty( "line.separator" );
m_charContent = "This is a test file." + eol + "With 2 lines in it." + eol;
}
@@ -86,8 +92,8 @@ public abstract class AbstractFileSystemTest
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 );
+ final String uri = m_baseFolder.getName().getURI();
+ final FileObject file = m_manager.resolveFile( uri );
assertSame( "file object", m_baseFolder, file );
}
@@ -123,7 +129,7 @@ public abstract class AbstractFileSystemTest
public void testRootFileName() throws Exception
{
// Locate the root file
- FileName rootName = m_baseFolder.getRoot().getName();
+ final FileName rootName = m_baseFolder.getRoot().getName();
// Test that the root path is "/"
assertEquals( "root path", "/", rootName.getPath() );
@@ -140,9 +146,9 @@ public abstract class AbstractFileSystemTest
*/
public void testChildName() throws Exception
{
- FileName baseName = m_baseFolder.getName();
- String basePath = baseName.getPath();
- FileName name = baseName.resolveName( "some-child", NameScope.CHILD );
+ final FileName baseName = m_baseFolder.getName();
+ final String basePath = baseName.getPath();
+ final FileName name = baseName.resolveName( "some-child", NameScope.CHILD );
// Test path is absolute
assertTrue( "is absolute", basePath.startsWith( "/" ) );
@@ -157,76 +163,95 @@ public abstract class AbstractFileSystemTest
assertEquals( "parent absolute path", basePath, name.getParent().getPath() );
// Try using a compound name to find a child
- try
- {
- name.resolveName( "a/b", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
+ assertBadName( name, "a/b", NameScope.CHILD );
- // Try using a empty name to find a child
- try
- {
- name.resolveName( "", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
-
- // Try using '.' to find a child
- try
- {
- name.resolveName( ".", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
+ // Check other invalid names
+ checkDescendentNames( name, NameScope.CHILD );
+ }
- // Try using '..' to find a child
- try
- {
- name.resolveName( "..", NameScope.CHILD );
- assertTrue( false );
- }
- catch( FileSystemException e )
- {
- }
+ /**
+ * Name resolution tests that are common for CHILD or DESCENDENT scope.
+ */
+ private void checkDescendentNames( final FileName name,
+ final NameScope scope )
+ throws Exception
+ {
+ // Make some assumptions about the name explicit
+ assertTrue( !name.getPath().equals( "/" ) );
+ assertTrue( !name.getPath().endsWith( "/a" ) );
+ assertTrue( !name.getPath().endsWith( "/a/b" ) );
+
+ // Test names with the same prefix
+ String path = name.getPath() + "/a";
+ assertSameName( path, name, path, scope );
+ assertSameName( path, name, "../" + name.getBaseName() + "/a", scope );
+
+ // Test an empty name
+ assertBadName( name, "", scope );
+
+ // Test . name
+ assertBadName( name, ".", scope );
+ assertBadName( name, "./", scope );
+
+ // Test ancestor names
+ assertBadName( name, "..", scope );
+ assertBadName( name, "../a", scope );
+ assertBadName( name, "../" + name.getBaseName() + "a", scope );
+ assertBadName( name, "a/..", scope );
+
+ // Test absolute names
+ assertBadName( name, "/", scope );
+ assertBadName( name, "/a", scope );
+ assertBadName( name, "/a/b", scope );
+ assertBadName( name, name.getPath(), scope );
+ assertBadName( name, name.getPath() + "a", scope );
}
/**
* Checks that a relative name resolves to the expected absolute path.
+ * Tests both forward and back slashes.
*/
- private void assertSameName( String expectedPath,
- FileName baseName,
- String relName ) throws Exception
+ private void assertSameName( final String expectedPath,
+ final FileName baseName,
+ final String relName,
+ final NameScope scope )
+ throws Exception
{
- FileName name = baseName.resolveName( relName );
+ // Try the supplied name
+ FileName name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
// Replace the separators
relName.replace( '\\', '/' );
- name = baseName.resolveName( relName );
+ name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
// And again
relName.replace( '/', '\\' );
- name = baseName.resolveName( relName );
+ name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
}
+ /**
+ * Checks that a relative name resolves to the expected absolute path.
+ * Tests both forward and back slashes.
+ */
+ private void assertSameName( String expectedPath,
+ FileName baseName,
+ String relName ) throws Exception
+ {
+ assertSameName( expectedPath, baseName, relName, NameScope.FILE_SYSTEM );
+ }
+
/**
* 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";
+ final FileName baseName = m_baseFolder.getName();
+ final String parentPath = baseName.getParent().getPath();
+ final String path = baseName.getPath();
+ final String childPath = path + "/some-child";
// Test empty relative path
assertSameName( path, baseName, "" );
@@ -280,23 +305,78 @@ public abstract class AbstractFileSystemTest
}
/**
- * Walks the folder structure, asserting it contains exactly the
+ * Tests descendent name resolution.
+ */
+ public void testDescendentName()
+ throws Exception
+ {
+ final FileName baseName = m_baseFolder.getName();
+
+ // Test direct child
+ String path = baseName.getPath() + "/some-child";
+ assertSameName( path, baseName, "some-child", NameScope.DESCENDENT );
+
+ // Test compound name
+ path = path + "/grand-child";
+ assertSameName( path, baseName, "some-child/grand-child", NameScope.DESCENDENT );
+
+ // Test relative names
+ assertSameName( path, baseName, "./some-child/grand-child", NameScope.DESCENDENT );
+ assertSameName( path, baseName, "./nada/../some-child/grand-child", NameScope.DESCENDENT );
+ assertSameName( path, baseName, "some-child/./grand-child", NameScope.DESCENDENT );
+
+ // Test badly formed descendent names
+ checkDescendentNames( baseName, NameScope.DESCENDENT );
+ }
+
+ /**
+ * Asserts that a particular relative name is invalid for a particular
+ * scope.
+ */
+ private void assertBadName( final FileName name,
+ final String relName,
+ final NameScope scope )
+ {
+ try
+ {
+ name.resolveName( relName, scope );
+ fail();
+ }
+ catch( FileSystemException e )
+ {
+ // TODO - should check error message
+ }
+ }
+
+ /**
+ * Walks the base folder structure, asserting it contains exactly the
* expected files and folders.
*/
public void testStructure() throws Exception
+ {
+ final FileInfo baseInfo = buildExpectedStructure();
+ assertSameStructure( m_baseFolder, baseInfo );
+ }
+
+ /**
+ * Walks a folder structure, asserting it contains exactly the
+ * expected files and folders.
+ */
+ protected void assertSameStructure( final FileObject folder,
+ final FileInfo expected )
+ throws Exception
{
// Setup the structure
- List queueExpected = new ArrayList();
- FileInfo baseInfo = buildExpectedStructure();
- queueExpected.add( baseInfo );
+ final List queueExpected = new ArrayList();
+ queueExpected.add( expected );
- List queueActual = new ArrayList();
- queueActual.add( m_baseFolder );
+ final List queueActual = new ArrayList();
+ queueActual.add( folder );
while( queueActual.size() > 0 )
{
- FileObject file = (FileObject)queueActual.remove( 0 );
- FileInfo info = (FileInfo)queueExpected.remove( 0 );
+ final FileObject file = (FileObject)queueActual.remove( 0 );
+ final FileInfo info = (FileInfo)queueExpected.remove( 0 );
// Check the type is correct
assertSame( file.getType(), info._type );
@@ -307,7 +387,7 @@ public abstract class AbstractFileSystemTest
}
// Check children
- FileObject[] children = file.getChildren();
+ final FileObject[] children = file.getChildren();
// Make sure all children were found
assertNotNull( children );
@@ -316,8 +396,8 @@ public abstract class AbstractFileSystemTest
// 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() );
+ final FileObject child = children[ i ];
+ final FileInfo childInfo = (FileInfo)info._children.get( child.getName().getBaseName() );
// Make sure the child is expected
assertNotNull( childInfo );
@@ -366,16 +446,16 @@ public abstract class AbstractFileSystemTest
// Test an unknown file
file = m_baseFolder.resolveFile( "unknown-child" );
- FileSystemException exc = null;
try
{
file.getType();
+ fail();
}
catch( FileSystemException e )
{
- exc = e;
+ final String message = REZ.getString( "get-type-no-exist.error", file );
+ assertSameMessage( message, e );
}
- assertNotNull( exc );
}
/**
@@ -419,10 +499,12 @@ public abstract class AbstractFileSystemTest
try
{
file.getChildren();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "list-children-not-folder.error", file );
+ assertSameMessage( message, e );
}
// Should be able to get child by name
@@ -435,10 +517,12 @@ public abstract class AbstractFileSystemTest
try
{
file.getChildren();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "list-children-no-exist.error", file );
+ assertSameMessage( message, e );
}
// Should be able to get child by name
@@ -468,23 +552,33 @@ public abstract class AbstractFileSystemTest
* 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
+ protected void assertSameContent( final String expected,
+ final FileContent content )
+ throws Exception
{
// Get file content as a binary stream
- byte[] expectedBin = expected.getBytes( "utf-8" );
+ final 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 )
+ final InputStream instr = content.getInputStream();
+ final ByteArrayOutputStream outstr;
+ try
+ {
+ outstr = new ByteArrayOutputStream();
+ final byte[] buffer = new byte[ 256 ];
+ int nread = 0;
+ while( nread >= 0 )
+ {
+ outstr.write( buffer, 0, nread );
+ nread = instr.read( buffer );
+ }
+ }
+ finally
{
- outstr.write( buffer, 0, nread );
- nread = instr.read( buffer );
+ instr.close();
}
// Compare
@@ -501,10 +595,12 @@ public abstract class AbstractFileSystemTest
try
{
folder.getContent();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "get-folder-content.error", folder );
+ assertSameMessage( message, e );
}
// Try getting the content of an unknown file
@@ -513,18 +609,22 @@ public abstract class AbstractFileSystemTest
try
{
content.getInputStream();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "read-no-exist.error", unknownFile );
+ assertSameMessage( message, e );
}
try
{
content.getSize();
- assertTrue( false );
+ fail();
}
catch( FileSystemException e )
{
+ final String message = REZ.getString( "get-size-no-exist.error", unknownFile );
+ assertSameMessage( message, e );
}
}
@@ -557,7 +657,7 @@ public abstract class AbstractFileSystemTest
/**
* Info about a file.
*/
- private static final class FileInfo
+ protected static final class FileInfo
{
String _baseName;
FileType _type;