Browse Source

Zap aut from main myrmidon tree

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@272338 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Donald 23 years ago
parent
commit
962cde1a6d
100 changed files with 0 additions and 10758 deletions
  1. +0
    -82
      proposal/myrmidon/src/java/org/apache/aut/converter/AbstractConverter.java
  2. +0
    -230
      proposal/myrmidon/src/java/org/apache/aut/converter/AbstractMasterConverter.java
  3. +0
    -34
      proposal/myrmidon/src/java/org/apache/aut/converter/Converter.java
  4. +0
    -56
      proposal/myrmidon/src/java/org/apache/aut/converter/ConverterException.java
  5. +0
    -8
      proposal/myrmidon/src/java/org/apache/aut/converter/Resources.properties
  6. +0
    -38
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/ObjectToStringConverter.java
  7. +0
    -9
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/Resources.properties
  8. +0
    -74
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/SimpleMasterConverter.java
  9. +0
    -53
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToBooleanConverter.java
  10. +0
    -67
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToByteConverter.java
  11. +0
    -47
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToClassConverter.java
  12. +0
    -52
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToDateConverter.java
  13. +0
    -46
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToDoubleConverter.java
  14. +0
    -46
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToFloatConverter.java
  15. +0
    -68
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToIntegerConverter.java
  16. +0
    -67
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToLongConverter.java
  17. +0
    -67
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToShortConverter.java
  18. +0
    -49
      proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToURLConverter.java
  19. +0
    -40
      proposal/myrmidon/src/java/org/apache/aut/nativelib/DefaultExecOutputHandler.java
  20. +0
    -63
      proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecException.java
  21. +0
    -83
      proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecManager.java
  22. +0
    -87
      proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecMetaData.java
  23. +0
    -30
      proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecOutputHandler.java
  24. +0
    -323
      proposal/myrmidon/src/java/org/apache/aut/nativelib/Os.java
  25. +0
    -48
      proposal/myrmidon/src/java/org/apache/aut/nativelib/OsFamily.java
  26. +0
    -64
      proposal/myrmidon/src/java/org/apache/aut/nativelib/PathUtil.java
  27. +0
    -218
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/DefaultExecManager.java
  28. +0
    -211
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/Environment.java
  29. +0
    -92
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/LogOutputStream.java
  30. +0
    -91
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/ProcessDestroyer.java
  31. +0
    -312
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/ProcessMonitor.java
  32. +0
    -41
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/CommandLauncher.java
  33. +0
    -124
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/DefaultCommandLauncher.java
  34. +0
    -131
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/ExecUtil.java
  35. +0
    -56
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/MacCommandLauncher.java
  36. +0
    -1
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/Resources.properties
  37. +0
    -75
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/ScriptCommandLauncher.java
  38. +0
    -46
      proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/WinNTCommandLauncher.java
  39. +0
    -36
      proposal/myrmidon/src/java/org/apache/aut/vfs/AllFileSelector.java
  40. +0
    -46
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileConstants.java
  41. +0
    -158
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileContent.java
  42. +0
    -46
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileDepthSelector.java
  43. +0
    -101
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java
  44. +0
    -262
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java
  45. +0
    -33
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileSelectInfo.java
  46. +0
    -38
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileSelector.java
  47. +0
    -56
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileSystemException.java
  48. +0
    -148
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileSystemManager.java
  49. +0
    -53
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileType.java
  50. +0
    -76
      proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java
  51. +0
    -2
      proposal/myrmidon/src/java/org/apache/aut/vfs/Resources.properties
  52. +0
    -98
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultFileReplicator.java
  53. +0
    -269
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultFileSystemManager.java
  54. +0
    -47
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultProviderContext.java
  55. +0
    -8
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/Resources.properties
  56. +0
    -10
      proposal/myrmidon/src/java/org/apache/aut/vfs/package.html
  57. +0
    -805
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileObject.java
  58. +0
    -114
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileSystem.java
  59. +0
    -162
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileSystemProvider.java
  60. +0
    -379
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileContent.java
  61. +0
    -139
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileName.java
  62. +0
    -55
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileSelectorInfo.java
  63. +0
    -36
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileReplicator.java
  64. +0
    -42
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystem.java
  65. +0
    -49
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProvider.java
  66. +0
    -35
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProviderContext.java
  67. +0
    -43
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/LocalFileSystemProvider.java
  68. +0
    -96
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ParsedUri.java
  69. +0
    -46
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties
  70. +0
    -853
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java
  71. +0
    -68
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileNameParser.java
  72. +0
    -245
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileObject.java
  73. +0
    -122
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileSystem.java
  74. +0
    -66
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileSystemProvider.java
  75. +0
    -42
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/ParsedFtpUri.java
  76. +0
    -10
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/Resources.properties
  77. +0
    -105
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/DefaultLocalFileSystemProvider.java
  78. +0
    -44
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/GenericFileNameParser.java
  79. +0
    -156
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFile.java
  80. +0
    -92
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java
  81. +0
    -45
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystem.java
  82. +0
    -31
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/ParsedFileUri.java
  83. +0
    -5
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/Resources.properties
  84. +0
    -150
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/WindowsFileNameParser.java
  85. +0
    -31
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/ParsedSmbUri.java
  86. +0
    -2
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/Resources.properties
  87. +0
    -74
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileNameParser.java
  88. +0
    -149
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileObject.java
  89. +0
    -39
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileSystem.java
  90. +0
    -51
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileSystemProvider.java
  91. +0
    -43
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ParsedZipUri.java
  92. +0
    -2
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/Resources.properties
  93. +0
    -92
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileNameParser.java
  94. +0
    -104
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileObject.java
  95. +0
    -134
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileSystem.java
  96. +0
    -92
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileSystemProvider.java
  97. +0
    -25
      proposal/myrmidon/src/test/org/apache/aut/converter/lib/test/SimpleConvertersTestCase.java
  98. +0
    -879
      proposal/myrmidon/src/test/org/apache/aut/vfs/test/AbstractFileSystemTestCase.java
  99. +0
    -24
      proposal/myrmidon/src/test/org/apache/aut/vfs/test/AbstractReadOnlyFileSystemTestCase.java
  100. +0
    -266
      proposal/myrmidon/src/test/org/apache/aut/vfs/test/AbstractWritableFileSystemTestCase.java

+ 0
- 82
proposal/myrmidon/src/java/org/apache/aut/converter/AbstractConverter.java View File

@@ -1,82 +0,0 @@
/*
* 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.converter;

import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* Instances of this interface are used to convert between different types.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public abstract class AbstractConverter
implements Converter
{
private static final Resources REZ =
ResourceManager.getPackageResources( AbstractConverter.class );

private final Class m_source;
private final Class m_destination;

/**
* Constructor for a converter between types source and destination
*
* @param source the source type
* @param destination the destination type
*/
public AbstractConverter( final Class source, final Class destination )
{
m_source = source;
m_destination = destination;
}

/**
* Convert an object from original to destination types
*
* @param destination the destination type
* @param original the original Object
* @param context the context in which to convert
* @return the converted object
* @exception ConverterException if an error occurs
*/
public Object convert( final Class destination,
final Object original,
final Object context )
throws org.apache.aut.converter.ConverterException
{
if( m_destination != destination )
{
final String message =
REZ.getString( "bad-destination.error", destination.getName(), m_destination );
throw new IllegalArgumentException( message );
}

if( !m_source.isInstance( original ) )
{
final String message =
REZ.getString( "bad-instance.error", original, m_source.getName() );
throw new IllegalArgumentException( message );
}

return convert( original, context );
}

/**
* Overide this in a particular converter to do the conversion.
*
* @param original the original Object
* @param context the context in which to convert
* @return the converted object
* @exception org.apache.aut.converter.ConverterException if an error occurs
*/
protected abstract Object convert( Object original, Object context )
throws org.apache.aut.converter.ConverterException;
}


+ 0
- 230
proposal/myrmidon/src/java/org/apache/aut/converter/AbstractMasterConverter.java View File

@@ -1,230 +0,0 @@
/*
* 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.converter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* This is an abstract implementation of a <code>MasterConverter</code>.
* A MasterConverter is capable of converting between many different
* source and destination types. The <code>MasterConverter</code>
* delegates to other converters that do the actual work.
*
* <p>To use this class you must subclass it, overide the
* (@link #createConverter(String)) method and register some
* converters using the (@link #registerConverter(String,String,String))
* method.</p>
*
* <p>The reason this class deals with strings rather than Class objects
* is because dealing with strings allows us to implement alternative
* mechanisms for defining Converters in the future, only defining converter
* when it is first used.</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public abstract class AbstractMasterConverter
implements Converter
{
private static final Resources REZ =
ResourceManager.getPackageResources( AbstractMasterConverter.class );

/**
* Map from converter classname to instance of converter.
*/
private final Map m_converters = new HashMap();

/**
* This holds the mapping between source/destination
* and converter name.
*/
private final HashMap m_mapping = new HashMap();

/**
* Convert object to destination type.
*
* @param destination the destination type
* @param original the original object
* @param context the context in which to convert
* @return the converted object
* @exception org.apache.aut.converter.ConverterException if an error occurs
*/
public Object convert( final Class destination,
final Object original,
final Object context )
throws ConverterException
{
final Class originalClass = original.getClass();

if( destination.isAssignableFrom( originalClass ) )
{
return original;
}

try
{
// Search inheritance hierarchy for converter
final String name = findConverter( originalClass, destination );

// Create the converter
Converter converter = (Converter)m_converters.get( name );
if( converter == null )
{
converter = createConverter( name );
m_converters.put( name, converter );
}

// Convert
final Object object = converter.convert( destination, original, context );
if( destination.isInstance( object ) )
{
return object;
}

final String message =
REZ.getString( "bad-return-type.error",
object.getClass().getName(),
destination.getName() );
throw new ConverterException( message );
}
catch( final Exception e )
{
final String message = REZ.getString( "convert.error",
originalClass.getName(),
destination.getName() );
throw new ConverterException( message, e );
}
}

/**
* Register a converter
*
* @param classname the className of converter
* @param source the source classname
* @param destination the destination classname
*/
protected void registerConverter( final String classname,
final String source,
final String destination )
{
HashMap map = (HashMap)m_mapping.get( source );
if( null == map )
{
map = new HashMap();
m_mapping.put( source, map );
}

map.put( destination, classname );

//Remove instance of converter if it has already been created
m_converters.remove( classname );
}

/**
* Create an instance of converter with specified name.
*
* @param name the name of converter
* @return the created converter instance
* @throws Exception if converter can not be created.
*/
protected abstract Converter createConverter( final String name )
throws Exception;

/**
* Determine the name of the converter to use to convert between
* original and destination classes.
*/
private String findConverter( final Class originalClass,
final Class destination )
throws ConverterException
{
//TODO: Maybe we should search the destination classes hierarchy as well

// Recursively iterate over the super-types of the original class,
// looking for a converter from source type -> destination type.
// If more than one is found, choose the most specialised.

Class match = null;
String converterName = null;
ArrayList queue = new ArrayList();
queue.add( originalClass );

while( !queue.isEmpty() )
{
Class clazz = (Class)queue.remove( 0 );

// Add superclass and all interfaces
if( clazz.getSuperclass() != null )
{
queue.add( clazz.getSuperclass() );
}
final Class[] interfaces = clazz.getInterfaces();
for( int i = 0; i < interfaces.length; i++ )
{
queue.add( interfaces[ i ] );
}

// Check if we can convert from current class to destination
final String name = getConverterClassname( clazz.getName(),
destination.getName() );
if( name == null )
{
continue;
}

// Choose the more specialised source class
if( match == null || match.isAssignableFrom( clazz ) )
{
match = clazz;
converterName = name;
}
else if( clazz.isAssignableFrom( clazz ) )
{
continue;
}
else
{
// Duplicate
final String message = REZ.getString( "ambiguous-converter.error" );
throw new ConverterException( message );
}
}

// TODO - should cache the (src, dest) -> converter mapping
if( match != null )
{
return converterName;
}

// Could not find a converter
final String message = REZ.getString( "no-converter.error" );
throw new ConverterException( message );
}

/**
* Retrieve name of ConverterInfo that describes converter that converts
* from source to destination.
*
* @param source the source classname
* @param destination the destination classname
* @return the className of converter or null if none available
*/
private String getConverterClassname( final String source, final String destination )
{
final HashMap map = (HashMap)m_mapping.get( source );
if( null == map )
{
return null;
}
return (String)map.get( destination );
}
}

+ 0
- 34
proposal/myrmidon/src/java/org/apache/aut/converter/Converter.java View File

@@ -1,34 +0,0 @@
/*
* 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.converter;

/**
* Instances of this interface are used to convert between different types.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
* @ant:role shorthand="converter"
*/
public interface Converter
{
String ROLE = Converter.class.getName();

/**
* Convert original to destination type.
* Destination is passed so that one converter can potentiall
* convert to multiple different types.
*
* @param destination the destinaiton type
* @param original the original type
* @param context the context in which to convert
* @return the converted object
* @exception ConverterException if an error occurs
*/
Object convert( Class destination, Object original, Object context )
throws ConverterException;
}

+ 0
- 56
proposal/myrmidon/src/java/org/apache/aut/converter/ConverterException.java View File

@@ -1,56 +0,0 @@
/*
* 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.converter;

/**
* ConverterException thrown when a problem occurs during convertion etc.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public class ConverterException
extends Exception
{
/**
* The Throwable that caused this exception to be thrown.
*/
private final Throwable m_throwable;

/**
* Basic constructor with a message
*
* @param message the message
*/
public ConverterException( final String message )
{
this( message, null );
}

/**
* Constructor that builds cascade so that other exception information can be retained.
*
* @param message the message
* @param throwable the throwable
*/
public ConverterException( final String message, final Throwable throwable )
{
super( message );
m_throwable = throwable;
}

/**
* Retrieve root cause of the exception.
*
* @return the root cause
*/
public final Throwable getCause()
{
return m_throwable;
}
}


+ 0
- 8
proposal/myrmidon/src/java/org/apache/aut/converter/Resources.properties View File

@@ -1,8 +0,0 @@
bad-destination.error=Destination type ({0}) is not equal to {1}.
bad-instance.error=Object {0} is not an instance of {1}.

#AbstractMasterConverter
convert.error=Could not convert from {0} to {1}.
no-converter.error=Could not find an appropriate converter.
bad-return-type.error=Converter {0} returned an object of type {1} which is assignable to the expected type {2}.
ambiguous-converter.error=More than one converter available for this conversion.

+ 0
- 38
proposal/myrmidon/src/java/org/apache/aut/converter/lib/ObjectToStringConverter.java View File

@@ -1,38 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;

/**
* A general-purpose converter that converts an Object to a String using
* its toString() method.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant.converter source="java.lang.Object" destination="java.lang.String"
*/
public class ObjectToStringConverter
extends AbstractConverter
{
public ObjectToStringConverter()
{
super( Object.class, String.class );
}

/**
* Converts an object.
*/
protected Object convert( final Object original, final Object context )
throws ConverterException
{
return original.toString();
}
}

+ 0
- 9
proposal/myrmidon/src/java/org/apache/aut/converter/lib/Resources.properties View File

@@ -1,9 +0,0 @@
convert.bad-boolean.error=Error converting object ({0}) to Boolean.
convert.bad-byte.error=Error converting object ({0}) to Byte.
convert.bad-class.error=Error converting object ({0}) to Class.
convert.bad-double.error=Error converting object ({0}) to Double.
convert.bad-float.error=Error converting object ({0}) to Float.
convert.bad-integer.error=Error converting object ({0}) to Integer.
convert.bad-long.error=Error converting object ({0}) to Long.
convert.bad-short.error=Error converting object ({0}) to Short.
convert.bad-url.error=Error converting object ({0}) to URL.

+ 0
- 74
proposal/myrmidon/src/java/org/apache/aut/converter/lib/SimpleMasterConverter.java View File

@@ -1,74 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractMasterConverter;
import org.apache.aut.converter.Converter;

/**
* A very simple master converter that is capable of using
* any of the converters in this package.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public class SimpleMasterConverter
extends AbstractMasterConverter
{
public SimpleMasterConverter()
{
registerConverter( ObjectToStringConverter.class.getName(),
"java.lang.Object",
"java.lang.String" );
registerConverter( StringToBooleanConverter.class.getName(),
"java.lang.String",
"java.lang.Boolean" );
registerConverter( StringToByteConverter.class.getName(),
"java.lang.String",
"java.lang.Byte" );
registerConverter( StringToClassConverter.class.getName(),
"java.lang.String",
"java.lang.Class" );
registerConverter( StringToDoubleConverter.class.getName(),
"java.lang.String",
"java.lang.Double" );
registerConverter( StringToFloatConverter.class.getName(),
"java.lang.String",
"java.lang.Float" );
registerConverter( StringToIntegerConverter.class.getName(),
"java.lang.String",
"java.lang.Integer" );
registerConverter( StringToLongConverter.class.getName(),
"java.lang.String",
"java.lang.Long" );
registerConverter( StringToShortConverter.class.getName(),
"java.lang.String",
"java.lang.Short" );
registerConverter( StringToURLConverter.class.getName(),
"java.lang.String",
"java.net.URL" );
registerConverter( StringToDateConverter.class.getName(),
"java.lang.String",
"java.util.Date" );
}

/**
* Create an instance of converter with specified name.
*
* @param name the name of converter
* @return the created converter instance
* @throws Exception if converter can not be created.
*/
protected Converter createConverter( final String name )
throws Exception
{
final ClassLoader classLoader = getClass().getClassLoader();
final Class clazz = classLoader.loadClass( name );
return (Converter)clazz.newInstance();
}
}

+ 0
- 53
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToBooleanConverter.java View File

@@ -1,53 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to boolean converter
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Boolean"
*/
public class StringToBooleanConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToBooleanConverter.class );

public StringToBooleanConverter()
{
super( String.class, Boolean.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
final String string = (String)object;
if( string.equalsIgnoreCase( "true" )
|| string.equalsIgnoreCase( "yes" ) )
{
return Boolean.TRUE;
}
else if( string.equalsIgnoreCase( "false" )
|| string.equalsIgnoreCase( "no" ) )
{
return Boolean.FALSE;
}
else
{
final String message = REZ.getString( "convert.bad-boolean.error", object );
throw new ConverterException( message );
}
}
}


+ 0
- 67
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToByteConverter.java View File

@@ -1,67 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to byte converter
*
* <p>Hexadecimal numbers begin with 0x, Octal numbers begin with 0o and binary
* numbers begin with 0b, all other values are assumed to be decimal.</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Byte"
*/
public class StringToByteConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToByteConverter.class );

public StringToByteConverter()
{
super( String.class, Byte.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
final String value = (String)object;
byte result = 0;
if( value.startsWith( "0x" ) )
{
result = Byte.parseByte( value.substring( 2 ), 16 );
}
else if( value.startsWith( "0o" ) )
{
result = Byte.parseByte( value.substring( 2 ), 8 );
}
else if( value.startsWith( "0b" ) )
{
result = Byte.parseByte( value.substring( 2 ), 2 );
}
else
{
result = Byte.parseByte( value );
}
return new Byte( result );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-byte.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 47
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToClassConverter.java View File

@@ -1,47 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to class converter
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Class"
*/
public class StringToClassConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToClassConverter.class );

public StringToClassConverter()
{
super( String.class, Class.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
//TODO: Should we use ContextClassLoader here???
try
{
return Class.forName( (String)object );
}
catch( final Exception e )
{
final String message = REZ.getString( "convert.bad-class.error", object );
throw new ConverterException( message, e );
}
}
}


+ 0
- 52
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToDateConverter.java View File

@@ -1,52 +0,0 @@
/*
* 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.converter.lib;

import java.util.Date;
import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to date converter.
*
* <p>Parses a date according to the same rules as the Date.parse() method. In
* particular it recognizes the IETF standard date syntax:</p>
* <p>"Sat, 12 Aug 1995 13:30:00 GMT"</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.util.Date"
* @see java.util.Date#parse
*/
public class StringToDateConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToDateConverter.class );

public StringToDateConverter()
{
super( String.class, Date.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
return new Date( object.toString() );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-byte.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 46
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToDoubleConverter.java View File

@@ -1,46 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to double converter
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Double"
*/
public class StringToDoubleConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToDoubleConverter.class );

public StringToDoubleConverter()
{
super( String.class, Double.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
return new Double( (String)object );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-double.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 46
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToFloatConverter.java View File

@@ -1,46 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to float converter
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Float"
*/
public class StringToFloatConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToFloatConverter.class );

public StringToFloatConverter()
{
super( String.class, Float.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
return new Float( (String)object );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-float.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 68
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToIntegerConverter.java View File

@@ -1,68 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.aut.converter.lib.StringToFloatConverter;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to integer converter.
*
* <p>Hexadecimal numbers begin with 0x, Octal numbers begin with 0o and binary
* numbers begin with 0b, all other values are assumed to be decimal.</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Integer"
*/
public class StringToIntegerConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToFloatConverter.class );

public StringToIntegerConverter()
{
super( String.class, Integer.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
final String value = (String)object;
int result = 0;
if( value.startsWith( "0x" ) )
{
result = Integer.parseInt( value.substring( 2 ), 16 );
}
else if( value.startsWith( "0o" ) )
{
result = Integer.parseInt( value.substring( 2 ), 8 );
}
else if( value.startsWith( "0b" ) )
{
result = Integer.parseInt( value.substring( 2 ), 2 );
}
else
{
result = Integer.parseInt( value );
}
return new Integer( result );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-integer.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 67
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToLongConverter.java View File

@@ -1,67 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to long converter
*
* <p>Hexadecimal numbers begin with 0x, Octal numbers begin with 0o and binary
* numbers begin with 0b, all other values are assumed to be decimal.</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Long"
*/
public class StringToLongConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToLongConverter.class );

public StringToLongConverter()
{
super( String.class, Long.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
final String value = (String)object;
long result = 0;
if( value.startsWith( "0x" ) )
{
result = Long.parseLong( value.substring( 2 ), 16 );
}
else if( value.startsWith( "0o" ) )
{
result = Long.parseLong( value.substring( 2 ), 8 );
}
else if( value.startsWith( "0b" ) )
{
result = Long.parseLong( value.substring( 2 ), 2 );
}
else
{
result = Long.parseLong( value );
}
return new Long( result );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-long.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 67
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToShortConverter.java View File

@@ -1,67 +0,0 @@
/*
* 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.converter.lib;

import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to short converter
*
* <p>Hexadecimal numbers begin with 0x, Octal numbers begin with 0o and binary
* numbers begin with 0b, all other values are assumed to be decimal.</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.lang.Short"
*/
public class StringToShortConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToShortConverter.class );

public StringToShortConverter()
{
super( String.class, Short.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
final String value = (String)object;
short result = 0;
if( value.startsWith( "0x" ) )
{
result = Short.parseShort( value.substring( 2 ), 16 );
}
else if( value.startsWith( "0o" ) )
{
result = Short.parseShort( value.substring( 2 ), 8 );
}
else if( value.startsWith( "0b" ) )
{
result = Short.parseShort( value.substring( 2 ), 2 );
}
else
{
result = Short.parseShort( value );
}
return new Short( result );
}
catch( final NumberFormatException nfe )
{
final String message = REZ.getString( "convert.bad-short.error", object );
throw new ConverterException( message, nfe );
}
}
}


+ 0
- 49
proposal/myrmidon/src/java/org/apache/aut/converter/lib/StringToURLConverter.java View File

@@ -1,49 +0,0 @@
/*
* 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.converter.lib;

import java.net.MalformedURLException;
import java.net.URL;
import org.apache.aut.converter.AbstractConverter;
import org.apache.aut.converter.ConverterException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* String to url converter
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @ant.converter source="java.lang.String" destination="java.net.URL"
*/
public class StringToURLConverter
extends AbstractConverter
{
private static final Resources REZ =
ResourceManager.getPackageResources( StringToURLConverter.class );

public StringToURLConverter()
{
super( String.class, URL.class );
}

public Object convert( final Object object, final Object context )
throws ConverterException
{
try
{
return new URL( (String)object );
}
catch( final MalformedURLException mue )
{
final String message = REZ.getString( "convert.bad-url.error", object );
throw new ConverterException( message, mue );
}

}
}


+ 0
- 40
proposal/myrmidon/src/java/org/apache/aut/nativelib/DefaultExecOutputHandler.java View File

@@ -1,40 +0,0 @@
/*
* 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.nativelib;

import org.apache.avalon.framework.logger.AbstractLogEnabled;

/**
* This class is used to receive notifications of what the native
* process outputs to standard output and standard error.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public class DefaultExecOutputHandler
extends AbstractLogEnabled
implements ExecOutputHandler
{
/**
* Receive notification about the process writing
* to standard output.
*/
public void stdout( final String line )
{
getLogger().info( line );
}

/**
* Receive notification about the process writing
* to standard error.
*/
public void stderr( final String line )
{
getLogger().warn( line );
}
}

+ 0
- 63
proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecException.java View File

@@ -1,63 +0,0 @@
/*
* 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.nativelib;

/**
* ExecException indicates there was an error executing native process.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class ExecException
extends Exception
{
/**
* The Throwable that caused this exception to be thrown.
*/
private final Throwable m_throwable;

/**
* Basic constructor for exception that does not specify a message
*/
public ExecException()
{
this( "", null );
}

/**
* Basic constructor with a message
*
* @param message the message
*/
public ExecException( final String message )
{
this( message, null );
}

/**
* Constructor that builds cascade so that other exception information can be retained.
*
* @param message the message
* @param throwable the throwable
*/
public ExecException( final String message, final Throwable throwable )
{
super( message );
m_throwable = throwable;
}

/**
* Retrieve root cause of the exception.
*
* @return the root cause
*/
public final Throwable getCause()
{
return m_throwable;
}
}


+ 0
- 83
proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecManager.java View File

@@ -1,83 +0,0 @@
/*
* 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.nativelib;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;

/**
* Interface via which clients can request that a native
* process be executed. This manages all aspects of running
* a native command including such things as;
*
* <ul>
* <li>Destroying a process if it times out</li>
* <li>Reading data from supplied input stream and
* writing it to processes standard input</li>
* <li>Reading data from processes standard output
* and error streams and writing it to supplied
* streams</li>
* </ul>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
* @ant:role shorthand="exec-manager"
*/
public interface ExecManager
{
/**
* Retrieve a properties object that contains a list of
* all the native environment variables.
*/
Properties getNativeEnvironment()
throws ExecException;

/**
* Execute a process and wait for it to finish before
* returning.
*
* @param execMetaData the metaData for native command to execute
* @param input the stream to read from and write to standard input.
* May be null in which case nothing will be written to standard.
* input
* @param output the stream to write the processes standard output to.
* May be null in which case that output will go into the void.
* @param error the stream to write the processes standard error to.
* May be null in which case that error will go into the void.
* @param timeout the maximum duration in milliseconds that a process
* can execute. The value must be positive or zero. If it is zero
* then the process will not timeout. If the process times out it
* will be forcibly shutdown and a TimeoutException thrown
*/
int execute( ExecMetaData execMetaData,
InputStream input,
OutputStream output,
OutputStream error,
long timeout )
throws IOException, ExecException /*TimeoutException*/;

/**
* Execute a process and wait for it to finish before
* returning. Note that this version of execute() does not allow you
* to specify input.
*
* @param execMetaData the metaData for native command to execute
* @param handler the handler to which line-orientated output of
* process is directed for standard output and standard error
* @param timeout the maximum duration in milliseconds that a process
* can execute. The value must be positive or zero. If it is zero
* then the process will not timeout. If the process times out it
* will be forcibly shutdown and a TimeoutException thrown
*/
int execute( ExecMetaData execMetaData,
ExecOutputHandler handler,
long timeout )
throws IOException, ExecException /*TimeoutException*/;
}

+ 0
- 87
proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecMetaData.java View File

@@ -1,87 +0,0 @@
/*
* 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.nativelib;

import java.io.File;
import java.util.Properties;

/**
* This class holds meta data that is used to launch a native executable.
* This class should be populated with valid data and passed to the
* <code>ExecManager</code> and it will be the responsibility of the
* <code>ExecManager</code> to actually launch the native executable.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public class ExecMetaData
{
/**
* The working directory in which the applicaiton is launched.
*/
private final File m_workingDirectory;

/**
* The array of strings that make up the command line for the command.
*/
private final String[] m_command;

/**
* The array of strings that make up the native environment for the
* command.
*
* <p>Note that these variables are yet to be translated into the ugly
* format expected by the Runtime.exec() call. For most systems this means
* that each entry must be translated into the form <code>key=value</code>.</p>
*
* <p>This set of variables is combined with the environment of current
* process if <code>isEnvironmentAdditive=true</code> else it specifies
* full environment.</p>
*/
private final Properties m_environment;

/**
* Construct the meta data for executable as appropriate.
* Note that it is invalid to specify a <code>null</code>
* workingDirectory or command. It is also invalid to specify
* a null environment and an additive environment.
*/
public ExecMetaData( final String[] command,
final Properties environment,
final File workingDirectory )
{
m_command = command;
m_environment = environment;
m_workingDirectory = workingDirectory;

if( null == m_workingDirectory )
{
throw new NullPointerException( "workingDirectory" );
}

if( null == m_command )
{
throw new NullPointerException( "command" );
}
}

public File getWorkingDirectory()
{
return m_workingDirectory;
}

public String[] getCommand()
{
return m_command;
}

public Properties getEnvironment()
{
return m_environment;
}
}

+ 0
- 30
proposal/myrmidon/src/java/org/apache/aut/nativelib/ExecOutputHandler.java View File

@@ -1,30 +0,0 @@
/*
* 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.nativelib;

/**
* This class is used to receive notifications of what the native
* process outputs to standard output and standard error.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public interface ExecOutputHandler
{
/**
* Receive notification about the process writing
* to standard output.
*/
void stdout( String line );

/**
* Receive notification about the process writing
* to standard error.
*/
void stderr( String line );
}

+ 0
- 323
proposal/myrmidon/src/java/org/apache/aut/nativelib/Os.java View File

@@ -1,323 +0,0 @@
/*
* 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.nativelib;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
* Class to help determining the OS.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
*/
public class Os
{
private static final String OS_NAME =
System.getProperty( "os.name" ).toLowerCase( Locale.US );
private static final String OS_ARCH =
System.getProperty( "os.arch" ).toLowerCase( Locale.US );
private static final String OS_VERSION =
System.getProperty( "os.version" ).toLowerCase( Locale.US );
private static final String PATH_SEP =
System.getProperty( "path.separator" );
private static final OsFamily OS_FAMILY;
private static final OsFamily[] OS_ALL_FAMILIES;

/** All Windows based OSes. */
public static final OsFamily OS_FAMILY_WINDOWS = new OsFamily( "windows" );

/** All DOS based OSes. */
public static final OsFamily OS_FAMILY_DOS = new OsFamily( "dos" );

/** All Windows NT based OSes. */
public static final OsFamily OS_FAMILY_WINNT =
new OsFamily( "nt", new OsFamily[]{OS_FAMILY_WINDOWS} );

/** All Windows 9x based OSes. */
public static final OsFamily OS_FAMILY_WIN9X =
new OsFamily( "win9x", new OsFamily[]{OS_FAMILY_WINDOWS, OS_FAMILY_DOS} );

/** OS/2 */
public static final OsFamily OS_FAMILY_OS2 =
new OsFamily( "os/2", new OsFamily[]{OS_FAMILY_DOS} );

/** Netware */
public static final OsFamily OS_FAMILY_NETWARE =
new OsFamily( "netware" );

/** All UNIX based OSes. */
public static final OsFamily OS_FAMILY_UNIX = new OsFamily( "unix" );

/** All Mac based OSes. */
public static final OsFamily OS_FAMILY_MAC = new OsFamily( "mac" );

/** OSX */
public static final OsFamily OS_FAMILY_OSX =
new OsFamily( "osx", new OsFamily[]{OS_FAMILY_UNIX, OS_FAMILY_MAC} );

private static final OsFamily[] ALL_FAMILIES = new OsFamily[]
{
OS_FAMILY_DOS,
OS_FAMILY_MAC,
OS_FAMILY_NETWARE,
OS_FAMILY_OS2,
OS_FAMILY_OSX,
OS_FAMILY_UNIX,
OS_FAMILY_WINDOWS,
OS_FAMILY_WINNT,
OS_FAMILY_WIN9X
};

static
{
OS_FAMILY = determineOsFamily();
OS_ALL_FAMILIES = determineAllFamilies();
}

/**
* Private constructor to block instantiation.
*/
private Os()
{
}

/**
* Determines if the OS on which Ant is executing matches the given OS
* version.
*/
public static boolean isVersion( final String version )
{
return isOs( (OsFamily)null, null, null, version );
}

/**
* Determines if the OS on which Ant is executing matches the given OS
* architecture.
*/
public static boolean isArch( final String arch )
{
return isOs( (OsFamily)null, null, arch, null );
}

/**
* Determines if the OS on which Ant is executing matches the given OS
* family.
*/
public static boolean isFamily( final String family )
{
return isOs( family, null, null, null );
}

/**
* Determines if the OS on which Ant is executing matches the given OS
* family.
*/
public static boolean isFamily( final OsFamily family )
{
return isOs( family, null, null, null );
}

/**
* Determines if the OS on which Ant is executing matches the given OS name.
*
* @param name Description of Parameter
* @return The Name value
* @since 1.7
*/
public static boolean isName( final String name )
{
return isOs( (OsFamily)null, name, null, null );
}

/**
* Determines if the OS on which Ant is executing matches the given OS
* family, name, architecture and version.
*
* @param family The OS family
* @param name The OS name
* @param arch The OS architecture
* @param version The OS version
* @return The Os value
*/
public static boolean isOs( final String family,
final String name,
final String arch,
final String version )
{
return isOs( getFamily( family ), name, arch, version );
}

/**
* Determines if the OS on which Ant is executing matches the given OS
* family, name, architecture and version
*
* @param family The OS family
* @param name The OS name
* @param arch The OS architecture
* @param version The OS version
* @return The Os value
*/
public static boolean isOs( final OsFamily family,
final String name,
final String arch,
final String version )
{
if( family != null || name != null || arch != null || version != null )
{
final boolean isFamily = familyMatches( family );
final boolean isName = nameMatches( name );
final boolean isArch = archMatches( arch );
final boolean isVersion = versionMatches( version );

return isFamily && isName && isArch && isVersion;
}
else
{
return false;
}
}

/**
* Locates an OsFamily by name (case-insensitive).
*
* @return the OS family, or null if not found.
*/
public static OsFamily getFamily( final String name )
{
for( int i = 0; i < ALL_FAMILIES.length; i++ )
{
final OsFamily osFamily = ALL_FAMILIES[ i ];
if( osFamily.getName().equalsIgnoreCase( name ) )
{
return osFamily;
}
}

return null;
}

private static boolean versionMatches( final String version )
{
boolean isVersion = true;
if( version != null )
{
isVersion = version.equalsIgnoreCase( OS_VERSION );
}
return isVersion;
}

private static boolean archMatches( final String arch )
{
boolean isArch = true;
if( arch != null )
{
isArch = arch.equalsIgnoreCase( OS_ARCH );
}
return isArch;
}

private static boolean nameMatches( final String name )
{
boolean isName = true;
if( name != null )
{
isName = name.equalsIgnoreCase( OS_NAME );
}
return isName;
}

private static boolean familyMatches( final OsFamily family )
{
if( family == null )
{
return false;
}
for( int i = 0; i < OS_ALL_FAMILIES.length; i++ )
{
final OsFamily osFamily = OS_ALL_FAMILIES[ i ];
if( family == osFamily )
{
return true;
}
}
return false;
}

private static OsFamily[] determineAllFamilies()
{
// Determine all families the current OS belongs to
Set allFamilies = new HashSet();
if( OS_FAMILY != null )
{
List queue = new ArrayList();
queue.add( OS_FAMILY );
while( queue.size() > 0 )
{
final OsFamily family = (OsFamily)queue.remove( 0 );
allFamilies.add( family );
final OsFamily[] families = family.getFamilies();
for( int i = 0; i < families.length; i++ )
{
OsFamily parent = families[ i ];
queue.add( parent );
}
}
}
return (OsFamily[])allFamilies.toArray( new OsFamily[ allFamilies.size() ] );
}

private static OsFamily determineOsFamily()
{
// Determine the most specific OS family
if( OS_NAME.indexOf( "windows" ) > -1 )
{
if( OS_NAME.indexOf( "xp" ) > -1 ||
OS_NAME.indexOf( "2000" ) > -1 ||
OS_NAME.indexOf( "nt" ) > -1 )
{
return OS_FAMILY_WINNT;
}
else
{
return OS_FAMILY_WIN9X;
}
}
else if( OS_NAME.indexOf( "os/2" ) > -1 )
{
return OS_FAMILY_OS2;
}
else if( OS_NAME.indexOf( "netware" ) > -1 )
{
return OS_FAMILY_NETWARE;
}
else if( OS_NAME.indexOf( "mac" ) > -1 )
{
if( OS_NAME.endsWith( "x" ) )
{
return OS_FAMILY_OSX;
}
else
{
return OS_FAMILY_MAC;
}
}
else if( PATH_SEP.equals( ":" ) )
{
return OS_FAMILY_UNIX;
}
else
{
return null;
}
}
}

+ 0
- 48
proposal/myrmidon/src/java/org/apache/aut/nativelib/OsFamily.java View File

@@ -1,48 +0,0 @@
/*
* 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.nativelib;

/**
* An enumerated type, which represents an OS family.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public final class OsFamily
{
private final String m_name;
private final OsFamily[] m_families;

OsFamily( final String name )
{
m_name = name;
m_families = new OsFamily[0];
}

OsFamily( final String name, final OsFamily[] families )
{
m_name = name;
m_families = families;
}

/**
* Returns the name of this family.
*/
public String getName()
{
return m_name;
}

/**
* Returns the OS families that this family belongs to.
*/
public OsFamily[] getFamilies()
{
return m_families;
}
}

+ 0
- 64
proposal/myrmidon/src/java/org/apache/aut/nativelib/PathUtil.java View File

@@ -1,64 +0,0 @@
/*
* 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.nativelib;

import java.io.File;

/**
* Utility methods for dealing with native paths.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class PathUtil
{
/**
* Formats a path into its native representation.
*/
public static String formatPath( final String[] path )
{
// empty path return empty string
if( path == null || path.length == 0 )
{
return "";
}

// path containing one or more elements
final StringBuffer result = new StringBuffer( path[ 0 ].toString() );
for( int i = 1; i < path.length; i++ )
{
result.append( File.pathSeparatorChar );
result.append( path[ i ] );
}

return result.toString();
}

/**
* Formats a path into its native representation.
*/
public static String formatPath( final File[] path )
{
// empty path return empty string
if( path == null || path.length == 0 )
{
return "";
}

// path containing one or more elements
final StringBuffer result = new StringBuffer( path[ 0 ].toString() );
for( int i = 1; i < path.length; i++ )
{
result.append( File.pathSeparatorChar );
result.append( path[ i ].getAbsolutePath() );
}

return result.toString();
}
}

+ 0
- 218
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/DefaultExecManager.java View File

@@ -1,218 +0,0 @@
/*
* 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.nativelib.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecManager;
import org.apache.aut.nativelib.ExecMetaData;
import org.apache.aut.nativelib.ExecOutputHandler;
import org.apache.aut.nativelib.Os;
import org.apache.aut.nativelib.impl.launchers.CommandLauncher;
import org.apache.aut.nativelib.impl.launchers.DefaultCommandLauncher;
import org.apache.aut.nativelib.impl.launchers.MacCommandLauncher;
import org.apache.aut.nativelib.impl.launchers.ScriptCommandLauncher;
import org.apache.aut.nativelib.impl.launchers.WinNTCommandLauncher;
import org.apache.avalon.excalibur.io.FileUtil;
import org.apache.avalon.excalibur.io.IOUtil;

/**
* Default implementation of <code>ExecManager</code>.
* Used to run processes in the ant environment.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>
* @version $Revision$ $Date$
* @see ExecManager
* @see ExecMetaData
* @see Environment
*/
public class DefaultExecManager
implements ExecManager
{
/**
* Used to destroy processes when the VM exits.
*/
private final ProcessDestroyer m_processDestroyer = new ProcessDestroyer();

private final CommandLauncher m_launcher;
private final CommandLauncher m_shellLauncher;

/**
* Utility class that is used to load and parse the native
* environment variables.
*/
private final Environment m_environment;

public DefaultExecManager( final File homeDir )
throws ExecException
{
m_launcher = new DefaultCommandLauncher();
m_shellLauncher = createShellLauncher( homeDir );
m_environment = new Environment( this );
}

/**
* Retrieve a properties object that contains a list of
* all the native environment variables.
*/
public Properties getNativeEnvironment()
throws ExecException
{
try
{
return m_environment.getNativeEnvironment();
}
catch( final IOException ioe )
{
throw new ExecException( ioe.getMessage(), ioe );
}
}

/**
* Execute a process and wait for it to finish before
* returning.
*/
public int execute( final ExecMetaData execMetaData,
final ExecOutputHandler handler,
long timeout )
throws IOException, ExecException /*TimeoutException*/
{
final LogOutputStream output = new LogOutputStream( handler, false );
final LogOutputStream error = new LogOutputStream( handler, true );
try
{
return execute( execMetaData, null, output, error, timeout );
}
finally
{
IOUtil.shutdownStream( output );
IOUtil.shutdownStream( error );
}
}

/**
* Execute a process and wait for it to finish before
* returning.
*/
public int execute( final ExecMetaData command,
final InputStream input,
final OutputStream output,
final OutputStream error,
final long timeout )
throws IOException, ExecException
{
final CommandLauncher launcher = getLauncher( command );
final Process process = launcher.exec( command );
final ProcessMonitor monitor =
new ProcessMonitor( process, input, output, error, timeout, false );

final Thread thread = new Thread( monitor, "ProcessMonitor" );
thread.start();

// add the process to the list of those to destroy if the VM exits
m_processDestroyer.add( process );

waitFor( process );

//Now wait for monitor to finish aswell
try
{
thread.join();
}
catch( InterruptedException e )
{
//should never occur.
}

// remove the process to the list of those to destroy if the VM exits
m_processDestroyer.remove( process );

if( monitor.didProcessTimeout() )
{
throw new ExecException( "Process Timed out" );
}

return process.exitValue();
}

private void waitFor( final Process process )
{
//Should loop around until process is terminated.
try
{
process.waitFor();
}
catch( final InterruptedException ie )
{
//should never happen
}
}

private CommandLauncher getLauncher( final ExecMetaData metaData )
{
CommandLauncher launcher = m_launcher;
if( false ) //!m_useVMLauncher )
{
launcher = m_shellLauncher;
}
return launcher;
}

private CommandLauncher createShellLauncher( final File homeDir )
throws ExecException
{
CommandLauncher launcher = null;

if( Os.isFamily( Os.OS_FAMILY_MAC ) )
{
// Mac
launcher = new MacCommandLauncher();
}
else if( Os.isFamily( Os.OS_FAMILY_OS2 ) )
{
// OS/2 - use same mechanism as Windows 2000
launcher = new WinNTCommandLauncher();
}
else if( Os.isFamily( Os.OS_FAMILY_WINNT ) )
{
// Windows 2000/NT
launcher = new WinNTCommandLauncher();
}
else if( Os.isFamily( Os.OS_FAMILY_WINDOWS ) )
{
// Windows 98/95 - need to use an auxiliary script
final String script = resolveCommand( homeDir, "bin/antRun.bat" );
launcher = new ScriptCommandLauncher( script );
}
else if( Os.isFamily( Os.OS_FAMILY_NETWARE ) )
{
// NetWare. Need to determine which JDK we're running in
final String perlScript = resolveCommand( homeDir, "bin/antRun.pl" );
final String[] script = new String[]{"perl", perlScript};
launcher = new ScriptCommandLauncher( script );
}
else
{
// Generic
final String script = resolveCommand( homeDir, "bin/antRun" );
launcher = new ScriptCommandLauncher( script );
}

return launcher;
}

private String resolveCommand( final File antDir, final String command )
{
return FileUtil.resolveFile( antDir, command ).toString();
}
}

+ 0
- 211
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/Environment.java View File

@@ -1,211 +0,0 @@
/*
* 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.nativelib.impl;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecManager;
import org.apache.aut.nativelib.ExecMetaData;
import org.apache.aut.nativelib.Os;
import org.apache.avalon.excalibur.io.IOUtil;
import org.apache.avalon.excalibur.util.StringUtil;

/**
* This is the class that can be used to retrieve the environment
* variables of the native process.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>
* @version $Revision$ $Date$
*/
final class Environment
{
private static final String[] COMMAND_COM = new String[]{"command.com", "/c", "set"};
private static final String[] CMD_EXE = new String[]{"cmd", "/c", "set"};

// Alternatively one could use: /bin/sh -c env
private static final String[] ENV_CMD = new String[]{"/usr/bin/env"};
private static final String[] ENV_RAW = new String[]{"env"};

/**
* This is a cached version of the native environment variables.
*/
private Properties m_procEnvironment;

/**
* This is the class that is used to invoke the native process
* to retrieve then environment variables.
*/
private final ExecManager m_execManager;

public Environment( final ExecManager execManager )
{
m_execManager = execManager;
}

/**
* Retrieve a Properties object that contains the list of all
* native EnvironmentData Variables for the current process.
*/
public Properties getNativeEnvironment()
throws IOException, ExecException
{
final Properties properties = new Properties();
properties.putAll( getEnvironmentVariables() );
return properties;
}

/**
* Get the Property object with all environment variables and
* attempt to load it if it has not already been loaded.
*/
private synchronized Properties getEnvironmentVariables()
throws IOException, ExecException
{
if( null == m_procEnvironment )
{
m_procEnvironment = retrieveEnvironmentVariables();
}

return m_procEnvironment;
}

/**
* Retrieve a last of environment variables from the native OS.
*/
private synchronized Properties retrieveEnvironmentVariables()
throws IOException, ExecException
{
final String data = getEnvironmentText();

final Properties properties = new Properties();
final BufferedReader in = new BufferedReader( new StringReader( data ) );
final StringBuffer var = new StringBuffer();
String line;
while( null != ( line = in.readLine() ) )
{
if( -1 == line.indexOf( '=' ) )
{
// Chunk part of previous env var (UNIX env vars can
// contain embedded new lines).
var.append( StringUtil.LINE_SEPARATOR );
}
else
{
// New env var...append the previous one if we have it.
if( 0 != var.length() )
{
addProperty( properties, var.toString() );
var.setLength( 0 );
}
}
var.append( line );
}

// Since we "look ahead" before adding, there's one last env var.
if( 0 != var.length() )
{
addProperty( properties, var.toString() );
}
return properties;
}

/**
* Parse the specified data into a key=value pair. If there is no
* '=' character then generate an exception. After parsed data place
* the key-value pair into the specified Properties object.
*/
private void addProperty( final Properties properties,
final String data )
throws ExecException
{
final int index = data.indexOf( '=' );
if( -1 == index )
{
//Our env variable does not have any = in it.
final String message = "EnvironmentData variable '" + data +
"' does not have a '=' character in it";
throw new ExecException( message );
}
else
{
final String key = data.substring( 0, index );
final String value = data.substring( index + 1 );
properties.setProperty( key, value );
}
}

/**
* Retrieve the text of data that is the result of
* running the environment command.
*/
private String getEnvironmentText()
throws IOException, ExecException
{
final String[] command = getEnvCommand();
final File workingDirectory = new File( "." );
final ExecMetaData metaData = new ExecMetaData( command, null, workingDirectory );

final ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
final int retval = m_execManager.execute( metaData, null, output, output, 0 );
if( retval != 0 )
{
// Just try to use what we got
}

return output.toString();
} finally {
IOUtil.shutdownStream( output );
}
}

/**
* Retrieve the command that will display a list of environment
* variables.
*/
private static String[] getEnvCommand()
throws ExecException
{
if( Os.isFamily( Os.OS_FAMILY_OS2 ) )
{
// OS/2 - use same mechanism as Windows 2000
return CMD_EXE;
}
else if( Os.isFamily( Os.OS_FAMILY_WINNT ) )
{
// Windows 2000/NT
return CMD_EXE;
}
else if( Os.isFamily( Os.OS_FAMILY_WINDOWS) )
{
// Windows 98/95 - need to use an auxiliary script
return COMMAND_COM;
}
else if( Os.isFamily( Os.OS_FAMILY_UNIX ) )
{
// Generic UNIX
return ENV_CMD;
}
else if( Os.isFamily( Os.OS_FAMILY_NETWARE ) )
{
return ENV_RAW;
}
else
{
final String message =
"Unable to determine native environment variables";
throw new ExecException( message );
}
}
}

+ 0
- 92
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/LogOutputStream.java View File

@@ -1,92 +0,0 @@
/*
* 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.nativelib.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.aut.nativelib.ExecOutputHandler;

/**
* Logs each line written to this stream to the specified
* <code>ExecOutputHandler</code>. Tries to be smart about
* line separators.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
class LogOutputStream
extends OutputStream
{
private final boolean m_isError;
private final ExecOutputHandler m_handler;

private final ByteArrayOutputStream m_buffer = new ByteArrayOutputStream();
private boolean m_skip;

public LogOutputStream( final ExecOutputHandler handler,
final boolean isError )
{
m_handler = handler;
m_isError = isError;
}

/**
* Writes all remaining
*/
public void close()
throws IOException
{
if( m_buffer.size() > 0 )
{
processBuffer();
}
super.close();
}

/**
* Write the data to the buffer and flush the buffer, if a line separator is
* detected.
*
* @param ch data to log (byte).
*/
public void write( final int ch )
throws IOException
{
if( ( ch == '\n' ) || ( ch == '\r' ) )
{
if( !m_skip )
{
processBuffer();
}
}
else
{
m_buffer.write( (byte)ch );
}

m_skip = ( ch == '\r' );
}

/**
* Converts the buffer to a string and sends it to <code>ExecOutputHandler</code>
*/
private void processBuffer()
{
final String line = m_buffer.toString();
if( m_isError )
{
m_handler.stderr( line );
}
else
{
m_handler.stdout( line );
}
m_buffer.reset();
}
}

+ 0
- 91
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/ProcessDestroyer.java View File

@@ -1,91 +0,0 @@
/*
* 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.nativelib.impl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;

/**
* Destroys all registered <code>Process</code>es when
* the VM exits (if in JDK1.3) or when requested.
*
* @author <a href="mailto:mnewcomb@tacintel.com">Michael Newcomb</a>
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
class ProcessDestroyer
extends Thread
{
private ArrayList m_processes = new ArrayList();

/**
* Constructs a <code>ProcessDestroyer</code> and registers it as a shutdown
* hook.
*/
public ProcessDestroyer()
{
try
{
// check to see if the method exists (support pre-JDK 1.3 VMs)
//
final Class[] paramTypes = {Thread.class};
final Method addShutdownHook =
Runtime.class.getMethod( "addShutdownHook", paramTypes );

// add the hook
Object[] args = {this};
addShutdownHook.invoke( Runtime.getRuntime(), args );
}
catch( final Exception e )
{
// it just won't be added as a shutdown hook... :(
}
}

/**
* Add process to list of processes to be shutdown.
*
* @param process the process to add
*/
public synchronized void add( final Process process )
{
if( !m_processes.contains( process ) )
{
m_processes.add( process );
}
}

/**
* Remove process from list of processes to be shutdown.
*
* @param process the process to remove
*/
public synchronized void remove( final Process process )
{
m_processes.remove( process );
}

/**
* Invoked by the VM when it is exiting.
*/
public void run()
{
destroyProcesses();
}

protected synchronized void destroyProcesses()
{
final Iterator processes = m_processes.iterator();
while( processes.hasNext() )
{
( (Process)processes.next() ).destroy();
processes.remove();
}
}
}

+ 0
- 312
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/ProcessMonitor.java View File

@@ -1,312 +0,0 @@
/*
* 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.nativelib.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.avalon.excalibur.io.IOUtil;
import org.apache.avalon.framework.logger.AbstractLogEnabled;

/**
* This class is responsible for monitoring a process.
* It will monitor a process and if it goes longer than its timeout
* then it will terminate it. The monitor will also read data from
* stdout and stderr of process and pass it onto user specified streams.
* It will also in the future do the same for stdin.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
class ProcessMonitor
extends AbstractLogEnabled
implements Runnable
{
//Time to sleep in loop while processing output
//of command and monitoring for timeout
private static final int SLEEP_TIME = 5;

//State to indicate process is still running
private static final int STATE_RUNNING = 0;

//State to indicate process shutdown by itself
private static final int STATE_STOPPED = 1;

//State to indicate process was terminated due to timeout
private static final int STATE_TERMINATED = 2;

/**
* The state of the process monitor and thus
* the state of the underlying process.
*/
private int m_state = STATE_RUNNING;

/**
* This is the process we are monitoring.
*/
private final Process m_process;

/**
* This specifies the time at which this process will
* timeout. 0 implies no timeout.
*/
private final long m_timeout;

/**
* Stream from which to read standard input.
*/
private InputStream m_input;

/**
* Stream to write standard output to.
*/
private final OutputStream m_output;

/**
* Stream to write standard error to.
*/
private final OutputStream m_error;

/**
* Specifies whether the monitor should shutdown
* input, output and error Streams when it finishes execution.
* This should be set to <code>true</code> for processes which
* will run asynchronously.
*/
private final boolean m_shutdownStreams;

/**
* Creates a monitor for a given {@link java.lang.Process}, which pipes
* input from the given input stream to the process, and pipes the process
* output to the given OutputStreams.
*
* @param process the Process to be monitored
* @param input is read into the Process' stdin
* @param output receives the Process' stdout
* @param error receives the Process' stderr
* @param timeoutDuration how long to let the Process run before killing it.
* @param shutdownStreams specifies if the monitor should shutdown the
* streams when the Process exits.
*/
public ProcessMonitor( final Process process,
final InputStream input,
final OutputStream output,
final OutputStream error,
final long timeoutDuration,
final boolean shutdownStreams )
{
if( null == process )
{
throw new NullPointerException( "process" );
}

if( 0 > timeoutDuration )
{
throw new IllegalArgumentException( "timeoutDuration" );
}

final long now = System.currentTimeMillis();
long timeout = 0;
if( 0 != timeoutDuration )
{
timeout = now + timeoutDuration;
}

m_process = process;
m_input = input;
m_output = output;
m_error = error;
m_timeout = timeout;
m_shutdownStreams = shutdownStreams;
}

/**
* Utility method to check if process timed out.
* Only valid after run() has exited.
*/
public boolean didProcessTimeout()
{
return ( m_state == STATE_TERMINATED );
}

/**
* Thread method to monitor the state of the process.
*/
public void run()
{
while( STATE_RUNNING == m_state )
{
processStreams();

if( !isProcessStopped() )
{
checkTimeout();
}

try
{
Thread.sleep( SLEEP_TIME );
}
catch( final InterruptedException ie )
{
//swallow it
}
}

//Process streams again to make sure
//that we have got all the data
processStreams();

cleanupStreams();
}

/**
* Utility method which cleans up all IO Streams, if required.
*/
private void cleanupStreams()
{
if( m_shutdownStreams )
{
IOUtil.shutdownStream( m_input );
IOUtil.shutdownStream( m_output );
IOUtil.shutdownStream( m_error );
}
}

/**
* Utility method to process all the standard streams.
*/
private void processStreams()
{
processStandardInput();
processStandardOutput();
processStandardError();
}

/**
* Check if process has stopped. If it has then update state
* and return true, else return false.
*/
private boolean isProcessStopped()
{
boolean stopped;
try
{
m_process.exitValue();
stopped = true;
}
catch( final IllegalThreadStateException itse )
{
stopped = false;
}

if( stopped )
{
m_state = STATE_STOPPED;
}

return stopped;
}

/**
* Check if the process has exceeded time allocated to it.
* If it has reached timeout then terminate the process
* and set state to <code>STATE_TERMINATED</code>.
*/
private void checkTimeout()
{
if( 0 == m_timeout )
{
return;
}

final long now = System.currentTimeMillis();
if( now > m_timeout )
{
m_state = STATE_TERMINATED;
m_process.destroy();
}
}

/**
* Process the standard input of process.
* Reading it from user specified stream and copy it
* to processes standard input stream.
*/
private void processStandardInput()
{
if( null != m_input )
{
//Note can not do this as the process may block
//when written to which will result in this whole
//thread being blocked. Probably need to write to
//stdin in another thread
//copy( m_input, m_process.getOutputStream() );

//Should we shutdown the processes input stream ?
//Why not - at least for now
IOUtil.shutdownStream( m_process.getOutputStream() );

IOUtil.shutdownStream( m_input );
m_input = null;
}
}

/**
* Process the standard output of process.
* Reading it and sending it to user specified stream
* or into the void.
*/
private void processStandardOutput()
{
final InputStream input = m_process.getInputStream();
copy( input, m_output );
}

/**
* Process the standard error of process.
* Reading it and sending it to user specified stream
* or into the void.
*/
private void processStandardError()
{
final InputStream input = m_process.getErrorStream();
copy( input, m_error );
}

/**
* Copy data from specified input stream to output stream if
* output stream exists. The size of data that should be attempted
* to read is determined by calling available() on input stream.
*/
private void copy( final InputStream input,
final OutputStream output )
{
try
{
final int available = input.available();
if( 0 >= available )
{
return;
}

final byte[] data = new byte[ available ];
final int read = input.read( data );

if( null != output )
{
output.write( data, 0, read );
}
}
catch( final IOException ioe )
{
final String message = "Error processing streams";
getLogger().error( message, ioe );
}
}
}

+ 0
- 41
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/CommandLauncher.java View File

@@ -1,41 +0,0 @@
/*
* 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.nativelib.impl.launchers;

import java.io.IOException;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecMetaData;

/**
* This is the interface implemented by objects which are capable of
* lauching a native command. Each different implementation is likely
* to have a different strategy or be restricted to specific platform.
*
* <p>It is expected that the user will get a reference to the
* <code>CommandLauncher</code> most appropriate for their environment.</p>
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public interface CommandLauncher
{
/**
* Execute the specified native command.
*
* @param metaData the native command to execute
* @return the Process launched by the CommandLauncher
* @exception IOException is thrown when the native code can not
* launch the application for some reason. Usually due
* to the command not being fully specified and not in
* the PATH env var.
* @exception ExecException if the command launcher detects that
* it can not execute the native command for some reason.
*/
Process exec( ExecMetaData metaData )
throws IOException, ExecException;
}

+ 0
- 124
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/DefaultCommandLauncher.java View File

@@ -1,124 +0,0 @@
/*
* 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.nativelib.impl.launchers;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecMetaData;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* A command launcher for a particular JVM/OS platform. This class is a
* general purpose command launcher which can only launch commands in the
* current working directory.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>
* @version $Revision$ $Date$
*/
public class DefaultCommandLauncher
implements CommandLauncher
{
private static final Resources REZ =
ResourceManager.getPackageResources( DefaultCommandLauncher.class );

private static final Method c_execWithCWD;

static
{
// Locate method Runtime.exec(String[] cmdarray, String[] envp, File dir)
Method method = null;
try
{
final Class[] types =
new Class[]{String[].class, String[].class, File.class};
method = Runtime.class.getMethod( "exec", types );
}
catch( final NoSuchMethodException nsme )
{
//ignore
}

c_execWithCWD = method;
}

/**
* Execute the specified native command.
*
* @param metaData the native command to execute
* @return the Process launched by the CommandLauncher
* @exception IOException is thrown when the native code can not
* launch the application for some reason. Usually due
* to the command not being fully specified and not in
* the PATH env var.
* @exception ExecException if the command launcher detects that
* it can not execute the native command for some reason.
*/
public Process exec( final ExecMetaData metaData )
throws IOException, ExecException
{
if( ExecUtil.isCwd( metaData.getWorkingDirectory() ) )
{
final String[] env = ExecUtil.getEnvironmentSpec( metaData );
return Runtime.getRuntime().exec( metaData.getCommand(), env );
}
else if( null == c_execWithCWD )
{
final String message = REZ.getString( "default.bad-dir.error" );
throw new ExecException( message );
}
else
{
return execJava13( metaData );
}
}

/**
* Execute the Java1.3 Runtime.exec() 3 parame method that sets working
* directory. This needs to be done via reflection so that it can compile
* under 1.2.
*/
private Process execJava13( final ExecMetaData metaData )
throws IOException, ExecException
{
final String[] env = ExecUtil.getEnvironmentSpec( metaData );
final Object[] args =
{metaData.getCommand(),
env,
metaData.getWorkingDirectory()};
try
{
return (Process)c_execWithCWD.invoke( Runtime.getRuntime(), args );
}
catch( final IllegalAccessException iae )
{
throw new ExecException( iae.getMessage(), iae );
}
catch( final IllegalArgumentException iae )
{
throw new ExecException( iae.getMessage(), iae );
}
catch( final InvocationTargetException ite )
{
final Throwable t = ite.getTargetException();
if( t instanceof IOException )
{
t.fillInStackTrace();
throw (IOException)t;
}
else
{
throw new ExecException( t.getMessage(), t );
}
}
}
}

+ 0
- 131
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/ExecUtil.java View File

@@ -1,131 +0,0 @@
/*
* 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.nativelib.impl.launchers;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecMetaData;

/**
* A set of utility functions useful when writing CommandLaunchers.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
class ExecUtil
{
/**
* The file representing the current working directory.
*/
private static final File c_cwd;

static
{
try
{
c_cwd = ( new File( "." ) ).getCanonicalFile();
}
catch( final IOException ioe )
{
//Should never happen
throw new IllegalStateException();
}
}

/**
* Private constructor to block instantiation.
*/
private ExecUtil()
{
}

/**
* Create a new ExecMetaData representing the same command with the specified
* prefix. This is useful when you are launching the native executable via a
* script of some sort.
*/
protected static ExecMetaData prepend( final ExecMetaData metaData,
final String[] prefix )
{
final String[] original = metaData.getCommand();
final String[] command = new String[ original.length + prefix.length ];

System.arraycopy( prefix, 0, command, 0, prefix.length );
System.arraycopy( original, 0, command, prefix.length, original.length );

return new ExecMetaData( command,
metaData.getEnvironment(),
metaData.getWorkingDirectory() );
}

/**
* Utility method to check if specified file is equal
* to the current working directory.
*/
protected static boolean isCwd( final File file )
throws IOException
{
return file.getCanonicalFile().equals( getCwd() );
}

private static String[] toNativeEnvironment( final Properties environment )
throws ExecException
{
if( null == environment )
{
return null;
}
else
{
final ArrayList newEnvironment = new ArrayList();

final Iterator keys = environment.keySet().iterator();
while( keys.hasNext() )
{
final String key = (String)keys.next();
final String value = environment.getProperty( key );
newEnvironment.add( key + '=' + value );
}

return (String[])newEnvironment.toArray( new String[ newEnvironment.size() ] );
}
}

/**
* Return the current working directory of the JVM.
* This value is initialized when this class is first loaded.
*/
protected static File getCwd()
{
return c_cwd;
}

/**
* Get the native environment according to proper rules.
* Return null if no environment specified, return environment combined
* with native environment if environment data is additive else just return
* converted environment data.
*/
protected static String[] getEnvironmentSpec( final ExecMetaData metaData )
throws ExecException, IOException
{
final Properties environment = metaData.getEnvironment();
if( null == environment || 0 == environment.size() )
{
return null;
}
else
{
return toNativeEnvironment( environment );
}
}
}

+ 0
- 56
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/MacCommandLauncher.java View File

@@ -1,56 +0,0 @@
/*
* 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.nativelib.impl.launchers;

import java.io.File;
import java.io.IOException;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecMetaData;

/**
* A command launcher for Mac that uses a dodgy mechanism to change working
* directory before launching commands. This class changes the value of the
* System property "user.dir" before the command is executed and then resets
* it after the command is executed. This can have really unhealthy side-effects
* if there are multiple threads in JVM and should be used with extreme caution.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>
* @version $Revision$ $Date$
*/
public class MacCommandLauncher
implements CommandLauncher
{
/**
* Execute the specified native command.
*/
public Process exec( final ExecMetaData metaData )
throws IOException, ExecException
{
final File directory = metaData.getWorkingDirectory().getCanonicalFile();
if( ExecUtil.isCwd( directory ) )
{
final String[] env = ExecUtil.getEnvironmentSpec( metaData );
return Runtime.getRuntime().exec( metaData.getCommand(), env );
}

//WARNING: This is an ugly hack and not thread safe in the slightest way
//It can have really really undersirable side-effects if multiple threads
//are running in the JVM
try
{
System.setProperty( "user.dir", directory.toString() );
final String[] env = ExecUtil.getEnvironmentSpec( metaData );
return Runtime.getRuntime().exec( metaData.getCommand(), env );
}
finally
{
System.setProperty( "user.dir", ExecUtil.getCwd().toString() );
}
}
}

+ 0
- 1
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/Resources.properties View File

@@ -1 +0,0 @@
default.bad-dir.error=Unable to launch native command in a working directory other than "." on Java 1.2 JVMs.

+ 0
- 75
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/ScriptCommandLauncher.java View File

@@ -1,75 +0,0 @@
/*
* 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.nativelib.impl.launchers;

import java.io.IOException;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecMetaData;

/**
* A command launcher that uses an auxiliary script to launch commands in
* directories other than the current working directory. The script specified
* in the constructor is invoked with the directory passed as second argument
* and the actual command as subsequent arguments.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>
* @version $Revision$ $Date$
*/
public class ScriptCommandLauncher
implements CommandLauncher
{
private String[] m_script;

/**
* Create a command launcher whos script is a single
* command. An example would be "bin/antRun.bat".
*/
public ScriptCommandLauncher( final String script )
{
this( new String[]{script} );
}

/**
* Create a command launcher whos script takes multiple
* commands. Examples would be "perl bin/antRun.pl",
* "python bin/antRun.py", ""tcl8 bin/antRun.tcl" etc
*/
public ScriptCommandLauncher( final String[] script )
{
m_script = script;
if( null == m_script )
{
throw new NullPointerException( "script" );
}
if( 0 == m_script.length )
{
throw new IllegalArgumentException( "script" );
}
}

/**
* Launches the given command in a new process using cmd.exe to
* set the working directory.
*/
public Process exec( final ExecMetaData metaData )
throws IOException, ExecException
{
// Build the command
final String[] prefix = new String[ m_script.length + 1 ];
for( int i = 0; i < m_script.length; i++ )
{
prefix[ i ] = m_script[ i ];
}
prefix[ m_script.length ] = metaData.getWorkingDirectory().getCanonicalPath();

final ExecMetaData newMetaData = ExecUtil.prepend( metaData, prefix );
final String[] env = ExecUtil.getEnvironmentSpec( metaData );
return Runtime.getRuntime().exec( newMetaData.getCommand(), env );
}
}

+ 0
- 46
proposal/myrmidon/src/java/org/apache/aut/nativelib/impl/launchers/WinNTCommandLauncher.java View File

@@ -1,46 +0,0 @@
/*
* 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.nativelib.impl.launchers;

import java.io.IOException;
import org.apache.aut.nativelib.ExecException;
import org.apache.aut.nativelib.ExecMetaData;

/**
* A command launcher for Windows 2000/NT that uses 'cmd.exe' when launching
* commands in directories other than the current working directory.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @author <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>
* @version $Revision$ $Date$
*/
public class WinNTCommandLauncher
implements CommandLauncher
{
/**
* Launches the given command in a new process using cmd.exe to
* set the working directory.
*/
public Process exec( final ExecMetaData metaData )
throws IOException, ExecException
{
// Use cmd.exe to change to the specified directory before running
// the command
final String[] prefix = new String[ 6 ];
prefix[ 0 ] = "cmd";
prefix[ 1 ] = "/c";
prefix[ 2 ] = "cd";
prefix[ 3 ] = "/d";
prefix[ 4 ] = metaData.getWorkingDirectory().getCanonicalPath();
prefix[ 5 ] = "&&";

final ExecMetaData newMetaData = ExecUtil.prepend( metaData, prefix );
final String[] env = ExecUtil.getEnvironmentSpec( metaData );
return Runtime.getRuntime().exec( newMetaData.getCommand(), env );
}
}

+ 0
- 36
proposal/myrmidon/src/java/org/apache/aut/vfs/AllFileSelector.java View File

@@ -1,36 +0,0 @@
/*
* 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;

/**
* A {@link FileSelector} which selects everything.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class AllFileSelector
implements FileSelector
{
/**
* Determines if a file or folder should be selected.
*/
public boolean includeFile( final FileSelectInfo fileInfo )
throws FileSystemException
{
return true;
}

/**
* Determines whether a folder should be traversed.
*/
public boolean traverseDescendents( final FileSelectInfo fileInfo )
throws FileSystemException
{
return true;
}
}

+ 0
- 46
proposal/myrmidon/src/java/org/apache/aut/vfs/FileConstants.java View File

@@ -1,46 +0,0 @@
/*
* 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;

/**
* Several constants for use in the VFS API.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileConstants
{
/**
* A {@link FileSelector} which selects only the base file/folder.
*/
FileSelector SELECT_SELF = new FileDepthSelector( 0, 0 );

/**
* A {@link FileSelector} which selects the base file/folder and its
* direct children.
*/
FileSelector SELECT_SELF_AND_CHILDREN = new FileDepthSelector( 0, 1 );

/**
* A {@link FileSelector} which selects only the direct children
* of the base folder.
*/
FileSelector SELECT_CHILDREN = new FileDepthSelector( 1, 1 );

/**
* A {@link FileSelector} which selects all the descendents of the
* base folder, but does not select the base folder itself.
*/
FileSelector EXCLUDE_SELF = new FileDepthSelector( 1, Integer.MAX_VALUE );

/**
* A {@link FileSelector} which selects the base file/folder, plus all
* its descendents.
*/
FileSelector SELECT_ALL = new AllFileSelector();
}

+ 0
- 158
proposal/myrmidon/src/java/org/apache/aut/vfs/FileContent.java View File

@@ -1,158 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs;

import java.io.InputStream;
import java.io.OutputStream;

/**
* This interface is used to access the data content of a file.
*
* <p>To read from a file, use the {@link #getInputStream} method.
*
* <p>To write to a file, use the {@link #getOutputStream} method. This
* method will create the file and the parent folder, if necessary.
*
* <p>To prevent concurrency problems, only a single <code>InputStream</code>,
* or <code>OutputStream</code> may be open at any time, for each file.
*
* <p>TODO - allow multiple input streams?
*
* @see FileObject#getContent
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileContent
{
/**
* Returns the file which this is the content of.
*/
FileObject getFile();

/**
* Determines the size of the file, in bytes.
*
* @return
* The size of the file, in bytes.
*
* @throws FileSystemException
* If the file does not exist, or is being written to, or on error
* determining the size.
*/
long getSize() throws FileSystemException;

/**
* Determines the last-modified timestamp of the file.
*
* @return
* The last-modified timestamp.
*
* @throws FileSystemException
* If the file does not exist, or is being written to, or on error
* determining the last-modified timestamp.
*/
long getLastModifiedTime() throws FileSystemException;

/**
* Sets the last-modified timestamp of the file. Creates the file if
* it does not exist.
*
* @param modTime
* The time to set the last-modified timestamp to.
*
* @throws FileSystemException
* If the file is read-only, or is being read, or on error setting
* the last-modified timestamp.
*/
void setLastModifiedTime( long modTime ) throws FileSystemException;

/**
* Gets the value of an attribute of the file's content.
*
* <p>TODO - change to <code>Map getAttributes()</code> instead?
*
* <p>TODO - define the standard attribute names, and define which attrs
* are guaranteed to be present.
*
* @param attrName
* The name of the attribute.
*
* @return
* The value of the attribute.
*
* @throws FileSystemException
* If the file does not exist, or is being written, or if the
* attribute is unknown.
*/
Object getAttribute( String attrName ) throws FileSystemException;

/**
* Sets the value of an attribute of the file's content. Creates the
* file if it does not exist.
*
* @param attrName
* The name of the attribute.
*
* @param value
* The value of the attribute.
*
* @throws FileSystemException
* If the file is read-only, or is being read, or if the attribute
* is not supported, or on error setting the attribute.
*/
void setAttribute( String attrName, Object value ) throws FileSystemException;

/**
* Returns an input stream for reading the file's content.
*
* <p>There may only be a single input or output stream open for the
* file at any time.
*
* @return
* An input stream to read the file's content from. The input
* stream is buffered, so there is no need to wrap it in a
* <code>BufferedInputStream</code>.
*
* @throws FileSystemException
* If the file does not exist, or is being read, or is being written,
* or on error opening the stream.
*/
InputStream getInputStream() throws FileSystemException;

/**
* Returns an output stream for writing the file's content.
*
* If the file does not exist, this method creates it, and the parent
* folder, if necessary. If the file does exist, it is replaced with
* whatever is written to the output stream.
*
* <p>There may only be a single input or output stream open for the
* file at any time.
*
* @return
* An output stream to write the file's content to. The stream is
* buffered, so there is no need to wrap it in a
* <code>BufferedOutputStream</code>.
*
* @throws FileSystemException
* If the file is read-only, or is being read, or is being written,
* or on error opening the stream.
*/
OutputStream getOutputStream() throws FileSystemException;

/**
* Closes all resources used by the content, including any open stream.
* Commits pending changes to the file.
*
* <p>This method is a hint to the implementation that it can release
* resources. This object can continue to be used after calling this
* method.
*/
void close() throws FileSystemException;
}

+ 0
- 46
proposal/myrmidon/src/java/org/apache/aut/vfs/FileDepthSelector.java View File

@@ -1,46 +0,0 @@
/*
* 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;

/**
* A {@link FileSelector} which selects all files in a particular depth range.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class FileDepthSelector
implements FileSelector
{
private final int m_minDepth;
private final int m_maxDepth;

public FileDepthSelector( int minDepth, int maxDepth )
{
m_minDepth = minDepth;
m_maxDepth = maxDepth;
}

/**
* Determines if a file or folder should be selected.
*/
public boolean includeFile( final FileSelectInfo fileInfo )
throws FileSystemException
{
final int depth = fileInfo.getDepth();
return m_minDepth <= depth && depth <= m_maxDepth;
}

/**
* Determines whether a folder should be traversed.
*/
public boolean traverseDescendents( final FileSelectInfo fileInfo )
throws FileSystemException
{
return fileInfo.getDepth() < m_maxDepth;
}
}

+ 0
- 101
proposal/myrmidon/src/java/org/apache/aut/vfs/FileName.java View File

@@ -1,101 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs;

/**
* The interface is used to perform operations on a file name. File names
* are immutable, and work correctly as keys in hash tables.
*
* @see FileObject
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileName
{
/**
* Returns the base name of this file. The base name is the last element
* of the file name. For example the base name of
* <code>/somefolder/somefile</code> is <code>somefile</code>.
*
* <p>The root file of a file system has an empty base name.
*/
String getBaseName();

/**
* Returns the absolute path of this file, within its file system. This
* path is normalised, so that <code>.</code> and <code>..</code> elements
* have been removed. Also, the path only contains <code>/</code> as its
* separator character. The path always starts with <code>/</code>
*
* <p>The root of a file system has <code>/</code> as its absolute path.
*/
String getPath();

/**
* Returns the absolute URI of this file.
*/
String getURI();

/**
* Returns the file name of the parent of this file. The root of a
* file system has no parent.
*
* @return
* A {@link FileName} object representing the parent name. Returns
* null for the root of a file system.
*/
FileName getParent();

/**
* Resolves a name, relative to this file name. Equivalent to calling
* <code>resolveName( path, NameScope.FILE_SYSTEM )</code>.
*
* @param name
* The name to resolve.
*
* @return
* A {@link FileName} object representing the resolved file name.
*
* @throws FileSystemException
* If the name is invalid.
*/
FileName resolveName( String name ) throws FileSystemException;

/**
* Resolves a name, relative to this file name. Refer to {@link NameScope}
* for a description of how names are resolved.
*
* @param name
* The name to resolve.
*
* @param scope
* The scope to use when resolving the name.
*
* @return
* A {@link FileName} object representing the resolved file name.
*
* @throws FileSystemException
* If the name is invalid.
*/
FileName resolveName( String name, NameScope scope ) throws FileSystemException;

/**
* Converts a file name to a relative name, relative to this file name.
*
* @param name
* The name to convert to a relative path.
*
* @return
* The relative name.
*
* @throws FileSystemException
* On error.
*/
String getRelativeName( FileName name ) throws FileSystemException;
}

+ 0
- 262
proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java View File

@@ -1,262 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs;

import java.io.File;

/**
* This interface represents a file, and is used to access the content and
* structure of the file.
*
* <p>Files are arranged in a hierarchy. Each hierachy forms a
* <i>file system</i>. A file system represents things like a local OS
* file system, a windows share, an HTTP server, or the contents of a Zip file.
*
* <p>There are two types of files: <i>Folders</i>, which contain other files,
* and <i>normal files</i>, which contain data, or <i>content</i>. A folder may
* not have any content, and a normal file cannot contain other files.
*
* <h4>File Naming</h4>
*
* <p>TODO - write this.
*
* <h4>Reading and Writing a File</h4>
*
* <p>Reading and writing a file, and all other operations on the file's
* <i>content</i>, is done using the {@link FileContent} object returned
* by {@link #getContent}.
*
* <h4>Creating and Deleting a File</h4>
*
* <p>A file is created using either {@link #create}, or by writing to the
* file using one of the {@link FileContent} methods.
*
* <p>A file is deleted using {@link #delete}. Deletion is recursive, so
* that when a folder is deleted, so are all its child files.
*
* <h4>Finding Files</h4>
*
* <p>Other files in the <i>same</i> file system as this file can be found using:
* <ul>
* <li>{@link #resolveFile} to find another file relative to this file.
* <li>{@link #getChildren} to find the children of this file.
* <li>{@link #getParent} to find the folder containing this file.
* <li>{@link #getRoot} to find the root folder of the file system.
* </ul>
*
* <p>To find files in another file system, use a {@link FileSystemManager}.
*
* @see FileSystemManager
* @see FileContent
* @see FileName
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileObject
{
/**
* Returns the name of this file.
*/
FileName getName();

/**
* Determines if this file exists.
*
* @return
* <code>true</code> if this file exists, <code>false</code> if not.
*
* @throws FileSystemException
* On error determining if this file exists.
*/
boolean exists() throws FileSystemException;

/**
* Returns this file's type.
*
* @return
* Either {@link FileType#FILE} or {@link FileType#FOLDER}. Never
* returns null.
*
* @throws FileSystemException
* If the file does not exist, or on error determining the file's type.
*/
FileType getType() throws FileSystemException;

/**
* Returns the folder that contains this file.
*
* @return
* The folder that contains this file. Returns null if this file is
* the root of a file system.
*
* @throws FileSystemException
* On error finding the file's parent.
*/
FileObject getParent() throws FileSystemException;

/**
* Returns the root of the file system containing this file.
*
* @return
* The root of the file system.
*
* @throws FileSystemException
* On error finding the root of the file system.
*/
FileObject getRoot() throws FileSystemException;

/**
* Lists the children of this file.
*
* @return
* An array containing the children of this file. The array is
* unordered. If the file does not have any children, a zero-length
* array is returned. This method never returns null.
*
* @throws FileSystemException
* If this file does not exist, or is not a folder, or on error
* listing this file's children.
*/
FileObject[] getChildren() throws FileSystemException;

/**
* Finds a file, relative to this file. Refer to {@link NameScope}
* for a description of how names are resolved in the different scopes.
*
* @param name
* The name to resolve.
*
* @return
* The file.
*
* @throws FileSystemException
* On error parsing the path, or on error finding the file.
*/
FileObject resolveFile( String name, NameScope scope ) throws FileSystemException;

/**
* Finds a file, relative to this file. Equivalent to calling
* <code>resolveFile( path, NameScope.FILE_SYSTEM )</code>.
*
* @param path
* The path of the file to locate. Can either be a relative
* path or an absolute path.
*
* @return
* The file.
*
* @throws FileSystemException
* On error parsing the path, or on error finding the file.
*/
FileObject resolveFile( String path ) throws FileSystemException;

/**
* Deletes this file, and all descendents. Does nothing if the file
* does not exist.
*
* <p>This method is not transactional. If it fails and throws an
* exception, this file will potentially only be partially deleted.
*
* @param selector The selector to use to select which files to delete.
*
* @throws FileSystemException
* If this file or one of its descendents is read-only, or on error
* deleting this file or one of its descendents.
*/
void delete( FileSelector selector ) throws FileSystemException;

/**
* Creates this file, if it does not exist. Also creates any ancestor
* folders which do not exist. This method does nothing if the file
* already exists with the requested type.
*
* @param type
* The type of file to create.
*
* @throws FileSystemException
* If the file already exists with the wrong type, or the parent
* folder is read-only, or on error creating this file or one of
* its ancestors.
*/
void create( FileType type ) throws FileSystemException;

/**
* Copies another file, and all its descendents, to this file.
*
* If this file does not exist, it is created. Its parent folder is also
* created, if necessary. If this file does exist, it is deleted first.
*
* <p>This method is not transactional. If it fails and throws an
* exception, this file will potentially only be partially copied.
*
* @param srcFile The source file to copy.
* @param selector The selector to use to select which files to copy.
*
* @throws FileSystemException
* If this file is read-only, or if the source file does not exist,
* or on error copying the file.
*/
void copyFrom( FileObject srcFile, FileSelector selector ) throws FileSystemException;

/**
* Creates a temporary local copy of this file, and its descendents. If
* this file is a local file, a copy is not made.
*
* <p>Note that the local copy may include additonal files, that were
* not selected by the given selector.
*
* @todo Add options to indicate whether the caller is happy to deal with
* extra files being present locally (eg if the file has been
* replicated previously), or whether the caller expects only
* the selected files to be present.
*
* @param selector the selector to use to select the files to replicate.
* @return The local copy of this file.
*
* @throws FileSystemException
* If this file does not exist, or on error replicating the file.
*/
File replicateFile( FileSelector selector ) throws FileSystemException;

/**
* Returns this file's content. The {@link FileContent} returned by this
* method can be used to read and write the content of the file.
*
* <p>This method can be called if the file does not exist, and
* the returned {@link FileContent} can be used to create the file
* by writing its content.
*
* @todo Do not throw an exception if this file is a folder. Instead,
* throw the exceptions when (if) any methods on the returned object
* are called. This is to hand 2 cases: when the folder is deleted
* and recreated as a file, and to allow attributes of the folder
* to be set (last modified time, permissions, etc).
*
* @return
* This file's content.
*
* @throws FileSystemException
* If this file is a folder.
*/
FileContent getContent() throws FileSystemException;

/**
* Closes this file, and its content. This method is a hint to the
* implementation that it can release any resources asociated with
* the file.
*
* <p>The file object can continue to be used after this method is called.
*
* @see FileContent#close
*
* @throws FileSystemException
* On error closing the file.
*/
void close() throws FileSystemException;
}

+ 0
- 33
proposal/myrmidon/src/java/org/apache/aut/vfs/FileSelectInfo.java View File

@@ -1,33 +0,0 @@
/*
* 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;

/**
* Information about a file, that is used to select files during the
* traversal of a hierarchy.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileSelectInfo
{
/**
* Returns the base folder of the traversal.
*/
FileObject getBaseFolder();

/**
* Returns the file (or folder) to be considered.
*/
FileObject getFile();

/**
* Returns the depth of the file relative to the base folder.
*/
int getDepth();
}

+ 0
- 38
proposal/myrmidon/src/java/org/apache/aut/vfs/FileSelector.java View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs;

/**
* This interface is used to select files when traversing a file hierarchy.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileSelector
{
/**
* Determines if a file or folder should be selected.
*
* @param fileInfo the file or folder to select.
* @return true if the file should be selected.
*/
boolean includeFile( FileSelectInfo fileInfo )
throws FileSystemException;

/**
* Determines whether a folder should be traversed. If this method returns
* true, {@link #includeFile} is called for each of the children of
* the folder, and each of the child folders is recursively traversed.
*
* @param fileInfo the file or folder to select.
*
* @return true if the folder should be traversed.
*/
boolean traverseDescendents( FileSelectInfo fileInfo )
throws FileSystemException;
}

+ 0
- 56
proposal/myrmidon/src/java/org/apache/aut/vfs/FileSystemException.java View File

@@ -1,56 +0,0 @@
/*
* 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;

/**
* Thrown for file system errors.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class FileSystemException
extends Exception
{
/**
* The Throwable that caused this exception to be thrown.
*/
private final Throwable m_throwable;

/**
* Constructs exception with the specified detail message.
*
* @param message the detail message.
*/
public FileSystemException( final String message )
{
this( message, null );
}

/**
* Constructs exception with the specified detail message.
*
* @param message the detail message.
* @param throwable the cause.
*/
public FileSystemException( final String message,
final Throwable throwable )
{
super( message );
m_throwable = throwable;
}

/**
* Retrieve root cause of the exception.
*
* @return the root cause
*/
public final Throwable getCause()
{
return m_throwable;
}
}

+ 0
- 148
proposal/myrmidon/src/java/org/apache/aut/vfs/FileSystemManager.java View File

@@ -1,148 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs;

import java.io.File;

/**
* A FileSystemManager is manages a set of file systems. This interface is
* used to locate a {@link FileObject} by name from one of those file systems.
*
* <p>To locate a {@link FileObject}, use one of the <code>resolveFile()</code>
* methods.</p>
*
* <h4><a name="naming">File Naming</a></h4>
*
* <p>A file system manager can recognise several types of file names:
*
* <ul>
*
* <li><p>Absolute URI. These must start with a scheme, such as
* <code>file:</code> or <code>ftp:</code>, followed by a scheme dependent
* file name. Some examples:</p>
* <pre>
* file:/c:/somefile
* ftp://somewhere.org/somefile
* </pre>
*
* <li><p>Absolute local file name. For example,
* <code>/home/someuser/a-file</code> or <code>c:\dir\somefile.html</code>.
* Elements in the name can be separated using any of the following
* characters: <code>/</code>, <code>\</code>, or the native file separator
* character. For example, the following file names are the same:</p>
* <pre>
* c:\somedir\somefile.xml
* c:/somedir/somefile.xml
* </pre>
*
* <li><p>Relative path. For example: <code>../somefile</code> or
* <code>somedir/file.txt</code>. The file system manager resolves relative
* paths against its <i>base file</i>. Elements in the relative path can be
* separated using <code>/</code>, <code>\</code>, or file system specific
* separator characters. Relative paths may also contain <code>..</code> and
* <code>.</code> elements. See {@link FileObject#resolveFile} for more details.</p>
*
* </ul>
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant:role shorthand="file-system-manager"
*/
public interface FileSystemManager
{
String ROLE = FileSystemManager.class.getName();

/**
* Returns the base file used to resolve relative paths.
*/
FileObject getBaseFile();

/**
* Locates a file by name. Equivalent to calling
* <code>resolveFile(uri, getBaseName())</code>.
*
* @param name
* The name of the file.
*
* @throws FileSystemException
* On error parsing the file name.
*/
FileObject resolveFile( String name )
throws FileSystemException;

/**
* Locates a file by name. The name is resolved as described
* <a href="#naming">above</a>. That is, the name can be either
* an absolute URI, an absolute file name, or a relative path to
* be resolved against <code>baseFile</code>.
*
* <p>Note that the file does not have to exist when this method is called.
*
* @param name
* The name of the file.
*
* @param baseFile
* The base file to use to resolve relative paths.
*
* @throws FileSystemException
* On error parsing the file name.
*/
FileObject resolveFile( FileObject baseFile, String name )
throws FileSystemException;

/**
* Locates a file by name. See {@link #resolveFile(FileObject, String)}
* for details.
*
* @param baseFile
* The base file to use to resolve relative paths.
*
* @param name
* The name of the file.
*
* @throws FileSystemException
* On error parsing the file name.
*
*/
FileObject resolveFile( File baseFile, String name )
throws FileSystemException;

/**
* Converts a local file into a {@link FileObject}.
*
* @param file
* The file to convert.
*
* @return
* The {@link FileObject} that represents the local file.
*
* @throws FileSystemException
* On error converting the file.
*/
FileObject convert( File file )
throws FileSystemException;

/**
* Creates a layered file system. A layered file system is a file system
* that is created from the contents of another file, such as a zip
* or tar file.
*
* @param provider
* The name of the file system provider to use. This name is
* the same as the scheme used in URI to identify the provider.
*
* @param file
* The file to use to create the file system.
*
* @throws FileSystemException
* On error creating the file system.
*/
FileObject createFileSystem( String provider, FileObject file )
throws FileSystemException;
}

+ 0
- 53
proposal/myrmidon/src/java/org/apache/aut/vfs/FileType.java View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
package org.apache.aut.vfs;

import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* An enumeration that represents a file's type.
*/
public final class FileType
{
private static final Resources REZ =
ResourceManager.getPackageResources( FileType.class );

/**
* A folder, which can contain other files, but does not have any data
* content.
*/
public static final FileType FOLDER = new FileType( REZ.getString( "folder.name" ) );

/**
* A regular file, which has data content, but cannot contain other files.
*/
public static final FileType FILE = new FileType( REZ.getString( "file.name" ) );

private String m_name;

private FileType( String name )
{
m_name = name;
}

/** Returns the name of the type. */
public String toString()
{
return m_name;
}

/** Returns the name of the type. */
public String getName()
{
return m_name;
}
}

+ 0
- 76
proposal/myrmidon/src/java/org/apache/aut/vfs/NameScope.java View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs;

/**
* An enumerated type for file name scope, used when resolving a name relative
* to a file.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public final class NameScope
{
/**
* 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 static final 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 static final NameScope DESCENDENT = new NameScope( "descendent" );

/**
* 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, or the base
* files itself.
*/
public static final NameScope DESCENDENT_OR_SELF = new NameScope( "descendent_or_self" );

/**
* Resolve against files in the same file system as the base file.
*
* <p>If the supplied name is an absolute path, then it is resolved
* relative to the root of the file system that the base file belongs to.
* If a relative name is supplied, then it is resolved relative to the base
* file.
*
* <p>The path may use any mix of <code>/</code>, <code>\</code>, or file
* system specific separators to separate elements in the path. It may
* also contain <code>.</code> and <code>..</code> elements.
*
* <p>A path is considered absolute if it starts with a separator character,
* and relative if it does not.
*/
public static final NameScope FILE_SYSTEM = new NameScope( "filesystem" );

private final String m_name;

private NameScope( final String name )
{
m_name = name;
}

/** Returns the name of the scope. */
public String toString()
{
return m_name;
}

/** Returns the name of the scope. */
public String getName()
{
return m_name;
}
}

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

@@ -1,2 +0,0 @@
folder.name=folder
file.name=file

+ 0
- 98
proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultFileReplicator.java View File

@@ -1,98 +0,0 @@
/*
* 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.impl;

import java.io.File;
import java.util.ArrayList;
import org.apache.aut.vfs.FileConstants;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSelector;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.FileReplicator;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;

/**
* A simple file replicator.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class DefaultFileReplicator
extends AbstractLogEnabled
implements FileReplicator, Disposable
{
private static final Resources REZ =
ResourceManager.getPackageResources( DefaultFileReplicator.class );

private final DefaultFileSystemManager m_manager;
private final File m_tempDir;
private final ArrayList m_copies = new ArrayList();
private long m_filecount;

public DefaultFileReplicator( final DefaultFileSystemManager manager )
{
m_manager = manager;
m_tempDir = new File( "ant_vfs_cache" ).getAbsoluteFile();
}

/**
* Deletes the temporary files.
*/
public void dispose()
{
while( m_copies.size() > 0 )
{
final FileObject file = (FileObject)m_copies.remove( 0 );
try
{
file.delete( FileConstants.SELECT_ALL );
}
catch( final FileSystemException e )
{
final String message = REZ.getString( "delete-temp.warn", file.getName() );
getLogger().warn( message, e );
}
}
}

/**
* Creates a local copy of the file, and all its descendents.
*/
public File replicateFile( final FileObject srcFile,
final FileSelector selector )
throws FileSystemException
{
// TODO - this is awful

// Create a unique-ish file name
final String basename = m_filecount + "_" + srcFile.getName().getBaseName();
m_filecount++;
final File file = new File( m_tempDir, basename );

try
{
// Copy from the source file
final FileObject destFile = m_manager.convert( file );
destFile.copyFrom( srcFile, selector );

// Keep track of the copy
m_copies.add( destFile );
}
catch( final FileSystemException e )
{
final String message = REZ.getString( "replicate-file.error", srcFile.getName(), file );
throw new FileSystemException( message, e );
}

return file;
}

}

+ 0
- 269
proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultFileSystemManager.java View File

@@ -1,269 +0,0 @@
/*
* 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.impl;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileSystemManager;
import org.apache.aut.vfs.provider.FileReplicator;
import org.apache.aut.vfs.provider.FileSystemProvider;
import org.apache.aut.vfs.provider.LocalFileSystemProvider;
import org.apache.aut.vfs.provider.UriParser;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;

/**
* A default file system manager implementation.
*
* @todo - Extract an AbstractFileSystemManager super-class from this class.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class DefaultFileSystemManager
extends AbstractLogEnabled
implements FileSystemManager, Disposable
{
private static final Resources REZ
= ResourceManager.getPackageResources( DefaultFileSystemManager.class );

/** The provider for local files. */
private LocalFileSystemProvider m_localFileProvider;

/** The file replicator to use. */
private final DefaultFileReplicator m_fileReplicator = new DefaultFileReplicator( this );

/** Mapping from URI scheme to FileSystemProvider. */
private final Map m_providers = new HashMap();

/** The base file to use for relative URI. */
private FileObject m_baseFile;

/**
* Registers a file system provider.
*/
public void addProvider( final String urlScheme,
final FileSystemProvider provider )
throws FileSystemException
{
addProvider( new String[]{urlScheme}, provider );
}

/**
* Registers a file system provider.
*/
public void addProvider( final String[] urlSchemes,
final FileSystemProvider provider )
throws FileSystemException
{
// Check for duplicates
for( int i = 0; i < urlSchemes.length; i++ )
{
final String scheme = urlSchemes[ i ];
if( m_providers.containsKey( scheme ) )
{
final String message = REZ.getString( "multiple-providers-for-scheme.error", scheme );
throw new FileSystemException( message );
}
}

// Contextualise
setupLogger( provider );
provider.setContext( new DefaultProviderContext( this ) );

// Add to map
for( int i = 0; i < urlSchemes.length; i++ )
{
final String scheme = urlSchemes[ i ];
m_providers.put( scheme, provider );
}

if( provider instanceof LocalFileSystemProvider )
{
m_localFileProvider = (LocalFileSystemProvider)provider;
}
}

/**
* Returns the file replicator.
*
* @return The file replicator. Never returns null.
*/
public FileReplicator getReplicator()
throws FileSystemException
{
return m_fileReplicator;
}

/**
* Enable logging.
*/
public void enableLogging( final Logger logger )
{
super.enableLogging( logger );
setupLogger( m_fileReplicator );
}

/**
* Closes all files created by this manager, and cleans up any temporary
* files.
*/
public void dispose()
{
// Dispose the providers (making sure we only dispose each provider
// once
final Set providers = new HashSet();
providers.addAll( m_providers.values() );
for( Iterator iterator = providers.iterator(); iterator.hasNext(); )
{
Object provider = iterator.next();
if( provider instanceof Disposable )
{
Disposable disposable = (Disposable)provider;
disposable.dispose();
}
}
m_providers.clear();

m_fileReplicator.dispose();
}

/**
* Sets the base file to use when resolving relative URI.
*/
public void setBaseFile( final FileObject baseFile ) throws FileSystemException
{
m_baseFile = baseFile;
}

/**
* Sets the base file to use when resolving relative URI.
*/
public void setBaseFile( final File baseFile ) throws FileSystemException
{
m_baseFile = getLocalFileProvider().findLocalFile( baseFile );
}

/**
* Returns the base file used to resolve relative URI.
*/
public FileObject getBaseFile()
{
return m_baseFile;
}

/**
* Locates a file by URI.
*/
public FileObject resolveFile( final String uri ) throws FileSystemException
{
return resolveFile( m_baseFile, uri );
}

/**
* Locates a file by URI.
*/
public FileObject resolveFile( final File baseFile, final String uri )
throws FileSystemException
{
final FileObject baseFileObj = getLocalFileProvider().findLocalFile( baseFile );
return resolveFile( baseFileObj, uri );
}

/**
* Resolves a URI, relative to a base file.
*/
public FileObject resolveFile( final FileObject baseFile, final String uri )
throws FileSystemException
{
// Extract the scheme
final String scheme = UriParser.extractScheme( uri );
if( scheme != null )
{
// An absolute URI - locate the provider
final FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme );
if( provider != null )
{
return provider.findFile( baseFile, uri );
}
}

// Decode the URI (remove %nn encodings)
final String decodedUri = UriParser.decode( uri );

// Handle absolute file names
if( m_localFileProvider != null
&& m_localFileProvider.isAbsoluteLocalName( decodedUri ) )
{
return m_localFileProvider.findLocalFile( decodedUri );
}

if( scheme != null )
{
// Assume a bad scheme
final String message = REZ.getString( "unknown-scheme.error", scheme, uri );
throw new FileSystemException( message );
}

// Assume a relative name - use the supplied base file
if( baseFile == null )
{
final String message = REZ.getString( "find-rel-file.error", uri );
throw new FileSystemException( message );
}
return baseFile.resolveFile( decodedUri );
}

/**
* Converts a local file into a {@link FileObject}.
*/
public FileObject convert( final File file )
throws FileSystemException
{
return getLocalFileProvider().findLocalFile( file );
}

/**
* Creates a layered file system.
*/
public FileObject createFileSystem( final String scheme,
final FileObject file )
throws FileSystemException
{
FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme );
if( provider == null )
{
final String message = REZ.getString( "unknown-provider.error", scheme );
throw new FileSystemException( message );
}
return provider.createFileSystem( scheme, file );
}

/**
* Locates the local file provider.
*/
private LocalFileSystemProvider getLocalFileProvider()
throws FileSystemException
{
if( m_localFileProvider == null )
{
final String message = REZ.getString( "no-local-file-provider.error" );
throw new FileSystemException( message );
}
return m_localFileProvider;
}
}

+ 0
- 47
proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultProviderContext.java View File

@@ -1,47 +0,0 @@
/*
* 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.impl;

import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.FileReplicator;
import org.apache.aut.vfs.provider.FileSystemProviderContext;

/**
* A provider context implementation.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
final class DefaultProviderContext
implements FileSystemProviderContext
{
private final DefaultFileSystemManager m_manager;

public DefaultProviderContext( final DefaultFileSystemManager manager )
{
m_manager = manager;
}

/**
* Locate a file by name.
*/
public FileObject resolveFile( final FileObject baseFile, final String name )
throws FileSystemException
{
return m_manager.resolveFile( baseFile, name );
}

/**
* Locates a file replicator for the provider to use.
*/
public FileReplicator getReplicator() throws FileSystemException
{
return m_manager.getReplicator();
}
}

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

@@ -1,8 +0,0 @@
# DefaultFileSystemManager
unknown-scheme.error=Unknown scheme "{0}" in URI "{1}".
find-rel-file.error=Could not find file with URI "{0}" because it is a relative path, and no base URI was provided.
multiple-providers-for-scheme.error=Multiple file system providers registered for URL scheme "{0}".
unknown-provider.error=No file system provider is registered for URI scheme "{0}".
no-local-file-provider.error=Could not find a file system provider which can handle local files.
replicate-file.error=Could not replicate "{0}" to "{1}".
delete-temp.warn=Could not clean up temporary file "{0}".

+ 0
- 10
proposal/myrmidon/src/java/org/apache/aut/vfs/package.html View File

@@ -1,10 +0,0 @@
<body>
<p>This package contains the interfaces used to access the VFS.</p>

<p>A {@link vfs.filesystem.FileSystemManager} is the starting point for
all file system access. It is used to locate a {@link vfs.filesystem.FileObject}
by name. Files are accessed using the {@link vfs.filesystem.FileObject}
interface. This interface allows a file's structure and content to be
accessed.</p>

</body>

+ 0
- 805
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileObject.java View File

@@ -1,805 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.aut.vfs.FileConstants;
import org.apache.aut.vfs.FileContent;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSelector;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.NameScope;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.avalon.excalibur.io.IOUtil;

/**
* A partial file object implementation.
*
* @todo Chop this class up - move all the protected methods to several
* interfaces, so that structure and content can be separately overridden.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public abstract class AbstractFileObject
implements FileObject
{
private static final Resources REZ =
ResourceManager.getPackageResources( AbstractFileObject.class );

private static final FileObject[] EMPTY_FILE_ARRAY = {};

private FileName m_name;
private AbstractFileSystem m_fs;
private DefaultFileContent m_content;

// Cached info
private boolean m_attached;
private AbstractFileObject m_parent;
private FileType m_type;
private FileObject[] m_children;

protected AbstractFileObject( FileName name, AbstractFileSystem fs )
{
m_name = name;
m_fs = fs;
}

/**
* Returns true if this file is read-only.
*/
protected boolean isReadOnly()
{
return false;
}

/**
* Attaches this file object to its file resource. This method is called
* before any of the doBlah() or onBlah() methods. Sub-classes can use
* this method to perform lazy initialisation.
*/
protected void doAttach() throws Exception
{
}

/**
* Detaches this file object from its file resource.
*
* <p>Called when this file is closed, or its type changes. Note that
* the file object may be reused later, so should be able to be reattached.
*/
protected void doDetach()
{
}

/**
* Determines the type of the file, returns null if the file does not
* exist. The return value of this method is cached, so the
* implementation can be expensive.
*/
protected abstract FileType doGetType() throws Exception;

/**
* Lists the children of the file. Is only called if {@link #doGetType}
* returns {@link FileType#FOLDER}. The return value of this method
* is cached, so the implementation can be expensive.
*/
protected abstract String[] doListChildren() throws Exception;

/**
* Deletes the file. Is only called when:
* <ul>
* <li>{@link #isReadOnly} returns false.
* <li>{@link #doGetType} does not return null.
* <li>This file has no children.
* </ul>
*/
protected void doDelete() throws Exception
{
final String message = REZ.getString( "delete-not-supported.error" );
throw new FileSystemException( message );
}

/**
* Creates this file as a folder. Is only called when:
* <ul>
* <li>{@link #isReadOnly} returns false.
* <li>{@link #doGetType} returns null.
* <li>The parent folder exists or this file is the root of the file
* system.
* </ul>
*/
protected void doCreateFolder() throws Exception
{
final String message = REZ.getString( "create-folder-not-supported.error" );
throw new FileSystemException( message );
}

/**
* Creates a local copy of this file.
*/
protected File doReplicateFile( final FileSelector selector ) throws FileSystemException
{
final FileReplicator replicator = m_fs.getContext().getReplicator();
return replicator.replicateFile( this, selector );
}

/**
* Called when the children of this file change.
*/
protected void onChildrenChanged()
{
}

/**
* Returns the size of the file content (in bytes). Is only called if
* {@link #doGetType} returns {@link FileType#FILE}.
*/
protected abstract long doGetContentSize() throws Exception;

/**
* Creates an input stream to read the file content from. Is only called
* if {@link #doGetType} returns {@link FileType#FILE}.
*
* <p>There is guaranteed never to be more than one stream for this file
* (input or output) open at any given time.
*
* <p>The returned stream does not have to be buffered.
*/
protected abstract InputStream doGetInputStream() throws Exception;

/**
* Creates an output stream to write the file content to. Is only
* called if:
* <ul>
* <li>This file is not read-only.
* <li>{@link #doGetType} returns {@link FileType#FILE}, or
* {@link #doGetType} returns null, and the file's parent exists
* and is a folder.
* </ul>
*
* <p>There is guaranteed never to be more than one stream for this file
* (input or output) open at any given time.
*
* <p>The returned stream does not have to be buffered.
*/
protected OutputStream doGetOutputStream() throws Exception
{
final String message = REZ.getString( "write-not-supported.error" );
throw new FileSystemException( message );
}

/**
* Notification of the output stream being closed.
* TODO - get rid of this.
*/
protected void doEndOutput() throws Exception
{
}

/**
* Notification of the input stream being closed.
* TODO - get rid of this.
*/
protected void doEndInput() throws Exception
{
}

/**
* Returns the URI of the file.
*/
public String toString()
{
return m_name.getURI();
}

/**
* Returns the name of the file.
*/
public FileName getName()
{
return m_name;
}

/**
* Determines if the file exists.
*/
public boolean exists() throws FileSystemException
{
attach();
return ( m_type != null );
}

/**
* Returns the file's type.
*/
public FileType getType() throws FileSystemException
{
attach();
if( m_type == null )
{
final String message = REZ.getString( "get-type-no-exist.error", m_name );
throw new FileSystemException( message );
}
return m_type;
}

/**
* Returns the parent of the file.
*/
public FileObject getParent() throws FileSystemException
{
if( this == m_fs.getRoot() )
{
// Root file has no parent
return null;
}

// Locate the parent of this file
if( m_parent == null )
{
m_parent = (AbstractFileObject)m_fs.findFile( m_name.getParent() );
}
return m_parent;
}

/**
* Returns the root of the file system containing the file.
*/
public FileObject getRoot() throws FileSystemException
{
return m_fs.getRoot();
}

/**
* Returns the children of the file.
*/
public FileObject[] getChildren() throws FileSystemException
{
attach();
if( m_type == null )
{
final String message = REZ.getString( "list-children-no-exist.error", m_name );
throw new FileSystemException( message );
}
if( m_type != FileType.FOLDER )
{
final String message = REZ.getString( "list-children-not-folder.error", m_name );
throw new FileSystemException( message );
}

// Use cached info, if present
if( m_children != null )
{
return m_children;
}

// List the children
String[] files;
try
{
files = doListChildren();
}
catch( Exception exc )
{
final String message = REZ.getString( "list-children.error", m_name );
throw new FileSystemException( message, exc );
}

if( files == null || files.length == 0 )
{
// No children
m_children = EMPTY_FILE_ARRAY;
}
else
{
// Create file objects for the children
m_children = new FileObject[ files.length ];
for( int i = 0; i < files.length; i++ )
{
String file = files[ i ];
m_children[ i ] = m_fs.findFile( m_name.resolveName( file, NameScope.CHILD ) );
}
}

return m_children;
}

/**
* Returns a child by name.
*/
public FileObject resolveFile( String name, NameScope scope ) throws FileSystemException
{
// TODO - cache children (only if they exist)
return m_fs.findFile( m_name.resolveName( name, scope ) );
}

/**
* Finds a file, relative to this file.
*
* @param path
* The path of the file to locate. Can either be a relative
* path, which is resolved relative to this file, or an
* absolute path, which is resolved relative to the file system
* that contains this file.
*/
public FileObject resolveFile( final String path ) throws FileSystemException
{
final FileName name = m_name.resolveName( path );
return m_fs.findFile( name );
}

/**
* Deletes this file, once all its children have been deleted
*/
private void deleteSelf() throws FileSystemException
{
if( isReadOnly() )
{
final String message = REZ.getString( "delete-read-only.error", m_name );
throw new FileSystemException( message );
}

// Delete the file
try
{
doDelete();
}
catch( Exception exc )
{
final String message = REZ.getString( "delete.error", m_name );
throw new FileSystemException( message, exc );
}

// Update cached info
updateType();
}

/**
* Deletes this file, and all children.
*/
public void delete( final FileSelector selector ) throws FileSystemException
{
attach();
if( m_type == null )
{
// File does not exist
return;
}

// Locate all the files to delete
ArrayList files = new ArrayList();
findFiles( selector, true, files );

// Delete 'em
final int count = files.size();
for( int i = 0; i < count; i++ )
{
final AbstractFileObject file = (AbstractFileObject)files.get( i );
file.attach();

// If the file is a folder, make sure all its children have been deleted
if( file.m_type == FileType.FOLDER && file.getChildren().length != 0 )
{
// Skip
continue;
}

// Delete the file
file.deleteSelf();
}
}

/**
* Creates this file, if it does not exist. Also creates any ancestor
* files which do not exist.
*/
public void create( FileType type ) throws FileSystemException
{
attach();
if( m_type == type )
{
// Already exists as correct type
return;
}
if( m_type != null )
{
final String message = REZ.getString( "create-mismatched-type.error", type, m_name, m_type );
throw new FileSystemException( message );
}
if( isReadOnly() )
{
final String message = REZ.getString( "create-read-only.error", type, m_name );
throw new FileSystemException( message );
}

// Traverse up the heirarchy and make sure everything is a folder
FileObject parent = getParent();
if( parent != null )
{
parent.create( FileType.FOLDER );
}

// Create the folder
try
{
if( type == FileType.FOLDER )
{
doCreateFolder();
m_children = EMPTY_FILE_ARRAY;
}
else if( type == FileType.FILE )
{
OutputStream outStr = doGetOutputStream();
outStr.close();
endOutput();
}
}
catch( Exception exc )
{
final String message = REZ.getString( "create.error", type, m_name );
throw new FileSystemException( message, exc );
}

// Update cached info
updateType();
}

/**
* Copies another file to this file.
*/
public void copyFrom( final FileObject file, final FileSelector selector )
throws FileSystemException
{
if( !file.exists() )
{
final String message = REZ.getString( "copy-missing-file.error", file.getName() );
throw new FileSystemException( message );
}
if( isReadOnly() )
{
final String message = REZ.getString( "copy-read-only.error", file.getType(), file.getName(), m_name );
throw new FileSystemException( message );
}

// Locate the files to copy across
final ArrayList files = new ArrayList();
( (AbstractFileObject)file ).findFiles( selector, false, files );

// Copy everything across
final int count = files.size();
for( int i = 0; i < count; i++ )
{
final FileObject srcFile = (FileObject)files.get( i );

// Determine the destination file
final String relPath = file.getName().getRelativeName( srcFile.getName() );
final FileObject destFile = resolveFile( relPath, NameScope.DESCENDENT_OR_SELF );

// Clean up the destination file, if necessary
if( destFile.exists() && destFile.getType() != srcFile.getType() )
{
// The destination file exists, and is not of the same type,
// so delete it
// TODO - add a pluggable policy for deleting and overwriting existing files
destFile.delete( FileConstants.SELECT_ALL );
}

// Copy across
if( srcFile.getType() == FileType.FILE )
{
copyContent( srcFile, destFile );
}
else
{
destFile.create( FileType.FOLDER );
}
}
}

/**
* Creates a temporary local copy of this file, and its descendents.
*/
public File replicateFile( final FileSelector selector )
throws FileSystemException
{
if( !exists() )
{
final String message = REZ.getString( "copy-missing-file.error", m_name );
throw new FileSystemException( message );
}

return doReplicateFile( selector );
}

/**
* Copies the content of another file to this file.
*/
private static void copyContent( final FileObject srcFile,
final FileObject destFile )
throws FileSystemException
{
try
{
final InputStream instr = srcFile.getContent().getInputStream();
try
{
// Create the output stream via getContent(), to pick up the
// validation it does
final OutputStream outstr = destFile.getContent().getOutputStream();
try
{
IOUtil.copy( instr, outstr );
}
finally
{
IOUtil.shutdownStream( outstr );
}
}
finally
{
IOUtil.shutdownStream( instr );
}
}
catch( final Exception exc )
{
final String message = REZ.getString( "copy-file.error", srcFile.getName(), destFile.getName() );
throw new FileSystemException( message, exc );
}
}

/**
* Returns the file's content.
*/
public FileContent getContent() throws FileSystemException
{
attach();
if( m_type == FileType.FOLDER )
{
final String message = REZ.getString( "get-folder-content.error", m_name );
throw new FileSystemException( message );
}
if( m_content == null )
{
m_content = new DefaultFileContent( this );
}
return m_content;
}

/**
* Closes this file, and its content.
*/
public void close() throws FileSystemException
{
FileSystemException exc = null;

// Close the content
if( m_content != null )
{
try
{
m_content.close();
}
catch( FileSystemException e )
{
exc = e;
}
}

// Detach from the file
if( m_attached )
{
doDetach();
m_attached = false;
m_type = null;
m_children = null;
}

if( exc != null )
{
throw exc;
}
}

/**
* Prepares this file for writing. Makes sure it is either a file,
* or its parent folder exists. Returns an output stream to use to
* write the content of the file to.
*/
public OutputStream getOutputStream() throws FileSystemException
{
attach();
if( isReadOnly() )
{
final String message = REZ.getString( "write-read-only.error", m_name );
throw new FileSystemException( message );
}
if( m_type == FileType.FOLDER )
{
final String message = REZ.getString( "write-folder.error", m_name );
throw new FileSystemException( message );
}

if( m_type == null )
{
// Does not exist - make sure parent does
FileObject parent = getParent();
if( parent != null )
{
parent.create( FileType.FOLDER );
}
}

// Get the raw output stream
try
{
return doGetOutputStream();
}
catch( FileSystemException exc )
{
throw exc;
}
catch( Exception exc )
{
final String message = REZ.getString( "write.error", m_name );
throw new FileSystemException( message, exc );
}
}

/**
* Attaches to the file.
*/
private void attach() throws FileSystemException
{
if( m_attached )
{
return;
}

try
{
// Attach and determine the file type
doAttach();
m_attached = true;
m_type = doGetType();
}
catch( FileSystemException exc )
{
throw exc;
}
catch( Exception exc )
{
final String message = REZ.getString( "get-type.error", m_name );
throw new FileSystemException( message, exc );
}

}

/**
* Called when the ouput stream for this file is closed.
*/
public void endOutput() throws Exception
{
updateType();
doEndOutput();
}

/**
* Update cached info when this file's type changes.
*/
private void updateType()
{
// Notify parent that its child list may no longer be valid
notifyParent();

// Detach
doDetach();
m_attached = false;
m_type = null;
m_children = null;
}

/**
* Notify the parent of a change to its children, when a child is created
* or deleted.
*/
private void notifyParent()
{
if( m_parent == null )
{
// Locate the parent, if it is cached
m_parent = (AbstractFileObject)m_fs.getFile( m_name.getParent() );
}

if( m_parent != null )
{
m_parent.invalidateChildren();
}
}

/**
* Notifies a file that children have been created or deleted.
*/
private void invalidateChildren()
{
m_children = null;
onChildrenChanged();
}

/**
* Traverses the descendents of this file, and builds a list of selected
* files.
*/
void findFiles( final FileSelector selector,
final boolean depthwise,
final List selected ) throws FileSystemException
{
if( exists() )
{
// Traverse starting at this file
final DefaultFileSelectorInfo info = new DefaultFileSelectorInfo();
info.setBaseFolder( this );
info.setDepth( 0 );
info.setFile( this );
traverse( info, selector, depthwise, selected );
}
}

/**
* Traverses a file.
*/
private void traverse( final DefaultFileSelectorInfo fileInfo,
final FileSelector selector,
final boolean depthwise,
final List selected )
throws FileSystemException
{
// Check the file itself
final boolean includeFile = selector.includeFile( fileInfo );
final FileObject file = fileInfo.getFile();

// Add the file if not doing depthwise traversal
if( !depthwise && includeFile )
{
selected.add( file );
}

// If the file is a folder, traverse it
if( file.getType() == FileType.FOLDER && selector.traverseDescendents( fileInfo ) )
{
final int curDepth = fileInfo.getDepth();
fileInfo.setDepth( curDepth + 1 );

// Traverse the children
final FileObject[] children = file.getChildren();
for( int i = 0; i < children.length; i++ )
{
final FileObject child = children[ i ];
fileInfo.setFile( child );
traverse( fileInfo, selector, depthwise, selected );
}

fileInfo.setFile( file );
fileInfo.setDepth( curDepth );
}

// Add the file if doing depthwise traversal
if( depthwise && includeFile )
{
selected.add( file );
}
}

}

+ 0
- 114
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileSystem.java View File

@@ -1,114 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.util.HashMap;
import java.util.Map;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;

/**
* A partial file system implementation.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public abstract class AbstractFileSystem
extends AbstractLogEnabled
implements FileSystem, Disposable
{
private FileObject m_root;
private final FileName m_rootName;
private final FileSystemProviderContext m_context;

/** Map from FileName to FileObject. */
private final Map m_files = new HashMap();

protected AbstractFileSystem( final FileSystemProviderContext context,
final FileName rootName )
{
m_rootName = rootName;
m_context = context;
}

public void dispose()
{
// Clean-up
m_files.clear();
}

/**
* Creates a file object. This method is called only if the requested
* file is not cached.
*/
protected abstract FileObject createFile( final FileName name ) throws FileSystemException;

/**
* Adds a file object to the cache.
*/
protected void putFile( final FileObject file )
{
m_files.put( file.getName(), file );
}

/**
* Returns a cached file.
*/
protected FileObject getFile( final FileName name )
{
return (FileObject)m_files.get( name );
}

/**
* Returns the context fir this file system.
*/
public FileSystemProviderContext getContext()
{
return m_context;
}

/**
* Returns the root file of this file system.
*/
public FileObject getRoot() throws FileSystemException
{
if( m_root == null )
{
m_root = findFile( m_rootName );
}
return m_root;
}

/**
* Finds a file in this file system.
*/
public FileObject findFile( final String nameStr ) throws FileSystemException
{
// Resolve the name, and create the file
final FileName name = m_rootName.resolveName( nameStr );
return findFile( name );
}

/**
* Finds a file in this file system.
*/
public FileObject findFile( final FileName name ) throws FileSystemException
{
// TODO - assert that name is from this file system
FileObject file = (FileObject)m_files.get( name );
if( file == null )
{
file = createFile( name );
m_files.put( name, file );
}
return file;
}
}

+ 0
- 162
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileSystemProvider.java View File

@@ -1,162 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;

/**
* A partial file system provider implementation.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public abstract class AbstractFileSystemProvider
extends AbstractLogEnabled
implements FileSystemProvider, Disposable
{
private static final Resources REZ =
ResourceManager.getPackageResources( AbstractFileSystemProvider.class );

private FileSystemProviderContext m_context;

/**
* The cached file systems. This is a mapping from root URI to
* FileSystem object.
*/
private final Map m_fileSystems = new HashMap();

/**
* Returns the context for this provider.
*/
protected FileSystemProviderContext getContext()
{
return m_context;
}

/**
* Sets the context for this file system provider. This method is called
* before any of the other provider methods.
*/
public void setContext( final FileSystemProviderContext context )
{
m_context = context;
}

/**
* Closes the file systems created by this provider.
*/
public void dispose()
{
for( Iterator iterator = m_fileSystems.values().iterator(); iterator.hasNext(); )
{
FileSystem fileSystem = (FileSystem)iterator.next();
if( fileSystem instanceof Disposable )
{
Disposable disposable = (Disposable)fileSystem;
disposable.dispose();
}
}
m_fileSystems.clear();
}

/**
* Locates a file object, by absolute URI.
*
* @param uri
* The absolute URI of the file to find.
*/
public FileObject findFile( final FileObject baseFile,
final String uri ) throws FileSystemException
{
// Parse the URI
ParsedUri parsedUri = null;
try
{
parsedUri = parseUri( baseFile, uri );
}
catch( FileSystemException exc )
{
final String message = REZ.getString( "invalid-absolute-uri.error", uri );
throw new FileSystemException( message, exc );
}

// Locate the file
return findFile( parsedUri );
}

/**
* Locates a file from its parsed URI.
*/
private FileObject findFile( final ParsedUri parsedUri )
throws FileSystemException
{
// Check in the cache for the file system
final String rootUri = parsedUri.getRootUri();
FileSystem fs = (FileSystem)m_fileSystems.get( rootUri );
if( fs == null )
{
// Need to create the file system, and cache it
fs = createFileSystem( parsedUri );
setupLogger( fs );
m_fileSystems.put( rootUri, fs );
}

// Locate the file
return fs.findFile( parsedUri.getPath() );
}

/**
* Creates a layered file system.
*/
public FileObject createFileSystem( final String scheme, final FileObject file )
throws FileSystemException
{
// TODO - this is a pretty shonky model for layered FS; need to revise

// Build the URI
final ParsedUri uri = buildUri( scheme, file );

// Locate the file
return findFile( uri );
}

/**
* Parses a URI into its components. The returned value is used to
* locate the file system in the cache (using the root prefix).
*
* <p>The provider can annotate this object with any additional
* information it requires to create a file system from the URI.
*/
protected abstract ParsedUri parseUri( final FileObject baseFile, final String uri )
throws FileSystemException;

/**
* Builds the URI for the root of a layered file system.
*/
protected ParsedUri buildUri( final String scheme,
final FileObject file )
throws FileSystemException
{
final String message = REZ.getString( "not-layered-fs.error" );
throw new FileSystemException( message );
}

/**
* Creates the filesystem.
*/
protected abstract FileSystem createFileSystem( final ParsedUri uri )
throws FileSystemException;
}

+ 0
- 379
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileContent.java View File

@@ -1,379 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.aut.vfs.FileContent;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* The content of a file.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class DefaultFileContent
implements FileContent
{
private static final Resources REZ =
ResourceManager.getPackageResources( DefaultFileContent.class );

private static final int STATE_NONE = 0;
private static final int STATE_READING = 1;
private static final int STATE_WRITING = 2;

private AbstractFileObject m_file;
private int _state = STATE_NONE;
private FileContentInputStream m_instr;
private FileContentOutputStream m_outstr;

public DefaultFileContent( AbstractFileObject file )
{
m_file = file;
}

/**
* Returns the file which this is the content of.
*/
public FileObject getFile()
{
return m_file;
}

/**
* Returns the size of the content (in bytes).
*/
public long getSize() throws FileSystemException
{
// Do some checking
if( !m_file.exists() )
{
final String message = REZ.getString( "get-size-no-exist.error", m_file );
throw new FileSystemException( message );
}
if( _state == STATE_WRITING )
{
final String message = REZ.getString( "get-size-write.error", m_file );
throw new FileSystemException( message );
}

try
{
// Get the size
return m_file.doGetContentSize();
}
catch( Exception exc )
{
final String message = REZ.getString( "get-size.error", m_file );
throw new FileSystemException( message, exc );
}
}

/**
* Returns the last-modified timestamp.
*/
public long getLastModifiedTime() throws FileSystemException
{
// TODO - implement this
throw new FileSystemException( "Not implemented." );
}

/**
* Sets the last-modified timestamp.
*/
public void setLastModifiedTime( long modTime ) throws FileSystemException
{
// TODO - implement this
throw new FileSystemException( "Not implemented." );
}

/**
* Gets the value of an attribute.
*/
public Object getAttribute( String attrName ) throws FileSystemException
{
// TODO - implement this
throw new FileSystemException( "Not implemented." );
}

/**
* Sets the value of an attribute.
*/
public void setAttribute( String attrName, Object value ) throws FileSystemException
{
// TODO - implement this
throw new FileSystemException( "Not implemented." );
}

/**
* Returns an input stream for reading the content.
*/
public InputStream getInputStream() throws FileSystemException
{
if( !m_file.exists() )
{
final String message = REZ.getString( "read-no-exist.error", m_file );
throw new FileSystemException( message );
}
if( _state != STATE_NONE )
{
final String message = REZ.getString( "read-in-use.error", m_file );
throw new FileSystemException( message );
}

// Get the raw input stream
InputStream instr = null;
try
{
instr = m_file.doGetInputStream();
}
catch( Exception exc )
{
final String message = REZ.getString( "read.error", m_file );
throw new FileSystemException( message, exc );
}

// TODO - reuse
m_instr = new FileContentInputStream( instr );
_state = STATE_READING;
return m_instr;
}

/**
* Returns an output stream for writing the content.
*/
public OutputStream getOutputStream() throws FileSystemException
{
if( _state != STATE_NONE )
{
final String message = REZ.getString( "write-in-use.error", m_file );
throw new FileSystemException( message );
}

// Get the raw output stream
OutputStream outstr = m_file.getOutputStream();

// Create wrapper
// TODO - reuse
m_outstr = new FileContentOutputStream( outstr );
_state = STATE_WRITING;
return m_outstr;
}

/**
* Closes all resources used by the content, including all streams, readers
* and writers.
*/
public void close() throws FileSystemException
{

try
{
// Close the input stream
if( m_instr != null )
{
try
{
m_instr.close();
}
catch( IOException ioe )
{
final String message = REZ.getString( "close-instr.error" );
throw new FileSystemException( message, ioe );
}
}

// Close the output stream
if( m_outstr != null )
{
try
{
m_outstr.close();
}
catch( IOException ioe )
{
final String message = REZ.getString( "close-outstr.error" );
throw new FileSystemException( message, ioe );
}
}
}
finally
{
_state = STATE_NONE;
}
}

/**
* Handles the end of input stream.
*/
private void endInput() throws Exception
{
m_instr = null;
_state = STATE_NONE;
m_file.doEndInput();
}

/**
* Handles the end of output stream.
*/
private void endOutput() throws Exception
{
m_outstr = null;
_state = STATE_NONE;
m_file.endOutput();
}

/**
* An input stream for reading content. Provides buffering, and
* end-of-stream monitoring.
*/
private final class FileContentInputStream extends BufferedInputStream
{
boolean _finished;

FileContentInputStream( InputStream instr )
{
super( instr );
}

/**
* Reads a character.
*/
public int read() throws IOException
{
if( _finished )
{
return -1;
}

int ch = super.read();
if( ch != -1 )
{
return ch;
}

// End-of-stream
close();
return -1;
}

/**
* Reads bytes from this input stream.error occurs.
*/
public int read( byte[] buffer, int offset, int length )
throws IOException
{
if( _finished )
{
return -1;
}

int nread = super.read( buffer, offset, length );
if( nread != -1 )
{
return nread;
}

// End-of-stream
close();
return -1;
}

/**
* Closes this input stream.
*/
public void close() throws IOException
{
if( _finished )
{
return;
}

// Close the stream
IOException exc = null;
try
{
super.close();
}
catch( IOException e )
{
exc = e;
}

// Notify the file object
try
{
endInput();
}
catch( Exception e )
{
exc = new IOException( e.getMessage() );
}

_finished = true;

if( exc != null )
{
throw exc;
}
}
}

/**
* An output stream for writing content.
*/
private final class FileContentOutputStream
extends BufferedOutputStream
{
FileContentOutputStream( OutputStream outstr )
{
super( outstr );
}

/**
* Closes this output stream.
*/
public void close() throws IOException
{
IOException exc = null;

// Close the output stream
try
{
super.close();
}
catch( IOException e )
{
exc = e;
}

// Notify of end of output
try
{
endOutput();
}
catch( Exception e )
{
exc = new IOException( e.getMessage() );
}

if( exc != null )
{
throw exc;
}
}
}

}

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

@@ -1,139 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.NameScope;

/**
* A default file name implementation.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class DefaultFileName implements FileName
{
private final UriParser m_parser;
private final String m_rootPrefix;
private final String m_absPath;

// Cached stuff
private String m_uri;
private String m_baseName;

public DefaultFileName( final UriParser parser,
final String rootPrefix,
final String absPath )
{
m_parser = parser;
m_rootPrefix = rootPrefix;
m_absPath = absPath;
}

/**
* 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.
*/
public String toString()
{
return getURI();
}

/**
* Returns the base name of the file.
*/
public String getBaseName()
{
if( m_baseName == null )
{
m_baseName = m_parser.getBaseName( m_absPath );
}
return m_baseName;
}

/**
* Returns the absolute path of the file, relative to the root of the
* file system that the file belongs to.
*/
public String getPath()
{
return m_absPath;
}

/**
* Returns the name of a child of the file.
*/
public FileName resolveName( final String name,
final NameScope scope )
throws FileSystemException
{
final String absPath = m_parser.resolvePath( m_absPath, name, scope );
return new DefaultFileName( m_parser, m_rootPrefix, absPath );
}

/**
* Returns the name of the parent of the file.
*/
public FileName getParent()
{
final String parentPath = m_parser.getParentPath( m_absPath );
if( parentPath == null )
{
return null;
}
return new DefaultFileName( m_parser, m_rootPrefix, parentPath );
}

/**
* Resolves a name, relative to the file. If the supplied name is an
* absolute path, then it is resolved relative to the root of the
* file system that the file belongs to. If a relative name is supplied,
* then it is resolved relative to this file name.
*/
public FileName resolveName( final String path ) throws FileSystemException
{
return resolveName( path, NameScope.FILE_SYSTEM );
}

/**
* Returns the absolute URI of the file.
*/
public String getURI()
{
if( m_uri == null )
{
m_uri = m_parser.getUri( m_rootPrefix, m_absPath );
}
return m_uri;
}

/**
* Converts a file name to a relative name, relative to this file name.
*/
public String getRelativeName( final FileName name ) throws FileSystemException
{
return m_parser.makeRelative( m_absPath, name.getPath() );
}
}

+ 0
- 55
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/DefaultFileSelectorInfo.java View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSelectInfo;

/**
* A default {@link FileSelectInfo} implementation.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class DefaultFileSelectorInfo
implements FileSelectInfo
{
private FileObject m_baseFolder;
private FileObject m_file;
private int m_depth;

public FileObject getBaseFolder()
{
return m_baseFolder;
}

public void setBaseFolder( final FileObject baseFolder )
{
m_baseFolder = baseFolder;
}

public FileObject getFile()
{
return m_file;
}

public void setFile( final FileObject file )
{
m_file = file;
}

public int getDepth()
{
return m_depth;
}

public void setDepth( final int depth )
{
m_depth = depth;
}
}

+ 0
- 36
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileReplicator.java View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.io.File;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSelector;
import org.apache.aut.vfs.FileSystemException;

/**
* Responsible for making local replicas of files.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileReplicator
{
/**
* Creates a local copy of the file, and all its descendents.
*
* @param srcFile The file to copy.
* @param selector Selects the files to copy.
*
* @return The local copy of the source file.
*
* @throws FileSystemException
* If the source files does not exist, or on error copying.
*/
File replicateFile( FileObject srcFile, FileSelector selector )
throws FileSystemException;
}

+ 0
- 42
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystem.java View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;

/**
* A file system.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileSystem
{
/**
* Returns the root of this file system.
*/
FileObject getRoot() throws FileSystemException;

/**
* Finds a file in this file system.
*
* @param name
* The name of the file.
*/
FileObject findFile( FileName name ) throws FileSystemException;

/**
* Finds a file in this file system.
*
* @param name
* The name of the file. This must be an absolute path.
*/
FileObject findFile( String name ) throws FileSystemException;
}

+ 0
- 49
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProvider.java View File

@@ -1,49 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;

/**
* A file system provider, or factory.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant:role shorthand="file-system"
*/
public interface FileSystemProvider
{
String ROLE = FileSystemProvider.class.getName();

/**
* Sets the context for this file system provider. This method is called
* before any of the other provider methods.
*
* @todo - move this to a lifecycle interface (this interface is accessable to
* other providers, so need to prevent this being called).
*/
void setContext( FileSystemProviderContext context );

/**
* Locates a file object, by absolute URI.
*
* @param baseFile
* The base file to use for resolving the individual parts of
* a compound URI.
* @param uri
* The absolute URI of the file to find.
*/
FileObject findFile( FileObject baseFile, String uri ) throws FileSystemException;

/**
* Creates a layered file system.
*/
FileObject createFileSystem( String scheme, FileObject file ) throws FileSystemException;
}

+ 0
- 35
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProviderContext.java View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileSystemManager;

/**
* Used for a file system provider to access the services it needs, such
* as the file system cache or other file system providers.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface FileSystemProviderContext
{
/**
* Locate a file by name. See
* {@link FileSystemManager#resolveFile(FileObject, String)} for a
* description of how this works.
*/
FileObject resolveFile( FileObject baseFile, String name )
throws FileSystemException;

/**
* Locates a file replicator for the provider to use.
*/
FileReplicator getReplicator() throws FileSystemException;
}

+ 0
- 43
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/LocalFileSystemProvider.java View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.io.File;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;

/**
* A file system provider which handles local file systems.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public interface LocalFileSystemProvider
extends FileSystemProvider
{
/**
* Determines if a name is an absolute file name.
*
* @todo Move this to a general file name parser interface.
*
* @param name The name to test.
*/
boolean isAbsoluteLocalName( final String name );

/**
* Finds a local file, from its local name.
*/
FileObject findLocalFile( final String name )
throws FileSystemException;

/**
* Finds a local file.
*/
FileObject findLocalFile( final File file )
throws FileSystemException;
}

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

@@ -1,96 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

/**
* A data container for information parsed from an absolute URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class ParsedUri
{
private String m_scheme;
private String m_rootURI;
private String m_path;
private String m_userInfo;
private String m_hostName;
private String m_port;

/** Returns the scheme. */
public String getScheme()
{
return m_scheme;
}

/** Sets the scheme. */
public void setScheme( String scheme )
{
m_scheme = scheme;
}

/** Returns the root URI, used to identify the file system. */
public String getRootUri()
{
return m_rootURI;
}

/** Sets the root URI. */
public void setRootUri( String rootPrefix )
{
m_rootURI = rootPrefix;
}

/** Returns the user info part of the URI. */
public String getUserInfo()
{
return m_userInfo;
}

/** Sets the user info part of the URI. */
public void setUserInfo( String userInfo )
{
m_userInfo = userInfo;
}

/** Returns the host name part of the URI. */
public String getHostName()
{
return m_hostName;
}

/** Sets the host name part of the URI. */
public void setHostName( String hostName )
{
m_hostName = hostName;
}

/** Returns the port part of the URI. */
public String getPort()
{
return m_port;
}

/** Sets the port part of the URI. */
public void setPort( String port )
{
m_port = port;
}

/** Returns the path part of the URI. */
public String getPath()
{
return m_path;
}

/** Sets the path part of the URI. */
public void setPath( String absolutePath )
{
m_path = absolutePath;
}
}

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

@@ -1,46 +0,0 @@
# AbstractFileObject
delete-not-supported.error=This file type does not support delete.
create-folder-not-supported.error=This file type does not support folder creation.
write-not-supported.error=This file type cannot be written to.
get-type-no-exist.error=Could not determine the type of file "{0}" because it does not exist.
get-type.error=Could not determine the type of file "{0}".
list-children-no-exist.error=Could not list the contents of folder "{0}" because it does not exist.
list-children-not-folder.error=Could not list the contents of "{0}" because it is not a folder.
list-children.error=Could not list the contents of folder "{0}".
delete-read-only.error=Could not delete "{0}" because it is read-only.
delete.error=Could not delete "{0}".
create-mismatched-type.error=Could not create {0} "{1}" because it already exists and is a {2}.
create-read-only.error=Could not create {0} "{1}" because the file system is read-only.
create.error=Could not create {0} "{1}".
get-folder-content.error=Could not get the content of "{0}" because it is a folder.
write-read-only.error=Could not write to "{0}" because it is read-only.
write-folder.error=Could not write to "{0}" because it is a folder.
write-in-use.error=Could not write to "{0}" because it is already in use.
write.error=Could not write to "{0}".
copy-file.error=Could not copy "{0}" to "{1}".
copy-read-only.error=Could not copy {0} "{1}" to "{2}" because the destination file is read-only.
copy-missing-file.error=Could not copy "{0}" because is does not exist.

# DefaultFileContent
get-size-no-exist.error=Could not determine the size of file "{0}" because it does not exist.
get-size-write.error=Could not determine the size of file "{0}" because it is being written to.
get-size.error=Could not determine the size of file "{0}".
read-no-exist.error=Could not read file "{0}" because it does not exist.
read-in-use.error=Could not read file "{0}" because it is already being used.
read.error=Could not read file "{0}".
close-instr.error=Could not close file input stream.
close-outstr.error=Could not close file output stream.

# AbstractFileSystemProvider
invalid-absolute-uri.error=Invalid absolute URI "{0}".
not-layered-fs.error=File system is not a layered file system.

# UriParser
missing-double-slashes.error=Expecting // to follow the scheme in URI "{0}".
missing-hostname.error=Hostname missing from URI "{0}".
missing-port.error=Port number is missing from URI "{0}".
missing-hostname-path-sep.error=Expecting / to follow the hostname in URI "{0}".
invalid-childname.error=Invalid file base-name "{0}".
invalid-descendent-name.error=Invalid descendent file name "{0}".
invalid-escape-sequence.error=Invalid URI escape sequence "{0}".
invalid-relative-path.error=Invalid relative file name.

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

@@ -1,853 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider;

import java.util.HashSet;
import java.util.Iterator;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.NameScope;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* A name parser which parses absolute URIs. See RFC 2396 for details.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class UriParser
{
private static final Resources REZ =
ResourceManager.getPackageResources( UriParser.class );

/** The normalised separator to use. */
private final char m_separatorChar;
private final String m_separator;

/**
* The set of valid separators. These are all converted to the normalised one.
* Does <i>not</i> contain the normalised separator
*/
private final char[] m_separators;

/**
* Creates a parser, using '/' and '\' as the path separators.
*/
public UriParser()
{
this( null );
}

/**
* Creates a parser, using '/' and '\' as the path separators, along with
* a provider-specific set of separators.
*
* @param separators
* Additional legal separator characters. Any occurrences of
* these in paths are replaced with the separator char.
*/
protected UriParser( final char[] separators )
{
m_separatorChar = '/';

// Remove the separator char from the separators array
final HashSet set = new HashSet();
set.add( new Character( '\\' ) );
if( separators != null )
{
for( int i = 0; i < separators.length; i++ )
{
char separator = separators[ i ];
if( separator == m_separatorChar )
{
continue;
}
set.add( new Character( separator ) );
}
}
m_separators = new char[ set.size() ];
final Iterator iter = set.iterator();
for( int i = 0; i < m_separators.length; i++ )
{
final Character ch = (Character)iter.next();
m_separators[ i ] = ch.charValue();
}

m_separator = String.valueOf( m_separatorChar );
}

/**
* Parses an absolute URI, splitting it into its components. This
* implementation assumes a "generic URI", as defined by RFC 2396. See
* {@link #parseGenericUri} for more info.
*/
public ParsedUri parseUri( final String uriStr ) throws FileSystemException
{
// Parse the URI
final ParsedUri uri = new ParsedUri();
parseGenericUri( uriStr, uri );

// Build the root URI
final StringBuffer rootUri = new StringBuffer();
appendRootUri( uri, rootUri );
uri.setRootUri( rootUri.toString() );

return uri;
}

/**
* Assembles a generic URI, appending to the supplied StringBuffer.
*/
protected void appendRootUri( final ParsedUri uri, final StringBuffer rootUri )
{
rootUri.append( uri.getScheme() );
rootUri.append( "://" );
final String userInfo = uri.getUserInfo();
if( userInfo != null && userInfo.length() != 0 )
{
rootUri.append( userInfo );
rootUri.append( "@" );
}
rootUri.append( uri.getHostName() );
final String port = uri.getPort();
if( port != null && port.length() > 0 )
{
rootUri.append( ":" );
rootUri.append( port );
}
}

/**
* Parses a generic URI, as defined by RFC 2396. Briefly, a generic URI
* looks like:
*
* <pre>
* &lt;scheme> '://' [ &lt;userinfo> '@' ] &lt;hostname> [ ':' &lt;port> ] '/' &lt;path>
* </pre>
*
* <p>This method differs from the RFC, in that either / or \ is allowed
* as a path separator.
*
* @param uriStr
* The URI to parse.
* @param uri
* Used to return the parsed components of the URI.
*/
protected void parseGenericUri( final String uriStr,
final ParsedUri uri )
throws FileSystemException
{
final StringBuffer name = new StringBuffer();

// Extract the scheme and authority parts
extractToPath( uriStr, name, uri );

// Decode and normalise the file name
decode( name, 0, name.length() );
normalisePath( name );
uri.setPath( name.toString() );

// Build the root uri
final StringBuffer rootUri = new StringBuffer();
rootUri.append( uri.getScheme() );
rootUri.append( "://" );
rootUri.append( uri.getHostName() );
uri.setRootUri( rootUri.toString() );
}

/**
* Extracts the scheme, userinfo, hostname and port components of an
* absolute "generic URI".
*
* @param uri
* The absolute URI to parse.
*
* @param name
* Used to return the remainder of the URI.
*
* @parsedUri
* Used to return the extracted components.
*/
protected void extractToPath( final String uri,
final StringBuffer name,
final ParsedUri parsedUri )
throws FileSystemException
{
// Extract the scheme
final String scheme = extractScheme( uri, name );
parsedUri.setScheme( scheme );

// Expecting "//"
if( name.length() < 2 || name.charAt( 0 ) != '/' || name.charAt( 1 ) != '/' )
{
final String message = REZ.getString( "missing-double-slashes.error", uri );
throw new FileSystemException( message );
}
name.delete( 0, 2 );

// Extract userinfo
final String userInfo = extractUserInfo( name );
parsedUri.setUserInfo( userInfo );

// Extract hostname
final String hostName = extractHostName( name );
if( hostName == null )
{
final String message = REZ.getString( "missing-hostname.error", uri );
throw new FileSystemException( message );
}
parsedUri.setHostName( hostName );

// Extract port
final String port = extractPort( name );
if( port != null && port.length() == 0 )
{
final String message = REZ.getString( "missing-port.error", uri );
throw new FileSystemException( message );
}
parsedUri.setPort( port );

// Expecting '/' or empty name
if( name.length() > 0 && name.charAt( 0 ) != '/' )
{
final String message = REZ.getString( "missing-hostname-path-sep.error", uri );
throw new FileSystemException( message );
}
}

/**
* Extracts the user info from a URI. The <scheme>:// part has been removed
* already.
*/
protected String extractUserInfo( final StringBuffer name )
{
final int maxlen = name.length();
for( int pos = 0; pos < maxlen; pos++ )
{
final char ch = name.charAt( pos );
if( ch == '@' )
{
// Found the end of the user info
String userInfo = name.substring( 0, pos );
name.delete( 0, pos + 1 );
return userInfo;
}
if( ch == '/' || ch == '?' )
{
// Not allowed in user info
break;
}
}

// Not found
return null;
}

/**
* Extracts the hostname from a URI. The <scheme>://<userinfo>@ part has
* been removed.
*/
protected String extractHostName( final StringBuffer name )
{
final int maxlen = name.length();
int pos = 0;
for( ; pos < maxlen; pos++ )
{
final char ch = name.charAt( pos );
if( ch == '/' || ch == ';' || ch == '?' || ch == ':'
|| ch == '@' || ch == '&' || ch == '=' || ch == '+'
|| ch == '$' || ch == ',' )
{
break;
}
}
if( pos == 0 )
{
return null;
}

final String hostname = name.substring( 0, pos );
name.delete( 0, pos );
return hostname;
}

/**
* Extracts the port from a URI. The <scheme>://<userinfo>@<hostname>
* part has been removed.
*/
protected String extractPort( final StringBuffer name )
{
if( name.length() < 1 || name.charAt( 0 ) != ':' )
{
return null;
}

final int maxlen = name.length();
int pos = 1;
for( ; pos < maxlen; pos++ )
{
final char ch = name.charAt( pos );
if( ch < '0' || ch > '9' )
{
break;
}
}

final String port = name.substring( 1, pos );
name.delete( 0, pos );
return port;
}

/**
* Extracts the first element of a path.
*/
protected String extractFirstElement( final StringBuffer name )
{
final int len = name.length();
if( len < 1 )
{
return null;
}
int startPos = 0;
if( name.charAt( 0 ) == m_separatorChar )
{
startPos = 1;
}
for( int pos = startPos; pos < len; pos++ )
{
if( name.charAt( pos ) == m_separatorChar )
{
// Found a separator
final String elem = name.substring( startPos, pos );
name.delete( startPos, pos + 1 );
return elem;
}
}

// No separator
final String elem = name.substring( startPos );
name.setLength( 0 );
return elem;
}

/**
* Builds a URI from a root URI and path.
*
* @param rootUri
* The root URI.
*
* @param path
* A <i>normalised</i> path.
*/
public String getUri( final String rootUri,
final String path )
{
final StringBuffer uri = new StringBuffer( rootUri );
final int len = uri.length();
if( uri.charAt( len - 1 ) == m_separatorChar )
{
uri.delete( len - 1, len );
}
if( !path.startsWith( m_separator ) )
{
uri.append( m_separatorChar );
}
uri.append( path );
return uri.toString();
}

/**
* Returns the base name of a path.
*
* @param path
* A <i>normalised</i> path.
*/
public String getBaseName( final String path )
{
final int idx = path.lastIndexOf( m_separatorChar );
if( idx == -1 )
{
return path;
}
return path.substring( idx + 1 );
}

/**
* Resolves a path, relative to a base path. If the supplied path
* is an absolute path, it is normalised and returned. If the supplied
* path is a relative path, it is resolved relative to the base path.
*
* @param basePath
* A <i>normalised</i> path.
*
* @param path
* The path to resolve. Does not need to be normalised, but
* does need to be a path (i.e. not an absolute URI).
*
*/
public String resolvePath( final String basePath,
final String path )
throws FileSystemException
{
final StringBuffer buffer = new StringBuffer( path );

// Adjust separators
fixSeparators( buffer );

// Determine whether to prepend the base path
if( path.length() == 0 || path.charAt( 0 ) != m_separatorChar )
{
// Supplied path is not absolute
buffer.insert( 0, m_separatorChar );
buffer.insert( 0, basePath );
}

// Normalise the path
normalisePath( buffer );
return buffer.toString();
}

/**
* Resolved a name, relative to a base file.
*
* @param baseFile
* A <i>normalised</i> path.
*
* @param path
* The path to resolve.
*
* @param scope
* The scope to resolve and validate the name in.
*/
public String resolvePath( final String baseFile,
final String path,
final NameScope scope )
throws FileSystemException
{
final String resolvedPath = resolvePath( baseFile, path );
if( scope == NameScope.CHILD )
{
final int baseLen = baseFile.length();
if( !resolvedPath.startsWith( baseFile )
|| resolvedPath.length() == baseLen
|| ( baseLen > 1 && 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 );
}
}
else if( scope == NameScope.DESCENDENT )
{
final int baseLen = baseFile.length();
if( !resolvedPath.startsWith( baseFile )
|| resolvedPath.length() == baseLen
|| ( baseLen > 1 && resolvedPath.charAt( baseLen ) != m_separatorChar ) )
{
final String message = REZ.getString( "invalid-descendent-name.error", path );
throw new FileSystemException( message );
}
}
else if( scope == NameScope.DESCENDENT_OR_SELF )
{
final int baseLen = baseFile.length();
if( !resolvedPath.startsWith( baseFile )
|| ( resolvedPath.length() != baseLen
&& resolvedPath.charAt( baseLen ) != m_separatorChar ) )
{
final String message = REZ.getString( "invalid-descendent-name.error", path );
throw new FileSystemException( message );
}
}
else if( scope != NameScope.FILE_SYSTEM )
{
throw new IllegalArgumentException();
}

return resolvedPath;
}

/**
* Returns a parent path, or null if the path has no parent.
*
* @param path
* A <i>normalised</i> path.
*/
public String getParentPath( final String path )
{
final int idx = path.lastIndexOf( m_separatorChar );
if( idx == -1 || idx == path.length() - 1 )
{
// No parent
return null;
}
if( idx == 0 )
{
// Root is the parent
return m_separator;
}
return path.substring( 0, idx );
}

/**
* Converts an absolute path into a relative path.
*
* @param basePath The base path.
* @param path The path to convert.
*/
public String makeRelative( final String basePath, final String path )
{
// Calculate the common prefix
final int basePathLen = basePath.length();
final int pathLen = path.length();

// Deal with root
if( basePathLen == 1 && pathLen == 1 )
{
return ".";
}
else if( basePathLen == 1 )
{
return path.substring( 1 );
}

final int maxlen = Math.min( basePathLen, pathLen );
int pos = 0;
for( ; pos < maxlen && basePath.charAt( pos ) == path.charAt( pos ); pos++ )
{
}

if( pos == basePathLen && pos == pathLen )
{
// Same names
return ".";
}
else if( pos == basePathLen && pos < pathLen && path.charAt( pos ) == m_separatorChar )
{
// A descendent of the base path
return path.substring( pos + 1 );
}

// Strip the common prefix off the path
final StringBuffer buffer = new StringBuffer();
if( pathLen > 1 && ( pos < pathLen || basePath.charAt( pos ) != m_separatorChar ) )
{
// Not a direct ancestor, need to back up
pos = basePath.lastIndexOf( m_separatorChar, pos );
buffer.append( path.substring( pos ) );
}

// Prepend a '../' for each element in the base path past the common
// prefix
buffer.insert( 0, ".." );
pos = basePath.indexOf( m_separatorChar, pos + 1 );
while( pos != -1 )
{
buffer.insert( 0, "../" );
pos = basePath.indexOf( m_separatorChar, pos + 1 );
}

return buffer.toString();
}

/**
* Normalises a path. Does the following:
* <ul>
* <li>Normalises separators, where more than one can be used.
* <li>Removes empty path elements.
* <li>Handles '.' and '..' elements.
* <li>Removes trailing separator.
* </ul>
*/
public void normalisePath( final StringBuffer path )
throws FileSystemException
{
if( path.length() == 0 )
{
return;
}

// Adjust separators
fixSeparators( path );

// Determine the start of the first element
int startFirstElem = 0;
if( path.charAt( 0 ) == m_separatorChar )
{
if( path.length() == 1 )
{
return;
}
startFirstElem = 1;
}

// Iterate over each element
int startElem = startFirstElem;
int maxlen = path.length();
while( startElem < maxlen )
{
// Find the end of the element
int endElem = startElem;
for( ; endElem < maxlen && path.charAt( endElem ) != m_separatorChar; endElem++ )
{
}

final int elemLen = endElem - startElem;
if( elemLen == 0 )
{
// An empty element - axe it
path.delete( endElem, endElem + 1 );
maxlen = path.length();
continue;
}
if( elemLen == 1 && path.charAt( startElem ) == '.' )
{
// A '.' element - axe it
path.delete( startElem, endElem + 1 );
maxlen = path.length();
continue;
}
if( elemLen == 2 &&
path.charAt( startElem ) == '.' &&
path.charAt( startElem + 1 ) == '.' )
{
// A '..' element - remove the previous element
if( startElem == startFirstElem )
{
// Previous element is missing
final String message = REZ.getString( "invalid-relative-path.error" );
throw new FileSystemException( message );
}

// Find start of previous element
int pos = startElem - 2;
for( ; pos >= 0 && path.charAt( pos ) != m_separatorChar; pos-- )
{
}
startElem = pos + 1;

path.delete( startElem, endElem + 1 );
maxlen = path.length();
continue;
}

// A regular element
startElem = endElem + 1;
}

// Remove trailing separator
if( maxlen > 0 && path.charAt( maxlen - 1 ) == m_separatorChar && maxlen > 1 )
{
path.delete( maxlen - 1, maxlen );
}
}

/**
* Adjusts the separators in a name.
*/
protected boolean fixSeparators( final StringBuffer name )
{
if( m_separators.length == 0 )
{
// Only one valid separator, so don't need to do anything
return false;
}

boolean changed = false;
final int maxlen = name.length();
for( int i = 0; i < maxlen; i++ )
{
final char ch = name.charAt( i );
for( int j = 0; j < m_separators.length; j++ )
{
char separator = m_separators[ j ];
if( ch == separator )
{
name.setCharAt( i, m_separatorChar );
changed = true;
break;
}
}
}
return changed;
}

/**
* Extracts the scheme from a URI.
*
* @param uri
* The URI.
*
* @return
* The scheme name. Returns null if there is no scheme.
*/
public static String extractScheme( final String uri )
{
return extractScheme( uri, null );
}

/**
* Extracts the scheme from a URI.
*
* @param uri
* The URI.
*
* @param buffer
* Returns the remainder of the URI.
*
* @return
* The scheme name. Returns null if there is no scheme.
*/
public static String extractScheme( final String uri,
final StringBuffer buffer )
{
if( buffer != null )
{
buffer.setLength( 0 );
buffer.append( uri );
}

final int maxPos = uri.length();
for( int pos = 0; pos < maxPos; pos++ )
{
final char ch = uri.charAt( pos );

if( ch == ':' )
{
// Found the end of the scheme
final String scheme = uri.substring( 0, pos );
if( buffer != null )
{
buffer.delete( 0, pos + 1 );
}
return scheme;
}

if( ( ch >= 'a' && ch <= 'z' )
|| ( ch >= 'A' && ch <= 'Z' ) )
{
// A scheme character
continue;
}
if( pos > 0 &&
( ( ch >= '0' && ch <= '9' )
|| ch == '+' || ch == '-' || ch == '.' ) )
{
// A scheme character (these are not allowed as the first
// character of the scheme, but can be used as subsequent
// characters.
continue;
}

// Not a scheme character
break;
}

// No scheme in URI
return null;
}

/**
* Removes %nn encodings from a string.
*/
public static String decode( final String encodedStr )
throws FileSystemException
{
final StringBuffer buffer = new StringBuffer( encodedStr );
decode( buffer, 0, buffer.length() );
return buffer.toString();
}

/**
* Removes %nn encodings from a string.
*/
public static void decode( final StringBuffer buffer,
final int offset,
final int length )
throws FileSystemException
{
int index = offset;
int count = length;
for( ; count > 0; count--, index++ )
{
final char ch = buffer.charAt( index );
if( ch != '%' )
{
continue;
}
if( count < 3 )
{
final String message = REZ.getString( "invalid-escape-sequence.error", buffer.substring( index, index + count ) );
throw new FileSystemException( message );
}

// Decode
int dig1 = Character.digit( buffer.charAt( index + 1 ), 16 );
int dig2 = Character.digit( buffer.charAt( index + 2 ), 16 );
if( dig1 == -1 || dig2 == -1 )
{
final String message = REZ.getString( "invalid-escape-sequence.error", buffer.substring( index, index + 3 ) );
throw new FileSystemException( message );
}
char value = (char)( dig1 << 4 | dig2 );

// Replace
buffer.setCharAt( index, value );
buffer.delete( index + 1, index + 3 );
count -= 2;
}
}

/**
* Encodes and appends a string to a StringBuffer.
*/
public static void appendEncoded( final StringBuffer buffer,
final String unencodedValue,
final char[] reserved )
{
final int offset = buffer.length();
buffer.append( unencodedValue );
encode( buffer, offset, unencodedValue.length(), reserved );
}

/**
* Encodes a set of reserved characters in a StringBuffer, using the URI
* %nn encoding. Always encodes % characters.
*/
public static void encode( final StringBuffer buffer,
final int offset,
final int length,
final char[] reserved )
{
int index = offset;
int count = length;
for( ; count > 0; index++, count-- )
{
final char ch = buffer.charAt( index );
boolean match = ( ch == '%' );
for( int i = 0; !match && i < reserved.length; i++ )
{
if( ch == reserved[ i ] )
{
match = true;
}
}
if( match )
{
// Encode
char[] digits = {
Character.forDigit( ( ( ch >> 4 ) & 0xF ), 16 ),
Character.forDigit( ( ch & 0xF ), 16 )
};
buffer.setCharAt( index, '%' );
buffer.insert( index + 1, digits );
index += 2;
}
}
}
}

+ 0
- 68
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileNameParser.java View File

@@ -1,68 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.ftp;

import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.UriParser;

/**
* A parser for FTP URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class FtpFileNameParser extends UriParser
{
/**
* Parses an absolute URI, splitting it into its components.
*/
public ParsedFtpUri parseFtpUri( final String uriStr )
throws FileSystemException
{
final ParsedFtpUri uri = new ParsedFtpUri();

// FTP URI are generic URI (as per RFC 2396)
parseGenericUri( uriStr, uri );

// Adjust the hostname to lower-case
final String hostname = uri.getHostName().toLowerCase();
uri.setHostName( hostname );

// Drop the port if it is 21
final String port = uri.getPort();
if( port != null && port.equals( "21" ) )
{
uri.setPort( null );
}

// Split up the userinfo into a username and password
final String userInfo = uri.getUserInfo();
if( userInfo != null )
{
int idx = userInfo.indexOf( ':' );
if( idx == -1 )
{
uri.setUserName( userInfo );
}
else
{
String userName = userInfo.substring( 0, idx );
String password = userInfo.substring( idx + 1 );
uri.setUserName( userName );
uri.setPassword( password );
}
}

// Now build the root URI
final StringBuffer rootUri = new StringBuffer();
appendRootUri( uri, rootUri );
uri.setRootUri( rootUri.toString() );

return uri;
}
}

+ 0
- 245
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileObject.java View File

@@ -1,245 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.ftp;

import com.oroinc.net.ftp.FTPClient;
import com.oroinc.net.ftp.FTPFile;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.provider.AbstractFileObject;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* An FTP file.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class FtpFileObject
extends AbstractFileObject
{
private static final Resources REZ =
ResourceManager.getPackageResources( FtpFileObject.class );

private static final FTPFile[] EMPTY_FTP_FILE_ARRAY = {};

private FtpFileSystem m_ftpFs;

// Cached info
private FTPFile m_fileInfo;
private FTPFile[] m_children;

public FtpFileObject( final FileName name, final FtpFileSystem fileSystem )
{
super( name, fileSystem );
m_ftpFs = fileSystem;
}

/**
* Called by child file objects, to locate their ftp file info.
*/
private FTPFile getChildFile( String name ) throws Exception
{
if( m_children == null )
{
// List the children of this file
m_children = m_ftpFs.getClient().listFiles( getName().getPath() );
if( m_children == null )
{
m_children = EMPTY_FTP_FILE_ARRAY;
}
}

// Look for the requested child
// TODO - use hash table
for( int i = 0; i < m_children.length; i++ )
{
FTPFile child = m_children[ i ];
if( child.getName().equals( name ) )
{
// TODO - should be using something else to compare names
return child;
}
}

return null;
}

/**
* Attaches this file object to its file resource.
*/
protected void doAttach()
throws Exception
{
// Get the parent folder to find the info for this file
FtpFileObject parent = (FtpFileObject)getParent();
m_fileInfo = parent.getChildFile( getName().getBaseName() );
if( m_fileInfo == null || !m_fileInfo.isDirectory() )
{
m_children = EMPTY_FTP_FILE_ARRAY;
}
}

/**
* Detaches this file object from its file resource.
*/
protected void doDetach()
{
m_fileInfo = null;
m_children = null;
}

/**
* Called when the children of this file change.
*/
protected void onChildrenChanged()
{
m_children = null;
}

/**
* Determines the type of the file, returns null if the file does not
* exist.
*/
protected FileType doGetType()
throws Exception
{
if( m_fileInfo == null )
{
// Does not exist
return null;
}
if( m_fileInfo.isDirectory() )
{
return FileType.FOLDER;
}
if( m_fileInfo.isFile() )
{
return FileType.FILE;
}

final String message = REZ.getString( "get-type.error", getName() );
throw new FileSystemException( message );
}

/**
* Lists the children of the file.
*/
protected String[] doListChildren()
throws Exception
{
if( m_children == null )
{
// List the children of this file
m_children = m_ftpFs.getClient().listFiles( getName().getPath() );
if( m_children == null )
{
m_children = EMPTY_FTP_FILE_ARRAY;
}
}

String[] children = new String[ m_children.length ];
for( int i = 0; i < m_children.length; i++ )
{
FTPFile child = m_children[ i ];
children[ i ] = child.getName();
}

return children;
}

/**
* Deletes the file.
*/
protected void doDelete() throws Exception
{
final FTPClient ftpClient = m_ftpFs.getClient();
boolean ok;
if( m_fileInfo.isDirectory() )
{
ok = ftpClient.removeDirectory( getName().getPath() );
}
else
{
ok = ftpClient.deleteFile( getName().getPath() );
}
if( !ok )
{
final String message = REZ.getString( "delete-file.error", getName() );
throw new FileSystemException( message );
}
}

/**
* Creates this file as a folder.
*/
protected void doCreateFolder()
throws Exception
{
if( !m_ftpFs.getClient().makeDirectory( getName().getPath() ) )
{
final String message = REZ.getString( "create-folder.error", getName() );
throw new FileSystemException( message );
}
}

/**
* Returns the size of the file content (in bytes).
*/
protected long doGetContentSize() throws Exception
{
return m_fileInfo.getSize();
}

/**
* Creates an input stream to read the file content from.
*/
protected InputStream doGetInputStream() throws Exception
{
return m_ftpFs.getClient().retrieveFileStream( getName().getPath() );
}

/**
* Notification of the input stream being closed.
*/
protected void doEndInput()
throws Exception
{
if( !m_ftpFs.getClient().completePendingCommand() )
{
final String message = REZ.getString( "finish-get.error", getName() );
throw new FileSystemException( message );
}
}

/**
* Creates an output stream to write the file content to.
*/
protected OutputStream doGetOutputStream()
throws Exception
{
return m_ftpFs.getClient().storeFileStream( getName().getPath() );
}

/**
* Notification of the output stream being closed.
*/
protected void doEndOutput()
throws Exception
{
if( !m_ftpFs.getClient().completePendingCommand() )
{
final String message = REZ.getString( "finish-put.error", getName() );
throw new FileSystemException( message );
}
}
}

+ 0
- 122
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileSystem.java View File

@@ -1,122 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.ftp;

import com.oroinc.net.ftp.FTP;
import com.oroinc.net.ftp.FTPClient;
import com.oroinc.net.ftp.FTPReply;
import java.io.IOException;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystem;
import org.apache.aut.vfs.provider.FileSystemProviderContext;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* An FTP file system.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class FtpFileSystem
extends AbstractFileSystem
{
private static final Resources REZ =
ResourceManager.getPackageResources( FtpFileSystem.class );

private FTPClient m_client;

public FtpFileSystem( final FileSystemProviderContext context,
final FileName rootName,
final String hostname,
final String username,
final String password )
throws FileSystemException
{
super( context, rootName );
try
{
m_client = new FTPClient();
m_client.connect( hostname );

int reply = m_client.getReplyCode();
if( !FTPReply.isPositiveCompletion( reply ) )
{
final String message = REZ.getString( "connect-rejected.error", hostname );
throw new FileSystemException( message );
}

// Login
if( !m_client.login( username, password ) )
{
final String message = REZ.getString( "login.error", hostname, username );
throw new FileSystemException( message );
}

// Set binary mode
if( !m_client.setFileType( FTP.BINARY_FILE_TYPE ) )
{
final String message = REZ.getString( "set-binary.error", hostname );
throw new FileSystemException( message );
}
}
catch( final Exception exc )
{
closeConnection();
final String message = REZ.getString( "connect.error", hostname );
throw new FileSystemException( message, exc );
}
}

public void dispose()
{
// Clean up the connection
super.dispose();
closeConnection();
}

/**
* Cleans up the connection to the server.
*/
private void closeConnection()
{
try
{
// Clean up
if( m_client.isConnected() )
{
m_client.disconnect();
}
}
catch( final IOException e )
{
final String message = REZ.getString( "close-connection.error" );
getLogger().warn( message, e );
}
}

/**
* Returns an FTP client to use.
*/
public FTPClient getClient()
{
// TODO - connect on demand, and garbage collect connections
return m_client;
}

/**
* Creates a file object.
*/
protected FileObject createFile( FileName name )
throws FileSystemException
{
return new FtpFileObject( name, this );
}
}

+ 0
- 66
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileSystemProvider.java View File

@@ -1,66 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.ftp;

import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystemProvider;
import org.apache.aut.vfs.provider.DefaultFileName;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.ParsedUri;

/**
* A provider for FTP file systems.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant.type type="file-system" name="ftp"
*/
public class FtpFileSystemProvider extends AbstractFileSystemProvider
{
private final FtpFileNameParser m_parser = new FtpFileNameParser();

/**
* Parses a URI into its components.
*/
protected ParsedUri parseUri( final FileObject baseFile,
final String uri )
throws FileSystemException
{
return m_parser.parseFtpUri( uri );
}

/**
* Creates the filesystem.
*/
protected FileSystem createFileSystem( final ParsedUri uri )
throws FileSystemException
{
final ParsedFtpUri ftpUri = (ParsedFtpUri)uri;

// Build the root name
final FileName rootName = new DefaultFileName( m_parser, ftpUri.getRootUri(), "/" );

// Determine the username and password to use
String username = ftpUri.getUserName();
if( username == null )
{
username = "anonymous";
}
String password = ftpUri.getPassword();
if( password == null )
{
password = "anonymous";
}

// Create the file system
return new FtpFileSystem( getContext(), rootName, ftpUri.getHostName(), username, password );
}
}

+ 0
- 42
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/ParsedFtpUri.java View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.ftp;

import org.apache.aut.vfs.provider.ParsedUri;

/**
* A parsed FTP URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class ParsedFtpUri extends ParsedUri
{
private String m_userName;
private String m_password;

public String getUserName()
{
return m_userName;
}

public void setUserName( String userName )
{
m_userName = userName;
}

public String getPassword()
{
return m_password;
}

public void setPassword( String password )
{
m_password = password;
}
}

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

@@ -1,10 +0,0 @@
get-type.error=Could not determine the file type of "{0}".
delete-file.error=Could not delete FTP file "{0}".
create-folder.error=Could not create FTP directory "{0}".
finish-get.error=Could not get FTP file "{0}".
finish-put.error=Could not put FTP file "{0}".
connect-rejected.error=Connection to FTP server on "{0}" rejected.
login.error=Could not login to FTP server on "{0}" as user "{1}".
set-binary.error=Could not switch to binary transfer mode.
connect.error=Could not connect to FTP server on "{0}".
close-connection.error=Could not close connection to FTP server.

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

@@ -1,105 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.local;

import java.io.File;
import org.apache.aut.nativelib.Os;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystemProvider;
import org.apache.aut.vfs.provider.DefaultFileName;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.LocalFileSystemProvider;
import org.apache.aut.vfs.provider.ParsedUri;

/**
* A file system provider, which uses direct file access.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant.type type="file-system" name="file"
*
*/
public class DefaultLocalFileSystemProvider
extends AbstractFileSystemProvider
implements LocalFileSystemProvider
{
private final LocalFileNameParser m_parser;

public DefaultLocalFileSystemProvider()
{
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.
*/
public boolean isAbsoluteLocalName( final String name )
{
return m_parser.isAbsoluteName( name );
}

/**
* Finds a local file, from its local name.
*/
public FileObject findLocalFile( final String name )
throws FileSystemException
{
// TODO - tidy this up, no need to turn the name into an absolute URI,
// and then straight back again
return findFile( null, "file:" + name );
}

/**
* Finds a local file.
*/
public FileObject findLocalFile( final File file )
throws FileSystemException
{
// TODO - tidy this up, should build file object straight from the file
return findFile( null, "file:" + file.getAbsolutePath() );
}

/**
* Parses a URI into its components. The returned value is used to
* locate the file system in the cache (using the root prefix), and is
* passed to {@link #createFileSystem} to create the file system.
*
* <p>The provider can annotate this object with any additional
* information it requires to create a file system from the URI.
*/
protected ParsedUri parseUri( final FileObject baseFile,
final String uri )
throws FileSystemException
{
return m_parser.parseFileUri( uri );
}

/**
* Creates the filesystem.
*/
protected FileSystem createFileSystem( final ParsedUri uri )
throws FileSystemException
{
// Build the name of the root file.
final ParsedFileUri fileUri = (ParsedFileUri)uri;
final String rootFile = fileUri.getRootFile();

// Create the file system
final DefaultFileName rootName = new DefaultFileName( m_parser, fileUri.getRootUri(), "/" );
return new LocalFileSystem( getContext(), rootName, rootFile );
}
}

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

@@ -1,44 +0,0 @@
/*
* 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 static final 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 "/";
}
}

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

@@ -1,156 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.local;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSelector;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.provider.AbstractFileObject;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* A file object implementation which uses direct file access.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
final class LocalFile
extends AbstractFileObject
implements FileObject
{
private static final Resources REZ =
ResourceManager.getPackageResources( LocalFile.class );

private File m_file;
private final String m_fileName;

/**
* Creates a non-root file.
*/
public LocalFile( final LocalFileSystem fileSystem,
final String fileName,
final FileName name )
{
super( name, fileSystem );
m_fileName = fileName;
}

/**
* Attaches this file object to its file resource.
*/
protected void doAttach()
throws Exception
{
if( m_file == null )
{
m_file = new File( m_fileName );
}
}

/**
* Returns the file's type.
*/
protected FileType doGetType()
throws Exception
{
if( !m_file.exists() )
{
return null;
}
if( m_file.isDirectory() )
{
return FileType.FOLDER;
}
if( m_file.isFile() )
{
return FileType.FILE;
}

final String message = REZ.getString( "get-type.error", m_file );
throw new FileSystemException( message );
}

/**
* Returns the children of the file.
*/
protected String[] doListChildren()
throws Exception
{
return m_file.list();
}

/**
* Deletes this file, and all children.
*/
protected void doDelete()
throws Exception
{
if( !m_file.delete() )
{
final String message = REZ.getString( "delete-file.error", m_file );
throw new FileSystemException( message );
}
}

/**
* Creates this folder.
*/
protected void doCreateFolder()
throws Exception
{
if( !m_file.mkdir() )
{
final String message = REZ.getString( "create-folder.error", m_file );
throw new FileSystemException( message );
}
}

/**
* Creates an input stream to read the content from.
*/
protected InputStream doGetInputStream()
throws Exception
{
return new FileInputStream( m_file );
}

/**
* Creates an output stream to write the file content to.
*/
protected OutputStream doGetOutputStream()
throws Exception
{
return new FileOutputStream( m_file );
}

/**
* Returns the size of the file content (in bytes).
*/
protected long doGetContentSize()
throws Exception
{
return m_file.length();
}

/**
* Creates a temporary local copy of this file, and its descendents.
*/
protected File doReplicateFile( final FileSelector selector )
throws FileSystemException
{
return m_file;
}
}

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

@@ -1,92 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.local;

import java.io.File;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.ParsedUri;
import org.apache.aut.vfs.provider.UriParser;

/**
* A name parser.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
abstract class LocalFileNameParser
extends UriParser
{
public LocalFileNameParser()
{
super( new char[]{File.separatorChar, '/', '\\'} );
}

/**
* Determines if a name is an absolute file name.
*/
public boolean isAbsoluteName( final String name )
{
// TODO - this is yucky
StringBuffer b = new StringBuffer( name );
try
{
fixSeparators( b );
extractRootPrefix( name, b );
return true;
}
catch( FileSystemException e )
{
return false;
}
}

/**
* Parses an absolute URI, splitting it into its components.
*
* @param uriStr The URI.
*/
public ParsedUri parseFileUri( final String uriStr )
throws FileSystemException
{
final StringBuffer name = new StringBuffer();
final ParsedFileUri uri = new ParsedFileUri();

// Extract the scheme
final String scheme = extractScheme( uriStr, name );
uri.setScheme( scheme );

// Remove encoding, and adjust the separators
decode( name, 0, name.length() );
fixSeparators( name );

// Extract the root prefix
final String rootFile = extractRootPrefix( uriStr, name );
uri.setRootFile( rootFile );

// Normalise the path
normalisePath( name );
uri.setPath( name.toString() );

// Build the root URI
final StringBuffer rootUri = new StringBuffer();
rootUri.append( scheme );
rootUri.append( "://" );
rootUri.append( rootFile );
uri.setRootUri( rootUri.toString() );

return uri;
}

/**
* Pops the root prefix off a URI, which has had the scheme removed.
*/
protected abstract String extractRootPrefix( final String uri,
final StringBuffer name )
throws FileSystemException;

}

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

@@ -1,45 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.local;

import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystem;
import org.apache.aut.vfs.provider.DefaultFileName;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.FileSystemProviderContext;

/**
* A local file system.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class LocalFileSystem extends AbstractFileSystem implements FileSystem
{
private String m_rootFile;

public LocalFileSystem( final FileSystemProviderContext context,
final DefaultFileName rootName,
final String rootFile )
{
super( context, rootName );
m_rootFile = rootFile;
}

/**
* Creates a file object.
*/
protected FileObject createFile( final FileName name ) throws FileSystemException
{
// Create the file
final String fileName = m_rootFile + name.getPath();
return new LocalFile( this, fileName, name );
}
}

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

@@ -1,31 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.local;

import org.apache.aut.vfs.provider.ParsedUri;

/**
* A parsed file URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class ParsedFileUri extends ParsedUri
{
private String m_rootFile;

public String getRootFile()
{
return m_rootFile;
}

public void setRootFile( final String rootPrefix )
{
m_rootFile = rootPrefix;
}
}

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

@@ -1,5 +0,0 @@
get-type.error=Could not determine the type of "{0}".
delete-file.error=Could not delete "{0}".
create-folder.error=Could not create directory "{0}".
not-absolute-file-name.error=URI "{0}" is not an absolute file name.
missing-share-name.error=Share name missing from UNC file name "{0}".

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

@@ -1,150 +0,0 @@
/*
* 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 static final 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;
}
}

+ 0
- 31
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/ParsedSmbUri.java View File

@@ -1,31 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.smb;

import org.apache.aut.vfs.provider.ParsedUri;

/**
* A parsed SMB URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class ParsedSmbUri extends ParsedUri
{
private String m_share;

public String getShare()
{
return m_share;
}

public void setShare( String share )
{
m_share = share;
}
}

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

@@ -1,2 +0,0 @@
missing-share-name.error=The share name is missing from URI "{0}".
get-type.error=Could not detemine the type of "{0}".

+ 0
- 74
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileNameParser.java View File

@@ -1,74 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.smb;

import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.ParsedUri;
import org.apache.aut.vfs.provider.UriParser;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* A parser for SMB URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class SmbFileNameParser
extends UriParser
{
private static final Resources REZ =
ResourceManager.getPackageResources( SmbFileNameParser.class );

/**
* Parses an absolute URI, splitting it into its components.
*/
public ParsedUri parseSmbUri( final String uriStr )
throws FileSystemException
{
final ParsedSmbUri uri = new ParsedSmbUri();
final StringBuffer name = new StringBuffer();

// Extract the scheme and authority parts
extractToPath( uriStr, name, uri );

// Convert the hostname to lowercase
final String hostname = uri.getHostName().toLowerCase();
uri.setHostName( hostname );

// TODO - drop the default port

// Decode and adjust separators
decode( name, 0, name.length() );
fixSeparators( name );

// Extract the share
final String share = extractFirstElement( name );
if( share == null )
{
final String message = REZ.getString( "missing-share-name.error", uriStr );
throw new FileSystemException( message );
}
uri.setShare( share );

// Normalise the path
normalisePath( name );

// Set the path
uri.setPath( name.toString() );

// Set the root URI
StringBuffer rootUri = new StringBuffer();
appendRootUri( uri, rootUri );
rootUri.append( '/' );
rootUri.append( share );
uri.setRootUri( rootUri.toString() );

return uri;
}
}

+ 0
- 149
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileObject.java View File

@@ -1,149 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.smb;

import java.io.InputStream;
import java.io.OutputStream;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import jcifs.smb.SmbFileOutputStream;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.provider.AbstractFileObject;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* A file in an SMB file system.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class SmbFileObject
extends AbstractFileObject
implements FileObject
{
private static final Resources REZ =
ResourceManager.getPackageResources( SmbFileObject.class );

private String m_fileName;
private SmbFile m_file;

protected SmbFileObject( final String fileName,
final FileName name,
final SmbFileSystem fileSystem )
{
super( name, fileSystem );
m_fileName = fileName;
}

/**
* Attaches this file object to its file resource.
*/
protected void doAttach() throws Exception
{
// Defer creation of the SmbFile to here
if( m_file == null )
{
m_file = new SmbFile( m_fileName );
}
}

/**
* Detaches this file object from its file resource.
*/
protected void doDetach()
{
// Need to throw away the file when the file's type changes, because
// the SmbFile caches the type
m_file = null;
}

/**
* Determines the type of the file, returns null if the file does not
* exist.
*/
protected FileType doGetType() throws Exception
{
// Need to check whether parent exists or not, because SmbFile.exists()
// throws an exception if it does not
// TODO - patch jCIFS?

FileObject parent = getParent();
if( parent != null && !parent.exists() )
{
return null;
}

if( !m_file.exists() )
{
return null;
}
if( m_file.isDirectory() )
{
return FileType.FOLDER;
}
if( m_file.isFile() )
{
return FileType.FILE;
}
final String message = REZ.getString( "get-type.error", getName() );
throw new FileSystemException( message );
}

/**
* Lists the children of the file. Is only called if {@link #doGetType}
* returns {@link FileType#FOLDER}.
*/
protected String[] doListChildren() throws Exception
{
return m_file.list();
}

/**
* Deletes the file.
*/
protected void doDelete() throws Exception
{
m_file.delete();
}

/**
* Creates this file as a folder.
*/
protected void doCreateFolder() throws Exception
{
m_file.mkdir();
}

/**
* Returns the size of the file content (in bytes).
*/
protected long doGetContentSize() throws Exception
{
return m_file.length();
}

/**
* Creates an input stream to read the file content from.
*/
protected InputStream doGetInputStream() throws Exception
{
return new SmbFileInputStream( m_file );
}

/**
* Creates an output stream to write the file content to.
*/
protected OutputStream doGetOutputStream() throws Exception
{
return new SmbFileOutputStream( m_file );
}
}

+ 0
- 39
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileSystem.java View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.smb;

import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystem;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.FileSystemProviderContext;

/**
* A SMB file system.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class SmbFileSystem extends AbstractFileSystem implements FileSystem
{
public SmbFileSystem( final FileSystemProviderContext context,
final FileName rootName )
{
super( context, rootName );
}

/**
* Creates a file object.
*/
protected FileObject createFile( final FileName name ) throws FileSystemException
{
final String fileName = name.getURI();
return new SmbFileObject( fileName, name, this );
}
}

+ 0
- 51
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileSystemProvider.java View File

@@ -1,51 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.smb;

import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystemProvider;
import org.apache.aut.vfs.provider.DefaultFileName;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.FileSystemProvider;
import org.apache.aut.vfs.provider.ParsedUri;

/**
* A provider for SMB (Samba, Windows share) file systems.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant.type type="file-system" name="smb"
*/
public class SmbFileSystemProvider extends AbstractFileSystemProvider implements FileSystemProvider
{
private final SmbFileNameParser m_parser = new SmbFileNameParser();

/**
* Parses a URI into its components.
*/
protected ParsedUri parseUri( final FileObject baseFile,
final String uri )
throws FileSystemException
{
return m_parser.parseSmbUri( uri );
}

/**
* Creates the filesystem.
*/
protected FileSystem createFileSystem( final ParsedUri uri )
throws FileSystemException
{
final ParsedSmbUri smbUri = (ParsedSmbUri)uri;
final FileName rootName = new DefaultFileName( m_parser, smbUri.getRootUri(), "/" );
return new SmbFileSystem( getContext(), rootName );
}
}

+ 0
- 43
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ParsedZipUri.java View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.zip;

import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.provider.ParsedUri;

/**
* A parsed Zip URI.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class ParsedZipUri extends ParsedUri
{
private String m_zipFileName;
private FileObject m_zipFile;

public String getZipFileName()
{
return m_zipFileName;
}

public void setZipFileName( final String zipFileName )
{
m_zipFileName = zipFileName;
}

public FileObject getZipFile()
{
return m_zipFile;
}

public void setZipFile( final FileObject zipFile )
{
m_zipFile = zipFile;
}
}

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

@@ -1,2 +0,0 @@
open-zip-file.error=Could not open Zip file "{0}".
close-zip-file.error=Could not close Zip file "{0}".

+ 0
- 92
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileNameParser.java View File

@@ -1,92 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.zip;

import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.UriParser;

/**
* A parser for Zip file names.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class ZipFileNameParser
extends UriParser
{
private static final char[] ZIP_URL_RESERVED_CHARS = {'!'};

/**
* Parses an absolute URI, splitting it into its components.
*
* @param uriStr
* The URI.
*/
public ParsedZipUri parseZipUri( final String uriStr )
throws FileSystemException
{
final StringBuffer name = new StringBuffer();
final ParsedZipUri uri = new ParsedZipUri();

// Extract the scheme
final String scheme = extractScheme( uriStr, name );
uri.setScheme( scheme );

// Extract the Zip file name
final String zipName = extractZipName( name );
uri.setZipFileName( zipName );

// Decode and normalise the file name
decode( name, 0, name.length() );
normalisePath( name );
uri.setPath( name.toString() );

return uri;
}

/**
* Assembles a root URI from the components of a parsed URI.
*/
public String buildRootUri( final ParsedZipUri uri )
{
final StringBuffer rootUri = new StringBuffer();
rootUri.append( uri.getScheme() );
rootUri.append( ":" );
appendEncoded( rootUri, uri.getZipFile().getName().getURI(), ZIP_URL_RESERVED_CHARS );
rootUri.append( "!" );
return rootUri.toString();
}

/**
* Pops the root prefix off a URI, which has had the scheme removed.
*/
private String extractZipName( final StringBuffer uri )
throws FileSystemException
{
// Looking for <name>!<abspath>
int maxlen = uri.length();
int pos = 0;
for( ; pos < maxlen && uri.charAt( pos ) != '!'; pos++ )
{
}

// Extract the name
String prefix = uri.substring( 0, pos );
if( pos < maxlen )
{
uri.delete( 0, pos + 1 );
}
else
{
uri.setLength( 0 );
}

// Decode the name
return decode( prefix );
}
}

+ 0
- 104
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileObject.java View File

@@ -1,104 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.zip;

import java.io.InputStream;
import java.util.HashSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.provider.AbstractFileObject;

/**
* A file in a Zip file system.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
class ZipFileObject extends AbstractFileObject implements FileObject
{
private ZipEntry m_entry;
private ZipFile m_file;
private FileType m_type;
private HashSet m_children = new HashSet();

public ZipFileObject( FileName name,
ZipEntry entry,
ZipFile zipFile,
ZipFileSystem fs )
{
super( name, fs );
m_type = FileType.FILE;
m_entry = entry;
m_file = zipFile;
}

public ZipFileObject( FileName name, boolean exists, ZipFileSystem fs )
{
super( name, fs );
if( exists )
{
m_type = FileType.FOLDER;
}
// else _type = null
}

/**
* Attaches a child
*/
public void attachChild( FileName childName )
{
m_children.add( childName.getBaseName() );
}

/**
* Returns true if this file is read-only.
*/
protected boolean isReadOnly()
{
return true;
}

/**
* Returns the file's type.
*/
protected FileType doGetType()
{
return m_type;
}

/**
* Lists the children of the file.
*/
protected String[] doListChildren()
{
return (String[])m_children.toArray( new String[ m_children.size() ] );
}

/**
* Returns the size of the file content (in bytes). Is only called if
* {@link #doGetType} returns {@link FileType#FILE}.
*/
protected long doGetContentSize()
{
return m_entry.getSize();
}

/**
* Creates an input stream to read the file content from. Is only called
* if {@link #doGetType} returns {@link FileType#FILE}. The input stream
* returned by this method is guaranteed to be closed before this
* method is called again.
*/
protected InputStream doGetInputStream() throws Exception
{
return m_file.getInputStream( m_entry );
}
}

+ 0
- 134
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileSystem.java View File

@@ -1,134 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.zip;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystem;
import org.apache.aut.vfs.provider.DefaultFileName;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.FileSystemProviderContext;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;

/**
* A read-only file system for Zip/Jar files.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*/
public class ZipFileSystem
extends AbstractFileSystem
implements FileSystem
{
private static final Resources REZ =
ResourceManager.getPackageResources( ZipFileSystem.class );

private File m_file;
private ZipFile m_zipFile;

public ZipFileSystem( final FileSystemProviderContext context,
final DefaultFileName rootName,
final File file ) throws FileSystemException
{
super( context, rootName );
m_file = file;

// Open the Zip file
if( !file.exists() )
{
// Don't need to do anything
return;
}

try
{
m_zipFile = new ZipFile( m_file );
}
catch( IOException ioe )
{
final String message = REZ.getString( "open-zip-file.error", m_file );
throw new FileSystemException( message, ioe );
}

// Build the index
Enumeration entries = m_zipFile.entries();
while( entries.hasMoreElements() )
{
ZipEntry entry = (ZipEntry)entries.nextElement();
FileName name = rootName.resolveName( entry.getName() );

// Create the file
ZipFileObject fileObj;
if( entry.isDirectory() )
{
if( getFile( name ) != null )
{
// Already created implicitly
continue;
}
fileObj = new ZipFileObject( name, true, this );
}
else
{
fileObj = new ZipFileObject( name, entry, m_zipFile, this );
}
putFile( fileObj );

// Make sure all ancestors exist
// TODO - create these on demand
ZipFileObject parent = null;
for( FileName parentName = name.getParent();
parentName != null;
fileObj = parent, parentName = parentName.getParent() )
{
// Locate the parent
parent = (ZipFileObject)getFile( parentName );
if( parent == null )
{
parent = new ZipFileObject( parentName, true, this );
putFile( parent );
}

// Attach child to parent
parent.attachChild( fileObj.getName() );
}
}
}

public void dispose()
{
super.dispose();

// Release the zip file
try
{
m_zipFile.close();
}
catch( final IOException e )
{
final String message = REZ.getString( "close-zip-file.error", m_file );
getLogger().warn( message, e );
}
}

/**
* Creates a file object.
*/
protected FileObject createFile( FileName name ) throws FileSystemException
{
// This is only called for files which do not exist in the Zip file
return new ZipFileObject( name, false, this );
}
}

+ 0
- 92
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileSystemProvider.java View File

@@ -1,92 +0,0 @@
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE.txt file.
*/
package org.apache.aut.vfs.provider.zip;

import java.io.File;
import org.apache.aut.vfs.FileConstants;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.provider.AbstractFileSystemProvider;
import org.apache.aut.vfs.provider.DefaultFileName;
import org.apache.aut.vfs.provider.FileSystem;
import org.apache.aut.vfs.provider.FileSystemProvider;
import org.apache.aut.vfs.provider.ParsedUri;

/**
* A file system provider for Zip/Jar files. Provides read-only file
* systems, for local Zip files only.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
* @version $Revision$ $Date$
*
* @ant.type type="file-system" name="zip"
*/
public class ZipFileSystemProvider
extends AbstractFileSystemProvider
implements FileSystemProvider
{
private final ZipFileNameParser m_parser = new ZipFileNameParser();

/**
* Parses a URI into its components.
*/
protected ParsedUri parseUri( final FileObject baseFile,
final String uriStr )
throws FileSystemException
{
// Parse the URI
final ParsedZipUri uri = m_parser.parseZipUri( uriStr );

// Make the URI canonical

// Resolve the Zip file name
final String fileName = uri.getZipFileName();
final FileObject file = getContext().resolveFile( baseFile, fileName );
uri.setZipFile( file );

// Rebuild the root URI
final String rootUri = m_parser.buildRootUri( uri );
uri.setRootUri( rootUri );

return uri;
}

/**
* Builds the URI for the root of a layered file system.
*/
protected ParsedUri buildUri( final String scheme,
final FileObject file )
throws FileSystemException
{
ParsedZipUri uri = new ParsedZipUri();
uri.setScheme( scheme );
uri.setZipFile( file );
final String rootUri = m_parser.buildRootUri( uri );
uri.setRootUri( rootUri );
uri.setPath( "/" );
return uri;
}

/**
* Creates the filesystem.
*/
protected FileSystem createFileSystem( final ParsedUri uri )
throws FileSystemException
{
final ParsedZipUri zipUri = (ParsedZipUri)uri;
final FileObject file = zipUri.getZipFile();

// Make a local copy of the file
final File zipFile = file.replicateFile( FileConstants.SELECT_SELF );

// Create the file system
DefaultFileName name = new DefaultFileName( m_parser, zipUri.getRootUri(), "/" );
return new ZipFileSystem( getContext(), name, zipFile );
}

}

+ 0
- 25
proposal/myrmidon/src/test/org/apache/aut/converter/lib/test/SimpleConvertersTestCase.java View File

@@ -1,25 +0,0 @@
/*
* 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.converter.lib.test;

import junit.framework.TestCase;

/**
* A Unit test that is used to test all the simple converters in the lib directorys.
*
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public class SimpleConvertersTestCase
extends TestCase
{
public SimpleConvertersTestCase( final String name )
{
super( name );
}
}

+ 0
- 879
proposal/myrmidon/src/test/org/apache/aut/vfs/test/AbstractFileSystemTestCase.java View File

@@ -1,879 +0,0 @@
/*
* 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.test;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.aut.vfs.impl.DefaultFileSystemManager;
import org.apache.aut.vfs.provider.AbstractFileObject;
import org.apache.aut.vfs.provider.LocalFileSystemProvider;
import org.apache.aut.vfs.provider.local.DefaultLocalFileSystemProvider;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileName;
import org.apache.aut.vfs.NameScope;
import org.apache.aut.vfs.FileContent;
import org.apache.avalon.excalibur.i18n.ResourceManager;
import org.apache.avalon.excalibur.i18n.Resources;
import org.apache.myrmidon.AbstractMyrmidonTest;

/**
* File system test cases, which verifies the structure and naming
* functionality.
*
* Works from a base folder, and assumes a particular structure under
* that base folder.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
*/
public abstract class AbstractFileSystemTestCase
extends AbstractMyrmidonTest
{
private final static Resources REZ
= ResourceManager.getPackageResources( AbstractFileObject.class );

protected FileObject m_baseFolder;
protected DefaultFileSystemManager m_manager;

// Contents of "file1.txt"
private String m_charContent;

public AbstractFileSystemTestCase( String name )
{
super( name );
}

/**
* Builds the expected folder structure.
*/
private FileInfo buildExpectedStructure()
{
// Build the expected structure
final FileInfo base = new FileInfo( "test", FileType.FOLDER );
base.addChild( "file1.txt", FileType.FILE );
base.addChild( "empty.txt", FileType.FILE );
base.addChild( "emptydir", FileType.FOLDER );

final FileInfo dir = new FileInfo( "dir1", FileType.FOLDER );
base.addChild( dir );
dir.addChild( "file1.txt", FileType.FILE );
dir.addChild( "file2.txt", FileType.FILE );
dir.addChild( "file3.txt", FileType.FILE );
return base;
}

/**
* Returns the base folder to run the tests against.
*/
protected abstract FileObject getBaseFolder() throws Exception;

/**
* Sets up the test
*/
protected void setUp() throws Exception
{
// Create the file system manager
m_manager = new DefaultFileSystemManager();
m_manager.enableLogging( getLogger() );
m_manager.addProvider( "file", new DefaultLocalFileSystemProvider() );

// Locate the base folder
m_baseFolder = getBaseFolder();

// Make some assumptions absout the name
assertTrue( ! m_baseFolder.getName().getPath().equals( "/" ) );

// Build the expected content of "file1.txt"
final String eol = System.getProperty( "line.separator" );
m_charContent = "This is a test file." + eol + "With 2 lines in it." + eol;
}

/**
* Cleans-up test.
*/
protected void tearDown() throws Exception
{
m_manager.dispose();
}

/**
* Tests resolution of absolute URI.
*/
public void testAbsoluteURI() throws Exception
{
// Try fetching base folder again by its URI
final String uri = m_baseFolder.getName().getURI();
final FileObject file = m_manager.resolveFile( uri );

assertSame( "file object", m_baseFolder, file );
}

/**
* Tests resolution of relative file names via the FS manager
*/
public void testRelativeURI() throws Exception
{
// Build base dir
m_manager.setBaseFile( m_baseFolder );

// Locate the base dir
FileObject file = m_manager.resolveFile( "." );
assertSame( "file object", m_baseFolder, file );

// Locate a child
file = m_manager.resolveFile( "some-child" );
assertSame( "file object", m_baseFolder, file.getParent() );

// Locate a descendent
file = m_manager.resolveFile( "some-folder/some-file" );
assertSame( "file object", m_baseFolder, file.getParent().getParent() );

// Locate parent
file = m_manager.resolveFile( ".." );
assertSame( "file object", m_baseFolder.getParent(), file );
}

/**
* Tests encoding of relative URI.
*/
public void testRelativeUriEncoding() throws Exception
{
// Build base dir
m_manager.setBaseFile( m_baseFolder );
final String path = m_baseFolder.getName().getPath();

// Encode "some file"
FileObject file = m_manager.resolveFile( "%73%6f%6d%65%20%66%69%6c%65" );
assertEquals( path + "/some file", file.getName().getPath() );

// Encode "."
file = m_manager.resolveFile( "%2e" );
assertEquals( path, file.getName().getPath() );

// Encode '%'
file = m_manager.resolveFile( "a%25" );
assertEquals( path + "/a%", file.getName().getPath() );

// Encode /
file = m_manager.resolveFile( "dir%2fchild" );
assertEquals( path + "/dir/child", file.getName().getPath() );

// Encode \
file = m_manager.resolveFile( "dir%5cchild" );
assertEquals( path + "/dir/child", file.getName().getPath() );

// Use "%" literal
try
{
m_manager.resolveFile( "%" );
fail();
}
catch( FileSystemException e )
{
}

// Not enough digits in encoded char
try
{
m_manager.resolveFile( "%5" );
fail();
}
catch( FileSystemException e )
{
}

// Invalid digit in encoded char
try
{
m_manager.resolveFile( "%q" );
fail();
}
catch( FileSystemException e )
{
}
}

/**
* Tests the root file name.
*/
public void testRootFileName() throws Exception
{
// Locate the root file
final FileName rootName = m_baseFolder.getRoot().getName();

// Test that the root path is "/"
assertEquals( "root path", "/", rootName.getPath() );

// Test that the root basname is ""
assertEquals( "root base name", "", rootName.getBaseName() );

// Test that the root name has no parent
assertNull( "root parent", rootName.getParent() );
}

/**
* Tests child file names.
*/
public void testChildName() throws Exception
{
final FileName baseName = m_baseFolder.getName();
final String basePath = baseName.getPath();
final FileName name = baseName.resolveName( "some-child", NameScope.CHILD );

// Test path is absolute
assertTrue( "is absolute", basePath.startsWith( "/" ) );

// Test base name
assertEquals( "base name", "some-child", name.getBaseName() );

// Test absolute path
assertEquals( "absolute path", basePath + "/some-child", name.getPath() );

// Test parent path
assertEquals( "parent absolute path", basePath, name.getParent().getPath() );

// Try using a compound name to find a child
assertBadName( name, "a/b", NameScope.CHILD );

// Check other invalid names
checkDescendentNames( name, NameScope.CHILD );
}

/**
* 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
assertTrue( !name.getPath().equals( "/" ) );
assertTrue( !name.getPath().endsWith( "/a" ) );
assertTrue( !name.getPath().endsWith( "/a/b" ) );

// Test names with the same prefix
String path = name.getPath() + "/a";
assertSameName( path, name, path, scope );
assertSameName( path, name, "../" + name.getBaseName() + "/a", scope );

// Test an empty name
assertBadName( name, "", scope );

// Test . name
assertBadName( name, ".", scope );
assertBadName( name, "./", scope );

// Test ancestor names
assertBadName( name, "..", scope );
assertBadName( name, "../a", scope );
assertBadName( name, "../" + name.getBaseName() + "a", scope );
assertBadName( name, "a/..", scope );

// Test absolute names
assertBadName( name, "/", scope );
assertBadName( name, "/a", scope );
assertBadName( name, "/a/b", scope );
assertBadName( name, name.getPath(), scope );
assertBadName( name, name.getPath() + "a", scope );
}

/**
* Checks that a relative name resolves to the expected absolute path.
* Tests both forward and back slashes.
*/
private void assertSameName( final String expectedPath,
final FileName baseName,
final String relName,
final NameScope scope )
throws Exception
{
// Try the supplied name
FileName name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );

// Replace the separators
relName.replace( '\\', '/' );
name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );

// And again
relName.replace( '/', '\\' );
name = baseName.resolveName( relName, scope );
assertEquals( expectedPath, name.getPath() );
}

/**
* Checks that a relative name resolves to the expected absolute path.
* Tests both forward and back slashes.
*/
private void assertSameName( String expectedPath,
FileName baseName,
String relName ) throws Exception
{
assertSameName( expectedPath, baseName, relName, NameScope.FILE_SYSTEM );
}

/**
* Tests relative name resolution, relative to the base folder.
*/
public void testNameResolution() throws Exception
{
final FileName baseName = m_baseFolder.getName();
final String parentPath = baseName.getParent().getPath();
final String path = baseName.getPath();
final String childPath = path + "/some-child";

// Test empty relative path
assertSameName( path, baseName, "" );

// Test . relative path
assertSameName( path, baseName, "." );

// Test ./ relative path
assertSameName( path, baseName, "./" );

// Test .// relative path
assertSameName( path, baseName, ".//" );

// Test .///.///. relative path
assertSameName( path, baseName, ".///.///." );
assertSameName( path, baseName, "./\\/.\\//." );

// Test <elem>/.. relative path
assertSameName( path, baseName, "a/.." );

// Test .. relative path
assertSameName( parentPath, baseName, ".." );

// Test ../ relative path
assertSameName( parentPath, baseName, "../" );

// Test ..//./ relative path
assertSameName( parentPath, baseName, "..//./" );
assertSameName( parentPath, baseName, "..//.\\" );

// Test <elem>/../.. relative path
assertSameName( parentPath, baseName, "a/../.." );

// Test <elem> relative path
assertSameName( childPath, baseName, "some-child" );

// Test ./<elem> relative path
assertSameName( childPath, baseName, "./some-child" );

// Test ./<elem>/ relative path
assertSameName( childPath, baseName, "./some-child/" );

// Test <elem>/././././ relative path
assertSameName( childPath, baseName, "./some-child/././././" );

// Test <elem>/../<elem> relative path
assertSameName( childPath, baseName, "a/../some-child" );

// Test <elem>/<elem>/../../<elem> relative path
assertSameName( childPath, baseName, "a/b/../../some-child" );
}

/**
* Tests 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 );
}

/**
* Tests resolution of absolute names.
*/
public void testAbsoluteNames() throws Exception
{
// Test against the base folder
FileName name = m_baseFolder.getName();
checkAbsoluteNames( name );

// Test against the root
name = m_baseFolder.getRoot().getName();
checkAbsoluteNames( name );

// Test against some unknown file
name = name.resolveName( "a/b/unknown" );
checkAbsoluteNames( name );
}

/**
* Tests resolution of absolute names.
*/
private void checkAbsoluteNames( final FileName name ) throws Exception
{
// Root
assertSameName( "/", name, "/" );
assertSameName( "/", name, "//" );
assertSameName( "/", name, "/." );
assertSameName( "/", name, "/some file/.." );

// Some absolute names
assertSameName( "/a", name, "/a" );
assertSameName( "/a", name, "/./a" );
assertSameName( "/a", name, "/a/." );
assertSameName( "/a/b", name, "/a/b" );

// Some bad names
assertBadName( name, "/..", NameScope.FILE_SYSTEM );
assertBadName( name, "/a/../..", NameScope.FILE_SYSTEM );
}

/**
* 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( "expected failure" );
}
catch( FileSystemException e )
{
// TODO - should check error message
}
}

/**
* Tests conversion from absolute to relative names.
*/
public void testAbsoluteNameConvert() throws Exception
{
final FileName baseName = m_baseFolder.getName();

String path = "/test1/test2";
FileName name = baseName.resolveName( path );
assertEquals( path, name.getPath() );

// Try child and descendent names
testRelName( name, "child" );
testRelName( name, "child1/child2" );

// Try own name
testRelName( name, "." );

// Try parent, and root
testRelName( name, ".." );
testRelName( name, "../.." );

// Try sibling and descendent of sibling
testRelName( name, "../sibling" );
testRelName( name, "../sibling/child" );

// Try siblings with similar names
testRelName( name, "../test2_not" );
testRelName( name, "../test2_not/child" );
testRelName( name, "../test" );
testRelName( name, "../test/child" );

// Try unrelated
testRelName( name, "../../unrelated" );
testRelName( name, "../../test" );
testRelName( name, "../../test/child" );

// Test against root
path = "/";
name = baseName.resolveName( path );
assertEquals( path, name.getPath() );

// Try child and descendent names (against root)
testRelName( name, "child" );
testRelName( name, "child1/child2" );

// Try own name (against root)
testRelName( name, "." );
}

/**
* Checks that a file name converts to an expected relative path
*/
private void testRelName( final FileName baseName,
final String relPath )
throws Exception
{
final FileName expectedName = baseName.resolveName( relPath );

// Convert to relative path, and check
final String actualRelPath = baseName.getRelativeName( expectedName );
assertEquals( relPath, actualRelPath );
}

/**
* Walks the base folder structure, asserting it contains exactly the
* expected files and folders.
*/
public void testStructure() throws Exception
{
final FileInfo baseInfo = buildExpectedStructure();
assertSameStructure( m_baseFolder, baseInfo );
}

/**
* Walks a folder structure, asserting it contains exactly the
* expected files and folders.
*/
protected void assertSameStructure( final FileObject folder,
final FileInfo expected )
throws Exception
{
// Setup the structure
final List queueExpected = new ArrayList();
queueExpected.add( expected );

final List queueActual = new ArrayList();
queueActual.add( folder );

while( queueActual.size() > 0 )
{
final FileObject file = (FileObject)queueActual.remove( 0 );
final FileInfo info = (FileInfo)queueExpected.remove( 0 );

// Check the type is correct
assertSame( file.getType(), info.m_type );

if( info.m_type == FileType.FILE )
{
continue;
}

// Check children
final FileObject[] children = file.getChildren();

// Make sure all children were found
assertNotNull( children );
assertEquals( "count children of \"" + file.getName() + "\"", info.m_children.size(), children.length );

// Recursively check each child
for( int i = 0; i < children.length; i++ )
{
final FileObject child = children[ i ];
final FileInfo childInfo = (FileInfo)info.m_children.get( child.getName().getBaseName() );

// Make sure the child is expected
assertNotNull( childInfo );

// Add to the queue of files to check
queueExpected.add( childInfo );
queueActual.add( child );
}
}
}

/**
* Tests existence determination.
*/
public void testExists() throws Exception
{
// Test a file
FileObject file = m_baseFolder.resolveFile( "file1.txt" );
assertTrue( "file exists", file.exists() );

// Test a folder
file = m_baseFolder.resolveFile( "dir1" );
assertTrue( "folder exists", file.exists() );

// Test an unknown file
file = m_baseFolder.resolveFile( "unknown-child" );
assertTrue( "unknown file does not exist", !file.exists() );

// Test an unknown file in an unknown folder
file = m_baseFolder.resolveFile( "unknown-folder/unknown-child" );
assertTrue( "unknown file does not exist", !file.exists() );
}

/**
* Tests type determination.
*/
public void testType() throws Exception
{
// Test a file
FileObject file = m_baseFolder.resolveFile( "file1.txt" );
assertSame( FileType.FILE, file.getType() );

// Test a folder
file = m_baseFolder.resolveFile( "dir1" );
assertSame( FileType.FOLDER, file.getType() );

// Test an unknown file
file = m_baseFolder.resolveFile( "unknown-child" );
try
{
file.getType();
fail();
}
catch( FileSystemException e )
{
final String message = REZ.getString( "get-type-no-exist.error", file );
assertSameMessage( message, e );
}
}

/**
* Tests parent identity
*/
public void testParent() throws FileSystemException
{
// Test when both exist
FileObject folder = m_baseFolder.resolveFile( "dir1" );
FileObject child = folder.resolveFile( "file3.txt" );
assertTrue( "folder exists", folder.exists() );
assertTrue( "child exists", child.exists() );
assertSame( folder, child.getParent() );

// Test when file does not exist
child = folder.resolveFile( "unknown-file" );
assertTrue( "folder exists", folder.exists() );
assertTrue( "child does not exist", !child.exists() );
assertSame( folder, child.getParent() );

// Test when neither exists
folder = m_baseFolder.resolveFile( "unknown-folder" );
child = folder.resolveFile( "unknown-file" );
assertTrue( "folder does not exist", !folder.exists() );
assertTrue( "child does not exist", !child.exists() );
assertSame( folder, child.getParent() );

// Test root of the file system has no parent
FileObject root = m_baseFolder.getRoot();
assertNull( "root has null parent", root.getParent() );
}

/**
* Tests that children cannot be listed for non-folders.
*/
public void testChildren() throws FileSystemException
{
// Check for file
FileObject file = m_baseFolder.resolveFile( "file1.txt" );
assertSame( FileType.FILE, file.getType() );
try
{
file.getChildren();
fail();
}
catch( FileSystemException e )
{
final String message = REZ.getString( "list-children-not-folder.error", file );
assertSameMessage( message, e );
}

// Should be able to get child by name
file = file.resolveFile( "some-child" );
assertNotNull( file );

// Check for unknown file
file = m_baseFolder.resolveFile( "unknown-file" );
assertTrue( !file.exists() );
try
{
file.getChildren();
fail();
}
catch( FileSystemException e )
{
final String message = REZ.getString( "list-children-no-exist.error", file );
assertSameMessage( message, e );
}

// Should be able to get child by name
FileObject child = file.resolveFile( "some-child" );
assertNotNull( child );
}

/**
* Tests content.
*/
public void testContent() throws Exception
{
// Test non-empty file
FileObject file = m_baseFolder.resolveFile( "file1.txt" );
FileContent content = file.getContent();
assertSameContent( m_charContent, content );

// Test empty file
file = m_baseFolder.resolveFile( "empty.txt" );
content = file.getContent();
assertSameContent( "", content );
}

/**
* Asserts that the content of a file is the same as expected. Checks the
* length reported by getSize() is correct, then reads the content as
* a byte stream, and as a char stream, and compares the result with
* the expected content. Assumes files are encoded using UTF-8.
*/
protected void assertSameContent( final String expected,
final FileContent content )
throws Exception
{
// Get file content as a binary stream
final byte[] expectedBin = expected.getBytes( "utf-8" );

// Check lengths
assertEquals( "same content length", expectedBin.length, content.getSize() );

// Read content into byte array
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
{
instr.close();
}

// Compare
assertTrue( "same binary content", Arrays.equals( expectedBin, outstr.toByteArray() ) );
}

/**
* Tests that folders and unknown files have no content.
*/
public void testNoContent() throws Exception
{
// Try getting the content of a folder
FileObject folder = m_baseFolder.resolveFile( "dir1" );
try
{
folder.getContent();
fail();
}
catch( FileSystemException e )
{
final String message = REZ.getString( "get-folder-content.error", folder );
assertSameMessage( message, e );
}

// Try getting the content of an unknown file
FileObject unknownFile = m_baseFolder.resolveFile( "unknown-file" );
FileContent content = unknownFile.getContent();
try
{
content.getInputStream();
fail();
}
catch( FileSystemException e )
{
final String message = REZ.getString( "read-no-exist.error", unknownFile );
assertSameMessage( message, e );
}
try
{
content.getSize();
fail();
}
catch( FileSystemException e )
{
final String message = REZ.getString( "get-size-no-exist.error", unknownFile );
assertSameMessage( message, e );
}
}

/**
* Tests that content and file objects are usable after being closed.
*/
public void testReuse() throws Exception
{
// Get the test file
FileObject file = m_baseFolder.resolveFile( "file1.txt" );
assertEquals( FileType.FILE, file.getType() );

// Get the file content
FileContent content = file.getContent();
assertSameContent( m_charContent, content );

// Read the content again
content = file.getContent();
assertSameContent( m_charContent, content );

// Close the content + file
content.close();
file.close();

// Read the content again
content = file.getContent();
assertSameContent( m_charContent, content );
}

/**
* Info about a file.
*/
protected static final class FileInfo
{
String m_baseName;
FileType m_type;
Map m_children = new HashMap();

public FileInfo( final String name, final FileType type )
{
m_baseName = name;
m_type = type;
}

/** Adds a child. */
public void addChild( final FileInfo child )
{
m_children.put( child.m_baseName, child );
}

/** Adds a child. */
public void addChild( final String baseName, final FileType type )
{
addChild( new FileInfo( baseName, type ) );
}

/** Adds a bunch of children. */
public void addChildren( final String[] baseNames, final FileType type )
{
for( int i = 0; i < baseNames.length; i++ )
{
String baseName = baseNames[i ];
addChild( new FileInfo( baseName, type ) );
}
}
}
}

+ 0
- 24
proposal/myrmidon/src/test/org/apache/aut/vfs/test/AbstractReadOnlyFileSystemTestCase.java View File

@@ -1,24 +0,0 @@
/*
* 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.test;

import org.apache.aut.vfs.test.AbstractFileSystemTestCase;

/**
* File system tests which check that a read-only file system cannot be
* changed.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
*/
public abstract class AbstractReadOnlyFileSystemTestCase extends AbstractFileSystemTestCase
{
public AbstractReadOnlyFileSystemTestCase( String name )
{
super( name );
}
}

+ 0
- 266
proposal/myrmidon/src/test/org/apache/aut/vfs/test/AbstractWritableFileSystemTestCase.java View File

@@ -1,266 +0,0 @@
/*
* 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.test;

import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import org.apache.aut.vfs.test.AbstractFileSystemTestCase;
import org.apache.aut.vfs.FileObject;
import org.apache.aut.vfs.FileType;
import org.apache.aut.vfs.FileSystemException;
import org.apache.aut.vfs.FileConstants;

/**
* File system test that check that a file system can be modified.
*
* @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
*/
public abstract class AbstractWritableFileSystemTestCase
extends AbstractFileSystemTestCase
{
public AbstractWritableFileSystemTestCase( String name )
{
super( name );
}

/**
* Returns the URI for the area to do tests in.
*/
protected abstract FileObject getWriteFolder() throws Exception;

/**
* Sets up a scratch folder for the test to use.
*/
protected FileObject createScratchFolder() throws Exception
{
FileObject scratchFolder = getWriteFolder();

// Make sure the test folder is empty
scratchFolder.delete( FileConstants.EXCLUDE_SELF );
scratchFolder.create( FileType.FOLDER );

return scratchFolder;
}

/**
* Tests folder creation.
*/
public void testFolderCreate() throws Exception
{
FileObject scratchFolder = createScratchFolder();

// Create direct child of the test folder
FileObject folder = scratchFolder.resolveFile( "dir1" );
assertTrue( !folder.exists() );
folder.create( FileType.FOLDER );
assertTrue( folder.exists() );
assertSame( FileType.FOLDER, folder.getType() );
assertEquals( 0, folder.getChildren().length );

// Create a descendant, where the intermediate folders don't exist
folder = scratchFolder.resolveFile( "dir2/dir1/dir1" );
assertTrue( !folder.exists() );
assertTrue( !folder.getParent().exists() );
assertTrue( !folder.getParent().getParent().exists() );
folder.create( FileType.FOLDER );
assertTrue( folder.exists() );
assertSame( FileType.FOLDER, folder.getType() );
assertEquals( 0, folder.getChildren().length );
assertTrue( folder.getParent().exists() );
assertTrue( folder.getParent().getParent().exists() );

// Test creating a folder that already exists
assertTrue( folder.exists() );
folder.create( FileType.FOLDER );
}

/**
* Tests file creation
*/
public void testFileCreate() throws Exception
{
FileObject scratchFolder = createScratchFolder();

// Create direct child of the test folder
FileObject file = scratchFolder.resolveFile( "file1.txt" );
assertTrue( !file.exists() );
file.create( FileType.FILE );
assertTrue( file.exists() );
assertSame( FileType.FILE, file.getType() );
assertEquals( 0, file.getContent().getSize() );

// Create a descendant, where the intermediate folders don't exist
file = scratchFolder.resolveFile( "dir1/dir1/file1.txt" );
assertTrue( !file.exists() );
assertTrue( !file.getParent().exists() );
assertTrue( !file.getParent().getParent().exists() );
file.create( FileType.FILE );
assertTrue( file.exists() );
assertSame( FileType.FILE, file.getType() );
assertEquals( 0, file.getContent().getSize() );
assertTrue( file.getParent().exists() );
assertTrue( file.getParent().getParent().exists() );

// Test creating a file that already exists
assertTrue( file.exists() );
file.create( FileType.FILE );
}

/**
* Tests file/folder creation with mismatched types.
*/
public void testFileCreateMismatched() throws Exception
{
FileObject scratchFolder = createScratchFolder();

// Create a test file and folder
FileObject file = scratchFolder.resolveFile( "dir1/file1.txt" );
file.create( FileType.FILE );
assertEquals( FileType.FILE, file.getType() );

FileObject folder = scratchFolder.resolveFile( "dir1/dir2" );
folder.create( FileType.FOLDER );
assertEquals( FileType.FOLDER, folder.getType() );

// Attempt to create a file that already exists as a folder
try
{
folder.create( FileType.FILE );
fail();
}
catch( FileSystemException exc )
{
}

// Attempt to create a folder that already exists as a file
try
{
file.create( FileType.FOLDER );
fail();
}
catch( FileSystemException exc )
{
}

// Attempt to create a folder as a child of a file
FileObject folder2 = file.resolveFile( "some-child" );
try
{
folder2.create( FileType.FOLDER );
fail();
}
catch( FileSystemException exc )
{
}
}

/**
* Tests deletion
*/
public void testDelete() throws Exception
{
// Set-up the test structure
FileObject folder = createScratchFolder();
folder.resolveFile( "file1.txt" ).create( FileType.FILE );
folder.resolveFile( "emptydir" ).create( FileType.FOLDER );
folder.resolveFile( "dir1/file1.txt" ).create( FileType.FILE );
folder.resolveFile( "dir1/dir2/file2.txt" ).create( FileType.FILE );

// Delete a file
FileObject file = folder.resolveFile( "file1.txt" );
assertTrue( file.exists() );
file.delete( FileConstants.SELECT_ALL );
assertTrue( !file.exists() );

// Delete an empty folder
file = folder.resolveFile( "emptydir" );
assertTrue( file.exists() );
file.delete( FileConstants.SELECT_ALL );
assertTrue( !file.exists() );

// Recursive delete
file = folder.resolveFile( "dir1" );
FileObject file2 = file.resolveFile( "dir2/file2.txt" );
assertTrue( file.exists() );
assertTrue( file2.exists() );
file.delete( FileConstants.SELECT_ALL );
assertTrue( !file.exists() );
assertTrue( !file2.exists() );

// Delete a file that does not exist
file = folder.resolveFile( "some-folder/some-file" );
assertTrue( !file.exists() );
file.delete( FileConstants.SELECT_ALL );
assertTrue( !file.exists() );
}

/**
* Test that children are handled correctly by create and delete.
*/
public void testListChildren() throws Exception
{
FileObject folder = createScratchFolder();
HashSet names = new HashSet();

// Make sure the folder is empty
assertEquals( 0, folder.getChildren().length );

// Create a child folder
folder.resolveFile( "dir1" ).create( FileType.FOLDER );
names.add( "dir1" );
assertSameFileSet( names, folder.getChildren() );

// Create a child file
folder.resolveFile( "file1.html" ).create( FileType.FILE );
names.add( "file1.html" );
assertSameFileSet( names, folder.getChildren() );

// Create a descendent
folder.resolveFile( "dir2/file1.txt" ).create( FileType.FILE );
names.add( "dir2" );
assertSameFileSet( names, folder.getChildren() );

// Create a child file via an output stream
OutputStream outstr = folder.resolveFile( "file2.txt" ).getContent().getOutputStream();
outstr.close();
names.add( "file2.txt" );
assertSameFileSet( names, folder.getChildren() );

// Delete a child folder
folder.resolveFile( "dir1" ).delete( FileConstants.SELECT_ALL );
names.remove( "dir1" );
assertSameFileSet( names, folder.getChildren() );

// Delete a child file
folder.resolveFile( "file1.html" ).delete( FileConstants.SELECT_ALL );
names.remove( "file1.html" );
assertSameFileSet( names, folder.getChildren() );

// Recreate the folder
folder.delete( FileConstants.SELECT_ALL );
folder.create( FileType.FOLDER );
assertEquals( 0, folder.getChildren().length );
}

/**
* Ensures the names of a set of files match an expected set.
*/
private void assertSameFileSet( Set names, FileObject[] files )
{
// Make sure the sets are the same length
assertEquals( names.size(), files.length );

// Check for unexpected names
for( int i = 0; i < files.length; i++ )
{
FileObject file = files[ i ];
assertTrue( names.contains( file.getName().getBaseName() ) );
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save