You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by jv...@apache.org on 2006/04/18 15:59:34 UTC

svn commit: r394946 [2/4] - in /maven/components/trunk/maven-core-it-verifier/src/main/java/org: apache/maven/it/ codehaus/plexus/util/

Modified: maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/FileUtils.java
URL: http://svn.apache.org/viewcvs/maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/FileUtils.java?rev=394946&r1=394945&r2=394946&view=diff
==============================================================================
--- maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/FileUtils.java (original)
+++ maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/FileUtils.java Tue Apr 18 06:59:22 2006
@@ -56,10 +56,1091 @@
  */
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Vector;
+
+/**
+ * This class provides basic facilities for manipulating files and file paths.
+ *
+ * <h3>Path-related methods</h3>
+ *
+ * <p>Methods exist to retrieve the components of a typical file path. For example
+ * <code>/www/hosted/mysite/index.html</code>, can be broken into:
+ * <ul>
+ *   <li><code>/www/hosted/mysite/</code> -- retrievable through {@link #getPath}</li>
+ *   <li><code>index.html</code> -- retrievable through {@link #removePath}</li>
+ *   <li><code>/www/hosted/mysite/index</code> -- retrievable through {@link #removeExtension}</li>
+ *   <li><code>html</code> -- retrievable through {@link #getExtension}</li>
+ * </ul>
+ * There are also methods to {@link #catPath concatenate two paths}, {@link #resolveFile resolve a
+ * path relative to a File} and {@link #normalize} a path.
+ * </p>
+ *
+ * <h3>File-related methods</h3>
+ * <p>
+ * There are methods to  create a {@link #toFile File from a URL}, copy a
+ * {@link #copyFileToDirectory File to a directory},
+ * copy a {@link #copyFile File to another File},
+ * copy a {@link #copyURLToFile URL's contents to a File},
+ * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File)
+ * clean} a directory.
+ * </p>
+ *
+ * Common {@link java.io.File} manipulation routines.
+ *
+ * Taken from the commons-utils repo.
+ * Also code from Alexandria's FileUtils.
+ * And from Avalon Excalibur's IO.
+ * And from Ant.
+ *
+ * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
+ * @author <a href="mailto:sanders@codehaus.org">Scott Sanders</a>
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
+ * @author <a href="mailto:peter@codehaus.org">Peter Donald</a>
+ * @author <a href="mailto:jefft@codehaus.org">Jeff Turner</a>
+ * @version $Id$
+ */
+public class FileUtils
+{
+    /**
+     * The number of bytes in a kilobyte.
+     */
+    public static final int ONE_KB = 1024;
+
+    /**
+     * The number of bytes in a megabyte.
+     */
+    public static final int ONE_MB = ONE_KB * ONE_KB;
+
+    /**
+     * The number of bytes in a gigabyte.
+     */
+    public static final int ONE_GB = ONE_KB * ONE_MB;
+
+    public static String[] getDefaultExcludes()
+    {
+        return DirectoryScanner.DEFAULTEXCLUDES;
+    }
+
+    public static List getDefaultExcludesAsList()
+    {
+        return Arrays.asList( getDefaultExcludes() );
+    }
+
+    /**
+     * Returns a human-readable version of the file size (original is in
+     * bytes).
+     *
+     * @param size The number of bytes.
+     * @return     A human-readable display value (includes units).
+     */
+    public static String byteCountToDisplaySize( int size )
+    {
+        String displaySize;
+
+        if ( size / ONE_GB > 0 )
+        {
+            displaySize = String.valueOf( size / ONE_GB ) + " GB";
+        }
+        else if ( size / ONE_MB > 0 )
+        {
+            displaySize = String.valueOf( size / ONE_MB ) + " MB";
+        }
+        else if ( size / ONE_KB > 0 )
+        {
+            displaySize = String.valueOf( size / ONE_KB ) + " KB";
+        }
+        else
+        {
+            displaySize = String.valueOf( size ) + " bytes";
+        }
+
+        return displaySize;
+    }
+
+    /**
+     * Returns the directory path portion of a file specification string.
+     * Matches the equally named unix command.
+     * @return The directory portion excluding the ending file separator.
+     */
+    public static String dirname( String filename )
+    {
+        int i = filename.lastIndexOf( File.separator );
+        return ( i >= 0 ? filename.substring( 0, i ) : "" );
+    }
+
+    /**
+     * Returns the filename portion of a file specification string.
+     * @return The filename string with extension.
+     */
+    public static String filename( String filename )
+    {
+        int i = filename.lastIndexOf( File.separator );
+        return ( i >= 0 ? filename.substring( i + 1 ) : filename );
+    }
+
+    /**
+     * Returns the filename portion of a file specification string.
+     * Matches the equally named unix command.
+     * @return The filename string without extension.
+     */
+    public static String basename( String filename )
+    {
+        return basename( filename, extension( filename ) );
+    }
+
+    /**
+     * Returns the filename portion of a file specification string.
+     * Matches the equally named unix command.
+     */
+    public static String basename( String filename, String suffix )
+    {
+        int i = filename.lastIndexOf( File.separator ) + 1;
+        int lastDot = ( ( suffix != null ) && ( suffix.length() > 0 ) )
+            ? filename.lastIndexOf( suffix ) : -1;
+
+        if ( lastDot >= 0 )
+        {
+            return filename.substring( i, lastDot );
+        }
+        else if ( i > 0 )
+        {
+            return filename.substring( i );
+        }
+        else
+        {
+            return filename; // else returns all (no path and no extension)
+        }
+    }
+
+    /**
+     * Returns the extension portion of a file specification string.
+     * This everything after the last dot '.' in the filename (NOT including
+     * the dot).
+     */
+    public static String extension( String filename )
+    {
+        int lastDot = filename.lastIndexOf( '.' );
+
+        if ( lastDot >= 0 )
+        {
+            return filename.substring( lastDot + 1 );
+        }
+        else
+        {
+            return "";
+        }
+    }
+
+    /**
+     * Check if a file exits.
+     *
+     * @param fileName The name of the file to check.
+     * @return true if file exists.
+     */
+    public static boolean fileExists( String fileName )
+    {
+        File file = new File( fileName );
+        return file.exists();
+    }
+
+    public static String fileRead( String file )
+        throws IOException
+    {
+        return fileRead( new File( file ) );
+    }
+
+    public static String fileRead( File file )
+        throws IOException
+    {
+        StringBuffer buf = new StringBuffer();
+
+        FileInputStream in = null;
+        
+        try
+        {
+            in = new FileInputStream( file );
+            int count;
+            byte[] b = new byte[512];
+            while ( ( count = in.read( b ) ) > 0 )  // blocking read
+            {
+                buf.append( new String( b, 0, count ) );
+            }
+        }
+        finally
+        {
+            IOUtil.close( in );
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Appends data to a file. The file will be created if it does not exist.
+     *
+     * @param fileName The name of the file to write.
+     * @param data The content to write to the file.
+     */
+    public static void fileAppend( String fileName, String data )
+        throws IOException
+    {
+        FileOutputStream out = null;
+        try
+        {
+            out = new FileOutputStream( fileName, true );
+            out.write( data.getBytes() );
+        }
+        finally
+        {
+            IOUtil.close( out );
+        }
+    }
+
+    /**
+     * Writes data to a file. The file will be created if it does not exist.
+     *
+     * @param fileName The name of the file to write.
+     * @param data The content to write to the file.
+     */
+    public static void fileWrite( String fileName, String data )
+        throws IOException
+    {
+        FileOutputStream out = null;
+        try
+        {
+            out = new FileOutputStream( fileName );
+            out.write( data.getBytes() );
+        }
+        finally
+        {
+            IOUtil.close( out );
+        }
+    }
+    
+    /**
+     * Deletes a file.
+     *
+     * @param fileName The name of the file to delete.
+     */
+    public static void fileDelete( String fileName )
+    {
+        File file = new File( fileName );
+        file.delete();
+    }
+
+    /**
+     * Waits for NFS to propagate a file creation, imposing a timeout.
+     *
+     * @param fileName The name of the file.
+     * @param seconds The maximum time in seconds to wait.
+     * @return True if file exists.
+     */
+    public static boolean waitFor( String fileName, int seconds )
+    {
+        return waitFor( new File( fileName ), seconds );
+    }
+
+    public static boolean waitFor( File file, int seconds )
+    {
+        int timeout = 0;
+        int tick = 0;
+        while ( !file.exists() )
+        {
+            if ( tick++ >= 10 )
+            {
+                tick = 0;
+                if ( timeout++ > seconds )
+                {
+                    return false;
+                }
+            }
+            try
+            {
+                Thread.sleep( 100 );
+            }
+            catch ( InterruptedException ignore )
+            {
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Creates a file handle.
+     *
+     * @param fileName The name of the file.
+     * @return A <code>File</code> manager.
+     */
+    public static File getFile( String fileName )
+    {
+        return new File( fileName );
+    }
+
+    /**
+     * Given a directory and an array of extensions return an array of compliant files.
+     *
+     * TODO Should an ignore list be passed in?
+     * TODO Should a recurse flag be passed in?
+     *
+     * The given extensions should be like "java" and not like ".java"
+     */
+    public static String[] getFilesFromExtension( String directory, String[] extensions )
+    {
+
+        Vector files = new Vector();
+
+        java.io.File currentDir = new java.io.File( directory );
+
+        String[] unknownFiles = currentDir.list();
+
+        if ( unknownFiles == null )
+        {
+            return new String[0];
+        }
+
+        for ( int i = 0; i < unknownFiles.length; ++i )
+        {
+            String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFiles[i];
+            java.io.File currentFile = new java.io.File( currentFileName );
+
+            if ( currentFile.isDirectory() )
+            {
+
+
+                //ignore all CVS directories...
+                if ( currentFile.getName().equals( "CVS" ) )
+                {
+                    continue;
+                }
+
+
+                //ok... transverse into this directory and get all the files... then combine
+                //them with the current list.
+
+                String[] fetchFiles = getFilesFromExtension( currentFileName, extensions );
+                files = blendFilesToVector( files, fetchFiles );
+
+            }
+            else
+            {
+                //ok... add the file
+
+                String add = currentFile.getAbsolutePath();
+                if ( isValidFile( add, extensions ) )
+                {
+                    files.addElement( add );
+
+                }
+
+            }
+        }
+
+        //ok... move the Vector into the files list...
+
+        String[] foundFiles = new String[files.size()];
+        files.copyInto( foundFiles );
+
+        return foundFiles;
+
+    }
+
+
+    /**
+     * Private hepler method for getFilesFromExtension()
+     */
+    private static Vector blendFilesToVector( Vector v, String[] files )
+    {
+
+        for ( int i = 0; i < files.length; ++i )
+        {
+            v.addElement( files[i] );
+        }
+
+        return v;
+    }
+
+    /**
+     * Checks to see if a file is of a particular type(s).
+     * Note that if the file does not have an extension, an empty string
+     * (&quot;&quot;) is matched for.
+     *
+     */
+    private static boolean isValidFile( String file, String[] extensions )
+    {
+
+
+        String extension = extension( file );
+        if ( extension == null )
+        {
+            extension = "";
+        }
+
+        //ok.. now that we have the "extension" go through the current know
+        //excepted extensions and determine if this one is OK.
+
+        for ( int i = 0; i < extensions.length; ++i )
+        {
+            if ( extensions[i].equals( extension ) )
+                return true;
+        }
+
+        return false;
+
+    }
+
+    /**
+     * Simple way to make a directory
+     */
+    public static void mkdir( String dir )
+    {
+        File file = new File( dir );
+        if ( !file.exists() )
+        {
+            file.mkdirs();
+        }
+    }
+
+    /**
+     * Compare the contents of two files to determine if they are equal or not.
+     *
+     * @param file1 the first file
+     * @param file2 the second file
+     * @return true if the content of the files are equal or they both don't exist, false otherwise
+     */
+    public static boolean contentEquals( final File file1, final File file2 )
+        throws IOException
+    {
+        final boolean file1Exists = file1.exists();
+        if ( file1Exists != file2.exists() )
+        {
+            return false;
+        }
+
+        if ( !file1Exists )
+        {
+            // two not existing files are equal
+            return true;
+        }
+
+        if ( file1.isDirectory() || file2.isDirectory() )
+        {
+            // don't want to compare directory contents
+            return false;
+        }
+
+        InputStream input1 = null;
+        InputStream input2 = null;
+        try
+        {
+            input1 = new FileInputStream( file1 );
+            input2 = new FileInputStream( file2 );
+            return IOUtil.contentEquals( input1, input2 );
+
+        }
+        finally
+        {
+            IOUtil.close( input1 );
+            IOUtil.close( input2 );
+        }
+    }
+
+    /**
+     * Convert from a <code>URL</code> to a <code>File</code>.
+     * @param url File URL.
+     * @return The equivalent <code>File</code> object, or <code>null</code> if the URL's protocol
+     * is not <code>file</code>
+     */
+    public static File toFile( final URL url )
+    {
+        if ( url.getProtocol().equals( "file" ) == false )
+        {
+            return null;
+        }
+        else
+        {
+            final String filename = url.getFile().replace( '/', File.separatorChar );
+            return new File( filename );
+        }
+    }
+
+    /**
+     * Convert the array of Files into a list of URLs.
+     *
+     * @param files the array of files
+     * @return the array of URLs
+     * @throws IOException if an error occurs
+     */
+    public static URL[] toURLs( final File[] files )
+        throws IOException
+    {
+        final URL[] urls = new URL[files.length];
+
+        for ( int i = 0; i < urls.length; i++ )
+        {
+            urls[i] = files[i].toURL();
+        }
+
+        return urls;
+    }
+
+    /**
+     * Remove extension from filename.
+     * ie
+     * <pre>
+     * foo.txt    --> foo
+     * a\b\c.jpg --> a\b\c
+     * a\b\c     --> a\b\c
+     * </pre>
+     *
+     * @param filename the filename
+     * @return the filename minus extension
+     */
+    public static String removeExtension( final String filename )
+    {
+        final int index = filename.lastIndexOf( '.' );
+
+        if ( -1 == index )
+        {
+            return filename;
+        }
+        else
+        {
+            return filename.substring( 0, index );
+        }
+    }
+
+    /**
+     * Get extension from filename.
+     * ie
+     * <pre>
+     * foo.txt    --> "txt"
+     * a\b\c.jpg --> "jpg"
+     * a\b\c     --> ""
+     * </pre>
+     *
+     * @param filename the filename
+     * @return the extension of filename or "" if none
+     */
+    public static String getExtension( final String filename )
+    {
+        final int index = filename.lastIndexOf( '.' );
+
+        if ( -1 == index )
+        {
+            return "";
+        }
+        else
+        {
+            return filename.substring( index + 1 );
+        }
+    }
+
+    /**
+     * Remove path from filename. Equivalent to the unix command <code>basename</code>
+     * ie.
+     * <pre>
+     * a/b/c.txt --> c.txt
+     * a.txt     --> a.txt
+     * </pre>
+     *
+     * @param filepath the filepath
+     * @return the filename minus path
+     */
+    public static String removePath( final String filepath )
+    {
+        return removePath( filepath, File.separatorChar );
+    }
+
+    /**
+     * Remove path from filename.
+     * ie.
+     * <pre>
+     * a/b/c.txt --> c.txt
+     * a.txt     --> a.txt
+     * </pre>
+     *
+     * @param filepath the filepath
+     * @return the filename minus path
+     */
+    public static String removePath( final String filepath, final char fileSeparatorChar )
+    {
+        final int index = filepath.lastIndexOf( fileSeparatorChar );
+
+        if ( -1 == index )
+        {
+            return filepath;
+        }
+        else
+        {
+            return filepath.substring( index + 1 );
+        }
+    }
+
+    /**
+     * Get path from filename. Roughly equivalent to the unix command <code>dirname</code>.
+     * ie.
+     * <pre>
+     * a/b/c.txt --> a/b
+     * a.txt     --> ""
+     * </pre>
+     *
+     * @param filepath the filepath
+     * @return the filename minus path
+     */
+    public static String getPath( final String filepath )
+    {
+        return getPath( filepath, File.separatorChar );
+    }
+
+    /**
+     * Get path from filename.
+     * ie.
+     * <pre>
+     * a/b/c.txt --> a/b
+     * a.txt     --> ""
+     * </pre>
+     *
+     * @param filepath the filepath
+     * @return the filename minus path
+     */
+    public static String getPath( final String filepath, final char fileSeparatorChar )
+    {
+        final int index = filepath.lastIndexOf( fileSeparatorChar );
+        if ( -1 == index )
+        {
+            return "";
+        }
+        else
+        {
+            return filepath.substring( 0, index );
+        }
+    }
+
+    /**
+     * Copy file from source to destination. If <code>destinationDirectory</code> does not exist, it
+     * (and any parent directories) will be created. If a file <code>source</code> in
+     * <code>destinationDirectory</code> exists, it will be overwritten.
+     *
+     * @param source An existing <code>File</code> to copy.
+     * @param destinationDirectory A directory to copy <code>source</code> into.
+     *
+     * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
+     * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
+     * @throws IOException if <code>source</code> does not exist, the file in
+     * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying.
+     */
+    public static void copyFileToDirectory( final String source,
+                                            final String destinationDirectory )
+        throws IOException
+    {
+        copyFileToDirectory( new File( source ),
+                             new File( destinationDirectory ) );
+    }
+
+    /**
+     * Copy file from source to destination only if source is newer than the target file.
+     * If <code>destinationDirectory</code> does not exist, it
+     * (and any parent directories) will be created. If a file <code>source</code> in
+     * <code>destinationDirectory</code> exists, it will be overwritten.
+     *
+     * @param source An existing <code>File</code> to copy.
+     * @param destinationDirectory A directory to copy <code>source</code> into.
+     *
+     * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
+     * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
+     * @throws IOException if <code>source</code> does not exist, the file in
+     * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying.
+     */
+    public static void copyFileToDirectoryIfModified( final String source,
+                                            final String destinationDirectory )
+        throws IOException
+    {
+        copyFileToDirectoryIfModified( new File( source ),
+                             new File( destinationDirectory ) );
+    }
+
+    /**
+     * Copy file from source to destination. If <code>destinationDirectory</code> does not exist, it
+     * (and any parent directories) will be created. If a file <code>source</code> in
+     * <code>destinationDirectory</code> exists, it will be overwritten.
+     *
+     * @param source An existing <code>File</code> to copy.
+     * @param destinationDirectory A directory to copy <code>source</code> into.
+     *
+     * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
+     * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
+     * @throws IOException if <code>source</code> does not exist, the file in
+     * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying.
+     */
+    public static void copyFileToDirectory( final File source,
+                                            final File destinationDirectory )
+        throws IOException
+    {
+        if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
+        {
+            throw new IllegalArgumentException( "Destination is not a directory" );
+        }
+
+        copyFile( source, new File( destinationDirectory, source.getName() ) );
+    }
+
+    /**
+     * Copy file from source to destination only if source is newer than the target file.
+     * If <code>destinationDirectory</code> does not exist, it
+     * (and any parent directories) will be created. If a file <code>source</code> in
+     * <code>destinationDirectory</code> exists, it will be overwritten.
+     *
+     * @param source An existing <code>File</code> to copy.
+     * @param destinationDirectory A directory to copy <code>source</code> into.
+     *
+     * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
+     * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
+     * @throws IOException if <code>source</code> does not exist, the file in
+     * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying.
+     */
+    public static void copyFileToDirectoryIfModified( final File source,
+                                            final File destinationDirectory )
+        throws IOException
+    {
+        if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
+        {
+            throw new IllegalArgumentException( "Destination is not a directory" );
+        }
+
+        copyFileIfModified( source, new File( destinationDirectory, source.getName() ) );
+    }
+
+
+    /**
+     * Copy file from source to destination. The directories up to <code>destination</code> will be
+     * created if they don't already exist. <code>destination</code> will be overwritten if it
+     * already exists.
+     *
+     * @param source An existing non-directory <code>File</code> to copy bytes from.
+     * @param destination A non-directory <code>File</code> to write bytes to (possibly
+     * overwriting).
+     *
+     * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be
+     * written to, or an IO error occurs during copying.
+     *
+     * @throws java.io.FileNotFoundException if <code>destination</code> is a directory
+     * (use {@link #copyFileToDirectory}).
+     */
+    public static void copyFile( final File source, final File destination )
+        throws IOException
+    {
+        //check source exists
+        if ( !source.exists() )
+        {
+            final String message = "File " + source + " does not exist";
+            throw new IOException( message );
+        }
+
+        //does destinations directory exist ?
+        if ( destination.getParentFile() != null &&
+            !destination.getParentFile().exists() )
+        {
+            destination.getParentFile().mkdirs();
+        }
+
+        //make sure we can write to destination
+        if ( destination.exists() && !destination.canWrite() )
+        {
+            final String message = "Unable to open file " +
+                destination + " for writing.";
+            throw new IOException( message );
+        }
+
+        FileInputStream input = null;
+        FileOutputStream output = null;
+        try
+        {
+            input = new FileInputStream( source );
+            output = new FileOutputStream( destination );
+            IOUtil.copy( input, output );
+        }
+        finally
+        {
+            IOUtil.close( input );
+            IOUtil.close( output );
+        }
+
+        if ( source.length() != destination.length() )
+        {
+            final String message = "Failed to copy full contents from " + source +
+                " to " + destination;
+            throw new IOException( message );
+        }
+    }
+
+    /**
+     * Copy file from source to destination only if source timestamp is later than the destination timestamp.
+     * The directories up to <code>destination</code> will be created if they don't already exist.
+     * <code>destination</code> will be overwritten if it already exists.
+     *
+     * @param source An existing non-directory <code>File</code> to copy bytes from.
+     * @param destination A non-directory <code>File</code> to write bytes to (possibly
+     * overwriting).
+     *
+     * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be
+     * written to, or an IO error occurs during copying.
+     *
+     * @throws java.io.FileNotFoundException if <code>destination</code> is a directory
+     * (use {@link #copyFileToDirectory}).
+     */
+    public static void copyFileIfModified( final File source, final File destination )
+        throws IOException
+    {
+        if ( destination.lastModified() < source.lastModified() )
+        {
+            copyFile( source, destination );
+        }
+    }
+    /**
+     * Copies bytes from the URL <code>source</code> to a file <code>destination</code>.
+     * The directories up to <code>destination</code> will be created if they don't already exist.
+     * <code>destination</code> will be overwritten if it already exists.
+     *
+     * @param source A <code>URL</code> to copy bytes from.
+     * @param destination A non-directory <code>File</code> to write bytes to (possibly
+     * overwriting).
+     *
+     * @throws IOException if
+     * <ul>
+     *  <li><code>source</code> URL cannot be opened</li>
+     *  <li><code>destination</code> cannot be written to</li>
+     *  <li>an IO error occurs during copying</li>
+     * </ul>
+     */
+    public static void copyURLToFile( final URL source, final File destination )
+        throws IOException
+    {
+        //does destination directory exist ?
+        if ( destination.getParentFile() != null &&
+            !destination.getParentFile().exists() )
+        {
+            destination.getParentFile().mkdirs();
+        }
+
+        //make sure we can write to destination
+        if ( destination.exists() && !destination.canWrite() )
+        {
+            final String message = "Unable to open file " +
+                destination + " for writing.";
+            throw new IOException( message );
+        }
+
+        InputStream input = null;
+        FileOutputStream output = null;
+        try
+        {
+            input = source.openStream();
+            output = new FileOutputStream( destination );
+            IOUtil.copy( input, output );
+        }
+        finally
+        {
+            IOUtil.close( input );
+            IOUtil.close( output );
+        }
+    }
+
+    /**
+     * Normalize a path.
+     * Eliminates "/../" and "/./" in a string. Returns <code>null</code> if the ..'s went past the
+     * root.
+     * Eg:
+     * <pre>
+     * /foo//               -->     /foo/
+     * /foo/./              -->     /foo/
+     * /foo/../bar          -->     /bar
+     * /foo/../bar/         -->     /bar/
+     * /foo/../bar/../baz   -->     /baz
+     * //foo//./bar         -->     /foo/bar
+     * /../                 -->     null
+     * </pre>
+     *
+     * @param path the path to normalize
+     * @return the normalized String, or <code>null</code> if too many ..'s.
+     */
+    public static String normalize( final String path )
+    {
+        String normalized = path;
+        // Resolve occurrences of "//" in the normalized path
+        while ( true )
+        {
+            int index = normalized.indexOf( "//" );
+            if ( index < 0 )
+                break;
+            normalized = normalized.substring( 0, index ) +
+                normalized.substring( index + 1 );
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while ( true )
+        {
+            int index = normalized.indexOf( "/./" );
+            if ( index < 0 )
+                break;
+            normalized = normalized.substring( 0, index ) +
+                normalized.substring( index + 2 );
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while ( true )
+        {
+            int index = normalized.indexOf( "/../" );
+            if ( index < 0 )
+                break;
+            if ( index == 0 )
+                return null;  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf( '/', index - 1 );
+            normalized = normalized.substring( 0, index2 ) +
+                normalized.substring( index + 3 );
+        }
+
+        // Return the normalized path that we have completed
+        return normalized;
+    }
+
+    /**
+     * Will concatenate 2 paths.  Paths with <code>..</code> will be
+     * properly handled.
+     * <p>Eg.,<br />
+     * <code>/a/b/c</code> + <code>d</code> = <code>/a/b/d</code><br />
+     * <code>/a/b/c</code> + <code>../d</code> = <code>/a/d</code><br />
+     * </p>
+     *
+     * Thieved from Tomcat sources...
+     *
+     * @return The concatenated paths, or null if error occurs
+     */
+    public static String catPath( final String lookupPath, final String path )
+    {
+        // Cut off the last slash and everything beyond
+        int index = lookupPath.lastIndexOf( "/" );
+        String lookup = lookupPath.substring( 0, index );
+        String pth = path;
+
+        // Deal with .. by chopping dirs off the lookup path
+        while ( pth.startsWith( "../" ) )
+        {
+            if ( lookup.length() > 0 )
+            {
+                index = lookup.lastIndexOf( "/" );
+                lookup = lookup.substring( 0, index );
+            }
+            else
+            {
+                // More ..'s than dirs, return null
+                return null;
+            }
+
+            index = pth.indexOf( "../" ) + 3;
+            pth = pth.substring( index );
+        }
+
+        return new StringBuffer( lookup ).append( "/" ).append( pth ).toString();
+    }
+
+    /**
+     * Resolve a file <code>filename</code> to it's canonical form. If <code>filename</code> is
+     * relative (doesn't start with <code>/</code>), it will be resolved relative to
+     * <code>baseFile</code>, otherwise it is treated as a normal root-relative path.
+     *
+     * @param baseFile Where to resolve <code>filename</code> from, if <code>filename</code> is
+     * relative.
+     * @param filename Absolute or relative file path to resolve.
+     * @return The canonical <code>File</code> of <code>filename</code>.
+     */
+    public static File resolveFile( final File baseFile, String filename )
+    {
+        String filenm = filename;
+        if ( '/' != File.separatorChar )
+        {
+            filenm = filename.replace( '/', File.separatorChar );
+        }
+
+        if ( '\\' != File.separatorChar )
+        {
+            filenm = filename.replace( '\\', File.separatorChar );
+        }
+
+        // deal with absolute files
+        if ( filenm.startsWith( File.separator ) )
+        {
+            File file = new File( filenm );
+
+            try
+            {
+                file = file.getCanonicalFile();
+            }
+            catch ( final IOException ioe )
+            {
+            }
+
+            return file;
+        }
+        // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips
+        // them. However, I'm not sure about this UNC stuff. (JT)
+        final char[] chars = filename.toCharArray();
+        final StringBuffer sb = new StringBuffer();
+
+        //remove duplicate file separators in succession - except
+        //on win32 at start of filename as UNC filenames can
+        //be \\AComputer\AShare\myfile.txt
+        int start = 0;
+        if ( '\\' == File.separatorChar )
+        {
+            sb.append( filenm.charAt( 0 ) );
+            start++;
+        }
+
+        for ( int i = start; i < chars.length; i++ )
+        {
+            final boolean doubleSeparator =
+                File.separatorChar == chars[i] && File.separatorChar == chars[i - 1];
+
+            if ( !doubleSeparator )
+            {
+                sb.append( chars[i] );
+            }
+        }
+
+        filenm = sb.toString();
+
+        //must be relative
+        File file = ( new File( baseFile, filenm ) ).getAbsoluteFile();
+
+        try
+        {
+            file = file.getCanonicalFile();
+        }
+        catch ( final IOException ioe )
+        {
+        }
+
+        return file;
+    }
 
-public class FileUtils
-{
     /**
      * Delete a file. If file is directory delete it and all sub-directories.
      */
@@ -358,4 +1439,367 @@
 
         return size;
     }
-}
\ No newline at end of file
+
+    public static List getFiles( File directory, String includes, String excludes )
+        throws IOException
+    {
+        return getFiles( directory, includes, excludes, true );
+    }
+
+    public static List getFiles( File directory, String includes, String excludes, boolean includeBasedir )
+        throws IOException
+    {
+        List fileNames = getFileNames( directory, includes, excludes, includeBasedir );
+
+        List files = new ArrayList();
+
+        for ( Iterator i = fileNames.iterator(); i.hasNext(); )
+        {
+            files.add( new File( (String) i.next() ) );
+        }
+
+        return files;
+    }
+
+    public static String FS = System.getProperty( "file.separator" );
+
+    /**
+     * Return a list of files as String depending options.
+     * This method use case sensitive file name.
+     * 
+     * @param directory the directory to scan
+     * @param includes the includes pattern, comma separated
+     * @param excludes the excludes pattern, comma separated
+     * @param includeBasedir true to include the base dir in each String of file
+     * @return a list of files as String
+     * @throws IOException
+     */
+    public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir )
+        throws IOException
+    {
+        return getFileNames( directory, includes, excludes, includeBasedir, true );
+    }
+
+    /**
+     * Return a list of files as String depending options.
+     * 
+     * @param directory the directory to scan
+     * @param includes the includes pattern, comma separated
+     * @param excludes the excludes pattern, comma separated
+     * @param includeBasedir true to include the base dir in each String of file
+     * @param isCaseSensitive true if case sensitive
+     * @return a list of files as String
+     * @throws IOException
+     */
+    public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir,
+                                     boolean isCaseSensitive )
+        throws IOException
+    {
+        DirectoryScanner scanner = new DirectoryScanner();
+
+        scanner.setBasedir( directory );
+
+        if ( includes != null )
+        {
+            scanner.setIncludes( StringUtils.split( includes, "," ) );
+        }
+
+        if ( excludes != null )
+        {
+            scanner.setExcludes( StringUtils.split( excludes, "," ) );
+        }
+
+        scanner.setCaseSensitive( isCaseSensitive );
+
+        scanner.scan();
+
+        String[] files = scanner.getIncludedFiles();
+
+        List list = new ArrayList();
+
+        for ( int i = 0; i < files.length; i++ )
+        {
+            if ( includeBasedir )
+            {
+                list.add( directory + FileUtils.FS + files[i] );
+            }
+            else
+            {
+                list.add( files[i] );
+            }
+        }
+
+        return list;
+    }
+
+   public static void copyDirectory( File sourceDirectory, File destinationDirectory )
+        throws IOException
+    {
+        copyDirectory( sourceDirectory, destinationDirectory, "**", null );
+    }
+
+   public static void copyDirectory( File sourceDirectory, File destinationDirectory, String includes, String excludes )
+        throws IOException
+    {
+        if ( ! sourceDirectory.exists() )
+        {
+            return;
+        }
+
+        List files = getFiles( sourceDirectory, includes, excludes );
+
+        for ( Iterator i = files.iterator(); i.hasNext(); )
+        {
+            File file = (File) i.next();
+
+            copyFileToDirectory( file, destinationDirectory );
+        }
+    }
+
+    /**
+     * Copies a entire directory structure.
+     * 
+     * Note:
+     * <ul>
+     * <li>It will include empty directories.
+     * <li>The <code>sourceDirectory</code> must exists.
+     * </ul>
+     * 
+     * @param sourceDirectory
+     * @param destinationDirectory
+     * @throws IOException
+     */
+    public static void copyDirectoryStructure( File sourceDirectory, File destinationDirectory )
+        throws IOException
+    {
+       if ( !sourceDirectory.exists() )
+       {
+           throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
+       }
+
+       File[] files = sourceDirectory.listFiles();
+
+       String sourcePath = sourceDirectory.getAbsolutePath();
+
+       for ( int i = 0; i < files.length; i++ )
+       {
+           File file = files[i];
+
+           String dest = file.getAbsolutePath();
+
+           dest = dest.substring( sourcePath.length() + 1 );
+
+           File destination = new File( destinationDirectory, dest );
+
+           if ( file.isFile() )
+           {
+               destination = destination.getParentFile();
+
+               FileUtils.copyFileToDirectory( file, destination );
+           }
+           else if ( file.isDirectory() )
+           {
+               if ( !destination.exists() && !destination.mkdirs() )
+               {
+                   throw new IOException( "Could not create destination directory '" + destination.getAbsolutePath() + "'." );
+               }
+
+               copyDirectoryStructure( file, destination );
+           }
+           else
+           {
+               throw new IOException( "Unknown file type: " + file.getAbsolutePath() );
+           }
+       }
+    }
+
+    /**
+     * Copies an entire directory structure but only source files with timestamp later than the destinations'.
+     *
+     * Note:
+     * <ul>
+     * <li>It will include empty directories.
+     * <li>The <code>sourceDirectory</code> must exists.
+     * </ul>
+     *
+     * @param sourceDirectory
+     * @param destinationDirectory
+     * @throws IOException
+     */
+    public static void copyDirectoryStructureIfModified( File sourceDirectory, File destinationDirectory )
+        throws IOException
+    {
+       if ( !sourceDirectory.exists() )
+       {
+           throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
+       }
+
+       File[] files = sourceDirectory.listFiles();
+
+       String sourcePath = sourceDirectory.getAbsolutePath();
+
+       for ( int i = 0; i < files.length; i++ )
+       {
+           File file = files[i];
+
+           String dest = file.getAbsolutePath();
+
+           dest = dest.substring( sourcePath.length() + 1 );
+
+           File destination = new File( destinationDirectory, dest );
+
+           if ( file.isFile() )
+           {
+               destination = destination.getParentFile();
+
+               copyFileToDirectoryIfModified( file, destination );
+           }
+           else if ( file.isDirectory() )
+           {
+               if ( !destination.exists() && !destination.mkdirs() )
+               {
+                   throw new IOException( "Could not create destination directory '" + destination.getAbsolutePath() + "'." );
+               }
+
+               copyDirectoryStructureIfModified( file, destination );
+           }
+           else
+           {
+               throw new IOException( "Unknown file type: " + file.getAbsolutePath() );
+           }
+       }
+    }
+
+    /**
+     * Renames a file, even if that involves crossing file system boundaries.
+     *
+     * <p>This will remove <code>to</code> (if it exists), ensure that
+     * <code>to</code>'s parent directory exists and move
+     * <code>from</code>, which involves deleting <code>from</code> as
+     * well.</p>
+     *
+     * @throws IOException if anything bad happens during this
+     * process.  Note that <code>to</code> may have been deleted
+     * already when this happens.
+     *
+     * @param from the file to move
+     * @param to the new file name
+     */
+    public static void rename( File from, File to ) throws IOException
+    {
+        if ( to.exists() && !to.delete() )
+        {
+            throw new IOException( "Failed to delete " + to
+                                  + " while trying to rename " + from );
+        }
+
+        File parent = to.getParentFile();
+        if (parent != null && !parent.exists() && !parent.mkdirs())
+        {
+            throw new IOException( "Failed to create directory " + parent
+                                  + " while trying to rename " + from );
+        }
+
+        if (!from.renameTo(to))
+        {
+            copyFile(from, to);
+            if (!from.delete())
+            {
+                throw new IOException( "Failed to delete " + from
+                                      + " while trying to rename it." );
+            }
+        }
+    }
+
+    /**
+     * Create a temporary file in a given directory.
+     *
+     * <p>The file denoted by the returned abstract pathname did not
+     * exist before this method was invoked, any subsequent invocation
+     * of this method will yield a different file name.</p>
+     * <p>
+     * The filename is prefixNNNNNsuffix where NNNN is a random number
+     * </p>
+     * <p>This method is different to File.createTempFile of JDK 1.2
+     * as it doesn't create the file itself.
+     * It uses the location pointed to by java.io.tmpdir
+     * when the parentDir attribute is
+     * null.</p>
+     *
+     * @param prefix prefix before the random number
+     * @param suffix file extension; include the '.'
+     * @param parentDir Directory to create the temporary file in -
+     * java.io.tmpdir used if not specificed
+     *
+     * @return a File reference to the new temporary file.
+     */
+    public static File createTempFile(String prefix, String suffix, File parentDir) {
+
+        File result = null;
+        String parent = System.getProperty("java.io.tmpdir");
+        if (parentDir != null) {
+            parent = parentDir.getPath();
+        }
+        DecimalFormat fmt = new DecimalFormat("#####");
+        Random rand = new Random(System.currentTimeMillis()
+            +Runtime.getRuntime().freeMemory());
+        synchronized (rand) {
+            do {
+                result = new File(parent,
+                                  prefix + fmt.format(Math.abs(rand.nextInt()))
+                                  + suffix);
+            } while (result.exists());
+        }
+        return result;
+    }
+
+    public static void copyFile(File from, File to, String encoding, FilterWrapper[] wrappers)
+        throws IOException
+    {
+        if (wrappers != null && wrappers.length > 0) {
+            // buffer so it isn't reading a byte at a time!
+            Reader fileReader = null;
+            Writer fileWriter = null;
+            try
+            {
+                if ( encoding == null || encoding.length() < 1 )
+                {
+                    fileReader = new BufferedReader( new FileReader( from ) );
+                    fileWriter = new FileWriter( to );
+                }
+                else
+                {
+                    FileInputStream instream = new FileInputStream( from );
+
+                    FileOutputStream outstream = new FileOutputStream( to );
+
+                    fileReader = new BufferedReader( new InputStreamReader( instream, encoding ) );
+
+                    fileWriter = new OutputStreamWriter( outstream, encoding );
+                }
+
+                Reader reader = fileReader;
+                for (int i = 0; i < wrappers.length; i++) {
+                    FilterWrapper wrapper = wrappers[i];
+                    reader = wrapper.getReader(reader);
+                }
+
+                IOUtil.copy( reader, fileWriter );
+            }
+            finally
+            {
+                IOUtil.close( fileReader );
+                IOUtil.close( fileWriter );
+            }
+        } else {
+            if ( to.lastModified() < from.lastModified() )
+            {
+                copyFile( from, to );
+            }
+        }
+    }
+
+    public static abstract class FilterWrapper {
+        public abstract Reader getReader(Reader fileReader);
+    }
+}

Added: maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/IOUtil.java
URL: http://svn.apache.org/viewcvs/maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/IOUtil.java?rev=394946&view=auto
==============================================================================
--- maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/IOUtil.java (added)
+++ maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/IOUtil.java Tue Apr 18 06:59:22 2006
@@ -0,0 +1,813 @@
+package org.codehaus.plexus.util;
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 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.codehaus.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache Turbine" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact codehaus@codehaus.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache Turbine", 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.codehaus.org/>.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * General IO Stream manipulation.
+ * <p>
+ * This class provides static utility methods for input/output operations, particularly buffered
+ * copying between sources (<code>InputStream</code>, <code>Reader</code>, <code>String</code> and
+ * <code>byte[]</code>) and destinations (<code>OutputStream</code>, <code>Writer</code>,
+ * <code>String</code> and <code>byte[]</code>).
+ * </p>
+ *
+ * <p>Unless otherwise noted, these <code>copy</code> methods do <em>not</em> flush or close the
+ * streams. Often, doing so would require making non-portable assumptions about the streams' origin
+ * and further use. This means that both streams' <code>close()</code> methods must be called after
+ * copying. if one omits this step, then the stream resources (sockets, file descriptors) are
+ * released when the associated Stream is garbage-collected. It is not a good idea to rely on this
+ * mechanism. For a good overview of the distinction between "memory management" and "resource
+ * management", see <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this
+ * UnixReview article</a></p>
+ *
+ * <p>For each <code>copy</code> method, a variant is provided that allows the caller to specify the
+ * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this
+ * may be worth tweaking. Often "large buffer -&gt; faster" does not hold, even for large data
+ * transfers.</p>
+ *
+ * <p>For byte-to-char methods, a <code>copy</code> variant allows the encoding to be selected
+ * (otherwise the platform default is used).</p>
+ *
+ * <p>The <code>copy</code> methods use an internal buffer when copying. It is therefore advisable
+ * <em>not</em> to deliberately wrap the stream arguments to the <code>copy</code> methods in
+ * <code>Buffered*</code> streams. For example, don't do the
+ * following:</p>
+ *
+ * <code>copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );</code>
+ *
+ * <p>The rationale is as follows:</p>
+ *
+ * <p>Imagine that an InputStream's read() is a very expensive operation, which would usually suggest
+ * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent
+ * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to
+ * fill an internal buffer, from which further <code>read</code> requests can inexpensively get
+ * their data (until the buffer runs out).</p>
+ * <p>However, the <code>copy</code> methods do the same thing, keeping an internal buffer,
+ * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers
+ * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer
+ * management hurts performance slightly (about 3%, according to some simple experiments).</p>
+ *
+ * @author <a href="mailto:peter@codehaus.org">Peter Donald</a>
+ * @author <a href="mailto:jefft@codehaus.org">Jeff Turner</a>
+ * @version CVS $Revision: 1106 $ $Date$
+ * @since 4.0
+ */
+
+/*
+ * Behold, intrepid explorers; a map of this class:
+ *
+ *       Method      Input               Output          Dependency
+ *       ------      -----               ------          -------
+ * 1     copy        InputStream         OutputStream    (primitive)
+ * 2     copy        Reader              Writer          (primitive)
+ *
+ * 3     copy        InputStream         Writer          2
+ * 4     toString    InputStream         String          3
+ * 5     toByteArray InputStream         byte[]          1
+ *
+ * 6     copy        Reader              OutputStream    2
+ * 7     toString    Reader              String          2
+ * 8     toByteArray Reader              byte[]          6
+ *
+ * 9     copy        String              OutputStream    2
+ * 10    copy        String              Writer          (trivial)
+ * 11    toByteArray String              byte[]          9
+ *
+ * 12    copy        byte[]              Writer          3
+ * 13    toString    byte[]              String          12
+ * 14    copy        byte[]              OutputStream    (trivial)
+ *
+ *
+ * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy
+ * using native Java copy methods. As there are method variants to specify buffer size and encoding,
+ * each row may correspond to up to 4 methods.
+ *
+ */
+
+public final class IOUtil
+{
+    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+    /**
+     * Private constructor to prevent instantiation.
+     */
+    private IOUtil()
+    {
+    }
+
+    ///////////////////////////////////////////////////////////////
+    // Core copy methods
+    ///////////////////////////////////////////////////////////////
+
+    /**
+     * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
+     */
+    public static void copy( final InputStream input, final OutputStream output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final InputStream input,
+                             final OutputStream output,
+                             final int bufferSize )
+        throws IOException
+    {
+        final byte[] buffer = new byte[bufferSize];
+        int n = 0;
+        while ( -1 != ( n = input.read( buffer ) ) )
+        {
+            output.write( buffer, 0, n );
+        }
+    }
+
+    /**
+     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
+     */
+    public static void copy( final Reader input, final Writer output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final Reader input, final Writer output, final int bufferSize )
+        throws IOException
+    {
+        final char[] buffer = new char[bufferSize];
+        int n = 0;
+        while ( -1 != ( n = input.read( buffer ) ) )
+        {
+            output.write( buffer, 0, n );
+        }
+        output.flush();
+    }
+
+    ///////////////////////////////////////////////////////////////
+    // Derived copy methods
+    // InputStream -> *
+    ///////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////
+    // InputStream -> Writer
+
+    /**
+     * Copy and convert bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code>.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     */
+    public static void copy( final InputStream input, final Writer output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Copy and convert bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code>.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final InputStream input, final Writer output, final int bufferSize )
+        throws IOException
+    {
+        final InputStreamReader in = new InputStreamReader( input );
+        copy( in, output, bufferSize );
+    }
+
+    /**
+     * Copy and convert bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code>, using the specified encoding.
+     * @param encoding The name of a supported character encoding. See the
+     * <a href="http://www.iana.org/assignments/character-sets">IANA
+     * Charset Registry</a> for a list of valid encoding types.
+     */
+    public static void copy( final InputStream input, final Writer output, final String encoding )
+        throws IOException
+    {
+        final InputStreamReader in = new InputStreamReader( input, encoding );
+        copy( in, output );
+    }
+
+    /**
+     * Copy and convert bytes from an <code>InputStream</code> to chars on a
+     * <code>Writer</code>, using the specified encoding.
+     * @param encoding The name of a supported character encoding. See the
+     *        <a href="http://www.iana.org/assignments/character-sets">IANA
+     *        Charset Registry</a> for a list of valid encoding types.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final InputStream input,
+                             final Writer output,
+                             final String encoding,
+                             final int bufferSize )
+        throws IOException
+    {
+        final InputStreamReader in = new InputStreamReader( input, encoding );
+        copy( in, output, bufferSize );
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // InputStream -> String
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a String.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     */
+    public static String toString( final InputStream input )
+        throws IOException
+    {
+        return toString( input, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a String.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static String toString( final InputStream input, final int bufferSize )
+        throws IOException
+    {
+        final StringWriter sw = new StringWriter();
+        copy( input, sw, bufferSize );
+        return sw.toString();
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a String.
+     * @param encoding The name of a supported character encoding. See the
+     *    <a href="http://www.iana.org/assignments/character-sets">IANA
+     *    Charset Registry</a> for a list of valid encoding types.
+     */
+    public static String toString( final InputStream input, final String encoding )
+        throws IOException
+    {
+        return toString( input, encoding, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a String.
+     * @param encoding The name of a supported character encoding. See the
+     *   <a href="http://www.iana.org/assignments/character-sets">IANA
+     *   Charset Registry</a> for a list of valid encoding types.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static String toString( final InputStream input,
+                                   final String encoding,
+                                   final int bufferSize )
+        throws IOException
+    {
+        final StringWriter sw = new StringWriter();
+        copy( input, sw, encoding, bufferSize );
+        return sw.toString();
+    }
+
+    ///////////////////////////////////////////////////////////////
+    // InputStream -> byte[]
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
+     */
+    public static byte[] toByteArray( final InputStream input )
+        throws IOException
+    {
+        return toByteArray( input, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static byte[] toByteArray( final InputStream input, final int bufferSize )
+        throws IOException
+    {
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy( input, output, bufferSize );
+        return output.toByteArray();
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // Derived copy methods
+    // Reader -> *
+    ///////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////
+    // Reader -> OutputStream
+    /**
+     * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
+     * flush the <code>OutputStream</code>.
+     */
+    public static void copy( final Reader input, final OutputStream output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and
+     * flush the <code>OutputStream</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final Reader input, final OutputStream output, final int bufferSize )
+        throws IOException
+    {
+        final OutputStreamWriter out = new OutputStreamWriter( output );
+        copy( input, out, bufferSize );
+        // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
+        // here.
+        out.flush();
+    }
+
+    ///////////////////////////////////////////////////////////////
+    // Reader -> String
+    /**
+     * Get the contents of a <code>Reader</code> as a String.
+     */
+    public static String toString( final Reader input )
+        throws IOException
+    {
+        return toString( input, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a String.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static String toString( final Reader input, final int bufferSize )
+        throws IOException
+    {
+        final StringWriter sw = new StringWriter();
+        copy( input, sw, bufferSize );
+        return sw.toString();
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // Reader -> byte[]
+    /**
+     * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
+     */
+    public static byte[] toByteArray( final Reader input )
+        throws IOException
+    {
+        return toByteArray( input, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of a <code>Reader</code> as a <code>byte[]</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static byte[] toByteArray( final Reader input, final int bufferSize )
+        throws IOException
+    {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy( input, output, bufferSize );
+        return output.toByteArray();
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // Derived copy methods
+    // String -> *
+    ///////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////
+    // String -> OutputStream
+
+    /**
+     * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
+     * flush the <code>OutputStream</code>.
+     */
+    public static void copy( final String input, final OutputStream output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and
+     * flush the <code>OutputStream</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final String input, final OutputStream output, final int bufferSize )
+        throws IOException
+    {
+        final StringReader in = new StringReader( input );
+        final OutputStreamWriter out = new OutputStreamWriter( output );
+        copy( in, out, bufferSize );
+        // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush
+        // here.
+        out.flush();
+    }
+
+
+
+    ///////////////////////////////////////////////////////////////
+    // String -> Writer
+
+    /**
+     * Copy chars from a <code>String</code> to a <code>Writer</code>.
+     */
+    public static void copy( final String input, final Writer output )
+        throws IOException
+    {
+        output.write( input );
+    }
+
+    /**
+     * Copy bytes from an <code>InputStream</code> to an
+     * <code>OutputStream</code>, with buffering.
+     * This is equivalent to passing a
+     * {@link java.io.BufferedInputStream} and
+     * {@link java.io.BufferedOutputStream} to {@link #copy(InputStream, OutputStream)},
+     * and flushing the output stream afterwards. The streams are not closed
+     * after the copy.
+     * @deprecated Buffering streams is actively harmful! See the class description as to why. Use
+     * {@link #copy(InputStream, OutputStream)} instead.
+     */
+    public static void bufferedCopy( final InputStream input, final OutputStream output )
+        throws IOException
+    {
+        final BufferedInputStream in = new BufferedInputStream( input );
+        final BufferedOutputStream out = new BufferedOutputStream( output );
+        copy( in, out );
+        out.flush();
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // String -> byte[]
+    /**
+     * Get the contents of a <code>String</code> as a <code>byte[]</code>.
+     */
+    public static byte[] toByteArray( final String input )
+        throws IOException
+    {
+        return toByteArray( input, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of a <code>String</code> as a <code>byte[]</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static byte[] toByteArray( final String input, final int bufferSize )
+        throws IOException
+    {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        copy( input, output, bufferSize );
+        return output.toByteArray();
+    }
+
+
+
+    ///////////////////////////////////////////////////////////////
+    // Derived copy methods
+    // byte[] -> *
+    ///////////////////////////////////////////////////////////////
+
+
+    ///////////////////////////////////////////////////////////////
+    // byte[] -> Writer
+
+    /**
+     * Copy and convert bytes from a <code>byte[]</code> to chars on a
+     * <code>Writer</code>.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     */
+    public static void copy( final byte[] input, final Writer output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Copy and convert bytes from a <code>byte[]</code> to chars on a
+     * <code>Writer</code>.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final byte[] input, final Writer output, final int bufferSize )
+        throws IOException
+    {
+        final ByteArrayInputStream in = new ByteArrayInputStream( input );
+        copy( in, output, bufferSize );
+    }
+
+    /**
+     * Copy and convert bytes from a <code>byte[]</code> to chars on a
+     * <code>Writer</code>, using the specified encoding.
+     * @param encoding The name of a supported character encoding. See the
+     * <a href="http://www.iana.org/assignments/character-sets">IANA
+     * Charset Registry</a> for a list of valid encoding types.
+     */
+    public static void copy( final byte[] input, final Writer output, final String encoding )
+        throws IOException
+    {
+        final ByteArrayInputStream in = new ByteArrayInputStream( input );
+        copy( in, output, encoding );
+    }
+
+    /**
+     * Copy and convert bytes from a <code>byte[]</code> to chars on a
+     * <code>Writer</code>, using the specified encoding.
+     * @param encoding The name of a supported character encoding. See the
+     *        <a href="http://www.iana.org/assignments/character-sets">IANA
+     *        Charset Registry</a> for a list of valid encoding types.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final byte[] input,
+                             final Writer output,
+                             final String encoding,
+                             final int bufferSize )
+        throws IOException
+    {
+        final ByteArrayInputStream in = new ByteArrayInputStream( input );
+        copy( in, output, encoding, bufferSize );
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // byte[] -> String
+
+    /**
+     * Get the contents of a <code>byte[]</code> as a String.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     */
+    public static String toString( final byte[] input )
+        throws IOException
+    {
+        return toString( input, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of a <code>byte[]</code> as a String.
+     * The platform's default encoding is used for the byte-to-char conversion.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static String toString( final byte[] input, final int bufferSize )
+        throws IOException
+    {
+        final StringWriter sw = new StringWriter();
+        copy( input, sw, bufferSize );
+        return sw.toString();
+    }
+
+    /**
+     * Get the contents of a <code>byte[]</code> as a String.
+     * @param encoding The name of a supported character encoding. See the
+     *    <a href="http://www.iana.org/assignments/character-sets">IANA
+     *    Charset Registry</a> for a list of valid encoding types.
+     */
+    public static String toString( final byte[] input, final String encoding )
+        throws IOException
+    {
+        return toString( input, encoding, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Get the contents of a <code>byte[]</code> as a String.
+     * @param encoding The name of a supported character encoding. See the
+     *   <a href="http://www.iana.org/assignments/character-sets">IANA
+     *   Charset Registry</a> for a list of valid encoding types.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static String toString( final byte[] input,
+                                   final String encoding,
+                                   final int bufferSize )
+        throws IOException
+    {
+        final StringWriter sw = new StringWriter();
+        copy( input, sw, encoding, bufferSize );
+        return sw.toString();
+    }
+
+
+    ///////////////////////////////////////////////////////////////
+    // byte[] -> OutputStream
+
+    /**
+     * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
+     */
+    public static void copy( final byte[] input, final OutputStream output )
+        throws IOException
+    {
+        copy( input, output, DEFAULT_BUFFER_SIZE );
+    }
+
+    /**
+     * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
+     * @param bufferSize Size of internal buffer to use.
+     */
+    public static void copy( final byte[] input,
+                             final OutputStream output,
+                             final int bufferSize )
+        throws IOException
+    {
+        output.write( input );
+    }
+
+    /**
+     * Compare the contents of two Streams to determine if they are equal or not.
+     *
+     * @param input1 the first stream
+     * @param input2 the second stream
+     * @return true if the content of the streams are equal or they both don't exist, false otherwise
+     */
+    public static boolean contentEquals( final InputStream input1,
+                                         final InputStream input2 )
+        throws IOException
+    {
+        final InputStream bufferedInput1 = new BufferedInputStream( input1 );
+        final InputStream bufferedInput2 = new BufferedInputStream( input2 );
+
+        int ch = bufferedInput1.read();
+        while ( -1 != ch )
+        {
+            final int ch2 = bufferedInput2.read();
+            if ( ch != ch2 )
+            {
+                return false;
+            }
+            ch = bufferedInput1.read();
+        }
+
+        final int ch2 = bufferedInput2.read();
+        if ( -1 != ch2 )
+        {
+            return false;
+        }
+        else
+        {
+            return true;
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // closeXXX()
+    // ----------------------------------------------------------------------
+
+    /**
+     * Closes the input stream. The input stream can be null and any IOException's will be swallowed.
+     * 
+     * @param inputStream The stream to close.
+     */
+    public static void close( InputStream inputStream )
+    {
+        if ( inputStream == null )
+        {
+            return;
+        }
+
+        try
+        {
+            inputStream.close();
+        }
+        catch( IOException ex )
+        {
+            // ignore
+        }
+    }
+
+    /**
+     * Closes the output stream. The output stream can be null and any IOException's will be swallowed.
+     * 
+     * @param outputStream The stream to close.
+     */
+    public static void close( OutputStream outputStream )
+    {
+        if ( outputStream == null )
+        {
+            return;
+        }
+
+        try
+        {
+            outputStream.close();
+        }
+        catch( IOException ex )
+        {
+            // ignore
+        }
+    }
+
+    /**
+     * Closes the reader. The reader can be null and any IOException's will be swallowed.
+     * 
+     * @param reader The reader to close.
+     */
+    public static void close( Reader reader )
+    {
+        if ( reader == null )
+        {
+            return;
+        }
+
+        try
+        {
+            reader.close();
+        }
+        catch( IOException ex )
+        {
+            // ignore
+        }
+    }
+
+    /**
+     * Closes the writer. The writer can be null and any IOException's will be swallowed.
+     * 
+     * @param wrtier The writer to close.
+     */
+    public static void close( Writer writer )
+    {
+        if ( writer == null )
+        {
+            return;
+        }
+
+        try
+        {
+            writer.close();
+        }
+        catch( IOException ex )
+        {
+            // ignore
+        }
+    }
+}

Propchange: maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/IOUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/components/trunk/maven-core-it-verifier/src/main/java/org/codehaus/plexus/util/IOUtil.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"