Browse Source

* Added 'descendent' name scope.

* Changed semantics of FileName.resolveName() with 'child' scope.
* Fixed a couple of problems in UriParser.normalise().
* Split up LocalFileNameParser into a Windows specifc parser and a generic
  parser.
* More test cases.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271668 13f79535-47bb-0310-9956-ffa450edef68
master
adammurdoch 23 years ago
parent
commit
5b48abc10d
15 changed files with 734 additions and 440 deletions
  1. +2
    -3
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java
  2. +3
    -4
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java
  3. +10
    -5
      proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java
  4. +29
    -22
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java
  5. +1
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties
  6. +96
    -84
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java
  7. +44
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/GenericFileNameParser.java
  8. +3
    -2
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java
  9. +6
    -146
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java
  10. +2
    -1
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java
  11. +16
    -2
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java
  12. +4
    -3
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java
  13. +150
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/WindowsFileNameParser.java
  14. +184
    -84
      proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java
  15. +184
    -84
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java

+ 2
- 3
proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java View File

@@ -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.


+ 3
- 4
proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java View File

@@ -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;


+ 10
- 5
proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java View File

@@ -16,14 +16,19 @@ package org.apache.aut.vfs;
public final class NameScope
{
/**
* 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.
* 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.
*


+ 29
- 22
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java View File

@@ -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 );
}


+ 1
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties View File

@@ -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}".

+ 96
- 84
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java View File

@@ -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 <i>not</i> 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
*
* <p>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 <scheme>:// 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 <scheme>://<userinfo>@ 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 <scheme>://<userinfo>@<hostname>
* 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 <i>normalised</i> 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 <i>normalised</i> 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 <i>normalised</i> 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 <i>normalised</i> 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
* <li>Removes trailing separator.
* </ul>
*/
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 );


+ 44
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/GenericFileNameParser.java View File

@@ -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 <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @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 <sep>
if( name.length() == 0 || name.charAt( 0 ) != '/' )
{
final String message = REZ.getString( "not-absolute-file-name.error", uri );
throw new FileSystemException( message );
}

return "/";
}
}

+ 3
- 2
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java View File

@@ -23,7 +23,8 @@ import org.apache.avalon.excalibur.i18n.Resources;
/**
* A file object implementation which uses direct file access.
*
* @author Adam Murdoch
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @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.


+ 6
- 146
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java View File

@@ -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 <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @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 <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( final String uri,
final StringBuffer name )
throws FileSystemException
{
// Looking for:
// ('/'){0, 3} <letter> ':' '/'
// ['/'] '//' <name> '/' <name> ( '/' | <end> )
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 <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( final String uri,
final 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;
}
}

+ 2
- 1
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java View File

@@ -17,7 +17,8 @@ import org.apache.aut.vfs.provider.FileSystem;
/**
* A local file system.
*
* @author Adam Murdoch
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class LocalFileSystem extends AbstractFileSystem implements FileSystem
{


+ 16
- 2
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java View File

@@ -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 <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @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.


+ 4
- 3
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java View File

@@ -12,9 +12,10 @@ import org.apache.aut.vfs.provider.ParsedUri;
/**
* A parsed file URI.
*
* @author Adam Murdoch
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @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;
}


+ 150
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/WindowsFileNameParser.java View File

@@ -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 <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @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} <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( final 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( final String uri,
final 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;
}
}

+ 184
- 84
proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java View File

@@ -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;


+ 184
- 84
proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java View File

@@ -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;


Loading…
Cancel
Save