sylvain     2003/04/04 08:36:52

  Modified:    sourceresolve build.xml
               sourceresolve/src/java/org/apache/excalibur/source
                        ModifiableTraversableSource.java
                        MoveableSource.java SourceFactory.java
                        SourceParameters.java SourceUtil.java
                        TraversableSource.java
               sourceresolve/src/java/org/apache/excalibur/source/impl
                        FileSource.java ResourceSource.java
                        SourceResolverImpl.java URLSource.java
                        URLSourceFactory.java
  Added:       sourceresolve/src/java/org/apache/excalibur/source/impl
                        FileSourceFactory.java
               sourceresolve/src/test/org/apache/excalibur/source/test
                        FileSourceTestCase.java
                        SourceResolverImplTestCase.java
                        SourceUtilTestCase.java
  Removed:     sourceresolve/src/test/org/apache/excalibur/source
                        SourceResolverImplTestCase.java
  Log:
  - simplify TraversableSource
  - make FileSource implement it
  - simplify URLSource and URLSourceFactory
  - scheme extraction methods in SourceUtil
  - lots of clean up
  - some unit tests
  
  Revision  Changes    Path
  1.31      +1 -5      avalon-excalibur/sourceresolve/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/sourceresolve/build.xml,v
  retrieving revision 1.30
  retrieving revision 1.31
  diff -u -r1.30 -r1.31
  --- build.xml 24 Mar 2003 20:42:35 -0000      1.30
  +++ build.xml 4 Apr 2003 16:36:51 -0000       1.31
  @@ -177,17 +177,13 @@
               <formatter type="xml"/>    <!-- xml reports for junitreport -->
               <formatter type="plain" usefile="false"/>  <!-- text reports for humans 
    -->
               
  -            <!-- added the next line because the standard approach does not seem to 
be working -->
  -            <test name="org.apache.excalibur.source.SourceResolverImplTestCase" 
todir="${build.tests}"/>
  -
  -            <!--
               <batchtest todir="${build.tests}">
                   <fileset dir="${build.testclasses}">
                       <include name="**/test/*TestCase.class"/>
                       <exclude name="**/Abstract*"/>
                   </fileset>
               </batchtest>
  -            -->
  +            
           </junit>
   
       </target>
  
  
  
  1.2       +4 -4      
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/ModifiableTraversableSource.java
  
  Index: ModifiableTraversableSource.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/ModifiableTraversableSource.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModifiableTraversableSource.java  29 Jan 2003 06:56:01 -0000      1.1
  +++ ModifiableTraversableSource.java  4 Apr 2003 16:36:51 -0000       1.2
  @@ -64,13 +64,13 @@
   public interface ModifiableTraversableSource extends ModifiableSource, 
TraversableSource
   {
       /**
  -     * If it doesn't already exist, create the directory corresponding to this 
source 
  +     * If it doesn't already exist, ensure this source is traversable
        * (equivalent to <code>File.mkdirs()</code>)
        * <p>
        * If the source already exists, this method does nothing if it's already
  -     * a directory, and fails otherwise.
  +     * traversable, and fails otherwise.
        */
  -    public void makeDirectory() throws SourceException;
  +    public void makeCollection() throws SourceException;
   
   }
   
  
  
  
  1.3       +3 -3      
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/MoveableSource.java
  
  Index: MoveableSource.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/MoveableSource.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MoveableSource.java       25 Feb 2003 16:28:51 -0000      1.2
  +++ MoveableSource.java       4 Apr 2003 16:36:51 -0000       1.3
  @@ -75,7 +75,7 @@
        * @throws SourceException If an exception occurs during
        *                         the copy.
        */
  -    void copy(Source destination) throws SourceException;
  +    void copyTo(Source destination) throws SourceException;
   
       /**
        * Move the current source to a specified destination.
  @@ -85,5 +85,5 @@
        * @throws SourceException If an exception occurs during
        *                         the move.
        */
  -    void move(Source source) throws SourceException;
  +    void moveTo(Source source) throws SourceException;
   }
  
  
  
  1.8       +3 -2      
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/SourceFactory.java
  
  Index: SourceFactory.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/SourceFactory.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- SourceFactory.java        29 Mar 2003 18:53:26 -0000      1.7
  +++ SourceFactory.java        4 Apr 2003 16:36:51 -0000       1.8
  @@ -55,6 +55,7 @@
   package org.apache.excalibur.source;
   
   import java.io.IOException;
  +import java.net.MalformedURLException;
   import java.util.Map;
   
   import org.apache.avalon.framework.component.Component;
  @@ -93,7 +94,7 @@
        * @throws IOException if the source couldn't be created for some reason.
        */
       Source getSource( String location, Map parameters )
  -        throws IOException;
  +        throws IOException, MalformedURLException;
       
       /**
        * Release a [EMAIL PROTECTED] Source} object.
  
  
  
  1.6       +3 -2      
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/SourceParameters.java
  
  Index: SourceParameters.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/SourceParameters.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- SourceParameters.java     29 Jan 2003 06:56:01 -0000      1.5
  +++ SourceParameters.java     4 Apr 2003 16:36:51 -0000       1.6
  @@ -73,7 +73,8 @@
    * <p>
    * Only particular kinds of <code>Source</code> implementations, such as
    * [EMAIL PROTECTED] org.apache.excalibur.source.impl.URLSource} support this kind 
of
  - * parameters, passed as an entry in the <code>parameters</code> argument of
  + * parameters, passed as the [EMAIL PROTECTED] SourceResolver#URI_PARAMETERS} entry
  + * in the <code>parameters</code> argument of
    * [EMAIL PROTECTED] SourceResolver#resolveURI(String, String, Map)}.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a>
  
  
  
  1.6       +120 -13   
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/SourceUtil.java
  
  Index: SourceUtil.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/SourceUtil.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- SourceUtil.java   30 Jan 2003 07:57:10 -0000      1.5
  +++ SourceUtil.java   4 Apr 2003 16:36:51 -0000       1.6
  @@ -344,7 +344,7 @@
           if (source instanceof MoveableSource
               && source.getClass().equals(destination.getClass()))
           {
  -            ((MoveableSource)source).move(destination);
  +            ((MoveableSource)source).moveTo(destination);
           } 
           else if (source instanceof ModifiableSource) 
           {
  @@ -356,6 +356,101 @@
               throw new SourceException("Source '"+source.getURI()+ "' is not 
writeable");
           }
       }
  +    
  +    /**
  +     * Get the position of the scheme-delimiting colon in an absolute URI, as 
specified
  +     * by <a href="http://www.ietf.org/rfc/rfc2396.txt";>RFC 2396</a>, appendix A. 
This method is
  +     * primarily useful for [EMAIL PROTECTED] Source} implementors that want to 
separate
  +     * the scheme part from the specific part of an URI.
  +     * <p>
  +     * Use this method when you need both the scheme and the scheme-specific part 
of an URI,
  +     * as calling successively [EMAIL PROTECTED] #getScheme(String)} and [EMAIL 
PROTECTED] #getSpecificPart(String)}
  +     * will call this method twice, and as such won't be efficient.
  +     * 
  +     * @param uri the URI
  +     * @return int the scheme-delimiting colon, or <code>-1</code> if not found.
  +     */
  +    public static int indexOfSchemeColon(String uri)
  +    {
  +        // absoluteURI   = scheme ":" ( hier_part | opaque_part )
  +        //
  +             // scheme        = alpha *( alpha | digit | "+" | "-" | "." )
  +             //
  +        // alpha         = lowalpha | upalpha
  +        //
  +        // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
  +        //            "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
  +        //            "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
  +        //
  +        // upalpha  = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
  +        //            "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
  +        //            "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
  +        //
  +        // digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
  +        //            "8" | "9"
  +
  +        // Must have at least one character followed by a colon
  +        if (uri == null || uri.length() < 2)
  +        {
  +            return -1;
  +        }
  +        
  +        // Check that first character is alpha
  +        // (lowercase first since it's the most common case)
  +        char ch = uri.charAt(0);
  +        if ( (ch < 'a' || ch > 'z') &&
  +             (ch < 'A' || ch > 'Z') )
  +        {
  +            // Invalid first character
  +            return -1;
  +        }
  +        
  +        int pos = uri.indexOf(':');
  +        if (pos != -1)
  +        {
  +            // Check that every character before the colon is in the allowed range
  +            // (the first one was tested above)
  +            for (int i = 1; i < pos; i++)
  +            {
  +                ch = uri.charAt(i);
  +                if ( (ch < 'a' || ch > 'z') &&
  +                     (ch < 'A' || ch > 'Z') &&
  +                     (ch < '0' || ch > '9') &&
  +                     ch != '+' && ch != '-' && ch != '.')
  +                {
  +                    return -1;
  +                }
  +            }
  +        }
  +        
  +        return pos;
  +    }
  +    
  +    /**
  +     * Get the scheme of an absolute URI.
  +     * 
  +     * @param uri the absolute URI
  +     * @return the URI scheme
  +     */
  +    public static String getScheme(String uri)
  +    {
  +        int pos = indexOfSchemeColon(uri);
  +        return (pos == -1) ? null : uri.substring(0, pos);
  +    }
  +    
  +    /**
  +     * Get the scheme-specific part of an absolute URI. Note that this includes 
everything
  +     * after the separating colon, including the fragment, if any (RFC 2396 
separates it
  +     * from the scheme-specific part).
  +     * 
  +     * @param uri the absolute URI
  +     * @return the scheme-specific part of the URI
  +     */
  +    public static String getSpecificPart(String uri)
  +    {
  +        int pos = indexOfSchemeColon(uri);
  +        return (pos == -1) ? null : uri.substring(pos+1);
  +    }
   
       /**
        * Copy the source to a specified destination.
  @@ -372,7 +467,7 @@
           if (source instanceof MoveableSource 
               && source.getClass().equals(destination.getClass())) 
           {
  -            ((MoveableSource) source).copy(destination);
  +            ((MoveableSource) source).copyTo(destination);
           } 
           else 
           {
  @@ -385,16 +480,8 @@
               try {
                   OutputStream out = ((ModifiableSource) 
destination).getOutputStream();
                   InputStream in = source.getInputStream();
  -
  -                byte[] buffer = new byte[8192];
  -                int length = -1;
  -
  -                while ((length = in.read(buffer))>-1) {
  -                    out.write(buffer, 0, length);
  -                }
  -                in.close();
  -                out.flush();
  -                out.close();
  +                
  +                copy(in, out);
               } catch (IOException ioe) {
                   throw new SourceException("Could not copy source '"+
                                             source.getURI()+"' to '"+
  @@ -402,6 +489,26 @@
                                             ioe.getMessage(), ioe);
               }
           }
  +    }
  +    
  +    /**
  +     * Copy the contents of an <code>InputStream</code> to an 
<code>OutputStream</code>.
  +     * 
  +     * @param in
  +     * @param out
  +     * @throws IOException
  +     */
  +    static public void copy(InputStream in, OutputStream out) throws IOException
  +    {
  +        byte[] buffer = new byte[8192];
  +        int length = -1;
  +
  +        while ((length = in.read(buffer))>-1) {
  +            out.write(buffer, 0, length);
  +        }
  +        in.close();
  +        out.flush();
  +        out.close();
       }
   
   }
  
  
  
  1.4       +24 -35    
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/TraversableSource.java
  
  Index: TraversableSource.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/TraversableSource.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TraversableSource.java    29 Jan 2003 06:56:01 -0000      1.3
  +++ TraversableSource.java    4 Apr 2003 16:36:51 -0000       1.4
  @@ -68,54 +68,44 @@
   public interface TraversableSource extends Source {
   
        /**
  -      * Does this source point to a directory (i.e. it possibly has children) ?
  +      * Is this source a collection, i.e. it possibly has children ?
  +      * For a filesystem-based implementation, this would typically mean that
  +      * this source represents a directory and not a file.
        * 
  -     * @return true if the source is a directory.
  +     * @return true if the source exists and is traversable.
         */
  -     boolean isDirectory();
  -    
  -     /**
  -      * Get the absolute URIs of the children of this source.
  -     * 
  -     * @return a possibly-empty collection if this source is a directory,
  -     *         <code>null</code> otherwise
  -     * @throws SourceException if some problem occurs.
  -      */
  -     Collection getChildrenURIs() throws SourceException;
  +     boolean isCollection();
       
       /**
  -     * Get the children of this source as [EMAIL PROTECTED] Source} objects.
  +     * Get the children of this source if this source is traversable.
        * <p>
        * <em>Note:</em> only those sources actually fetched from the
        * collection need to be released using the [EMAIL PROTECTED] SourceResolver}.
        * 
  -     * @return a possibly-empty collection if this source is a directory,
  -     *         <code>null</code> otherwise
  -     * @throws SourceException if some problem occurs.
  +     * @see #isTraversable()
  +     * @return a collection of [EMAIL PROTECTED] Source}s (actually most probably 
<code>TraversableSource</code>s).
  +     * @throws SourceException this source is not traversable, or if some problem 
occurs.
        */
  -    Collection getChildrenSources() throws SourceException;
  +    Collection getChildren() throws SourceException;
       
       /**
  -     * Get the children of this source as relative names.
  -     * @return a possibly-empty collection if this source is a directory,
  -     *         <code>null</code> otherwise
  -     * @throws SourceException if some problem occurs.
  +     * Get a child of this source, given its name. Note that the returned source
  +     * may not actually physically exist, and that this must be checked using
  +     * [EMAIL PROTECTED] Source#exists()}.
  +     * 
  +     * @param name the child name.
  +     * @return the child source.
  +     * @throws SourceException if this source is not traversable or if some other
  +     *         error occurs.
        */
  -    Collection getChildrenNames() throws SourceException;
  +    Source getChild(String name) throws SourceException;
       
       /**
        * Return the name of this source relative to its parent.
  -     * @throws SourceException if some problem occurs.
  -     */
  -    String getName() throws SourceException;
  -    
  -    /**
  -     * Return the absolute URI of the parent source. 
  -     * 
  -     * @return the parent URI, or <code>null</code> if this source has no parent.
  -     * @throws SourceException if some problem occurs.
  +      *
  +      * @return the name
        */
  -    String getParentURI() throws SourceException;
  +    String getName();
       
       /**
        * Get the parent of this source as a [EMAIL PROTECTED] Source} object.
  @@ -123,6 +113,5 @@
        * @return the parent source, or <code>null</code> if this source has no parent.
        * @throws SourceException if some problem occurs.
        */
  -    Source getParentSource() throws SourceException;
  -
  +    Source getParent() throws SourceException;
   }
  
  
  
  1.5       +392 -91   
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/FileSource.java
  
  Index: FileSource.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/FileSource.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- FileSource.java   31 Mar 2003 14:21:42 -0000      1.4
  +++ FileSource.java   4 Apr 2003 16:36:51 -0000       1.5
  @@ -55,111 +55,195 @@
   package org.apache.excalibur.source.impl;
   
   import java.io.File;
  +import java.io.FileInputStream;
  +import java.io.FileNotFoundException;
   import java.io.FileOutputStream;
   import java.io.IOException;
  +import java.io.InputStream;
   import java.io.OutputStream;
  -import java.net.URL;
  +import java.net.MalformedURLException;
  +import java.net.URLConnection;
  +import java.util.Arrays;
  +import java.util.Collection;
   import java.util.ConcurrentModificationException;
  -import java.util.Map;
   
   import org.apache.excalibur.source.ModifiableSource;
  +import org.apache.excalibur.source.ModifiableTraversableSource;
  +import org.apache.excalibur.source.MoveableSource;
  +import org.apache.excalibur.source.Source;
   import org.apache.excalibur.source.SourceException;
  +import org.apache.excalibur.source.SourceNotFoundException;
  +import org.apache.excalibur.source.SourceUtil;
  +import org.apache.excalibur.source.SourceValidity;
  +import org.apache.excalibur.source.impl.validity.FileTimeStampValidity;
   
   /**
  - * A [EMAIL PROTECTED] ModifiableSource} for 'file:/' system IDs.
  + * A [EMAIL PROTECTED] ModifiableTraversableSource} for filesystem objects.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a>
    * @version $Id$
    */
   
  -public class FileSource
  -    extends URLSource
  -    implements ModifiableSource {
  +public class FileSource implements ModifiableTraversableSource, MoveableSource
  +{
  +
  +    /** The file */
  +    private File m_file;
  +
  +    /** The scheme */
  +    private String m_scheme;
  +
  +    /** The URI of this source */
  +    private String m_uri;
   
       /**
  -     * Initialize a new object from a <code>URL</code>.
  -     * @param parameters This is optional
  +     * Builds a FileSource given an URI, which doesn't necessarily have to start 
with "file:"
  +     * @param uri
  +     * @throws SourceException
  +     * @throws MalformedURLException
        */
  -    public void init(URL url,
  -                     Map parameters )
  -    throws IOException {
  -        super.init( url, parameters );
  +    public FileSource(String uri) throws SourceException, MalformedURLException
  +    {
  +        int pos = SourceUtil.indexOfSchemeColon(uri);
  +        if (pos == -1)
  +        {
  +            throw new MalformedURLException("Invalid URI : " + uri);
  +        }
  +
  +        String scheme = uri.substring(0, pos);
  +        String fileName = uri.substring(pos + 1);
  +        init(scheme, new File(fileName));
  +    }
  +
  +    /**
  +     * Builds a FileSource, given an URI scheme and a File.
  +     * 
  +     * @param scheme
  +     * @param file
  +     * @throws SourceException
  +     */
  +    public FileSource(String scheme, File file) throws SourceException
  +    {
  +        init(scheme, file);
  +    }
  +
  +    private void init(String scheme, File file) throws SourceException
  +    {
  +        m_scheme = scheme;
   
  -        if ( null == getFile() ) {
  -            throw new IllegalArgumentException("Malformed url for a file source : " 
+ url);
  +        String uri;
  +        try
  +        {
  +            uri = file.toURL().toExternalForm();
           }
  +        catch (MalformedURLException mue)
  +        {
  +            // Can this really happen ?
  +            throw new SourceException("Failed to get URL for file " + file, mue);
  +        }
  +
  +        if (!uri.startsWith(scheme))
  +        {
  +            // Scheme is not "file:"
  +            uri = scheme + ':' + uri.substring(uri.indexOf(':') + 1);
  +        }
  +
  +        m_uri = uri;
  +
  +        m_file = file;
       }
   
       /**
        * Get the associated file
        */
  -    public File getFile() {
  -        return super.getFile();
  +    public File getFile()
  +    {
  +        return m_file;
       }
   
  +    
//----------------------------------------------------------------------------------
  +    //  Source interface methods
  +    
//----------------------------------------------------------------------------------
  +
       /**
  -     * A file outputStream that will rename the temp file to the destination file 
upon close()
  -     * and discard the temp file upon cancel().
  +     * @see org.apache.excalibur.source.Source#getContentLength()
        */
  -    private class FileSourceOutputStream extends FileOutputStream {
  -
  -        private File m_tmpFile;
  -        private boolean m_isClosed = false;
  -        private FileSource m_source;
  +    public long getContentLength()
  +    {
  +        return m_file.length();
  +    }
   
  -        public FileSourceOutputStream(File tmpFile, FileSource source) throws 
IOException {
  -            super(tmpFile);
  -            m_tmpFile = tmpFile;
  -            m_source = source;
  +    /**
  +     * @see org.apache.excalibur.source.Source#getInputStream()
  +     */
  +    public InputStream getInputStream() throws IOException, SourceNotFoundException
  +    {
  +        try
  +        {
  +            return new FileInputStream(m_file);
  +        }
  +        catch (FileNotFoundException fnfe)
  +        {
  +            throw new SourceNotFoundException(m_uri + " doesn't exist.", fnfe);
           }
  +    }
   
  -        public void close() throws IOException {
  -            if (!m_isClosed) {
  -                super.close();
  -                try {
  -                    // Delete destination file
  -                    if (m_source.getFile().exists()) {
  -                        m_source.getFile().delete();
  -                    }
  -                    // Rename temp file to destination file
  -                    m_tmpFile.renameTo(m_source.getFile());
  -    
  -                } finally {
  -                    // Ensure temp file is deleted, ie lock is released.
  -                    // If there was a failure above, written data is lost.
  -                    if (m_tmpFile.exists()) {
  -                        m_tmpFile.delete();
  -                    }
  -                    m_isClosed = true;
  -                }
  -            }
  +    /**
  +     * @see org.apache.excalibur.source.Source#getLastModified()
  +     */
  +    public long getLastModified()
  +    {
  +        return m_file.lastModified();
  +    }
   
  -        }
  +    /**
  +     * @see org.apache.excalibur.source.Source#getMimeType()
  +     */
  +    public String getMimeType()
  +    {
  +        return URLConnection.getFileNameMap().getContentTypeFor(m_file.getName());
  +    }
   
  -        public boolean canCancel() {
  -            return !m_isClosed;
  -        }
  +    /* (non-Javadoc)
  +     * @see org.apache.excalibur.source.Source#getScheme()
  +     */
  +    public String getScheme()
  +    {
  +        return m_scheme;
   
  -        public void cancel() throws Exception {
  -            if (m_isClosed) {
  -                throw new IllegalStateException("Cannot cancel : outputstrem is 
already closed");
  -            }
  +    }
   
  -            m_isClosed = true;
  -            super.close();
  -            m_tmpFile.delete();
  -        }
  +    /* (non-Javadoc)
  +     * @see org.apache.excalibur.source.Source#getURI()
  +     */
  +    public String getURI()
  +    {
  +        return m_uri;
  +    }
   
  -        public void finalize() {
  -            if (!m_isClosed && m_tmpFile.exists()) {
  -                // Something wrong happened while writing : delete temp file
  -                m_tmpFile.delete();
  -            }
  +    /**
  +     * Return a validity object based on the file's modification date.
  +     * 
  +     * @see org.apache.excalibur.source.Source#getValidity()
  +     */
  +    public SourceValidity getValidity()
  +    {
  +        if (m_file.exists())
  +        {
  +            return new FileTimeStampValidity(m_file);
  +        }
  +        else
  +        {
  +            return null;
           }
  +    }
   
  -        public FileSource getSource() {
  -            return m_source;
  -        }
  +    /**
  +     * @see org.apache.excalibur.source.Source#refresh()
  +     */
  +    public void refresh()
  +    {
  +        // Nothing to do...
       }
   
       /**
  @@ -167,26 +251,97 @@
        *
        * @return true if the resource exists.
        */
  -    public boolean exists() {
  +    public boolean exists()
  +    {
           return getFile().exists();
       }
   
  +    
//----------------------------------------------------------------------------------
  +    //  TraversableSource interface methods
  +    
//----------------------------------------------------------------------------------
  +
  +    /**
  +     * @see org.apache.excalibur.source.TraversableSource#getChild(java.lang.String)
  +     */
  +    public Source getChild(String name) throws SourceException
  +    {
  +        if (!m_file.isDirectory())
  +        {
  +            throw new SourceException(getURI() + " is not a directory");
  +        }
  +
  +        return new FileSource(this.getScheme(), new File(m_file, name));
  +
  +    }
  +
  +    /**
  +     * @see org.apache.excalibur.source.TraversableSource#getChildren()
  +     */
  +    public Collection getChildren() throws SourceException
  +    {
  +
  +        if (!m_file.isDirectory())
  +        {
  +            throw new SourceException(getURI() + " is not a directory");
  +        }
  +
  +        // Build a FileSource object for each of the children
  +        File[] files = m_file.listFiles();
  +
  +        FileSource[] children = new FileSource[files.length];
  +        for (int i = 0; i < files.length; i++)
  +        {
  +            children[i] = new FileSource(this.getScheme(), files[i]);
  +        }
  +
  +        // Return it as a list
  +        return Arrays.asList(children);
  +    }
  +
  +    /**
  +     * @see org.apache.excalibur.source.TraversableSource#getName()
  +     */
  +    public String getName()
  +    {
  +        return m_file.getName();
  +    }
  +
  +    /**
  +     * @see org.apache.excalibur.source.TraversableSource#getParent()
  +     */
  +    public Source getParent() throws SourceException
  +    {
  +        return new FileSource(getScheme(), m_file.getParentFile());
  +    }
  +
  +    /**
  +     * @see org.apache.excalibur.source.TraversableSource#isCollection()
  +     */
  +    public boolean isCollection()
  +    {
  +        return m_file.isDirectory();
  +    }
  +
  +    
//----------------------------------------------------------------------------------
  +    //  ModifiableSource interface methods
  +    
//----------------------------------------------------------------------------------
  +
       /**
        * Get an <code>InputStream</code> where raw bytes can be written to.
        * The signification of these bytes is implementation-dependent and
        * is not restricted to a serialized XML document.
        *
  -     * Get an output stream to write to this source. The output stream returned
  -     * actually writes to a temp file that replaces the real one on close. This
  -     * temp file is used as lock to forbid multiple simultaneous writes. The
  -     * real file is updated atomically when the output stream is closed.
  +     * The output stream returned actually writes to a temp file that replaces
  +     * the real one on close. This temp file is used as lock to forbid multiple
  +     * simultaneous writes. The real file is updated atomically when the output
  +     * stream is closed.
        *
        * @return a stream to write to
        * @throws ConcurrentModificationException if another thread is currently
        *         writing to this file.
        */
  -    public OutputStream getOutputStream()
  -    throws IOException, SourceException {
  +    public OutputStream getOutputStream() throws IOException
  +    {
           // Create a temp file. It will replace the right one when writing 
terminates,
           // and serve as a lock to prevent concurrent writes.
           File tmpFile = new File(getFile().getPath() + ".tmp");
  @@ -195,14 +350,16 @@
           tmpFile.getParentFile().mkdirs();
   
           // Can we write the file ?
  -        if (getFile().exists() && !getFile().canWrite()) {
  +        if (getFile().exists() && !getFile().canWrite())
  +        {
               throw new IOException("Cannot write to file " + getFile().getPath());
           }
   
           // Check if it temp file already exists, meaning someone else currently 
writing
  -        if (!tmpFile.createNewFile()) {
  -            throw new ConcurrentModificationException("File " + getFile().getPath() 
+
  -              " is already being written by another thread");
  +        if (!tmpFile.createNewFile())
  +        {
  +            throw new ConcurrentModificationException(
  +                "File " + getFile().getPath() + " is already being written by 
another thread");
           }
   
           // Return a stream that will rename the temp file on close.
  @@ -215,10 +372,13 @@
        *
        * @return true if the stream can be cancelled
        */
  -    public boolean canCancel(OutputStream stream) {
  -        if (stream instanceof FileSourceOutputStream) {
  -            FileSourceOutputStream fsos = (FileSourceOutputStream)stream;
  -            if (fsos.getSource() == this) {
  +    public boolean canCancel(OutputStream stream)
  +    {
  +        if (stream instanceof FileSourceOutputStream)
  +        {
  +            FileSourceOutputStream fsos = (FileSourceOutputStream) stream;
  +            if (fsos.getSource() == this)
  +            {
                   return fsos.canCancel();
               }
           }
  @@ -233,13 +393,19 @@
        * <p>
        * After cancel, the stream should no more be used.
        */
  -    public void cancel(OutputStream stream) throws SourceException {
  -        if (stream instanceof FileSourceOutputStream) {
  -            FileSourceOutputStream fsos = (FileSourceOutputStream)stream;
  -            if (fsos.getSource() == this) {
  -                try {
  +    public void cancel(OutputStream stream) throws SourceException
  +    {
  +        if (stream instanceof FileSourceOutputStream)
  +        {
  +            FileSourceOutputStream fsos = (FileSourceOutputStream) stream;
  +            if (fsos.getSource() == this)
  +            {
  +                try
  +                {
                       fsos.cancel();
  -                } catch (Exception e) {
  +                }
  +                catch (Exception e)
  +                {
                       throw new SourceException("Exception during cancel.", e);
                   }
                   return;
  @@ -253,7 +419,142 @@
       /**
        * Delete the source.
        */
  -    public boolean delete()  {
  -        return getFile().delete();
  +    public boolean delete()
  +    {
  +        return m_file.delete();
  +    }
  +
  +    
//----------------------------------------------------------------------------------
  +    //  ModifiableTraversableSource interface methods
  +    
//----------------------------------------------------------------------------------
  +
  +    /**
  +     * @see org.apache.excalibur.source.ModifiableTraversableSource#makeCollection()
  +     */
  +    public void makeCollection() throws SourceException
  +    {
  +        m_file.mkdirs();
  +    }
  +
  +    
//----------------------------------------------------------------------------------
  +    //  MoveableSource interface methods
  +    
//----------------------------------------------------------------------------------
  +
  +    /**
  +     * @see 
org.apache.excalibur.source.MoveableSource#copy(org.apache.excalibur.source.Source)
  +     */
  +    public void copyTo(Source destination) throws SourceException
  +    {
  +        try
  +        {
  +            SourceUtil.copy(this.getInputStream(), ((ModifiableSource) 
destination).getOutputStream());
  +        }
  +        catch (IOException ioe)
  +        {
  +            throw new SourceException("Couldn't copy " + getURI() + " to " + 
destination.getURI(), ioe);
  +        }
  +    }
  +
  +    /**
  +     * @see 
org.apache.excalibur.source.MoveableSource#move(org.apache.excalibur.source.Source)
  +     */
  +    public void moveTo(Source destination) throws SourceException
  +    {
  +        if (destination instanceof FileSource)
  +        {
  +            if (!m_file.renameTo(((FileSource) destination).getFile()))
  +            {
  +                throw new SourceException("Couldn't move " + getURI() + " to " + 
destination.getURI());
  +            }
  +        }
  +        else
  +        {
  +            SourceUtil.move(this, destination);
  +        }
  +
  +    }
  +
  +    
//----------------------------------------------------------------------------------
  +    //  Private helper class for ModifiableSource implementation
  +    
//----------------------------------------------------------------------------------
  +
  +    /**
  +     * A file outputStream that will rename the temp file to the destination file 
upon close()
  +     * and discard the temp file upon cancel().
  +     */
  +    private static class FileSourceOutputStream extends FileOutputStream
  +    {
  +
  +        private File m_tmpFile;
  +        private boolean m_isClosed = false;
  +        private FileSource m_source;
  +
  +        public FileSourceOutputStream(File tmpFile, FileSource source) throws 
IOException
  +        {
  +            super(tmpFile);
  +            m_tmpFile = tmpFile;
  +            m_source = source;
  +        }
  +
  +        public void close() throws IOException
  +        {
  +            if (!m_isClosed)
  +            {
  +                super.close();
  +                try
  +                {
  +                    // Delete destination file
  +                    if (m_source.getFile().exists())
  +                    {
  +                        m_source.getFile().delete();
  +                    }
  +                    // Rename temp file to destination file
  +                    m_tmpFile.renameTo(m_source.getFile());
  +
  +                }
  +                finally
  +                {
  +                    // Ensure temp file is deleted, ie lock is released.
  +                    // If there was a failure above, written data is lost.
  +                    if (m_tmpFile.exists())
  +                    {
  +                        m_tmpFile.delete();
  +                    }
  +                    m_isClosed = true;
  +                }
  +            }
  +
  +        }
  +
  +        public boolean canCancel()
  +        {
  +            return !m_isClosed;
  +        }
  +
  +        public void cancel() throws Exception
  +        {
  +            if (m_isClosed)
  +            {
  +                throw new IllegalStateException("Cannot cancel : outputstrem is 
already closed");
  +            }
  +
  +            m_isClosed = true;
  +            super.close();
  +            m_tmpFile.delete();
  +        }
  +
  +        public void finalize()
  +        {
  +            if (!m_isClosed && m_tmpFile.exists())
  +            {
  +                // Something wrong happened while writing : delete temp file
  +                m_tmpFile.delete();
  +            }
  +        }
  +
  +        public FileSource getSource()
  +        {
  +            return m_source;
  +        }
       }
   }
  
  
  
  1.10      +11 -4     
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/ResourceSource.java
  
  Index: ResourceSource.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/ResourceSource.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- ResourceSource.java       29 Mar 2003 18:53:26 -0000      1.9
  +++ ResourceSource.java       4 Apr 2003 16:36:51 -0000       1.10
  @@ -56,12 +56,14 @@
   
   import java.io.IOException;
   import java.io.InputStream;
  +import java.net.MalformedURLException;
   import java.net.URL;
   import java.net.URLConnection;
   
   import org.apache.excalibur.source.Source;
   import org.apache.excalibur.source.SourceException;
   import org.apache.excalibur.source.SourceNotFoundException;
  +import org.apache.excalibur.source.SourceUtil;
   import org.apache.excalibur.source.SourceValidity;
   import org.apache.excalibur.source.impl.validity.NOPValidity;
   
  @@ -80,10 +82,15 @@
       private URL m_location;
       private String m_mimeType;
   
  -    public ResourceSource( final String systemId )
  +    public ResourceSource( final String systemId ) throws MalformedURLException
       {
  +        final int pos = SourceUtil.indexOfSchemeColon(systemId);
  +        if (pos == -1 || ! systemId.startsWith("://", pos))
  +        {
  +            throw new MalformedURLException("Invalid format for ResourceSource : " 
+ systemId);
  +        }
  +        
           setSystemId(systemId);
  -        final int pos = systemId.indexOf( "://" );
           m_location = getClassLoader().getResource(systemId.substring( pos + 3 ));
           setScheme(systemId.substring(0, pos));
       }
  @@ -141,7 +148,7 @@
           return NOPValidity.SHARED_INSTANCE;
       }
       
  -    private ClassLoader getClassLoader() {
  +    protected ClassLoader getClassLoader() {
           ClassLoader loader = Thread.currentThread().getContextClassLoader();
           if( loader == null )
           {
  
  
  
  1.30      +4 -3      
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/SourceResolverImpl.java
  
  Index: SourceResolverImpl.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/SourceResolverImpl.java,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- SourceResolverImpl.java   4 Feb 2003 20:12:59 -0000       1.29
  +++ SourceResolverImpl.java   4 Apr 2003 16:36:51 -0000       1.30
  @@ -75,6 +75,7 @@
   import org.apache.excalibur.source.SourceException;
   import org.apache.excalibur.source.SourceFactory;
   import org.apache.excalibur.source.SourceResolver;
  +import org.apache.excalibur.source.SourceUtil;
   
   /**
    * This is the default implemenation of a [EMAIL PROTECTED] SourceResolver}.
  @@ -203,7 +204,7 @@
               getLogger().debug( "Resolving '" + location + "' with base '" + baseURI 
+ "' in context '" + m_baseURL + "'" );
           }
           if( location == null ) throw new MalformedURLException( "Invalid System ID" 
);
  -        if( null != baseURI && baseURI.indexOf( ':' ) == -1 )
  +        if( null != baseURI && SourceUtil.indexOfSchemeColon(baseURI) == -1 )
           {
               throw new MalformedURLException( "BaseURI is not valid, it must contain 
a protocol: " + baseURI );
           }
  @@ -270,7 +271,7 @@
   
           Source source = null;
           // search for a SourceFactory implementing the protocol
  -        final int protocolPos = systemID.indexOf( ':' );
  +        final int protocolPos = SourceUtil.indexOfSchemeColon(systemID);
           if( protocolPos != -1)
           {
               final String protocol = systemID.substring( 0, protocolPos );
  
  
  
  1.24      +108 -207  
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/URLSource.java
  
  Index: URLSource.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/URLSource.java,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- URLSource.java    31 Mar 2003 14:21:42 -0000      1.23
  +++ URLSource.java    4 Apr 2003 16:36:51 -0000       1.24
  @@ -54,29 +54,21 @@
    */
   package org.apache.excalibur.source.impl;
   
  -import java.io.File;
  -import java.io.FileInputStream;
  -import java.io.FileNotFoundException;
   import java.io.IOException;
   import java.io.InputStream;
   import java.lang.reflect.Method;
   import java.net.HttpURLConnection;
   import java.net.URL;
   import java.net.URLConnection;
  -import java.util.Arrays;
  -import java.util.Collection;
  -import java.util.Collections;
   import java.util.Iterator;
   import java.util.Map;
   
   import org.apache.excalibur.source.Source;
   import org.apache.excalibur.source.SourceException;
  -import org.apache.excalibur.source.SourceNotFoundException;
   import org.apache.excalibur.source.SourceParameters;
   import org.apache.excalibur.source.SourceResolver;
   import org.apache.excalibur.source.SourceUtil;
   import org.apache.excalibur.source.SourceValidity;
  -import org.apache.excalibur.source.impl.validity.FileTimeStampValidity;
   import org.apache.excalibur.source.impl.validity.TimeStampValidity;
   
   /**
  @@ -85,12 +77,8 @@
    * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a>
    * @version CVS $Revision$ $Date$
    */
  -public class URLSource
  -    extends AbstractSource
  -    implements Source
  +public class URLSource extends AbstractSource implements Source
   {
  -    /** Identifier for file urls */
  -    protected final String FILE = "file:";
   
       /** The URL of the source */
       protected URL m_url;
  @@ -98,21 +86,15 @@
       /** The connection for a real URL */
       protected URLConnection m_connection;
   
  -    /** The file, if URL is a file
  -     * @deprecated use the accessor instead (getFile())
  -     */
  -    protected File file;
  -
       /** The <code>SourceParameters</code> used for a post*/
       protected SourceParameters m_parameters;
   
       /** Is this a post? */
       protected boolean m_isPost = false;
  -    
  +
       /** Does this source exist ? */
       protected boolean m_exists = false;
   
  -
       /** the prev returned SourceValidity */
       protected SourceValidity m_cachedValidity;
   
  @@ -120,7 +102,7 @@
   
       /** The content type (if known) */
       protected String m_mimeType;
  -    
  +
       /**
        * Constructor
        */
  @@ -132,78 +114,56 @@
        * Initialize a new object from a <code>URL</code>.
        * @param parameters This is optional
        */
  -    public void init( URL url,
  -                      Map parameters )
  -        throws IOException
  +    public void init(URL url, Map parameters) throws IOException
       {
           String systemId = url.toExternalForm();
           setSystemId(systemId);
  -        int pos = systemId.indexOf(':');
  -        setScheme(systemId.substring(0, pos));
  -        if (systemId.startsWith( FILE ))
  -        {
  -            setFile( new File( systemId.substring( FILE.length() ) ) );
  -        }
  -        else
  -        {
  -            setFile( null );
  -        }
  +        setScheme(SourceUtil.getScheme(systemId));
  +
           m_url = url;
           m_isPost = false;
  -        
  -        if( null != parameters )
  +
  +        if (null != parameters)
           {
  -            m_parameters = (SourceParameters)parameters.get( 
SourceResolver.URI_PARAMETERS );
  -            final String method = (String)parameters.get( SourceResolver.METHOD );
  -            if( "POST".equalsIgnoreCase( method ) )
  +            m_parameters = (SourceParameters) 
parameters.get(SourceResolver.URI_PARAMETERS);
  +            final String method = (String) parameters.get(SourceResolver.METHOD);
  +            
  +            if ("POST".equalsIgnoreCase(method))
                   m_isPost = true;
           }
  -        if( null == getFile()
  -            && null != m_parameters
  -            && m_parameters.hasParameters()
  -            && !m_isPost )
  +        
  +        if (null != m_parameters && m_parameters.hasParameters() && !m_isPost)
           {
  -            StringBuffer urlBuffer = new StringBuffer( systemId );
  +            StringBuffer urlBuffer = new StringBuffer(systemId);
               String key;
               final Iterator i = m_parameters.getParameterNames();
               Iterator values;
               String value;
  -            boolean first = ( systemId.indexOf( '?' ) == -1 );
  -            if( first == true ) urlBuffer.append( '?' );
  -            while( i.hasNext() )
  +            boolean first = (systemId.indexOf('?') == -1);
  +            if (first == true)
  +                urlBuffer.append('?');
  +            while (i.hasNext())
               {
  -                key = (String)i.next();
  -                values = m_parameters.getParameterValues( key );
  -                while( values.hasNext() == true )
  +                key = (String) i.next();
  +                values = m_parameters.getParameterValues(key);
  +                while (values.hasNext() == true)
                   {
  -                    value = SourceUtil.encode( (String)values.next() );
  -                    if( first == false ) urlBuffer.append( '&' );
  +                    value = SourceUtil.encode((String) values.next());
  +                    if (first == false)
  +                        urlBuffer.append('&');
                       first = false;
  -                    urlBuffer.append( key );
  -                    urlBuffer.append( '=' );
  -                    urlBuffer.append( value );
  +                    urlBuffer.append(key);
  +                    urlBuffer.append('=');
  +                    urlBuffer.append(value);
                   }
               }
  -            
  -            m_url = new URL( urlBuffer.toString() );
  +
  +            m_url = new URL(urlBuffer.toString());
               m_parameters = null;
           }
       }
   
       /**
  -     * @param file
  -     */
  -    protected void setFile(File file)
  -    {
  -        this.file = file;
  -    }
  -    
  -    protected File getFile()
  -    {
  -        return file;
  -    }
  -
  -    /**
        * Get the last modification date and content length of the source.
        * Any exceptions are ignored.
        * Override this to get the real information
  @@ -212,44 +172,35 @@
       {
           // exists will be set below depending on the m_url type
           m_exists = false;
  -        
  -        if( null != getFile() )
  -        {
  -            setLastModified( getFile().lastModified() );
  -            setContentLength( getFile().length() );
  -            m_exists = getFile().exists();
  -        }
  -        else
  +
  +        if (!m_isPost)
           {
  -            if( !m_isPost )
  +            try
               {
  -                try
  +                if (null == m_connection)
                   {
  -                    if( null == m_connection )
  +                    m_connection = m_url.openConnection();
  +                    String userInfo = getUserInfo();
  +                    if (m_url.getProtocol().startsWith("http") && userInfo != null)
                       {
  -                        m_connection = m_url.openConnection();
  -                        String userInfo = getUserInfo();
  -                        if( m_url.getProtocol().startsWith( "http" ) && userInfo != 
null )
  -                        {
  -                            m_connection.setRequestProperty( "Authorization", 
"Basic " + SourceUtil.encodeBASE64( userInfo ) );
  -                        }
  +                        m_connection.setRequestProperty("Authorization", "Basic " + 
SourceUtil.encodeBASE64(userInfo));
                       }
  -                    setLastModified(m_connection.getLastModified());
  -                    setContentLength(m_connection.getContentLength());
  -                    m_mimeType = m_connection.getContentType();
  -                    m_exists = true;
  -                }
  -                catch( IOException ignore )
  -                {
  -                    super.getInfos();
                   }
  +                setLastModified(m_connection.getLastModified());
  +                setContentLength(m_connection.getContentLength());
  +                m_mimeType = m_connection.getContentType();
  +                m_exists = true;
               }
  -            else
  +            catch (IOException ignore)
               {
  -                // do not open m_connection when using post!
                   super.getInfos();
               }
           }
  +        else
  +        {
  +            // do not open m_connection when using post!
  +            super.getInfos();
  +        }
       }
   
       /**
  @@ -268,89 +219,74 @@
        *         HTTP location does not exist.
        * @throws IOException if I/O error occured.
        */
  -    public InputStream getInputStream()
  -        throws IOException, SourceException
  +    public InputStream getInputStream() throws IOException, SourceException
       {
  -        try
  +        checkInfos();
  +        InputStream input = null;
  +        if (m_connection == null)
           {
  -            checkInfos();
  -            InputStream input = null;
  -            if( null != getFile() )
  +            m_connection = m_url.openConnection();
  +            /* The following requires a jdk 1.3 */
  +            String userInfo = getUserInfo();
  +            if (m_url.getProtocol().startsWith("http") && userInfo != null)
               {
  -                input = new FileInputStream( getFile() );
  +                m_connection.setRequestProperty("Authorization", "Basic " + 
SourceUtil.encodeBASE64(userInfo));
               }
  -            else
  +
  +            // do a post operation
  +            if (m_connection instanceof HttpURLConnection && m_isPost)
               {
  -                if( m_connection == null )
  +                StringBuffer buffer = new StringBuffer(2000);
  +                String key;
  +                Iterator i = m_parameters.getParameterNames();
  +                Iterator values;
  +                String value;
  +                boolean first = true;
  +                while (i.hasNext())
                   {
  -                    m_connection = m_url.openConnection();
  -                    /* The following requires a jdk 1.3 */
  -                    String userInfo = getUserInfo();
  -                    if( m_url.getProtocol().startsWith( "http" ) && userInfo != 
null )
  +                    key = (String) i.next();
  +                    values = m_parameters.getParameterValues(key);
  +                    while (values.hasNext() == true)
                       {
  -                        m_connection.setRequestProperty( "Authorization", "Basic " 
+ SourceUtil.encodeBASE64( userInfo ) );
  +                        value = SourceUtil.encode((String) values.next());
  +                        if (first == false)
  +                            buffer.append('&');
  +                        first = false;
  +                        buffer.append(key.toString());
  +                        buffer.append('=');
  +                        buffer.append(value);
                       }
  +                }
  +                HttpURLConnection httpCon = (HttpURLConnection) m_connection;
  +                httpCon.setDoInput(true);
   
  -                    // do a post operation
  -                    if( m_connection instanceof HttpURLConnection
  -                        && m_isPost )
  -                    {
  -                        StringBuffer buffer = new StringBuffer( 2000 );
  -                        String key;
  -                        Iterator i = m_parameters.getParameterNames();
  -                        Iterator values;
  -                        String value;
  -                        boolean first = true;
  -                        while( i.hasNext() )
  -                        {
  -                            key = (String)i.next();
  -                            values = m_parameters.getParameterValues( key );
  -                            while( values.hasNext() == true )
  -                            {
  -                                value = SourceUtil.encode( (String)values.next() );
  -                                if( first == false ) buffer.append( '&' );
  -                                first = false;
  -                                buffer.append( key.toString() );
  -                                buffer.append( '=' );
  -                                buffer.append( value );
  -                            }
  -                        }
  -                        HttpURLConnection httpCon = (HttpURLConnection)m_connection;
  -                        httpCon.setDoInput( true );
  -
  -                        if( buffer.length() > 1 )
  -                        { // only post if we have parameters
  -                            String postString = buffer.toString();
  -                            httpCon.setRequestMethod( "POST" ); // this is POST
  -                            httpCon.setDoOutput( true );
  -                            httpCon.setRequestProperty( "Content-type", 
"application/x-www-form-urlencoded" );
  -
  -                            // A content-length header must be contained in a POST 
request
  -                            httpCon.setRequestProperty( "Content-length", 
Integer.toString( postString.length() ) );
  -                            java.io.OutputStream out = new 
java.io.BufferedOutputStream( httpCon.getOutputStream() );
  -                            out.write( postString.getBytes() );
  -                            out.close();
  -                        }
  -                        input = httpCon.getInputStream();
  -                        m_connection = null; // make sure a new m_connection is 
created next time
  -                        return input;
  -                    }
  +                if (buffer.length() > 1)
  +                { // only post if we have parameters
  +                    String postString = buffer.toString();
  +                    httpCon.setRequestMethod("POST"); // this is POST
  +                    httpCon.setDoOutput(true);
  +                    httpCon.setRequestProperty("Content-type", 
"application/x-www-form-urlencoded");
  +
  +                    // A content-length header must be contained in a POST request
  +                    httpCon.setRequestProperty("Content-length", 
Integer.toString(postString.length()));
  +                    java.io.OutputStream out = new 
java.io.BufferedOutputStream(httpCon.getOutputStream());
  +                    out.write(postString.getBytes());
  +                    out.close();
                   }
  -                input = m_connection.getInputStream();
  +                input = httpCon.getInputStream();
                   m_connection = null; // make sure a new m_connection is created 
next time
  +                return input;
               }
  -            return input;
  -        }
  -        catch (FileNotFoundException fnfe)
  -        {
  -            throw new SourceNotFoundException("Resource not found " + getURI());
           }
  +        input = m_connection.getInputStream();
  +        m_connection = null; // make sure a new m_connection is created next time
  +        return input;
       }
   
       private static boolean checkedURLClass = false;
       private static boolean urlSupportsGetUserInfo = false;
       private static Method urlGetUserInfo = null;
  -    private static Object[] emptyParams = new Object[ 0 ];
  +    private static Object[] emptyParams = new Object[0];
   
       /**
        * Check if the <code>URL</code> class supports the getUserInfo()
  @@ -358,15 +294,15 @@
        */
       protected String getUserInfo()
       {
  -        if( URLSource.checkedURLClass == true )
  +        if (URLSource.checkedURLClass == true)
           {
  -            if( URLSource.urlSupportsGetUserInfo == true )
  +            if (URLSource.urlSupportsGetUserInfo == true)
               {
                   try
                   {
  -                    return (String)URLSource.urlGetUserInfo.invoke( m_url, 
URLSource.emptyParams );
  +                    return (String) URLSource.urlGetUserInfo.invoke(m_url, 
URLSource.emptyParams);
                   }
  -                catch( Exception e )
  +                catch (Exception e)
                   {
                       // ignore this anyway
                   }
  @@ -378,13 +314,13 @@
               // test if the m_url class supports the getUserInfo method
               try
               {
  -                URLSource.urlGetUserInfo = URL.class.getMethod( "getUserInfo", null 
);
  -                String ui = (String)URLSource.urlGetUserInfo.invoke( m_url, 
URLSource.emptyParams );
  +                URLSource.urlGetUserInfo = URL.class.getMethod("getUserInfo", null);
  +                String ui = (String) URLSource.urlGetUserInfo.invoke(m_url, 
URLSource.emptyParams);
                   URLSource.checkedURLClass = true;
                   URLSource.urlSupportsGetUserInfo = true;
                   return ui;
               }
  -            catch( Exception e )
  +            catch (Exception e)
               {
               }
               URLSource.checkedURLClass = true;
  @@ -403,20 +339,13 @@
       public SourceValidity getValidity()
       {
           final long lm = getLastModified();
  -        if( lm > 0 )
  +        if (lm > 0)
           {
               if (lm == m_cachedLastModificationDate)
                   return m_cachedValidity;
   
               m_cachedLastModificationDate = lm;
  -            if (getFile() != null)
  -            {
  -                m_cachedValidity = new FileTimeStampValidity(getFile(), lm);
  -            }
  -            else
  -            {
  -                m_cachedValidity = new TimeStampValidity( lm );
  -            }
  +            m_cachedValidity = new TimeStampValidity(lm);
               return m_cachedValidity;
           }
           return null;
  @@ -434,33 +363,6 @@
       }
   
       /**
  -     * Does this source point to a directory?
  -     */
  -    public boolean isDirectory()
  -    {
  -     if ( null != getFile() ) 
  -     {
  -             return getFile().isDirectory();
  -     }
  -     return false;
  -    }
  -    
  -    /**
  -     * Return the URIs of the children
  -     * The returned URIs are relative to the URI of the parent
  -     * (this object)
  -     */
  -    public Collection getChildrenLocations() 
  -    {
  -     if ( null != getFile() && getFile().isDirectory() )
  -     {
  -             final String[] files = getFile().list();
  -             return Arrays.asList(files);
  -     }
  -     return Collections.EMPTY_LIST;
  -    }
  -    
  -    /**
        * The mime-type of the content described by this object.
        * If the source is not able to determine the mime-type by itself
        * this can be null.
  @@ -469,5 +371,4 @@
       {
           return m_mimeType;
       }
  -    
   }
  
  
  
  1.4       +39 -92    
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/URLSourceFactory.java
  
  Index: URLSourceFactory.java
  ===================================================================
  RCS file: 
/home/cvs/avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/URLSourceFactory.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- URLSourceFactory.java     7 Feb 2003 11:21:09 -0000       1.3
  +++ URLSourceFactory.java     4 Apr 2003 16:36:52 -0000       1.4
  @@ -54,144 +54,91 @@
    */
   package org.apache.excalibur.source.impl;
   
  -import java.io.File;
   import java.io.IOException;
   import java.net.MalformedURLException;
   import java.net.URL;
   import java.util.Map;
   
   import org.apache.avalon.framework.logger.AbstractLogEnabled;
  -import org.apache.avalon.framework.parameters.ParameterException;
  -import org.apache.avalon.framework.parameters.Parameterizable;
  -import org.apache.avalon.framework.parameters.Parameters;
   import org.apache.avalon.framework.thread.ThreadSafe;
   import org.apache.excalibur.source.Source;
  -import org.apache.excalibur.source.SourceException;
   import org.apache.excalibur.source.SourceFactory;
   
   /**
    * A factory for a [EMAIL PROTECTED] URL} wrapper
    *
    * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a>
  + * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a>
    * @version $Id$
    */
  -public class URLSourceFactory
  -    extends AbstractLogEnabled
  -    implements SourceFactory, Parameterizable, ThreadSafe
  +public class URLSourceFactory extends AbstractLogEnabled implements SourceFactory, 
ThreadSafe
   {
   
  -    /** The URLSource class used */
  -    protected Class m_urlSourceClass;
  -
  -    public void parameterize( Parameters pars )
  -        throws ParameterException
  +    /**
  +     * Create an URL-based source. This class actually creates an [EMAIL PROTECTED] 
URLSource}, but if another
  +     * implementation is needed, subclasses can override this method.
  +     */
  +    protected Source createURLSource(URL url, Map parameters) throws 
MalformedURLException, IOException
       {
  -        final String urlSourceClassName = pars.getParameter( "url-source",
  -                                                             
"org.apache.excalibur.source.impl.URLSource" );
  -        ClassLoader loader = Thread.currentThread().getContextClassLoader();
  -        if( loader == null )
  -        {
  -            loader = getClass().getClassLoader();
  -        }
  -        try
  -        {
  -            m_urlSourceClass = loader.loadClass( urlSourceClassName );
  -        }
  -        catch( ClassNotFoundException cnfe )
  -        {
  -            this.getLogger().error( "Class not found: " + urlSourceClassName, cnfe 
);
  -            throw new ParameterException( "Class not found: " + urlSourceClassName, 
cnfe );
  -        }
  +        URLSource result = new URLSource();
  +        result.init(url, parameters);
  +        return result;
       }
   
       /**
  -     * Create a correct instance for the uri
  +     * Create an file-based source. This class actually creates an [EMAIL 
PROTECTED] FileSource}, but if another
  +     * implementation is needed, subclasses can override this method.
        */
  -    protected URLSource getSourceImplementation(String uri, Map parameters) 
  -    throws Exception
  +    protected Source createFileSource(String uri) throws MalformedURLException, 
IOException
       {
  -        if (uri.startsWith("file:")) 
  -        { 
  -            final URLSource fileSource = (URLSource)new FileSource();
  -            fileSource.init( new URL( uri ), parameters);
  -            return fileSource;
  -        } 
  -        else 
  -        {
  -            final URLSource urlSource =
  -                (URLSource)this.m_urlSourceClass.newInstance();
  -            urlSource.init( new URL( uri ), parameters );
  -            return urlSource;
  -        }
  +        return new FileSource(uri);
       }
  -    
  +
       /**
        * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, 
java.util.Map)
        */
  -    public Source getSource(String uri, Map parameters)
  -        throws MalformedURLException, IOException 
  +    public Source getSource(String uri, Map parameters) throws 
MalformedURLException, IOException
       {
  -        if( getLogger().isDebugEnabled() )
  +        if (getLogger().isDebugEnabled())
           {
               final String message = "Creating source object for " + uri;
  -            getLogger().debug( message );
  +            getLogger().debug(message);
           }
   
  -        Source source;
  -        try
  +        // First check if it's a file
  +        if (uri.startsWith("file:"))
           {
  -            if( getLogger().isDebugEnabled() == true )
  -            {
  -                this.getLogger().debug( "Making URL from " + uri );
  -            }
  -            try
  -            {
  -                source = this.getSourceImplementation( uri, parameters );
  -            }
  -            catch( MalformedURLException mue )
  -            {
  -                throw mue;
  -            }
  -            catch( Exception ie )
  -            {
  -                throw new SourceException( "Unable to create new instance of " +
  -                                           this.m_urlSourceClass, ie );
  -            }
  +            // Yes : return a file source
  +            return createFileSource(uri);
           }
  -        catch( MalformedURLException mue )
  +        else
           {
  -            if( getLogger().isDebugEnabled() )
  -            {
  -                this.getLogger().debug( "Making URL - MalformedURLException in 
getURL:", mue );
  -                this.getLogger().debug( "Making URL a File (assuming that it is 
full path):" + uri );
  -            }
  +            // Not a "file:" : create an URLSource
  +            // First try to create the URL
  +            URL url;
               try
               {
  -                final URLSource urlSource = new FileSource();
  -                urlSource.init( ( new File( uri ) ).toURL(), parameters );
  -                source = urlSource;
  +                url = new URL(uri);
               }
  -            catch( Exception ie )
  +            catch (MalformedURLException mue)
               {
  -                throw new SourceException( "Unable to create new instance of " +
  -                                           this.m_urlSourceClass, ie );
  +                // Maybe a file name containing a ':' ?
  +                if (getLogger().isDebugEnabled())
  +                {
  +                    this.getLogger().debug("URL " + uri + " is malformed. Assuming 
it's a file path.", mue);
  +                }
  +                return createFileSource(uri);
               }
  -        }
   
  -        return source;
  +            return createURLSource(url, parameters);
  +        }
       }
   
       /**
        * @see 
org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
        */
  -    public void release(Source source) {
  -        if( null != source && getLogger().isDebugEnabled() )
  -        {
  -            final String message = "Releasing source object for " + source.getURI();
  -            getLogger().debug( message );
  -        }
  +    public void release(Source source)
  +    {
           // do nothing here
       }
  -
   }
  -
  
  
  
  1.1                  
avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/FileSourceFactory.java
  
  Index: FileSourceFactory.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software
   *    itself, if and wherever such third-party acknowledgments
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.excalibur.source.impl;
  
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.util.Map;
  
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceFactory;
  
  /**
   * A factory for filesystem-based sources (see [EMAIL PROTECTED] FileSource}).
   * 
   * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a>
   * @version $Id: FileSourceFactory.java,v 1.1 2003/04/04 16:36:51 sylvain Exp $
   */
  public class FileSourceFactory implements SourceFactory, ThreadSafe
  {
  
      /**
       * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, 
java.util.Map)
       */
      public Source getSource(String location, Map parameters) throws IOException, 
MalformedURLException
      {
          return new FileSource(location);
      }
  
      /**
       * Does nothing, since [EMAIL PROTECTED] FileSource}s don't need to be released.
       * 
       * @see 
org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
       */
      public void release(Source source)
      {
          // Nothing to do here
      }
  }
  
  
  
  1.1                  
avalon-excalibur/sourceresolve/src/test/org/apache/excalibur/source/test/FileSourceTestCase.java
  
  Index: FileSourceTestCase.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software
   *    itself, if and wherever such third-party acknowledgments
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.excalibur.source.test;
  
  import java.io.File;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.Collection;
  import java.util.ConcurrentModificationException;
  
  import junit.framework.TestCase;
  
  import org.apache.excalibur.source.ModifiableSource;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceUtil;
  import org.apache.excalibur.source.SourceValidity;
  import org.apache.excalibur.source.impl.FileSource;
  
  /**
   * Test case for FileSource.
   * 
   * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a>
   * @version $Id: FileSourceTestCase.java,v 1.1 2003/04/04 16:36:52 sylvain Exp $
   */
  public class FileSourceTestCase extends TestCase
  {
  
      private File m_tempDir;
  
      public FileSourceTestCase()
      {
          this("FileSource");
      }
  
      public FileSourceTestCase(String name)
      {
          super(name);
      }
  
      protected void setUp() throws Exception
      {
          // Create a temp file
          m_tempDir = File.createTempFile("filesource", "test");
          // and make it a directory
          m_tempDir.delete();
          m_tempDir.mkdir();
      }
  
      public void testDirExistence() throws Exception
      {
          FileSource src = new FileSource("file", m_tempDir);
          assertTrue("Temp dir doesn't exist", src.exists());
          assertTrue("Temp dir is not traversable", src.isCollection());
          // Check it was created less than 1 secs ago
          assertEquals("Wrong creation date", System.currentTimeMillis() / 1000L, 
src.getLastModified() / 1000L);
  
          assertTrue("Temp dir is not empty", src.getChildren().isEmpty());
      }
  
      public void testChildCreation() throws Exception
      {
          final String text = "Writing to a source";
  
          FileSource src = new FileSource("file", m_tempDir);
  
          FileSource child = (FileSource) src.getChild("child.txt");
          assertTrue("New file already exists", !child.exists());
  
          // Should not have a validity, since it doesn't exist
          assertNull("New file has a validity", child.getValidity());
  
          // Test the name
          assertEquals("Wrong name", "child.txt", child.getName());
  
          // Feed with some content
          fillSource(child, text);
  
          // And test it
          assertEquals(
              "Wrong length",
              text.length() + System.getProperty("line.separator").length(),
              child.getContentLength());
          assertEquals("Wrong content-type", "text/plain", child.getMimeType());
          assertTrue("New file is traversable", !child.isCollection());
  
          // Check that parent now has children
          Collection children = src.getChildren();
          assertEquals("Wrong number of children", 1, children.size());
  
          // And also that crawling up the hierarchy is OK
          Source parent = child.getParent();
          assertEquals("Wrong parent URI", src.getURI(), parent.getURI());
  
      }
  
      public void testMove() throws Exception
      {
          final String text = "Original text";
  
          FileSource src = new FileSource("file", m_tempDir);
  
          FileSource child = (FileSource) src.getChild("child.txt");
          assertTrue("New file already exists", !child.exists());
  
          fillSource(child, text);
          assertTrue("New file doesn't exist", child.exists());
          long length = child.getContentLength();
  
          FileSource child2 = (FileSource) src.getChild("child2.txt");
          assertTrue("Second file already exist", !child2.exists());
  
          SourceUtil.move(child, child2);
          assertTrue("First file still exists", !child.exists());
          assertTrue("Second file doesn't exist", child2.exists());
          assertEquals("Wrong length of second file", length, 
child2.getContentLength());
      }
  
      public void testCopy() throws Exception
      {
          final String text = "Original text";
  
          FileSource src = new FileSource("file", m_tempDir);
  
          FileSource child = (FileSource) src.getChild("child.txt");
          assertTrue("New file already exists", !child.exists());
  
          fillSource(child, text);
          assertTrue("New file doesn't exist", child.exists());
          long length = child.getContentLength();
  
          FileSource child2 = (FileSource) src.getChild("child2.txt");
          assertTrue("Second file already exist", !child2.exists());
  
          SourceUtil.copy(child, child2);
  
          assertTrue("First file doesn't exist", child.exists());
          assertTrue("Second file doesn't exist", child2.exists());
          assertEquals("Wrong length of second file", length, 
child2.getContentLength());
  
      }
  
      public void testDelete() throws Exception
      {
          final String text = "Original text";
  
          FileSource src = new FileSource("file", m_tempDir);
  
          FileSource child = (FileSource) src.getChild("child.txt");
          assertTrue("New file already exists", !child.exists());
          fillSource(child, text);
          assertTrue("New file doesn't exist", child.exists());
  
          child.delete();
          assertTrue("File still exists", !child.exists());
      }
  
      public void testConcurrentAccess() throws Exception
      {
          FileSource src = new FileSource("file", m_tempDir);
  
          FileSource child = (FileSource) src.getChild("child.txt");
          assertTrue("New file already exists", !child.exists());
  
          OutputStream os = child.getOutputStream();
  
          try
          {
              // Get it a second time
              OutputStream os2 = child.getOutputStream();
          }
          catch (ConcurrentModificationException cme)
          {
              return; // This is what is expected
          }
          fail("Undedected concurrent modification");
  
      }
  
      public void testAtomicUpdate() throws Exception
      {
          final String text = "Blah, blah";
          FileSource src = new FileSource("file", m_tempDir);
  
          FileSource child = (FileSource) src.getChild("child.txt");
          assertTrue("New file already exists", !child.exists());
          fillSource(child, text + " and blah!");
  
          long length = child.getContentLength();
  
          SourceValidity validity = child.getValidity();
          assertEquals("Validity is not valid", 1, validity.isValid());
  
                // Wait a bit, otherwise the update speed is faster than the clock 
resolution
          Thread.sleep(100L);
          
          // Now change its content
          PrintWriter pw = new PrintWriter(child.getOutputStream());
          pw.write(text);
  
          assertEquals("File length modified", length, child.getContentLength());
  
          pw.close();
  
          assertTrue("File length not modified", length != child.getContentLength());
          
          assertEquals("Validity is valid", -1, validity.isValid());
      }
  
      protected void tearDown() throws Exception
      {
          deleteAll(m_tempDir);
      }
  
      // Recursively delete a file or directory
      private void deleteAll(File f)
      {
          if (f.isDirectory())
          {
              File[] children = f.listFiles();
              for (int i = 0; i < children.length; i++)
              {
                  deleteAll(children[i]);
              }
          }
  
          f.delete();
      }
  
      private void fillSource(ModifiableSource src, String text) throws Exception
      {
          OutputStream os = src.getOutputStream();
          PrintWriter pw = new PrintWriter(os);
  
          pw.println("Writing to a source");
          pw.close();
      }
  
  }
  
  
  
  1.1                  
avalon-excalibur/sourceresolve/src/test/org/apache/excalibur/source/test/SourceResolverImplTestCase.java
  
  Index: SourceResolverImplTestCase.java
  ===================================================================
  
  
  package org.apache.excalibur.source.test;
  
  import java.io.File;
  
  import junit.framework.TestCase;
  
  import org.apache.avalon.framework.context.DefaultContext;
  import org.apache.avalon.framework.logger.ConsoleLogger;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.avalon.framework.service.DefaultServiceManager;
  import org.apache.avalon.framework.service.DefaultServiceSelector;
  import org.apache.excalibur.source.SourceFactory;
  import org.apache.excalibur.source.impl.ResourceSourceFactory;
  import org.apache.excalibur.source.impl.SourceResolverImpl;
  
  public class SourceResolverImplTestCase extends TestCase
  {
  
      public SourceResolverImplTestCase( )
      {
          this( "source" );
      }
  
      public SourceResolverImplTestCase( String name )
      {
          super( name );
      }
  
      public void testResolver() throws Exception
      {
          Logger logger = new ConsoleLogger( ConsoleLogger.LEVEL_DEBUG );
  
          //
          // create the component to handle source resolution
          //
  
          final SourceResolverImpl resolver = new SourceResolverImpl();
          resolver.enableLogging( logger );
  
          //
          // create the context argument and set the root directory and
          // contextualize the resolver
          //
          // NOTE: javadoc needed on contextualize method
          //
  
          final DefaultContext context = new DefaultContext();
          context.put( "context-root", new File( System.getProperty( "user.dir" ) ) );
          resolver.contextualize( context );
  
          //
          // create a service selector to be included in a service manager
          // to be supplied to the resolver
          //
  
          final ResourceSourceFactory factory = new ResourceSourceFactory();
          factory.enableLogging( logger.getChildLogger( "factory" ) );
  
          // create a selector and add the factory to the selector,
          // add the selector to the manager, and service the resolver
          // NOTE: javadoc missing on the serviceable method
          //
  
          final DefaultServiceSelector selector = new DefaultServiceSelector();
          selector.put( "resource", factory );
  
          final DefaultServiceManager manager = new DefaultServiceManager();
          manager.put( SourceFactory.ROLE + "Selector", selector );
  
          resolver.service( manager );
  
  
          logger.debug( "resolver created - but is this correct ?" );
  
          //
          // setup a protocol handler - TO BE DONE
          //
  
          logger.debug( "help me - need to setup a handler" );
  
          //
          // test source URL creation - TO BE DONE
          //
  
          logger.debug( "help me - need to test source creation" );
  
          //
          // test source resolution - TO BE DONE
          //
  
          logger.debug( "help me - need to test source resolution" );
  
          assertTrue( true );
      }
  
  }
  
  
  
  1.1                  
avalon-excalibur/sourceresolve/src/test/org/apache/excalibur/source/test/SourceUtilTestCase.java
  
  Index: SourceUtilTestCase.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software
   *    itself, if and wherever such third-party acknowledgments
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.excalibur.source.test;
  
  import org.apache.excalibur.source.SourceUtil;
  
  import junit.framework.TestCase;
  
  /**
   * Test case for SourceUtil.
   * 
   * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a>
   * @version $Id: SourceUtilTestCase.java,v 1.1 2003/04/04 16:36:52 sylvain Exp $
   */
  public class SourceUtilTestCase extends TestCase
  {
      public SourceUtilTestCase()
      {
          this("SourceUtil");
      }
  
      public SourceUtilTestCase(String name)
      {
          super(name);
      }
  
        public void testNominalScheme() throws Exception
        {
            String uri = "http://foo";;
            assertEquals(4, SourceUtil.indexOfSchemeColon(uri));
            assertEquals("http", SourceUtil.getScheme(uri));
            assertEquals("//foo", SourceUtil.getSpecificPart(uri));
        }
        
        public void testDoubleColon() throws Exception
        {
            assertEquals(4, SourceUtil.indexOfSchemeColon("file:foo:bar"));
        }
        
        public void testSpecialScheme() throws Exception
        {
            String uri = "a-+.:foo"; // Strange, but valid !
            assertEquals(4, SourceUtil.indexOfSchemeColon(uri));
            assertEquals("a-+.", SourceUtil.getScheme(uri));
            assertEquals("foo", SourceUtil.getSpecificPart(uri));
        }
        
        public void testSpecialPart() throws Exception
        {
            String uri = "bar:";
            assertEquals(3, SourceUtil.indexOfSchemeColon(uri));
            assertEquals("bar", SourceUtil.getScheme(uri));
            assertEquals("", SourceUtil.getSpecificPart(uri));
        }
        
        public void testInvalidScheme() throws Exception
        {
            String uri = "2foo:bar";
            assertEquals(-1, SourceUtil.indexOfSchemeColon(uri));
            assertEquals(null, SourceUtil.getScheme(uri));
            assertEquals(null, SourceUtil.getSpecificPart(uri));
            
            // Invalid character before any of the allowed ones
            assertEquals(-1, SourceUtil.indexOfSchemeColon("h ttp:foo"));
          assertEquals(-1, SourceUtil.indexOfSchemeColon(" http:foo"));
          assertEquals(-1, SourceUtil.indexOfSchemeColon("http :foo"));
          
         // Invalid character between allowed ranges
          assertEquals(-1, SourceUtil.indexOfSchemeColon("h_ttp:foo"));
          assertEquals(-1, SourceUtil.indexOfSchemeColon("_http:foo"));
          assertEquals(-1, SourceUtil.indexOfSchemeColon("http_:foo"));
          
          // Invalid character after any of the allowed ones
          assertEquals(-1, SourceUtil.indexOfSchemeColon("h~ttp:foo"));
          assertEquals(-1, SourceUtil.indexOfSchemeColon("~http:foo"));
          assertEquals(-1, SourceUtil.indexOfSchemeColon("http~:foo"));
  
            assertEquals(-1, SourceUtil.indexOfSchemeColon("/file/with:colon"));
            assertEquals(-1, SourceUtil.indexOfSchemeColon(".foo:bar"));
            assertEquals(-1, SourceUtil.indexOfSchemeColon("no-colon"));
        }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to