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