* 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-ffa450edef68master
@@ -67,9 +67,8 @@ public interface FileName | |||||
FileName resolveName( String path ) throws FileSystemException; | 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 | * @param name | ||||
* The path to resolve. | * The path to resolve. | ||||
@@ -123,9 +123,8 @@ public interface FileObject | |||||
FileObject[] getChildren() throws FileSystemException; | 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 | * @param name | ||||
* The name to resolve. | * The name to resolve. | ||||
@@ -207,7 +206,7 @@ public interface FileObject | |||||
* | * | ||||
* @see FileContent#close | * @see FileContent#close | ||||
* | * | ||||
* @throws FileSystemEception | |||||
* @throws FileSystemException | |||||
* On error closing the file. | * On error closing the file. | ||||
*/ | */ | ||||
void close() throws FileSystemException; | void close() throws FileSystemException; | ||||
@@ -16,14 +16,19 @@ package org.apache.aut.vfs; | |||||
public final class NameScope | 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" ); | 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. | * Resolve against files in the same file system as the base file. | ||||
* | * | ||||
@@ -18,22 +18,39 @@ import org.apache.aut.vfs.NameScope; | |||||
*/ | */ | ||||
public class DefaultFileName implements FileName | 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 | // Cached stuff | ||||
private String m_uri; | private String m_uri; | ||||
private String m_baseName; | 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_parser = parser; | ||||
m_rootPrefix = rootPrefix; | m_rootPrefix = rootPrefix; | ||||
m_absPath = absPath; | 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. | * Returns the URI of the file. | ||||
@@ -67,22 +84,12 @@ public class DefaultFileName implements FileName | |||||
/** | /** | ||||
* Returns the name of a child of the file. | * 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() | public FileName getParent() | ||||
{ | { | ||||
String parentPath = m_parser.getParentPath( m_absPath ); | |||||
final String parentPath = m_parser.getParentPath( m_absPath ); | |||||
if( parentPath == null ) | if( parentPath == null ) | ||||
{ | { | ||||
return 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, | * file system that the file belongs to. If a relative name is supplied, | ||||
* then it is resolved relative to this file name. | * 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 ); | return resolveName( path, NameScope.FILE_SYSTEM ); | ||||
} | } | ||||
@@ -37,3 +37,4 @@ missing-hostname.error=Hostname missing from URI "{0}". | |||||
missing-port.error=Port number is 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}". | missing-hostname-path-sep.error=Expecting / to follow the hostname in URI "{0}". | ||||
invalid-childname.error=Invalid file base-name "{0}". | invalid-childname.error=Invalid file base-name "{0}". | ||||
invalid-descendent-name.error=Invalid descendent file name "{0}". |
@@ -10,6 +10,7 @@ package org.apache.aut.vfs.provider; | |||||
import java.util.HashSet; | import java.util.HashSet; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import org.apache.aut.vfs.FileSystemException; | 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.ResourceManager; | ||||
import org.apache.avalon.excalibur.i18n.Resources; | import org.apache.avalon.excalibur.i18n.Resources; | ||||
@@ -24,14 +25,14 @@ public class UriParser | |||||
ResourceManager.getPackageResources( UriParser.class ); | ResourceManager.getPackageResources( UriParser.class ); | ||||
/** The normalised separator to use. */ | /** 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. | * The set of valid separators. These are all converted to the normalised one. | ||||
* Does <i>not</i> contain the normalised separator | * 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. | * Creates a parser, using '/' and '\' as the path separators. | ||||
@@ -49,12 +50,12 @@ public class UriParser | |||||
* Additional legal separator characters. Any occurrences of | * Additional legal separator characters. Any occurrences of | ||||
* these in paths are replaced with the separator char. | * these in paths are replaced with the separator char. | ||||
*/ | */ | ||||
protected UriParser( char[] separators ) | |||||
protected UriParser( final char[] separators ) | |||||
{ | { | ||||
m_separatorChar = '/'; | m_separatorChar = '/'; | ||||
// Remove the separator char from the separators array | // Remove the separator char from the separators array | ||||
HashSet set = new HashSet(); | |||||
final HashSet set = new HashSet(); | |||||
set.add( new Character( '\\' ) ); | set.add( new Character( '\\' ) ); | ||||
if( separators != null ) | if( separators != null ) | ||||
{ | { | ||||
@@ -69,10 +70,10 @@ public class UriParser | |||||
} | } | ||||
} | } | ||||
m_separators = new char[ set.size() ]; | m_separators = new char[ set.size() ]; | ||||
Iterator iter = set.iterator(); | |||||
final Iterator iter = set.iterator(); | |||||
for( int i = 0; i < m_separators.length; i++ ) | 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(); | m_separators[ i ] = ch.charValue(); | ||||
} | } | ||||
@@ -86,9 +87,9 @@ public class UriParser | |||||
* | * | ||||
* <p>Sub-classes should override this method. | * <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 ); | parseGenericUri( uriStr, retval ); | ||||
return retval; | return retval; | ||||
} | } | ||||
@@ -109,9 +110,11 @@ public class UriParser | |||||
* @param uri | * @param uri | ||||
* Used to return the parsed components of the 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 | // Extract the scheme and authority parts | ||||
extractToPath( uriStr, name, uri ); | extractToPath( uriStr, name, uri ); | ||||
@@ -121,7 +124,7 @@ public class UriParser | |||||
uri.setPath( name.toString() ); | uri.setPath( name.toString() ); | ||||
// Build the root uri | // Build the root uri | ||||
StringBuffer rootUri = new StringBuffer(); | |||||
final StringBuffer rootUri = new StringBuffer(); | |||||
rootUri.append( uri.getScheme() ); | rootUri.append( uri.getScheme() ); | ||||
rootUri.append( "://" ); | rootUri.append( "://" ); | ||||
rootUri.append( uri.getHostName() ); | rootUri.append( uri.getHostName() ); | ||||
@@ -141,11 +144,13 @@ public class UriParser | |||||
* @parsedUri | * @parsedUri | ||||
* Used to return the extracted components. | * 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 | throws FileSystemException | ||||
{ | { | ||||
// Extract the scheme | // Extract the scheme | ||||
String scheme = extractScheme( uri, name ); | |||||
final String scheme = extractScheme( uri, name ); | |||||
parsedUri.setScheme( scheme ); | parsedUri.setScheme( scheme ); | ||||
// Expecting "//" | // Expecting "//" | ||||
@@ -157,11 +162,11 @@ public class UriParser | |||||
name.delete( 0, 2 ); | name.delete( 0, 2 ); | ||||
// Extract userinfo | // Extract userinfo | ||||
String userInfo = extractUserInfo( name ); | |||||
final String userInfo = extractUserInfo( name ); | |||||
parsedUri.setUserInfo( userInfo ); | parsedUri.setUserInfo( userInfo ); | ||||
// Extract hostname | // Extract hostname | ||||
String hostName = extractHostName( name ); | |||||
final String hostName = extractHostName( name ); | |||||
if( hostName == null ) | if( hostName == null ) | ||||
{ | { | ||||
final String message = REZ.getString( "missing-hostname.error", uri ); | final String message = REZ.getString( "missing-hostname.error", uri ); | ||||
@@ -170,7 +175,7 @@ public class UriParser | |||||
parsedUri.setHostName( hostName ); | parsedUri.setHostName( hostName ); | ||||
// Extract port | // Extract port | ||||
String port = extractPort( name ); | |||||
final String port = extractPort( name ); | |||||
if( port != null && port.length() == 0 ) | if( port != null && port.length() == 0 ) | ||||
{ | { | ||||
final String message = REZ.getString( "missing-port.error", uri ); | 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 | * Extracts the user info from a URI. The <scheme>:// part has been removed | ||||
* already. | * 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++ ) | for( int pos = 0; pos < maxlen; pos++ ) | ||||
{ | { | ||||
char ch = name.charAt( pos ); | |||||
final char ch = name.charAt( pos ); | |||||
if( ch == '@' ) | if( ch == '@' ) | ||||
{ | { | ||||
// Found the end of the user info | // 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 | * Extracts the hostname from a URI. The <scheme>://<userinfo>@ part has | ||||
* been removed. | * 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; | int pos = 0; | ||||
for( ; pos < maxlen; pos++ ) | for( ; pos < maxlen; pos++ ) | ||||
{ | { | ||||
char ch = name.charAt( pos ); | |||||
final char ch = name.charAt( pos ); | |||||
if( ch == '/' || ch == ';' || ch == '?' || ch == ':' | if( ch == '/' || ch == ';' || ch == '?' || ch == ':' | ||||
|| ch == '@' || ch == '&' || ch == '=' || ch == '+' | || ch == '@' || ch == '&' || ch == '=' || ch == '+' | ||||
|| ch == '$' || ch == ',' ) | || ch == '$' || ch == ',' ) | ||||
@@ -237,7 +242,7 @@ public class UriParser | |||||
return null; | return null; | ||||
} | } | ||||
String hostname = name.substring( 0, pos ); | |||||
final String hostname = name.substring( 0, pos ); | |||||
name.delete( 0, pos ); | name.delete( 0, pos ); | ||||
return hostname; | return hostname; | ||||
} | } | ||||
@@ -246,23 +251,25 @@ public class UriParser | |||||
* Extracts the port from a URI. The <scheme>://<userinfo>@<hostname> | * Extracts the port from a URI. The <scheme>://<userinfo>@<hostname> | ||||
* part has been removed. | * part has been removed. | ||||
*/ | */ | ||||
protected String extractPort( StringBuffer name ) | |||||
protected String extractPort( final StringBuffer name ) | |||||
{ | { | ||||
if( name.length() < 1 || name.charAt( 0 ) != ':' ) | if( name.length() < 1 || name.charAt( 0 ) != ':' ) | ||||
{ | { | ||||
return null; | return null; | ||||
} | } | ||||
int maxlen = name.length(); | |||||
final int maxlen = name.length(); | |||||
int pos = 1; | int pos = 1; | ||||
for( ; pos < maxlen; pos++ ) | for( ; pos < maxlen; pos++ ) | ||||
{ | { | ||||
char ch = name.charAt( pos ); | |||||
final char ch = name.charAt( pos ); | |||||
if( ch < '0' || ch > '9' ) | if( ch < '0' || ch > '9' ) | ||||
{ | { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
String port = name.substring( 1, pos ); | |||||
final String port = name.substring( 1, pos ); | |||||
name.delete( 0, pos ); | name.delete( 0, pos ); | ||||
return port; | return port; | ||||
} | } | ||||
@@ -270,9 +277,9 @@ public class UriParser | |||||
/** | /** | ||||
* Extracts the first element of a path. | * 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 ) | if( len < 1 ) | ||||
{ | { | ||||
return null; | return null; | ||||
@@ -287,14 +294,14 @@ public class UriParser | |||||
if( name.charAt( pos ) == m_separatorChar ) | if( name.charAt( pos ) == m_separatorChar ) | ||||
{ | { | ||||
// Found a separator | // Found a separator | ||||
String elem = name.substring( startPos, pos ); | |||||
final String elem = name.substring( startPos, pos ); | |||||
name.delete( startPos, pos + 1 ); | name.delete( startPos, pos + 1 ); | ||||
return elem; | return elem; | ||||
} | } | ||||
} | } | ||||
// No separator | // No separator | ||||
String elem = name.substring( startPos ); | |||||
final String elem = name.substring( startPos ); | |||||
name.setLength( 0 ); | name.setLength( 0 ); | ||||
return elem; | return elem; | ||||
} | } | ||||
@@ -308,10 +315,11 @@ public class UriParser | |||||
* @param path | * @param path | ||||
* A <i>normalised</i> 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 ) | if( uri.charAt( len - 1 ) == m_separatorChar ) | ||||
{ | { | ||||
uri.delete( len - 1, len ); | uri.delete( len - 1, len ); | ||||
@@ -330,9 +338,9 @@ public class UriParser | |||||
* @param path | * @param path | ||||
* A <i>normalised</i> 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 ) | if( idx == -1 ) | ||||
{ | { | ||||
return path; | return path; | ||||
@@ -353,9 +361,11 @@ public class UriParser | |||||
* does need to be a path (i.e. not an absolute URI). | * 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 | // Adjust separators | ||||
fixSeparators( buffer ); | 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. | * 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 ); | 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 | * @param path | ||||
* A <i>normalised</i> 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 ) | if( idx == -1 || idx == path.length() - 1 ) | ||||
{ | { | ||||
// No parent | // No parent | ||||
@@ -448,7 +463,7 @@ public class UriParser | |||||
* <li>Removes trailing separator. | * <li>Removes trailing separator. | ||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
public void normalisePath( StringBuffer path ) throws FileSystemException | |||||
public void normalisePath( final StringBuffer path ) throws FileSystemException | |||||
{ | { | ||||
if( path.length() == 0 ) | if( path.length() == 0 ) | ||||
{ | { | ||||
@@ -480,7 +495,7 @@ public class UriParser | |||||
{ | { | ||||
} | } | ||||
int elemLen = endElem - startElem; | |||||
final int elemLen = endElem - startElem; | |||||
if( elemLen == 0 ) | if( elemLen == 0 ) | ||||
{ | { | ||||
// An empty element - axe it | // An empty element - axe it | ||||
@@ -503,11 +518,8 @@ public class UriParser | |||||
if( startElem > startFirstElem ) | if( startElem > startFirstElem ) | ||||
{ | { | ||||
int pos = startElem - 2; | 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; | startElem = pos + 1; | ||||
} | } | ||||
@@ -521,7 +533,7 @@ public class UriParser | |||||
} | } | ||||
// Remove trailing separator | // 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 ); | path.delete( maxlen - 1, maxlen ); | ||||
} | } | ||||
@@ -530,7 +542,7 @@ public class UriParser | |||||
/** | /** | ||||
* Adjusts the separators in a name. | * Adjusts the separators in a name. | ||||
*/ | */ | ||||
protected boolean fixSeparators( StringBuffer name ) | |||||
protected boolean fixSeparators( final StringBuffer name ) | |||||
{ | { | ||||
if( m_separators.length == 0 ) | if( m_separators.length == 0 ) | ||||
{ | { | ||||
@@ -539,10 +551,10 @@ public class UriParser | |||||
} | } | ||||
boolean changed = false; | boolean changed = false; | ||||
int maxlen = name.length(); | |||||
final int maxlen = name.length(); | |||||
for( int i = 0; i < maxlen; i++ ) | 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++ ) | for( int j = 0; j < m_separators.length; j++ ) | ||||
{ | { | ||||
char separator = m_separators[ j ]; | char separator = m_separators[ j ]; | ||||
@@ -566,7 +578,7 @@ public class UriParser | |||||
* @return | * @return | ||||
* The scheme name. Returns null if there is no scheme. | * The scheme name. Returns null if there is no scheme. | ||||
*/ | */ | ||||
public static String extractScheme( String uri ) | |||||
public static String extractScheme( final String uri ) | |||||
{ | { | ||||
return extractScheme( uri, null ); | return extractScheme( uri, null ); | ||||
} | } | ||||
@@ -583,7 +595,7 @@ public class UriParser | |||||
* @return | * @return | ||||
* The scheme name. Returns null if there is no scheme. | * The scheme name. Returns null if there is no scheme. | ||||
*/ | */ | ||||
protected static String extractScheme( String uri, StringBuffer buffer ) | |||||
protected static String extractScheme( final String uri, final StringBuffer buffer ) | |||||
{ | { | ||||
if( buffer != null ) | if( buffer != null ) | ||||
{ | { | ||||
@@ -591,15 +603,15 @@ public class UriParser | |||||
buffer.append( uri ); | buffer.append( uri ); | ||||
} | } | ||||
int maxPos = uri.length(); | |||||
final int maxPos = uri.length(); | |||||
for( int pos = 0; pos < maxPos; pos++ ) | for( int pos = 0; pos < maxPos; pos++ ) | ||||
{ | { | ||||
char ch = uri.charAt( pos ); | |||||
final char ch = uri.charAt( pos ); | |||||
if( ch == ':' ) | if( ch == ':' ) | ||||
{ | { | ||||
// Found the end of the scheme | // Found the end of the scheme | ||||
String scheme = uri.substring( 0, pos ); | |||||
final String scheme = uri.substring( 0, pos ); | |||||
if( buffer != null ) | if( buffer != null ) | ||||
{ | { | ||||
buffer.delete( 0, pos + 1 ); | buffer.delete( 0, pos + 1 ); | ||||
@@ -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 "/"; | |||||
} | |||||
} |
@@ -23,7 +23,8 @@ import org.apache.avalon.excalibur.i18n.Resources; | |||||
/** | /** | ||||
* A file object implementation which uses direct file access. | * 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 | final class LocalFile | ||||
extends AbstractFileObject | extends AbstractFileObject | ||||
@@ -33,7 +34,7 @@ final class LocalFile | |||||
ResourceManager.getPackageResources( LocalFile.class ); | ResourceManager.getPackageResources( LocalFile.class ); | ||||
private File m_file; | private File m_file; | ||||
private String m_fileName; | |||||
private final String m_fileName; | |||||
/** | /** | ||||
* Creates a non-root file. | * Creates a non-root file. | ||||
@@ -11,26 +11,19 @@ import java.io.File; | |||||
import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
import org.apache.aut.vfs.provider.ParsedUri; | import org.apache.aut.vfs.provider.ParsedUri; | ||||
import org.apache.aut.vfs.provider.UriParser; | import org.apache.aut.vfs.provider.UriParser; | ||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
/** | /** | ||||
* A name parser. | * 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 | extends UriParser | ||||
{ | { | ||||
private final static Resources REZ = | |||||
ResourceManager.getPackageResources( LocalFileNameParser.class ); | |||||
private boolean m_windowsNames; | |||||
public LocalFileNameParser() | public LocalFileNameParser() | ||||
{ | { | ||||
super( new char[]{File.separatorChar, '/', '\\'} ); | 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. | * 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; | |||||
} | |||||
} | } |
@@ -17,7 +17,8 @@ import org.apache.aut.vfs.provider.FileSystem; | |||||
/** | /** | ||||
* A local file system. | * 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 | class LocalFileSystem extends AbstractFileSystem implements FileSystem | ||||
{ | { | ||||
@@ -8,6 +8,7 @@ | |||||
package org.apache.aut.vfs.provider.local; | package org.apache.aut.vfs.provider.local; | ||||
import java.io.File; | import java.io.File; | ||||
import org.apache.aut.nativelib.Os; | |||||
import org.apache.aut.vfs.FileObject; | import org.apache.aut.vfs.FileObject; | ||||
import org.apache.aut.vfs.FileSystemException; | import org.apache.aut.vfs.FileSystemException; | ||||
import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | 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. | * 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 | public class LocalFileSystemProvider extends AbstractFileSystemProvider | ||||
implements FileSystemProvider | 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. | * Determines if a name is an absolute file name. | ||||
@@ -12,9 +12,10 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||||
/** | /** | ||||
* A parsed file URI. | * 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; | private String m_rootFile; | ||||
@@ -23,7 +24,7 @@ public class ParsedFileUri extends ParsedUri | |||||
return m_rootFile; | return m_rootFile; | ||||
} | } | ||||
public void setRootFile( String rootPrefix ) | |||||
public void setRootFile( final String rootPrefix ) | |||||
{ | { | ||||
m_rootFile = rootPrefix; | m_rootFile = rootPrefix; | ||||
} | } | ||||
@@ -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; | |||||
} | |||||
} |
@@ -15,6 +15,9 @@ import java.util.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.aut.vfs.impl.DefaultFileSystemManager; | 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; | import org.apache.myrmidon.AbstractMyrmidonTest; | ||||
/** | /** | ||||
@@ -29,6 +32,9 @@ import org.apache.myrmidon.AbstractMyrmidonTest; | |||||
public abstract class AbstractFileSystemTest | public abstract class AbstractFileSystemTest | ||||
extends AbstractMyrmidonTest | extends AbstractMyrmidonTest | ||||
{ | { | ||||
private final static Resources REZ | |||||
= ResourceManager.getPackageResources( AbstractFileObject.class ); | |||||
protected FileObject m_baseFolder; | protected FileObject m_baseFolder; | ||||
protected DefaultFileSystemManager m_manager; | protected DefaultFileSystemManager m_manager; | ||||
@@ -46,12 +52,12 @@ public abstract class AbstractFileSystemTest | |||||
private FileInfo buildExpectedStructure() | private FileInfo buildExpectedStructure() | ||||
{ | { | ||||
// Build the expected structure | // 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( "file1.txt", FileType.FILE ) ); | ||||
base.addChild( new FileInfo( "empty.txt", FileType.FILE ) ); | base.addChild( new FileInfo( "empty.txt", FileType.FILE ) ); | ||||
base.addChild( new FileInfo( "emptydir", FileType.FOLDER ) ); | 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 ); | base.addChild( dir ); | ||||
dir.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | dir.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | ||||
dir.addChild( new FileInfo( "file2.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() ); | m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | ||||
// Build the expected content of "file1.txt" | // 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; | 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 | public void testAbsoluteURI() throws Exception | ||||
{ | { | ||||
// Try fetching base folder again by its URI | // 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 ); | assertSame( "file object", m_baseFolder, file ); | ||||
} | } | ||||
@@ -123,7 +129,7 @@ public abstract class AbstractFileSystemTest | |||||
public void testRootFileName() throws Exception | public void testRootFileName() throws Exception | ||||
{ | { | ||||
// Locate the root file | // Locate the root file | ||||
FileName rootName = m_baseFolder.getRoot().getName(); | |||||
final FileName rootName = m_baseFolder.getRoot().getName(); | |||||
// Test that the root path is "/" | // Test that the root path is "/" | ||||
assertEquals( "root path", "/", rootName.getPath() ); | assertEquals( "root path", "/", rootName.getPath() ); | ||||
@@ -140,9 +146,9 @@ public abstract class AbstractFileSystemTest | |||||
*/ | */ | ||||
public void testChildName() throws Exception | 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 | // Test path is absolute | ||||
assertTrue( "is absolute", basePath.startsWith( "/" ) ); | assertTrue( "is absolute", basePath.startsWith( "/" ) ); | ||||
@@ -157,76 +163,95 @@ public abstract class AbstractFileSystemTest | |||||
assertEquals( "parent absolute path", basePath, name.getParent().getPath() ); | assertEquals( "parent absolute path", basePath, name.getParent().getPath() ); | ||||
// Try using a compound name to find a child | // 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. | * 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() ); | assertEquals( expectedPath, name.getPath() ); | ||||
// Replace the separators | // Replace the separators | ||||
relName.replace( '\\', '/' ); | relName.replace( '\\', '/' ); | ||||
name = baseName.resolveName( relName ); | |||||
name = baseName.resolveName( relName, scope ); | |||||
assertEquals( expectedPath, name.getPath() ); | assertEquals( expectedPath, name.getPath() ); | ||||
// And again | // And again | ||||
relName.replace( '/', '\\' ); | relName.replace( '/', '\\' ); | ||||
name = baseName.resolveName( relName ); | |||||
name = baseName.resolveName( relName, scope ); | |||||
assertEquals( expectedPath, name.getPath() ); | 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. | * Tests relative name resolution, relative to the base folder. | ||||
*/ | */ | ||||
public void testNameResolution() throws Exception | 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 | // Test empty relative path | ||||
assertSameName( path, baseName, "" ); | 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. | * expected files and folders. | ||||
*/ | */ | ||||
public void testStructure() throws Exception | 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 | // 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 ) | 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 | // Check the type is correct | ||||
assertSame( file.getType(), info._type ); | assertSame( file.getType(), info._type ); | ||||
@@ -307,7 +387,7 @@ public abstract class AbstractFileSystemTest | |||||
} | } | ||||
// Check children | // Check children | ||||
FileObject[] children = file.getChildren(); | |||||
final FileObject[] children = file.getChildren(); | |||||
// Make sure all children were found | // Make sure all children were found | ||||
assertNotNull( children ); | assertNotNull( children ); | ||||
@@ -316,8 +396,8 @@ public abstract class AbstractFileSystemTest | |||||
// Recursively check each child | // Recursively check each child | ||||
for( int i = 0; i < children.length; i++ ) | 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 | // Make sure the child is expected | ||||
assertNotNull( childInfo ); | assertNotNull( childInfo ); | ||||
@@ -366,16 +446,16 @@ public abstract class AbstractFileSystemTest | |||||
// Test an unknown file | // Test an unknown file | ||||
file = m_baseFolder.resolveFile( "unknown-child" ); | file = m_baseFolder.resolveFile( "unknown-child" ); | ||||
FileSystemException exc = null; | |||||
try | try | ||||
{ | { | ||||
file.getType(); | file.getType(); | ||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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 | try | ||||
{ | { | ||||
file.getChildren(); | file.getChildren(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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 | // Should be able to get child by name | ||||
@@ -435,10 +517,12 @@ public abstract class AbstractFileSystemTest | |||||
try | try | ||||
{ | { | ||||
file.getChildren(); | file.getChildren(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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 | // 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 | * a byte stream, and as a char stream, and compares the result with | ||||
* the expected content. Assumes files are encoded using UTF-8. | * 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 | // Get file content as a binary stream | ||||
byte[] expectedBin = expected.getBytes( "utf-8" ); | |||||
final byte[] expectedBin = expected.getBytes( "utf-8" ); | |||||
// Check lengths | // Check lengths | ||||
assertEquals( "same content length", expectedBin.length, content.getSize() ); | assertEquals( "same content length", expectedBin.length, content.getSize() ); | ||||
// Read content into byte array | // 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 | // Compare | ||||
@@ -501,10 +595,12 @@ public abstract class AbstractFileSystemTest | |||||
try | try | ||||
{ | { | ||||
folder.getContent(); | folder.getContent(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | catch( FileSystemException e ) | ||||
{ | { | ||||
final String message = REZ.getString( "get-folder-content.error", folder ); | |||||
assertSameMessage( message, e ); | |||||
} | } | ||||
// Try getting the content of an unknown file | // Try getting the content of an unknown file | ||||
@@ -513,18 +609,22 @@ public abstract class AbstractFileSystemTest | |||||
try | try | ||||
{ | { | ||||
content.getInputStream(); | content.getInputStream(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | catch( FileSystemException e ) | ||||
{ | { | ||||
final String message = REZ.getString( "read-no-exist.error", unknownFile ); | |||||
assertSameMessage( message, e ); | |||||
} | } | ||||
try | try | ||||
{ | { | ||||
content.getSize(); | content.getSize(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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. | * Info about a file. | ||||
*/ | */ | ||||
private static final class FileInfo | |||||
protected static final class FileInfo | |||||
{ | { | ||||
String _baseName; | String _baseName; | ||||
FileType _type; | FileType _type; | ||||
@@ -15,6 +15,9 @@ import java.util.HashMap; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.aut.vfs.impl.DefaultFileSystemManager; | 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; | import org.apache.myrmidon.AbstractMyrmidonTest; | ||||
/** | /** | ||||
@@ -29,6 +32,9 @@ import org.apache.myrmidon.AbstractMyrmidonTest; | |||||
public abstract class AbstractFileSystemTest | public abstract class AbstractFileSystemTest | ||||
extends AbstractMyrmidonTest | extends AbstractMyrmidonTest | ||||
{ | { | ||||
private final static Resources REZ | |||||
= ResourceManager.getPackageResources( AbstractFileObject.class ); | |||||
protected FileObject m_baseFolder; | protected FileObject m_baseFolder; | ||||
protected DefaultFileSystemManager m_manager; | protected DefaultFileSystemManager m_manager; | ||||
@@ -46,12 +52,12 @@ public abstract class AbstractFileSystemTest | |||||
private FileInfo buildExpectedStructure() | private FileInfo buildExpectedStructure() | ||||
{ | { | ||||
// Build the expected structure | // 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( "file1.txt", FileType.FILE ) ); | ||||
base.addChild( new FileInfo( "empty.txt", FileType.FILE ) ); | base.addChild( new FileInfo( "empty.txt", FileType.FILE ) ); | ||||
base.addChild( new FileInfo( "emptydir", FileType.FOLDER ) ); | 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 ); | base.addChild( dir ); | ||||
dir.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | dir.addChild( new FileInfo( "file1.txt", FileType.FILE ) ); | ||||
dir.addChild( new FileInfo( "file2.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() ); | m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | ||||
// Build the expected content of "file1.txt" | // 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; | 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 | public void testAbsoluteURI() throws Exception | ||||
{ | { | ||||
// Try fetching base folder again by its URI | // 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 ); | assertSame( "file object", m_baseFolder, file ); | ||||
} | } | ||||
@@ -123,7 +129,7 @@ public abstract class AbstractFileSystemTest | |||||
public void testRootFileName() throws Exception | public void testRootFileName() throws Exception | ||||
{ | { | ||||
// Locate the root file | // Locate the root file | ||||
FileName rootName = m_baseFolder.getRoot().getName(); | |||||
final FileName rootName = m_baseFolder.getRoot().getName(); | |||||
// Test that the root path is "/" | // Test that the root path is "/" | ||||
assertEquals( "root path", "/", rootName.getPath() ); | assertEquals( "root path", "/", rootName.getPath() ); | ||||
@@ -140,9 +146,9 @@ public abstract class AbstractFileSystemTest | |||||
*/ | */ | ||||
public void testChildName() throws Exception | 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 | // Test path is absolute | ||||
assertTrue( "is absolute", basePath.startsWith( "/" ) ); | assertTrue( "is absolute", basePath.startsWith( "/" ) ); | ||||
@@ -157,76 +163,95 @@ public abstract class AbstractFileSystemTest | |||||
assertEquals( "parent absolute path", basePath, name.getParent().getPath() ); | assertEquals( "parent absolute path", basePath, name.getParent().getPath() ); | ||||
// Try using a compound name to find a child | // 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. | * 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() ); | assertEquals( expectedPath, name.getPath() ); | ||||
// Replace the separators | // Replace the separators | ||||
relName.replace( '\\', '/' ); | relName.replace( '\\', '/' ); | ||||
name = baseName.resolveName( relName ); | |||||
name = baseName.resolveName( relName, scope ); | |||||
assertEquals( expectedPath, name.getPath() ); | assertEquals( expectedPath, name.getPath() ); | ||||
// And again | // And again | ||||
relName.replace( '/', '\\' ); | relName.replace( '/', '\\' ); | ||||
name = baseName.resolveName( relName ); | |||||
name = baseName.resolveName( relName, scope ); | |||||
assertEquals( expectedPath, name.getPath() ); | 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. | * Tests relative name resolution, relative to the base folder. | ||||
*/ | */ | ||||
public void testNameResolution() throws Exception | 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 | // Test empty relative path | ||||
assertSameName( path, baseName, "" ); | 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. | * expected files and folders. | ||||
*/ | */ | ||||
public void testStructure() throws Exception | 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 | // 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 ) | 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 | // Check the type is correct | ||||
assertSame( file.getType(), info._type ); | assertSame( file.getType(), info._type ); | ||||
@@ -307,7 +387,7 @@ public abstract class AbstractFileSystemTest | |||||
} | } | ||||
// Check children | // Check children | ||||
FileObject[] children = file.getChildren(); | |||||
final FileObject[] children = file.getChildren(); | |||||
// Make sure all children were found | // Make sure all children were found | ||||
assertNotNull( children ); | assertNotNull( children ); | ||||
@@ -316,8 +396,8 @@ public abstract class AbstractFileSystemTest | |||||
// Recursively check each child | // Recursively check each child | ||||
for( int i = 0; i < children.length; i++ ) | 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 | // Make sure the child is expected | ||||
assertNotNull( childInfo ); | assertNotNull( childInfo ); | ||||
@@ -366,16 +446,16 @@ public abstract class AbstractFileSystemTest | |||||
// Test an unknown file | // Test an unknown file | ||||
file = m_baseFolder.resolveFile( "unknown-child" ); | file = m_baseFolder.resolveFile( "unknown-child" ); | ||||
FileSystemException exc = null; | |||||
try | try | ||||
{ | { | ||||
file.getType(); | file.getType(); | ||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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 | try | ||||
{ | { | ||||
file.getChildren(); | file.getChildren(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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 | // Should be able to get child by name | ||||
@@ -435,10 +517,12 @@ public abstract class AbstractFileSystemTest | |||||
try | try | ||||
{ | { | ||||
file.getChildren(); | file.getChildren(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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 | // 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 | * a byte stream, and as a char stream, and compares the result with | ||||
* the expected content. Assumes files are encoded using UTF-8. | * 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 | // Get file content as a binary stream | ||||
byte[] expectedBin = expected.getBytes( "utf-8" ); | |||||
final byte[] expectedBin = expected.getBytes( "utf-8" ); | |||||
// Check lengths | // Check lengths | ||||
assertEquals( "same content length", expectedBin.length, content.getSize() ); | assertEquals( "same content length", expectedBin.length, content.getSize() ); | ||||
// Read content into byte array | // 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 | // Compare | ||||
@@ -501,10 +595,12 @@ public abstract class AbstractFileSystemTest | |||||
try | try | ||||
{ | { | ||||
folder.getContent(); | folder.getContent(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | catch( FileSystemException e ) | ||||
{ | { | ||||
final String message = REZ.getString( "get-folder-content.error", folder ); | |||||
assertSameMessage( message, e ); | |||||
} | } | ||||
// Try getting the content of an unknown file | // Try getting the content of an unknown file | ||||
@@ -513,18 +609,22 @@ public abstract class AbstractFileSystemTest | |||||
try | try | ||||
{ | { | ||||
content.getInputStream(); | content.getInputStream(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | catch( FileSystemException e ) | ||||
{ | { | ||||
final String message = REZ.getString( "read-no-exist.error", unknownFile ); | |||||
assertSameMessage( message, e ); | |||||
} | } | ||||
try | try | ||||
{ | { | ||||
content.getSize(); | content.getSize(); | ||||
assertTrue( false ); | |||||
fail(); | |||||
} | } | ||||
catch( FileSystemException e ) | 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. | * Info about a file. | ||||
*/ | */ | ||||
private static final class FileInfo | |||||
protected static final class FileInfo | |||||
{ | { | ||||
String _baseName; | String _baseName; | ||||
FileType _type; | FileType _type; | ||||