git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@272338 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -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; | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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*/; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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() ); | |||
| } | |||
| } | |||
| } | |||
| @@ -1 +0,0 @@ | |||
| default.bad-dir.error=Unable to launch native command in a working directory other than "." on Java 1.2 JVMs. | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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(); | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -1,2 +0,0 @@ | |||
| folder.name=folder | |||
| file.name=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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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}". | |||
| @@ -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> | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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() ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -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> | |||
| * <scheme> '://' [ <userinfo> '@' ] <hostname> [ ':' <port> ] '/' <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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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. | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 "/"; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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}". | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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}". | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -1,2 +0,0 @@ | |||
| open-zip-file.error=Could not open Zip file "{0}". | |||
| close-zip-file.error=Could not close Zip file "{0}". | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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 ) ); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ); | |||
| } | |||
| } | |||
| @@ -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() ) ); | |||
| } | |||
| } | |||
| } | |||