Coverage Report - org.codehaus.plexus.util.FileUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
FileUtils
64%
396/618
51%
171/330
3.713
FileUtils$FilterWrapper
100%
1/1
N/A
3.713
 
 1  
 package org.codehaus.plexus.util;
 2  
 
 3  
 /* ====================================================================
 4  
  * The Apache Software License, Version 1.1
 5  
  *
 6  
  * Copyright (c) 2001 The Apache Software Foundation.  All rights
 7  
  * reserved.
 8  
  *
 9  
  * Redistribution and use in source and binary forms, with or without
 10  
  * modification, are permitted provided that the following conditions
 11  
  * are met:
 12  
  *
 13  
  * 1. Redistributions of source code must retain the above copyright
 14  
  *    notice, this list of conditions and the following disclaimer.
 15  
  *
 16  
  * 2. Redistributions in binary form must reproduce the above copyright
 17  
  *    notice, this list of conditions and the following disclaimer in
 18  
  *    the documentation and/or other materials provided with the
 19  
  *    distribution.
 20  
  *
 21  
  * 3. The end-user documentation included with the redistribution,
 22  
  *    if any, must include the following acknowledgment:
 23  
  *       "This product includes software developed by the
 24  
  *        Apache Software Foundation (http://www.codehaus.org/)."
 25  
  *    Alternately, this acknowledgment may appear in the software itself,
 26  
  *    if and wherever such third-party acknowledgments normally appear.
 27  
  *
 28  
  * 4. The names "Apache" and "Apache Software Foundation" and
 29  
  *    "Apache Turbine" must not be used to endorse or promote products
 30  
  *    derived from this software without prior written permission. For
 31  
  *    written permission, please contact codehaus@codehaus.org.
 32  
  *
 33  
  * 5. Products derived from this software may not be called "Apache",
 34  
  *    "Apache Turbine", nor may "Apache" appear in their name, without
 35  
  *    prior written permission of the Apache Software Foundation.
 36  
  *
 37  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 38  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 39  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 40  
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 41  
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 42  
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 43  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 44  
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 45  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 46  
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 47  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 48  
  * SUCH DAMAGE.
 49  
  * ====================================================================
 50  
  *
 51  
  * This software consists of voluntary contributions made by many
 52  
  * individuals on behalf of the Apache Software Foundation.  For more
 53  
  * information on the Apache Software Foundation, please see
 54  
  * <http://www.codehaus.org/>.
 55  
  *
 56  
  */
 57  
 
 58  
 import org.codehaus.plexus.util.io.InputStreamFacade;
 59  
 import org.codehaus.plexus.util.io.URLInputStreamFacade;
 60  
 
 61  
 import java.io.BufferedReader;
 62  
 import java.io.File;
 63  
 import java.io.FileInputStream;
 64  
 import java.io.FileOutputStream;
 65  
 import java.io.FileReader;
 66  
 import java.io.FileWriter;
 67  
 import java.io.IOException;
 68  
 import java.io.InputStream;
 69  
 import java.io.InputStreamReader;
 70  
 import java.io.OutputStream;
 71  
 import java.io.OutputStreamWriter;
 72  
 import java.io.Reader;
 73  
 import java.io.Writer;
 74  
 import java.net.URL;
 75  
 import java.nio.channels.FileChannel;
 76  
 import java.security.SecureRandom;
 77  
 import java.text.DecimalFormat;
 78  
 import java.util.ArrayList;
 79  
 import java.util.Arrays;
 80  
 import java.util.List;
 81  
 import java.util.Random;
 82  
 
 83  
 /**
 84  
  * This class provides basic facilities for manipulating files and file paths.
 85  
  * <p/>
 86  
  * <h3>Path-related methods</h3>
 87  
  * <p/>
 88  
  * <p>
 89  
  * Methods exist to retrieve the components of a typical file path. For example
 90  
  * <code>/www/hosted/mysite/index.html</code>, can be broken into:
 91  
  * <ul>
 92  
  * <li><code>/www/hosted/mysite/</code> -- retrievable through {@link #getPath}</li>
 93  
  * <li><code>index.html</code> -- retrievable through {@link #removePath}</li>
 94  
  * <li><code>/www/hosted/mysite/index</code> -- retrievable through {@link #removeExtension}</li>
 95  
  * <li><code>html</code> -- retrievable through {@link #getExtension}</li>
 96  
  * </ul>
 97  
  * There are also methods to {@link #catPath concatenate two paths}, {@link #resolveFile resolve a path relative to a
 98  
  * File} and {@link #normalize} a path.
 99  
  * </p>
 100  
  * <p/>
 101  
  * <h3>File-related methods</h3>
 102  
  * <p/>
 103  
  * There are methods to create a {@link #toFile File from a URL}, copy a {@link #copyFileToDirectory File to a
 104  
  * directory}, copy a {@link #copyFile File to another File}, copy a {@link #copyURLToFile URL's contents to a File}, as
 105  
  * well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) clean} a directory.
 106  
  * </p>
 107  
  * <p/>
 108  
  * Common {@link java.io.File} manipulation routines.
 109  
  * <p/>
 110  
  * Taken from the commons-utils repo. Also code from Alexandria's FileUtils. And from Avalon Excalibur's IO. And from
 111  
  * Ant.
 112  
  *
 113  
  * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
 114  
  * @author <a href="mailto:sanders@codehaus.org">Scott Sanders</a>
 115  
  * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 116  
  * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
 117  
  * @author <a href="mailto:peter@codehaus.org">Peter Donald</a>
 118  
  * @author <a href="mailto:jefft@codehaus.org">Jeff Turner</a>
 119  
  * @version $Id$
 120  
  */
 121  0
 public class FileUtils
 122  
 {
 123  
     /**
 124  
      * The number of bytes in a kilobyte.
 125  
      */
 126  
     public static final int ONE_KB = 1024;
 127  
 
 128  
     /**
 129  
      * The number of bytes in a megabyte.
 130  
      */
 131  
     public static final int ONE_MB = ONE_KB * ONE_KB;
 132  
 
 133  
     /**
 134  
      * The number of bytes in a gigabyte.
 135  
      */
 136  
     public static final int ONE_GB = ONE_KB * ONE_MB;
 137  
 
 138  
     /**
 139  
      * The file copy buffer size (30 MB)
 140  
      */
 141  
     private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
 142  
 
 143  
     /**
 144  
      * The vm file separator
 145  
      */
 146  1
     public static String FS = System.getProperty( "file.separator" );
 147  
 
 148  
     /**
 149  
      * Non-valid Characters for naming files, folders under Windows: <code>":", "*", "?", "\"", "<", ">", "|"</code>
 150  
      *
 151  
      * @see <a href="http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13">
 152  
      *      http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13</a>
 153  
      */
 154  1
     private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" };
 155  
 
 156  
     /**
 157  
      * @return the default excludes pattern
 158  
      * @see DirectoryScanner#DEFAULTEXCLUDES
 159  
      */
 160  
     public static String[] getDefaultExcludes()
 161  
     {
 162  0
         return DirectoryScanner.DEFAULTEXCLUDES;
 163  
     }
 164  
 
 165  
     /**
 166  
      * @return the default excludes pattern as list.
 167  
      * @see #getDefaultExcludes()
 168  
      */
 169  
     public static List<String> getDefaultExcludesAsList()
 170  
     {
 171  0
         return Arrays.asList( getDefaultExcludes() );
 172  
     }
 173  
 
 174  
     /**
 175  
      * @return the default excludes pattern as comma separated string.
 176  
      * @see DirectoryScanner#DEFAULTEXCLUDES
 177  
      * @see StringUtils#join(Object[], String)
 178  
      */
 179  
     public static String getDefaultExcludesAsString()
 180  
     {
 181  0
         return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," );
 182  
     }
 183  
 
 184  
     /**
 185  
      * Returns a human-readable version of the file size (original is in bytes).
 186  
      *
 187  
      * @param size The number of bytes.
 188  
      * @return A human-readable display value (includes units).
 189  
      */
 190  
     public static String byteCountToDisplaySize( int size )
 191  
     {
 192  
         String displaySize;
 193  
 
 194  4
         if ( size / ONE_GB > 0 )
 195  
         {
 196  1
             displaySize = String.valueOf( size / ONE_GB ) + " GB";
 197  
         }
 198  3
         else if ( size / ONE_MB > 0 )
 199  
         {
 200  1
             displaySize = String.valueOf( size / ONE_MB ) + " MB";
 201  
         }
 202  2
         else if ( size / ONE_KB > 0 )
 203  
         {
 204  1
             displaySize = String.valueOf( size / ONE_KB ) + " KB";
 205  
         }
 206  
         else
 207  
         {
 208  1
             displaySize = String.valueOf( size ) + " bytes";
 209  
         }
 210  
 
 211  4
         return displaySize;
 212  
     }
 213  
 
 214  
     /**
 215  
      * Returns the directory path portion of a file specification string. Matches the equally named unix command.
 216  
      *
 217  
      * @param filename the file path
 218  
      * @return The directory portion excluding the ending file separator.
 219  
      */
 220  
     public static String dirname( String filename )
 221  
     {
 222  0
         int i = filename.lastIndexOf( File.separator );
 223  0
         return ( i >= 0 ? filename.substring( 0, i ) : "" );
 224  
     }
 225  
 
 226  
     /**
 227  
      * Returns the filename portion of a file specification string.
 228  
      *
 229  
      * @param filename the file path
 230  
      * @return The filename string with extension.
 231  
      */
 232  
     public static String filename( String filename )
 233  
     {
 234  0
         int i = filename.lastIndexOf( File.separator );
 235  0
         return ( i >= 0 ? filename.substring( i + 1 ) : filename );
 236  
     }
 237  
 
 238  
     /**
 239  
      * Returns the filename portion of a file specification string. Matches the equally named unix command.
 240  
      *
 241  
      * @param filename the file path
 242  
      * @return The filename string without extension.
 243  
      */
 244  
     public static String basename( String filename )
 245  
     {
 246  0
         return basename( filename, extension( filename ) );
 247  
     }
 248  
 
 249  
     /**
 250  
      * Returns the filename portion of a file specification string. Matches the equally named unix command.
 251  
      *
 252  
      * @param filename the file path
 253  
      * @param suffix the file suffix
 254  
      * @return the basename of the file
 255  
      */
 256  
     public static String basename( String filename, String suffix )
 257  
     {
 258  0
         int i = filename.lastIndexOf( File.separator ) + 1;
 259  0
         int lastDot = ( ( suffix != null ) && ( suffix.length() > 0 ) ) ? filename.lastIndexOf( suffix ) : -1;
 260  
 
 261  0
         if ( lastDot >= 0 )
 262  
         {
 263  0
             return filename.substring( i, lastDot );
 264  
         }
 265  0
         else if ( i > 0 )
 266  
         {
 267  0
             return filename.substring( i );
 268  
         }
 269  
         else
 270  
         {
 271  0
             return filename; // else returns all (no path and no extension)
 272  
         }
 273  
     }
 274  
 
 275  
     /**
 276  
      * Returns the extension portion of a file specification string. This everything after the last dot '.' in the
 277  
      * filename (NOT including the dot).
 278  
      *
 279  
      * @param filename the file path
 280  
      * @return the extension of the file
 281  
      */
 282  
     public static String extension( String filename )
 283  
     {
 284  
         // Ensure the last dot is after the last file separator
 285  39
         int lastSep = filename.lastIndexOf( File.separatorChar );
 286  
         int lastDot;
 287  39
         if ( lastSep < 0 )
 288  
         {
 289  13
             lastDot = filename.lastIndexOf( '.' );
 290  
         }
 291  
         else
 292  
         {
 293  26
             lastDot = filename.substring( lastSep + 1 ).lastIndexOf( '.' );
 294  26
             if ( lastDot >= 0 )
 295  
             {
 296  19
                 lastDot += lastSep + 1;
 297  
             }
 298  
         }
 299  
 
 300  39
         if ( lastDot >= 0 && lastDot > lastSep )
 301  
         {
 302  28
             return filename.substring( lastDot + 1 );
 303  
         }
 304  
 
 305  11
         return "";
 306  
     }
 307  
 
 308  
     /**
 309  
      * Check if a file exits.
 310  
      *
 311  
      * @param fileName the file path.
 312  
      * @return true if file exists.
 313  
      */
 314  
     public static boolean fileExists( String fileName )
 315  
     {
 316  4
         File file = new File( fileName );
 317  4
         return file.exists();
 318  
     }
 319  
 
 320  
     /**
 321  
      * Note: the file content is read with platform encoding.
 322  
      *
 323  
      * @param file the file path
 324  
      * @return the file content using the platform encoding.
 325  
      * @throws IOException if any
 326  
      */
 327  
     public static String fileRead( String file )
 328  
         throws IOException
 329  
     {
 330  5
         return fileRead( file, null );
 331  
     }
 332  
 
 333  
     /**
 334  
      * @param file the file path
 335  
      * @param encoding the wanted encoding
 336  
      * @return the file content using the specified encoding.
 337  
      * @throws IOException if any
 338  
      */
 339  
     public static String fileRead( String file, String encoding )
 340  
         throws IOException
 341  
     {
 342  6
         return fileRead( new File( file ), encoding );
 343  
     }
 344  
 
 345  
     /**
 346  
      * Note: the file content is read with platform encoding
 347  
      *
 348  
      * @param file the file path
 349  
      * @return the file content using the platform encoding.
 350  
      * @throws IOException if any
 351  
      */
 352  
     public static String fileRead( File file )
 353  
         throws IOException
 354  
     {
 355  1
         return fileRead( file, null );
 356  
     }
 357  
 
 358  
     /**
 359  
      * @param file the file path
 360  
      * @param encoding the wanted encoding
 361  
      * @return the file content using the specified encoding.
 362  
      * @throws IOException if any
 363  
      */
 364  
     public static String fileRead( File file, String encoding )
 365  
         throws IOException
 366  
     {
 367  10
         StringBuilder buf = new StringBuilder();
 368  
 
 369  10
         Reader reader = null;
 370  
 
 371  
         try
 372  
         {
 373  10
             if ( encoding != null )
 374  
             {
 375  4
                 reader = new InputStreamReader( new FileInputStream( file ), encoding );
 376  
             }
 377  
             else
 378  
             {
 379  6
                 reader = new InputStreamReader( new FileInputStream( file ) );
 380  
             }
 381  
             int count;
 382  10
             char[] b = new char[512];
 383  20
             while ( ( count = reader.read( b ) ) >= 0 ) // blocking read
 384  
             {
 385  10
                 buf.append( b, 0, count );
 386  
             }
 387  10
             reader.close();
 388  10
             reader = null;
 389  
         }
 390  
         finally
 391  
         {
 392  10
             IOUtil.close( reader );
 393  10
         }
 394  
 
 395  10
         return buf.toString();
 396  
     }
 397  
 
 398  
     /**
 399  
      * Appends data to a file. The file will be created if it does not exist. Note: the data is written with platform
 400  
      * encoding
 401  
      *
 402  
      * @param fileName The path of the file to write.
 403  
      * @param data The content to write to the file.
 404  
      * @throws IOException if any
 405  
      */
 406  
     public static void fileAppend( String fileName, String data )
 407  
         throws IOException
 408  
     {
 409  3
         fileAppend( fileName, null, data );
 410  3
     }
 411  
 
 412  
     /**
 413  
      * Appends data to a file. The file will be created if it does not exist.
 414  
      *
 415  
      * @param fileName The path of the file to write.
 416  
      * @param encoding The encoding of the file.
 417  
      * @param data The content to write to the file.
 418  
      * @throws IOException if any
 419  
      */
 420  
     public static void fileAppend( String fileName, String encoding, String data )
 421  
         throws IOException
 422  
     {
 423  4
         FileOutputStream out = null;
 424  
         try
 425  
         {
 426  4
             out = new FileOutputStream( fileName, true );
 427  4
             if ( encoding != null )
 428  
             {
 429  1
                 out.write( data.getBytes( encoding ) );
 430  
             }
 431  
             else
 432  
             {
 433  3
                 out.write( data.getBytes() );
 434  
             }
 435  4
             out.close();
 436  4
             out = null;
 437  
         }
 438  
         finally
 439  
         {
 440  4
             IOUtil.close( out );
 441  4
         }
 442  4
     }
 443  
 
 444  
     /**
 445  
      * Writes data to a file. The file will be created if it does not exist. Note: the data is written with platform
 446  
      * encoding
 447  
      *
 448  
      * @param fileName The path of the file to write.
 449  
      * @param data The content to write to the file.
 450  
      * @throws IOException if any
 451  
      */
 452  
     public static void fileWrite( String fileName, String data )
 453  
         throws IOException
 454  
     {
 455  2
         fileWrite( fileName, null, data );
 456  2
     }
 457  
 
 458  
     /**
 459  
      * Writes data to a file. The file will be created if it does not exist.
 460  
      *
 461  
      * @param fileName The path of the file to write.
 462  
      * @param encoding The encoding of the file.
 463  
      * @param data The content to write to the file.
 464  
      * @throws IOException if any
 465  
      */
 466  
     public static void fileWrite( String fileName, String encoding, String data )
 467  
         throws IOException
 468  
     {
 469  9
         File file = ( fileName == null ) ? null : new File( fileName );
 470  9
         fileWrite( file, encoding, data );
 471  9
     }
 472  
 
 473  
     /**
 474  
      * Writes data to a file. The file will be created if it does not exist. Note: the data is written with platform
 475  
      * encoding
 476  
      *
 477  
      * @param file The file to write.
 478  
      * @param data The content to write to the file.
 479  
      * @throws IOException if any
 480  
      * @since 2.0.6
 481  
      */
 482  
     public static void fileWrite( File file, String data )
 483  
         throws IOException
 484  
     {
 485  0
         fileWrite( file, null, data );
 486  0
     }
 487  
 
 488  
     /**
 489  
      * Writes data to a file. The file will be created if it does not exist.
 490  
      *
 491  
      * @param file The file to write.
 492  
      * @param encoding The encoding of the file.
 493  
      * @param data The content to write to the file.
 494  
      * @throws IOException if any
 495  
      * @since 2.0.6
 496  
      */
 497  
     public static void fileWrite( File file, String encoding, String data )
 498  
         throws IOException
 499  
     {
 500  9
         Writer writer = null;
 501  
         try
 502  
         {
 503  9
             OutputStream out = new FileOutputStream( file );
 504  9
             if ( encoding != null )
 505  
             {
 506  7
                 writer = new OutputStreamWriter( out, encoding );
 507  
             }
 508  
             else
 509  
             {
 510  2
                 writer = new OutputStreamWriter( out );
 511  
             }
 512  9
             writer.write( data );
 513  9
             writer.close();
 514  9
             writer = null;
 515  
         }
 516  
         finally
 517  
         {
 518  9
             IOUtil.close( writer );
 519  9
         }
 520  9
     }
 521  
 
 522  
     /**
 523  
      * Deletes a file.
 524  
      *
 525  
      * @param fileName The path of the file to delete.
 526  
      */
 527  
     public static void fileDelete( String fileName )
 528  
     {
 529  1
         File file = new File( fileName );
 530  1
         if ( Java7Detector.isJava7() )
 531  
         {
 532  
             try
 533  
             {
 534  1
                 NioFiles.deleteIfExists( file );
 535  
             }
 536  0
             catch ( IOException e )
 537  
             {
 538  0
                 throw new RuntimeException( e );
 539  1
             }
 540  
         }
 541  
         else
 542  
         {
 543  0
             file.delete();
 544  
         }
 545  1
     }
 546  
 
 547  
     /**
 548  
      * Waits for NFS to propagate a file creation, imposing a timeout.
 549  
      *
 550  
      * @param fileName The path of the file.
 551  
      * @param seconds The maximum time in seconds to wait.
 552  
      * @return True if file exists.
 553  
      */
 554  
     public static boolean waitFor( String fileName, int seconds )
 555  
     {
 556  2
         return waitFor( new File( fileName ), seconds );
 557  
     }
 558  
 
 559  
     /**
 560  
      * Waits for NFS to propagate a file creation, imposing a timeout.
 561  
      *
 562  
      * @param file The file.
 563  
      * @param seconds The maximum time in seconds to wait.
 564  
      * @return True if file exists.
 565  
      */
 566  
     public static boolean waitFor( File file, int seconds )
 567  
     {
 568  2
         int timeout = 0;
 569  2
         int tick = 0;
 570  55
         while ( !file.exists() )
 571  
         {
 572  55
             if ( tick++ >= 10 )
 573  
             {
 574  5
                 tick = 0;
 575  5
                 if ( timeout++ > seconds )
 576  
                 {
 577  2
                     return false;
 578  
                 }
 579  
             }
 580  
             try
 581  
             {
 582  53
                 Thread.sleep( 100 );
 583  
             }
 584  0
             catch ( InterruptedException ignore )
 585  
             {
 586  
                 // nop
 587  53
             }
 588  
         }
 589  0
         return true;
 590  
     }
 591  
 
 592  
     /**
 593  
      * Creates a file handle.
 594  
      *
 595  
      * @param fileName The path of the file.
 596  
      * @return A <code>File</code> manager.
 597  
      */
 598  
     public static File getFile( String fileName )
 599  
     {
 600  0
         return new File( fileName );
 601  
     }
 602  
 
 603  
     /**
 604  
      * Given a directory and an array of extensions return an array of compliant files.
 605  
      * <p/>
 606  
      * TODO Should an ignore list be passed in? TODO Should a recurse flag be passed in?
 607  
      * <p/>
 608  
      * The given extensions should be like "java" and not like ".java"
 609  
      *
 610  
      * @param directory The path of the directory.
 611  
      * @param extensions an array of expected extensions.
 612  
      * @return An array of files for the wanted extensions.
 613  
      */
 614  
     public static String[] getFilesFromExtension( String directory, String[] extensions )
 615  
     {
 616  2
         List<String> files = new ArrayList<String>();
 617  
 
 618  2
         File currentDir = new File( directory );
 619  
 
 620  2
         String[] unknownFiles = currentDir.list();
 621  
 
 622  2
         if ( unknownFiles == null )
 623  
         {
 624  1
             return new String[0];
 625  
         }
 626  
 
 627  3
         for ( String unknownFile : unknownFiles )
 628  
         {
 629  2
             String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile;
 630  2
             File currentFile = new File( currentFileName );
 631  
 
 632  2
             if ( currentFile.isDirectory() )
 633  
             {
 634  
                 // ignore all CVS directories...
 635  0
                 if ( currentFile.getName().equals( "CVS" ) )
 636  
                 {
 637  0
                     continue;
 638  
                 }
 639  
 
 640  
                 // ok... transverse into this directory and get all the files... then combine
 641  
                 // them with the current list.
 642  
 
 643  0
                 String[] fetchFiles = getFilesFromExtension( currentFileName, extensions );
 644  0
                 files = blendFilesToVector( files, fetchFiles );
 645  0
             }
 646  
             else
 647  
             {
 648  
                 // ok... add the file
 649  
 
 650  2
                 String add = currentFile.getAbsolutePath();
 651  2
                 if ( isValidFile( add, extensions ) )
 652  
                 {
 653  0
                     files.add( add );
 654  
                 }
 655  
             }
 656  
         }
 657  
 
 658  
         // ok... move the Vector into the files list...
 659  1
         String[] foundFiles = new String[files.size()];
 660  1
         files.toArray( foundFiles );
 661  
 
 662  1
         return foundFiles;
 663  
     }
 664  
 
 665  
     /**
 666  
      * Private helper method for getFilesFromExtension()
 667  
      */
 668  
     private static List<String> blendFilesToVector( List<String> v, String[] files )
 669  
     {
 670  0
         for ( String file : files )
 671  
         {
 672  0
             v.add( file );
 673  
         }
 674  
 
 675  0
         return v;
 676  
     }
 677  
 
 678  
     /**
 679  
      * Checks to see if a file is of a particular type(s). Note that if the file does not have an extension, an empty
 680  
      * string (&quot;&quot;) is matched for.
 681  
      */
 682  
     private static boolean isValidFile( String file, String[] extensions )
 683  
     {
 684  2
         String extension = extension( file );
 685  2
         if ( extension == null )
 686  
         {
 687  0
             extension = "";
 688  
         }
 689  
 
 690  
         // ok.. now that we have the "extension" go through the current know
 691  
         // excepted extensions and determine if this one is OK.
 692  
 
 693  4
         for ( String extension1 : extensions )
 694  
         {
 695  2
             if ( extension1.equals( extension ) )
 696  
             {
 697  0
                 return true;
 698  
             }
 699  
         }
 700  
 
 701  2
         return false;
 702  
 
 703  
     }
 704  
 
 705  
     /**
 706  
      * Simple way to make a directory
 707  
      *
 708  
      * @param dir the directory to create
 709  
      * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS.
 710  
      * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
 711  
      */
 712  
     public static void mkdir( String dir )
 713  
     {
 714  7
         File file = new File( dir );
 715  
 
 716  7
         if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) )
 717  
         {
 718  0
             throw new IllegalArgumentException( "The file (" + dir
 719  
                 + ") cannot contain any of the following characters: \n"
 720  
                 + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) );
 721  
         }
 722  
 
 723  7
         if ( !file.exists() )
 724  
         {
 725  5
             file.mkdirs();
 726  
         }
 727  7
     }
 728  
 
 729  
     /**
 730  
      * Compare the contents of two files to determine if they are equal or not.
 731  
      *
 732  
      * @param file1 the first file
 733  
      * @param file2 the second file
 734  
      * @return true if the content of the files are equal or they both don't exist, false otherwise
 735  
      * @throws IOException if any
 736  
      */
 737  
     public static boolean contentEquals( final File file1, final File file2 )
 738  
         throws IOException
 739  
     {
 740  5
         final boolean file1Exists = file1.exists();
 741  5
         if ( file1Exists != file2.exists() )
 742  
         {
 743  0
             return false;
 744  
         }
 745  
 
 746  5
         if ( !file1Exists )
 747  
         {
 748  
             // two not existing files are equal
 749  1
             return true;
 750  
         }
 751  
 
 752  4
         if ( file1.isDirectory() || file2.isDirectory() )
 753  
         {
 754  
             // don't want to compare directory contents
 755  1
             return false;
 756  
         }
 757  
 
 758  3
         InputStream input1 = null;
 759  3
         InputStream input2 = null;
 760  3
         boolean equals = false;
 761  
         try
 762  
         {
 763  3
             input1 = new FileInputStream( file1 );
 764  3
             input2 = new FileInputStream( file2 );
 765  3
             equals = IOUtil.contentEquals( input1, input2 );
 766  3
             input1.close();
 767  3
             input1 = null;
 768  3
             input2.close();
 769  3
             input2 = null;
 770  
         }
 771  
         finally
 772  
         {
 773  3
             IOUtil.close( input1 );
 774  3
             IOUtil.close( input2 );
 775  3
         }
 776  3
         return equals;
 777  
     }
 778  
 
 779  
     /**
 780  
      * Convert from a <code>URL</code> to a <code>File</code>.
 781  
      *
 782  
      * @param url File URL.
 783  
      * @return The equivalent <code>File</code> object, or <code>null</code> if the URL's protocol is not
 784  
      *         <code>file</code>
 785  
      */
 786  
     public static File toFile( final URL url )
 787  
     {
 788  3
         if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) )
 789  
         {
 790  2
             return null;
 791  
         }
 792  
 
 793  1
         String filename = url.getFile().replace( '/', File.separatorChar );
 794  1
         int pos = -1;
 795  11
         while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 )
 796  
         {
 797  10
             if ( pos + 2 < filename.length() )
 798  
             {
 799  10
                 String hexStr = filename.substring( pos + 1, pos + 3 );
 800  10
                 char ch = (char) Integer.parseInt( hexStr, 16 );
 801  10
                 filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 );
 802  10
             }
 803  
         }
 804  1
         return new File( filename );
 805  
     }
 806  
 
 807  
     /**
 808  
      * Convert the array of Files into a list of URLs.
 809  
      *
 810  
      * @param files the array of files
 811  
      * @return the array of URLs
 812  
      * @throws IOException if an error occurs
 813  
      */
 814  
     public static URL[] toURLs( final File[] files )
 815  
         throws IOException
 816  
     {
 817  1
         final URL[] urls = new URL[files.length];
 818  
 
 819  3
         for ( int i = 0; i < urls.length; i++ )
 820  
         {
 821  2
             urls[i] = files[i].toURI().toURL();
 822  
         }
 823  
 
 824  1
         return urls;
 825  
     }
 826  
 
 827  
     /**
 828  
      * Remove extension from filename. ie
 829  
      * 
 830  
      * <pre>
 831  
      * foo.txt    --> foo
 832  
      * a\b\c.jpg --> a\b\c
 833  
      * a\b\c     --> a\b\c
 834  
      * </pre>
 835  
      *
 836  
      * @param filename the path of the file
 837  
      * @return the filename minus extension
 838  
      */
 839  
     public static String removeExtension( final String filename )
 840  
     {
 841  12
         String ext = extension( filename );
 842  
 
 843  12
         if ( "".equals( ext ) )
 844  
         {
 845  3
             return filename;
 846  
         }
 847  
 
 848  9
         final int index = filename.lastIndexOf( ext ) - 1;
 849  9
         return filename.substring( 0, index );
 850  
     }
 851  
 
 852  
     /**
 853  
      * Get extension from filename. ie
 854  
      * 
 855  
      * <pre>
 856  
      * foo.txt    --> "txt"
 857  
      * a\b\c.jpg --> "jpg"
 858  
      * a\b\c     --> ""
 859  
      * </pre>
 860  
      *
 861  
      * @param filename the path of the file
 862  
      * @return the extension of filename or "" if none
 863  
      */
 864  
     public static String getExtension( final String filename )
 865  
     {
 866  15
         return extension( filename );
 867  
     }
 868  
 
 869  
     /**
 870  
      * Remove path from filename. Equivalent to the unix command <code>basename</code> ie.
 871  
      * 
 872  
      * <pre>
 873  
      * a/b/c.txt --> c.txt
 874  
      * a.txt     --> a.txt
 875  
      * </pre>
 876  
      *
 877  
      * @param filepath the path of the file
 878  
      * @return the filename minus path
 879  
      */
 880  
     public static String removePath( final String filepath )
 881  
     {
 882  1
         return removePath( filepath, File.separatorChar );
 883  
     }
 884  
 
 885  
     /**
 886  
      * Remove path from filename. ie.
 887  
      * 
 888  
      * <pre>
 889  
      * a/b/c.txt --> c.txt
 890  
      * a.txt     --> a.txt
 891  
      * </pre>
 892  
      *
 893  
      * @param filepath the path of the file
 894  
      * @param fileSeparatorChar the file separator character like <b>/</b> on Unix platforms.
 895  
      * @return the filename minus path
 896  
      */
 897  
     public static String removePath( final String filepath, final char fileSeparatorChar )
 898  
     {
 899  1
         final int index = filepath.lastIndexOf( fileSeparatorChar );
 900  
 
 901  1
         if ( -1 == index )
 902  
         {
 903  0
             return filepath;
 904  
         }
 905  
 
 906  1
         return filepath.substring( index + 1 );
 907  
     }
 908  
 
 909  
     /**
 910  
      * Get path from filename. Roughly equivalent to the unix command <code>dirname</code>. ie.
 911  
      * 
 912  
      * <pre>
 913  
      * a/b/c.txt --> a/b
 914  
      * a.txt     --> ""
 915  
      * </pre>
 916  
      *
 917  
      * @param filepath the filepath
 918  
      * @return the filename minus path
 919  
      */
 920  
     public static String getPath( final String filepath )
 921  
     {
 922  1
         return getPath( filepath, File.separatorChar );
 923  
     }
 924  
 
 925  
     /**
 926  
      * Get path from filename. ie.
 927  
      * 
 928  
      * <pre>
 929  
      * a/b/c.txt --> a/b
 930  
      * a.txt     --> ""
 931  
      * </pre>
 932  
      *
 933  
      * @param filepath the filepath
 934  
      * @param fileSeparatorChar the file separator character like <b>/</b> on Unix platforms.
 935  
      * @return the filename minus path
 936  
      */
 937  
     public static String getPath( final String filepath, final char fileSeparatorChar )
 938  
     {
 939  1
         final int index = filepath.lastIndexOf( fileSeparatorChar );
 940  1
         if ( -1 == index )
 941  
         {
 942  0
             return "";
 943  
         }
 944  
 
 945  1
         return filepath.substring( 0, index );
 946  
     }
 947  
 
 948  
     /**
 949  
      * Copy file from source to destination. If <code>destinationDirectory</code> does not exist, it (and any parent
 950  
      * directories) will be created. If a file <code>source</code> in <code>destinationDirectory</code> exists, it will
 951  
      * be overwritten.
 952  
      *
 953  
      * @param source An existing <code>File</code> to copy.
 954  
      * @param destinationDirectory A directory to copy <code>source</code> into.
 955  
      * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
 956  
      * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
 957  
      * @throws IOException if <code>source</code> does not exist, the file in <code>destinationDirectory</code> cannot
 958  
      *             be written to, or an IO error occurs during copying.
 959  
      */
 960  
     public static void copyFileToDirectory( final String source, final String destinationDirectory )
 961  
         throws IOException
 962  
     {
 963  0
         copyFileToDirectory( new File( source ), new File( destinationDirectory ) );
 964  0
     }
 965  
 
 966  
     /**
 967  
      * Copy file from source to destination only if source is newer than the target file. If
 968  
      * <code>destinationDirectory</code> does not exist, it (and any parent directories) will be created. If a file
 969  
      * <code>source</code> in <code>destinationDirectory</code> exists, it will be overwritten.
 970  
      *
 971  
      * @param source An existing <code>File</code> to copy.
 972  
      * @param destinationDirectory A directory to copy <code>source</code> into.
 973  
      * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
 974  
      * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
 975  
      * @throws IOException if <code>source</code> does not exist, the file in <code>destinationDirectory</code> cannot
 976  
      *             be written to, or an IO error occurs during copying.
 977  
      */
 978  
     public static void copyFileToDirectoryIfModified( final String source, final String destinationDirectory )
 979  
         throws IOException
 980  
     {
 981  0
         copyFileToDirectoryIfModified( new File( source ), new File( destinationDirectory ) );
 982  0
     }
 983  
 
 984  
     /**
 985  
      * Copy file from source to destination. If <code>destinationDirectory</code> does not exist, it (and any parent
 986  
      * directories) will be created. If a file <code>source</code> in <code>destinationDirectory</code> exists, it will
 987  
      * be overwritten.
 988  
      *
 989  
      * @param source An existing <code>File</code> to copy.
 990  
      * @param destinationDirectory A directory to copy <code>source</code> into.
 991  
      * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
 992  
      * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
 993  
      * @throws IOException if <code>source</code> does not exist, the file in <code>destinationDirectory</code> cannot
 994  
      *             be written to, or an IO error occurs during copying.
 995  
      */
 996  
     public static void copyFileToDirectory( final File source, final File destinationDirectory )
 997  
         throws IOException
 998  
     {
 999  5
         if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
 1000  
         {
 1001  0
             throw new IllegalArgumentException( "Destination is not a directory" );
 1002  
         }
 1003  
 
 1004  5
         copyFile( source, new File( destinationDirectory, source.getName() ) );
 1005  5
     }
 1006  
 
 1007  
     /**
 1008  
      * Copy file from source to destination only if source is newer than the target file. If
 1009  
      * <code>destinationDirectory</code> does not exist, it (and any parent directories) will be created. If a file
 1010  
      * <code>source</code> in <code>destinationDirectory</code> exists, it will be overwritten.
 1011  
      *
 1012  
      * @param source An existing <code>File</code> to copy.
 1013  
      * @param destinationDirectory A directory to copy <code>source</code> into.
 1014  
      * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file.
 1015  
      * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory.
 1016  
      * @throws IOException if <code>source</code> does not exist, the file in <code>destinationDirectory</code> cannot
 1017  
      *             be written to, or an IO error occurs during copying.
 1018  
      */
 1019  
     public static void copyFileToDirectoryIfModified( final File source, final File destinationDirectory )
 1020  
         throws IOException
 1021  
     {
 1022  13
         if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() )
 1023  
         {
 1024  0
             throw new IllegalArgumentException( "Destination is not a directory" );
 1025  
         }
 1026  
 
 1027  13
         copyFileIfModified( source, new File( destinationDirectory, source.getName() ) );
 1028  13
     }
 1029  
 
 1030  
     /**
 1031  
      * Creates a number of directories, as delivered from DirectoryScanner
 1032  
      * 
 1033  
      * @param sourceBase The basedir used for the directory scan
 1034  
      * @param dirs The getIncludedDirs from the dirscanner
 1035  
      * @param destination The base dir of the output structure
 1036  
      */
 1037  
     public static void mkDirs( final File sourceBase, String[] dirs, final File destination )
 1038  
         throws IOException
 1039  
     {
 1040  0
         for ( String dir : dirs )
 1041  
         {
 1042  0
             File src = new File( sourceBase, dir );
 1043  0
             File dst = new File( destination, dir );
 1044  0
             if ( Java7Detector.isJava7() && NioFiles.isSymbolicLink( src ) )
 1045  
             {
 1046  0
                 File target = NioFiles.readSymbolicLink( src );
 1047  0
                 NioFiles.createSymbolicLink( dst, target );
 1048  0
             }
 1049  
             else
 1050  
             {
 1051  0
                 dst.mkdirs();
 1052  
             }
 1053  
         }
 1054  0
     }
 1055  
 
 1056  
     /**
 1057  
      * Copy file from source to destination. The directories up to <code>destination</code> will be created if they
 1058  
      * don't already exist. <code>destination</code> will be overwritten if it already exists.
 1059  
      *
 1060  
      * @param source An existing non-directory <code>File</code> to copy bytes from.
 1061  
      * @param destination A non-directory <code>File</code> to write bytes to (possibly overwriting).
 1062  
      * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be written to, or an
 1063  
      *             IO error occurs during copying.
 1064  
      * @throws java.io.FileNotFoundException if <code>destination</code> is a directory (use
 1065  
      *             {@link #copyFileToDirectory}).
 1066  
      */
 1067  
     public static void copyFile( final File source, final File destination )
 1068  
         throws IOException
 1069  
     {
 1070  
         // check source exists
 1071  23
         if ( !source.exists() )
 1072  
         {
 1073  0
             final String message = "File " + source + " does not exist";
 1074  0
             throw new IOException( message );
 1075  
         }
 1076  
 
 1077  
         // check source != destination, see PLXUTILS-10
 1078  23
         if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) )
 1079  
         {
 1080  
             // if they are equal, we can exit the method without doing any work
 1081  1
             return;
 1082  
         }
 1083  22
         mkdirsFor( destination );
 1084  
 
 1085  22
         doCopyFile( source, destination );
 1086  
 
 1087  22
         if ( source.length() != destination.length() )
 1088  
         {
 1089  0
             String message = "Failed to copy full contents from " + source + " to " + destination;
 1090  0
             throw new IOException( message );
 1091  
         }
 1092  22
     }
 1093  
 
 1094  
     private static void doCopyFile( File source, File destination )
 1095  
         throws IOException
 1096  
     {
 1097  
         // offload to operating system if supported
 1098  22
         if ( Java7Detector.isJava7() )
 1099  
         {
 1100  22
             doCopyFileUsingNewIO( source, destination );
 1101  
         }
 1102  
         else
 1103  
         {
 1104  0
             doCopyFileUsingLegacyIO( source, destination );
 1105  
         }
 1106  22
     }
 1107  
 
 1108  
     private static void doCopyFileUsingLegacyIO( File source, File destination )
 1109  
         throws IOException
 1110  
     {
 1111  0
         FileInputStream fis = null;
 1112  0
         FileOutputStream fos = null;
 1113  0
         FileChannel input = null;
 1114  0
         FileChannel output = null;
 1115  
         try
 1116  
         {
 1117  0
             fis = new FileInputStream( source );
 1118  0
             fos = new FileOutputStream( destination );
 1119  0
             input = fis.getChannel();
 1120  0
             output = fos.getChannel();
 1121  0
             long size = input.size();
 1122  0
             long pos = 0;
 1123  0
             long count = 0;
 1124  0
             while ( pos < size )
 1125  
             {
 1126  0
                 count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos;
 1127  0
                 pos += output.transferFrom( input, pos, count );
 1128  
             }
 1129  0
             output.close();
 1130  0
             output = null;
 1131  0
             fos.close();
 1132  0
             fos = null;
 1133  0
             input.close();
 1134  0
             input = null;
 1135  0
             fis.close();
 1136  0
             fis = null;
 1137  
         }
 1138  
         finally
 1139  
         {
 1140  0
             IOUtil.close( output );
 1141  0
             IOUtil.close( fos );
 1142  0
             IOUtil.close( input );
 1143  0
             IOUtil.close( fis );
 1144  0
         }
 1145  0
     }
 1146  
 
 1147  
     private static void doCopyFileUsingNewIO( File source, File destination )
 1148  
         throws IOException
 1149  
     {
 1150  22
         NioFiles.copy( source, destination );
 1151  22
     }
 1152  
 
 1153  
     /**
 1154  
      * Copy file from source to destination only if source timestamp is later than the destination timestamp. The
 1155  
      * directories up to <code>destination</code> will be created if they don't already exist. <code>destination</code>
 1156  
      * will be overwritten if it already exists.
 1157  
      *
 1158  
      * @param source An existing non-directory <code>File</code> to copy bytes from.
 1159  
      * @param destination A non-directory <code>File</code> to write bytes to (possibly overwriting).
 1160  
      * @return true if no problem occured
 1161  
      * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be written to, or an
 1162  
      *             IO error occurs during copying.
 1163  
      */
 1164  
     public static boolean copyFileIfModified( final File source, final File destination )
 1165  
         throws IOException
 1166  
     {
 1167  15
         if ( destination.lastModified() < source.lastModified() )
 1168  
         {
 1169  7
             copyFile( source, destination );
 1170  
 
 1171  7
             return true;
 1172  
         }
 1173  
 
 1174  8
         return false;
 1175  
     }
 1176  
 
 1177  
     /**
 1178  
      * Copies bytes from the URL <code>source</code> to a file <code>destination</code>. The directories up to
 1179  
      * <code>destination</code> will be created if they don't already exist. <code>destination</code> will be
 1180  
      * overwritten if it already exists.
 1181  
      *
 1182  
      * @param source A <code>URL</code> to copy bytes from.
 1183  
      * @param destination A non-directory <code>File</code> to write bytes to (possibly overwriting).
 1184  
      * @throws IOException if
 1185  
      *             <ul>
 1186  
      *             <li><code>source</code> URL cannot be opened</li>
 1187  
      *             <li><code>destination</code> cannot be written to</li>
 1188  
      *             <li>an IO error occurs during copying</li>
 1189  
      *             </ul>
 1190  
      */
 1191  
     public static void copyURLToFile( final URL source, final File destination )
 1192  
         throws IOException
 1193  
     {
 1194  3
         copyStreamToFile( new URLInputStreamFacade( source ), destination );
 1195  3
     }
 1196  
 
 1197  
     /**
 1198  
      * Copies bytes from the {@link InputStream} <code>source</code> to a file <code>destination</code>. The directories
 1199  
      * up to <code>destination</code> will be created if they don't already exist. <code>destination</code> will be
 1200  
      * overwritten if it already exists.
 1201  
      *
 1202  
      * @param source An {@link InputStream} to copy bytes from. This stream is guaranteed to be closed.
 1203  
      * @param destination A non-directory <code>File</code> to write bytes to (possibly overwriting).
 1204  
      * @throws IOException if
 1205  
      *             <ul>
 1206  
      *             <li><code>source</code> URL cannot be opened</li>
 1207  
      *             <li><code>destination</code> cannot be written to</li>
 1208  
      *             <li>an IO error occurs during copying</li>
 1209  
      *             </ul>
 1210  
      */
 1211  
     public static void copyStreamToFile( final InputStreamFacade source, final File destination )
 1212  
         throws IOException
 1213  
     {
 1214  3
         mkdirsFor( destination );
 1215  3
         checkCanWrite( destination );
 1216  
 
 1217  3
         InputStream input = null;
 1218  3
         FileOutputStream output = null;
 1219  
         try
 1220  
         {
 1221  3
             input = source.getInputStream();
 1222  3
             output = new FileOutputStream( destination );
 1223  3
             IOUtil.copy( input, output );
 1224  3
             output.close();
 1225  3
             output = null;
 1226  3
             input.close();
 1227  3
             input = null;
 1228  
         }
 1229  
         finally
 1230  
         {
 1231  3
             IOUtil.close( input );
 1232  3
             IOUtil.close( output );
 1233  3
         }
 1234  3
     }
 1235  
 
 1236  
     private static void checkCanWrite( File destination )
 1237  
         throws IOException
 1238  
     {
 1239  
         // make sure we can write to destination
 1240  3
         if ( destination.exists() && !destination.canWrite() )
 1241  
         {
 1242  0
             final String message = "Unable to open file " + destination + " for writing.";
 1243  0
             throw new IOException( message );
 1244  
         }
 1245  3
     }
 1246  
 
 1247  
     private static void mkdirsFor( File destination )
 1248  
     {
 1249  
         // does destination directory exist ?
 1250  25
         File parentFile = destination.getParentFile();
 1251  25
         if ( parentFile != null && !parentFile.exists() )
 1252  
         {
 1253  1
             parentFile.mkdirs();
 1254  
         }
 1255  25
     }
 1256  
 
 1257  
     /**
 1258  
      * Normalize a path. Eliminates "/../" and "/./" in a string. Returns <code>null</code> if the ..'s went past the
 1259  
      * root. Eg:
 1260  
      * 
 1261  
      * <pre>
 1262  
      * /foo//               -->     /foo/
 1263  
      * /foo/./              -->     /foo/
 1264  
      * /foo/../bar          -->     /bar
 1265  
      * /foo/../bar/         -->     /bar/
 1266  
      * /foo/../bar/../baz   -->     /baz
 1267  
      * //foo//./bar         -->     /foo/bar
 1268  
      * /../                 -->     null
 1269  
      * </pre>
 1270  
      *
 1271  
      * @param path the path to normalize
 1272  
      * @return the normalized String, or <code>null</code> if too many ..'s.
 1273  
      */
 1274  
     public static String normalize( final String path )
 1275  
     {
 1276  17
         String normalized = path;
 1277  
         // Resolve occurrences of "//" in the normalized path
 1278  
         while ( true )
 1279  
         {
 1280  22
             int index = normalized.indexOf( "//" );
 1281  22
             if ( index < 0 )
 1282  
             {
 1283  17
                 break;
 1284  
             }
 1285  5
             normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 );
 1286  5
         }
 1287  
 
 1288  
         // Resolve occurrences of "/./" in the normalized path
 1289  
         while ( true )
 1290  
         {
 1291  25
             int index = normalized.indexOf( "/./" );
 1292  25
             if ( index < 0 )
 1293  
             {
 1294  17
                 break;
 1295  
             }
 1296  8
             normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 );
 1297  8
         }
 1298  
 
 1299  
         // Resolve occurrences of "/../" in the normalized path
 1300  
         while ( true )
 1301  
         {
 1302  25
             int index = normalized.indexOf( "/../" );
 1303  25
             if ( index < 0 )
 1304  
             {
 1305  15
                 break;
 1306  
             }
 1307  10
             if ( index == 0 )
 1308  
             {
 1309  2
                 return null; // Trying to go outside our context
 1310  
             }
 1311  8
             int index2 = normalized.lastIndexOf( '/', index - 1 );
 1312  8
             normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 );
 1313  8
         }
 1314  
 
 1315  
         // Return the normalized path that we have completed
 1316  15
         return normalized;
 1317  
     }
 1318  
 
 1319  
     /**
 1320  
      * Will concatenate 2 paths. Paths with <code>..</code> will be properly handled.
 1321  
      * <p>
 1322  
      * Eg.,<br />
 1323  
      * <code>/a/b/c</code> + <code>d</code> = <code>/a/b/d</code><br />
 1324  
      * <code>/a/b/c</code> + <code>../d</code> = <code>/a/d</code><br />
 1325  
      * </p>
 1326  
      * <p/>
 1327  
      * Thieved from Tomcat sources...
 1328  
      *
 1329  
      * @param lookupPath a path
 1330  
      * @param path the path to concatenate
 1331  
      * @return The concatenated paths, or null if error occurs
 1332  
      */
 1333  
     public static String catPath( final String lookupPath, final String path )
 1334  
     {
 1335  
         // Cut off the last slash and everything beyond
 1336  2
         int index = lookupPath.lastIndexOf( "/" );
 1337  2
         String lookup = lookupPath.substring( 0, index );
 1338  2
         String pth = path;
 1339  
 
 1340  
         // Deal with .. by chopping dirs off the lookup path
 1341  3
         while ( pth.startsWith( "../" ) )
 1342  
         {
 1343  1
             if ( lookup.length() > 0 )
 1344  
             {
 1345  1
                 index = lookup.lastIndexOf( "/" );
 1346  1
                 lookup = lookup.substring( 0, index );
 1347  
             }
 1348  
             else
 1349  
             {
 1350  
                 // More ..'s than dirs, return null
 1351  0
                 return null;
 1352  
             }
 1353  
 
 1354  1
             index = pth.indexOf( "../" ) + 3;
 1355  1
             pth = pth.substring( index );
 1356  
         }
 1357  
 
 1358  2
         return new StringBuffer( lookup ).append( "/" ).append( pth ).toString();
 1359  
     }
 1360  
 
 1361  
     /**
 1362  
      * Resolve a file <code>filename</code> to it's canonical form. If <code>filename</code> is relative (doesn't start
 1363  
      * with <code>/</code>), it will be resolved relative to <code>baseFile</code>, otherwise it is treated as a normal
 1364  
      * root-relative path.
 1365  
      *
 1366  
      * @param baseFile Where to resolve <code>filename</code> from, if <code>filename</code> is relative.
 1367  
      * @param filename Absolute or relative file path to resolve.
 1368  
      * @return The canonical <code>File</code> of <code>filename</code>.
 1369  
      */
 1370  
     public static File resolveFile( final File baseFile, String filename )
 1371  
     {
 1372  2
         String filenm = filename;
 1373  2
         if ( '/' != File.separatorChar )
 1374  
         {
 1375  0
             filenm = filename.replace( '/', File.separatorChar );
 1376  
         }
 1377  
 
 1378  2
         if ( '\\' != File.separatorChar )
 1379  
         {
 1380  2
             filenm = filename.replace( '\\', File.separatorChar );
 1381  
         }
 1382  
 
 1383  
         // deal with absolute files
 1384  2
         if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) )
 1385  
         {
 1386  0
             File file = new File( filenm );
 1387  
 
 1388  
             try
 1389  
             {
 1390  0
                 file = file.getCanonicalFile();
 1391  
             }
 1392  0
             catch ( final IOException ioe )
 1393  
             {
 1394  
                 // nop
 1395  0
             }
 1396  
 
 1397  0
             return file;
 1398  
         }
 1399  
         // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips
 1400  
         // them. However, I'm not sure about this UNC stuff. (JT)
 1401  2
         final char[] chars = filename.toCharArray();
 1402  2
         final StringBuilder sb = new StringBuilder();
 1403  
 
 1404  
         // remove duplicate file separators in succession - except
 1405  
         // on win32 at start of filename as UNC filenames can
 1406  
         // be \\AComputer\AShare\myfile.txt
 1407  2
         int start = 0;
 1408  2
         if ( '\\' == File.separatorChar )
 1409  
         {
 1410  0
             sb.append( filenm.charAt( 0 ) );
 1411  0
             start++;
 1412  
         }
 1413  
 
 1414  5
         for ( int i = start; i < chars.length; i++ )
 1415  
         {
 1416  3
             final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1];
 1417  
 
 1418  3
             if ( !doubleSeparator )
 1419  
             {
 1420  3
                 sb.append( chars[i] );
 1421  
             }
 1422  
         }
 1423  
 
 1424  2
         filenm = sb.toString();
 1425  
 
 1426  
         // must be relative
 1427  2
         File file = ( new File( baseFile, filenm ) ).getAbsoluteFile();
 1428  
 
 1429  
         try
 1430  
         {
 1431  2
             file = file.getCanonicalFile();
 1432  
         }
 1433  0
         catch ( final IOException ioe )
 1434  
         {
 1435  
             // nop
 1436  2
         }
 1437  
 
 1438  2
         return file;
 1439  
     }
 1440  
 
 1441  
     /**
 1442  
      * Delete a file. If file is directory delete it and all sub-directories.
 1443  
      *
 1444  
      * @param file the file path
 1445  
      * @throws IOException if any
 1446  
      */
 1447  
     public static void forceDelete( final String file )
 1448  
         throws IOException
 1449  
     {
 1450  0
         forceDelete( new File( file ) );
 1451  0
     }
 1452  
 
 1453  
     /**
 1454  
      * Delete a file. If file is directory delete it and all sub-directories.
 1455  
      *
 1456  
      * @param file a file
 1457  
      * @throws IOException if any
 1458  
      */
 1459  
     public static void forceDelete( final File file )
 1460  
         throws IOException
 1461  
     {
 1462  270
         if ( file.isDirectory() )
 1463  
         {
 1464  80
             deleteDirectory( file );
 1465  
         }
 1466  
         else
 1467  
         {
 1468  
             /*
 1469  
              * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a
 1470  
              * symlink whose target does not exist is deleted, too.
 1471  
              */
 1472  190
             boolean filePresent = file.getCanonicalFile().exists();
 1473  190
             if ( !deleteFile( file ) && filePresent )
 1474  
             {
 1475  0
                 final String message = "File " + file + " unable to be deleted.";
 1476  0
                 throw new IOException( message );
 1477  
             }
 1478  
         }
 1479  270
     }
 1480  
 
 1481  
     /**
 1482  
      * Accommodate Windows bug encountered in both Sun and IBM JDKs. Others possible. If the delete does not work, call
 1483  
      * System.gc(), wait a little and try again.
 1484  
      *
 1485  
      * @param file a file
 1486  
      * @throws IOException if any
 1487  
      */
 1488  
     private static boolean deleteFile( File file )
 1489  
         throws IOException
 1490  
     {
 1491  190
         if ( file.isDirectory() )
 1492  
         {
 1493  0
             throw new IOException( "File " + file + " isn't a file." );
 1494  
         }
 1495  
 
 1496  190
         if ( !file.delete() )
 1497  
         {
 1498  0
             if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
 1499  
             {
 1500  0
                 file = file.getCanonicalFile();
 1501  0
                 System.gc();
 1502  
             }
 1503  
 
 1504  
             try
 1505  
             {
 1506  0
                 Thread.sleep( 10 );
 1507  0
                 return file.delete();
 1508  
             }
 1509  0
             catch ( InterruptedException ignore )
 1510  
             {
 1511  0
                 return file.delete();
 1512  
             }
 1513  
         }
 1514  
 
 1515  190
         return true;
 1516  
     }
 1517  
 
 1518  
     /**
 1519  
      * Schedule a file to be deleted when JVM exits. If file is directory delete it and all sub-directories.
 1520  
      *
 1521  
      * @param file a file
 1522  
      * @throws IOException if any
 1523  
      */
 1524  
     public static void forceDeleteOnExit( final File file )
 1525  
         throws IOException
 1526  
     {
 1527  0
         if ( !file.exists() )
 1528  
         {
 1529  0
             return;
 1530  
         }
 1531  
 
 1532  0
         if ( file.isDirectory() )
 1533  
         {
 1534  0
             deleteDirectoryOnExit( file );
 1535  
         }
 1536  
         else
 1537  
         {
 1538  0
             file.deleteOnExit();
 1539  
         }
 1540  0
     }
 1541  
 
 1542  
     /**
 1543  
      * Recursively schedule directory for deletion on JVM exit.
 1544  
      *
 1545  
      * @param directory a directory
 1546  
      * @throws IOException if any
 1547  
      */
 1548  
     private static void deleteDirectoryOnExit( final File directory )
 1549  
         throws IOException
 1550  
     {
 1551  0
         if ( !directory.exists() )
 1552  
         {
 1553  0
             return;
 1554  
         }
 1555  0
         directory.deleteOnExit(); // The hook reverses the list
 1556  
 
 1557  0
         cleanDirectoryOnExit( directory );
 1558  0
     }
 1559  
 
 1560  
     /**
 1561  
      * Clean a directory without deleting it.
 1562  
      *
 1563  
      * @param directory a directory
 1564  
      * @throws IOException if any
 1565  
      */
 1566  
     private static void cleanDirectoryOnExit( final File directory )
 1567  
         throws IOException
 1568  
     {
 1569  0
         if ( !directory.exists() )
 1570  
         {
 1571  0
             final String message = directory + " does not exist";
 1572  0
             throw new IllegalArgumentException( message );
 1573  
         }
 1574  
 
 1575  0
         if ( !directory.isDirectory() )
 1576  
         {
 1577  0
             final String message = directory + " is not a directory";
 1578  0
             throw new IllegalArgumentException( message );
 1579  
         }
 1580  
 
 1581  0
         IOException exception = null;
 1582  
 
 1583  0
         final File[] files = directory.listFiles();
 1584  0
         for ( final File file : files )
 1585  
         {
 1586  
             try
 1587  
             {
 1588  0
                 forceDeleteOnExit( file );
 1589  
             }
 1590  0
             catch ( final IOException ioe )
 1591  
             {
 1592  0
                 exception = ioe;
 1593  0
             }
 1594  
         }
 1595  
 
 1596  0
         if ( null != exception )
 1597  
         {
 1598  0
             throw exception;
 1599  
         }
 1600  0
     }
 1601  
 
 1602  
     /**
 1603  
      * Make a directory.
 1604  
      *
 1605  
      * @param file not null
 1606  
      * @throws IOException If there already exists a file with specified name or the directory is unable to be created
 1607  
      * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS.
 1608  
      * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
 1609  
      */
 1610  
     public static void forceMkdir( final File file )
 1611  
         throws IOException
 1612  
     {
 1613  5
         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
 1614  
         {
 1615  0
             if ( !isValidWindowsFileName( file ) )
 1616  
             {
 1617  0
                 throw new IllegalArgumentException( "The file (" + file.getAbsolutePath()
 1618  
                     + ") cannot contain any of the following characters: \n"
 1619  
                     + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) );
 1620  
             }
 1621  
         }
 1622  
 
 1623  5
         if ( file.exists() )
 1624  
         {
 1625  2
             if ( file.isFile() )
 1626  
             {
 1627  1
                 final String message =
 1628  
                     "File " + file + " exists and is " + "not a directory. Unable to create directory.";
 1629  1
                 throw new IOException( message );
 1630  
             }
 1631  
         }
 1632  
         else
 1633  
         {
 1634  3
             if ( false == file.mkdirs() )
 1635  
             {
 1636  0
                 final String message = "Unable to create directory " + file;
 1637  0
                 throw new IOException( message );
 1638  
             }
 1639  
         }
 1640  4
     }
 1641  
 
 1642  
     /**
 1643  
      * Recursively delete a directory.
 1644  
      *
 1645  
      * @param directory a directory
 1646  
      * @throws IOException if any
 1647  
      */
 1648  
     public static void deleteDirectory( final String directory )
 1649  
         throws IOException
 1650  
     {
 1651  0
         deleteDirectory( new File( directory ) );
 1652  0
     }
 1653  
 
 1654  
     /**
 1655  
      * Recursively delete a directory.
 1656  
      *
 1657  
      * @param directory a directory
 1658  
      * @throws IOException if any
 1659  
      */
 1660  
     public static void deleteDirectory( final File directory )
 1661  
         throws IOException
 1662  
     {
 1663  146
         if ( !directory.exists() )
 1664  
         {
 1665  4
             return;
 1666  
         }
 1667  
 
 1668  
         /*
 1669  
          * try delete the directory before its contents, which will take care of any directories that are really
 1670  
          * symbolic links.
 1671  
          */
 1672  142
         if ( directory.delete() )
 1673  
         {
 1674  13
             return;
 1675  
         }
 1676  
 
 1677  129
         cleanDirectory( directory );
 1678  129
         if ( !directory.delete() )
 1679  
         {
 1680  0
             final String message = "Directory " + directory + " unable to be deleted.";
 1681  0
             throw new IOException( message );
 1682  
         }
 1683  129
     }
 1684  
 
 1685  
     /**
 1686  
      * Clean a directory without deleting it.
 1687  
      *
 1688  
      * @param directory a directory
 1689  
      * @throws IOException if any
 1690  
      */
 1691  
     public static void cleanDirectory( final String directory )
 1692  
         throws IOException
 1693  
     {
 1694  0
         cleanDirectory( new File( directory ) );
 1695  0
     }
 1696  
 
 1697  
     /**
 1698  
      * Clean a directory without deleting it.
 1699  
      *
 1700  
      * @param directory a directory
 1701  
      * @throws IOException if any
 1702  
      */
 1703  
     public static void cleanDirectory( final File directory )
 1704  
         throws IOException
 1705  
     {
 1706  130
         if ( !directory.exists() )
 1707  
         {
 1708  0
             final String message = directory + " does not exist";
 1709  0
             throw new IllegalArgumentException( message );
 1710  
         }
 1711  
 
 1712  130
         if ( !directory.isDirectory() )
 1713  
         {
 1714  0
             final String message = directory + " is not a directory";
 1715  0
             throw new IllegalArgumentException( message );
 1716  
         }
 1717  
 
 1718  130
         IOException exception = null;
 1719  
 
 1720  130
         final File[] files = directory.listFiles();
 1721  
 
 1722  130
         if ( files == null )
 1723  
         {
 1724  0
             return;
 1725  
         }
 1726  
 
 1727  397
         for ( final File file : files )
 1728  
         {
 1729  
             try
 1730  
             {
 1731  267
                 forceDelete( file );
 1732  
             }
 1733  0
             catch ( final IOException ioe )
 1734  
             {
 1735  0
                 exception = ioe;
 1736  267
             }
 1737  
         }
 1738  
 
 1739  130
         if ( null != exception )
 1740  
         {
 1741  0
             throw exception;
 1742  
         }
 1743  130
     }
 1744  
 
 1745  
     /**
 1746  
      * Recursively count size of a directory.
 1747  
      *
 1748  
      * @param directory a directory
 1749  
      * @return size of directory in bytes.
 1750  
      */
 1751  
     public static long sizeOfDirectory( final String directory )
 1752  
     {
 1753  0
         return sizeOfDirectory( new File( directory ) );
 1754  
     }
 1755  
 
 1756  
     /**
 1757  
      * Recursively count size of a directory.
 1758  
      *
 1759  
      * @param directory a directory
 1760  
      * @return size of directory in bytes.
 1761  
      */
 1762  
     public static long sizeOfDirectory( final File directory )
 1763  
     {
 1764  3
         if ( !directory.exists() )
 1765  
         {
 1766  1
             final String message = directory + " does not exist";
 1767  1
             throw new IllegalArgumentException( message );
 1768  
         }
 1769  
 
 1770  2
         if ( !directory.isDirectory() )
 1771  
         {
 1772  1
             final String message = directory + " is not a directory";
 1773  1
             throw new IllegalArgumentException( message );
 1774  
         }
 1775  
 
 1776  1
         long size = 0;
 1777  
 
 1778  1
         final File[] files = directory.listFiles();
 1779  1
         for ( final File file : files )
 1780  
         {
 1781  0
             if ( file.isDirectory() )
 1782  
             {
 1783  0
                 size += sizeOfDirectory( file );
 1784  
             }
 1785  
             else
 1786  
             {
 1787  0
                 size += file.length();
 1788  
             }
 1789  
         }
 1790  
 
 1791  1
         return size;
 1792  
     }
 1793  
 
 1794  
     /**
 1795  
      * Return the files contained in the directory, using inclusion and exclusion Ant patterns, including the directory
 1796  
      * name in each of the files
 1797  
      *
 1798  
      * @param directory the directory to scan
 1799  
      * @param includes the includes pattern, comma separated
 1800  
      * @param excludes the excludes pattern, comma separated
 1801  
      * @return a list of File objects
 1802  
      * @throws IOException
 1803  
      * @see #getFileNames(File, String, String, boolean)
 1804  
      */
 1805  
     public static List<File> getFiles( File directory, String includes, String excludes )
 1806  
         throws IOException
 1807  
     {
 1808  0
         return getFiles( directory, includes, excludes, true );
 1809  
     }
 1810  
 
 1811  
     /**
 1812  
      * Return the files contained in the directory, using inclusion and exclusion Ant patterns
 1813  
      *
 1814  
      * @param directory the directory to scan
 1815  
      * @param includes the includes pattern, comma separated
 1816  
      * @param excludes the excludes pattern, comma separated
 1817  
      * @param includeBasedir true to include the base dir in each file
 1818  
      * @return a list of File objects
 1819  
      * @throws IOException
 1820  
      * @see #getFileNames(File, String, String, boolean)
 1821  
      */
 1822  
     public static List<File> getFiles( File directory, String includes, String excludes, boolean includeBasedir )
 1823  
         throws IOException
 1824  
     {
 1825  2
         List<String> fileNames = getFileNames( directory, includes, excludes, includeBasedir );
 1826  
 
 1827  2
         List<File> files = new ArrayList<File>();
 1828  
 
 1829  2
         for ( String filename : fileNames )
 1830  
         {
 1831  6
             files.add( new File( filename ) );
 1832  6
         }
 1833  
 
 1834  2
         return files;
 1835  
     }
 1836  
 
 1837  
     /**
 1838  
      * Return a list of files as String depending options. This method use case sensitive file name.
 1839  
      *
 1840  
      * @param directory the directory to scan
 1841  
      * @param includes the includes pattern, comma separated
 1842  
      * @param excludes the excludes pattern, comma separated
 1843  
      * @param includeBasedir true to include the base dir in each String of file
 1844  
      * @return a list of files as String
 1845  
      * @throws IOException
 1846  
      */
 1847  
     public static List<String> getFileNames( File directory, String includes, String excludes, boolean includeBasedir )
 1848  
         throws IOException
 1849  
     {
 1850  2
         return getFileNames( directory, includes, excludes, includeBasedir, true );
 1851  
     }
 1852  
 
 1853  
     /**
 1854  
      * Return a list of files as String depending options.
 1855  
      *
 1856  
      * @param directory the directory to scan
 1857  
      * @param includes the includes pattern, comma separated
 1858  
      * @param excludes the excludes pattern, comma separated
 1859  
      * @param includeBasedir true to include the base dir in each String of file
 1860  
      * @param isCaseSensitive true if case sensitive
 1861  
      * @return a list of files as String
 1862  
      * @throws IOException
 1863  
      */
 1864  
     public static List<String> getFileNames( File directory, String includes, String excludes, boolean includeBasedir,
 1865  
                                              boolean isCaseSensitive )
 1866  
         throws IOException
 1867  
     {
 1868  2
         return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false );
 1869  
     }
 1870  
 
 1871  
     /**
 1872  
      * Return a list of directories as String depending options. This method use case sensitive file name.
 1873  
      *
 1874  
      * @param directory the directory to scan
 1875  
      * @param includes the includes pattern, comma separated
 1876  
      * @param excludes the excludes pattern, comma separated
 1877  
      * @param includeBasedir true to include the base dir in each String of file
 1878  
      * @return a list of directories as String
 1879  
      * @throws IOException
 1880  
      */
 1881  
     public static List<String> getDirectoryNames( File directory, String includes, String excludes,
 1882  
                                                   boolean includeBasedir )
 1883  
         throws IOException
 1884  
     {
 1885  0
         return getDirectoryNames( directory, includes, excludes, includeBasedir, true );
 1886  
     }
 1887  
 
 1888  
     /**
 1889  
      * Return a list of directories as String depending options.
 1890  
      *
 1891  
      * @param directory the directory to scan
 1892  
      * @param includes the includes pattern, comma separated
 1893  
      * @param excludes the excludes pattern, comma separated
 1894  
      * @param includeBasedir true to include the base dir in each String of file
 1895  
      * @param isCaseSensitive true if case sensitive
 1896  
      * @return a list of directories as String
 1897  
      * @throws IOException
 1898  
      */
 1899  
     public static List<String> getDirectoryNames( File directory, String includes, String excludes,
 1900  
                                                   boolean includeBasedir, boolean isCaseSensitive )
 1901  
         throws IOException
 1902  
     {
 1903  0
         return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true );
 1904  
     }
 1905  
 
 1906  
     /**
 1907  
      * Return a list of files as String depending options.
 1908  
      *
 1909  
      * @param directory the directory to scan
 1910  
      * @param includes the includes pattern, comma separated
 1911  
      * @param excludes the excludes pattern, comma separated
 1912  
      * @param includeBasedir true to include the base dir in each String of file
 1913  
      * @param isCaseSensitive true if case sensitive
 1914  
      * @param getFiles true if get files
 1915  
      * @param getDirectories true if get directories
 1916  
      * @return a list of files as String
 1917  
      * @throws IOException
 1918  
      */
 1919  
     public static List<String> getFileAndDirectoryNames( File directory, String includes, String excludes,
 1920  
                                                          boolean includeBasedir, boolean isCaseSensitive,
 1921  
                                                          boolean getFiles, boolean getDirectories )
 1922  
         throws IOException
 1923  
     {
 1924  2
         DirectoryScanner scanner = new DirectoryScanner();
 1925  
 
 1926  2
         scanner.setBasedir( directory );
 1927  
 
 1928  2
         if ( includes != null )
 1929  
         {
 1930  2
             scanner.setIncludes( StringUtils.split( includes, "," ) );
 1931  
         }
 1932  
 
 1933  2
         if ( excludes != null )
 1934  
         {
 1935  2
             scanner.setExcludes( StringUtils.split( excludes, "," ) );
 1936  
         }
 1937  
 
 1938  2
         scanner.setCaseSensitive( isCaseSensitive );
 1939  
 
 1940  2
         scanner.scan();
 1941  
 
 1942  2
         List<String> list = new ArrayList<String>();
 1943  
 
 1944  2
         if ( getFiles )
 1945  
         {
 1946  2
             String[] files = scanner.getIncludedFiles();
 1947  
 
 1948  8
             for ( String file : files )
 1949  
             {
 1950  6
                 if ( includeBasedir )
 1951  
                 {
 1952  0
                     list.add( directory + FileUtils.FS + file );
 1953  
                 }
 1954  
                 else
 1955  
                 {
 1956  6
                     list.add( file );
 1957  
                 }
 1958  
             }
 1959  
         }
 1960  
 
 1961  2
         if ( getDirectories )
 1962  
         {
 1963  0
             String[] directories = scanner.getIncludedDirectories();
 1964  
 
 1965  0
             for ( String directory1 : directories )
 1966  
             {
 1967  0
                 if ( includeBasedir )
 1968  
                 {
 1969  0
                     list.add( directory + FileUtils.FS + directory1 );
 1970  
                 }
 1971  
                 else
 1972  
                 {
 1973  0
                     list.add( directory1 );
 1974  
                 }
 1975  
             }
 1976  
         }
 1977  
 
 1978  2
         return list;
 1979  
     }
 1980  
 
 1981  
     /**
 1982  
      * Copy a directory to an other one.
 1983  
      *
 1984  
      * @param sourceDirectory the source dir
 1985  
      * @param destinationDirectory the target dir
 1986  
      * @throws IOException if any
 1987  
      */
 1988  
     public static void copyDirectory( File sourceDirectory, File destinationDirectory )
 1989  
         throws IOException
 1990  
     {
 1991  0
         copyDirectory( sourceDirectory, destinationDirectory, "**", null );
 1992  0
     }
 1993  
 
 1994  
     /**
 1995  
      * Copy a directory to an other one.
 1996  
      *
 1997  
      * @param sourceDirectory the source dir
 1998  
      * @param destinationDirectory the target dir
 1999  
      * @param includes include pattern
 2000  
      * @param excludes exclude pattern
 2001  
      * @throws IOException if any
 2002  
      * @see #getFiles(File, String, String)
 2003  
      */
 2004  
     public static void copyDirectory( File sourceDirectory, File destinationDirectory, String includes,
 2005  
                                       String excludes )
 2006  
         throws IOException
 2007  
     {
 2008  0
         if ( !sourceDirectory.exists() )
 2009  
         {
 2010  0
             return;
 2011  
         }
 2012  
 
 2013  0
         List<File> files = getFiles( sourceDirectory, includes, excludes );
 2014  
 
 2015  0
         for ( File file : files )
 2016  
         {
 2017  0
             copyFileToDirectory( file, destinationDirectory );
 2018  0
         }
 2019  0
     }
 2020  
 
 2021  
     /**
 2022  
      * Copies a entire directory layout : no files will be copied only directories
 2023  
      * <p/>
 2024  
      * Note:
 2025  
      * <ul>
 2026  
      * <li>It will include empty directories.
 2027  
      * <li>The <code>sourceDirectory</code> must exists.
 2028  
      * </ul>
 2029  
      *
 2030  
      * @param sourceDirectory the source dir
 2031  
      * @param destinationDirectory the target dir
 2032  
      * @param includes include pattern
 2033  
      * @param excludes exclude pattern
 2034  
      * @throws IOException if any
 2035  
      * @since 1.5.7
 2036  
      */
 2037  
     public static void copyDirectoryLayout( File sourceDirectory, File destinationDirectory, String[] includes,
 2038  
                                             String[] excludes )
 2039  
         throws IOException
 2040  
     {
 2041  1
         if ( sourceDirectory == null )
 2042  
         {
 2043  0
             throw new IOException( "source directory can't be null." );
 2044  
         }
 2045  
 
 2046  1
         if ( destinationDirectory == null )
 2047  
         {
 2048  0
             throw new IOException( "destination directory can't be null." );
 2049  
         }
 2050  
 
 2051  1
         if ( sourceDirectory.equals( destinationDirectory ) )
 2052  
         {
 2053  0
             throw new IOException( "source and destination are the same directory." );
 2054  
         }
 2055  
 
 2056  1
         if ( !sourceDirectory.exists() )
 2057  
         {
 2058  0
             throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
 2059  
         }
 2060  
 
 2061  1
         DirectoryScanner scanner = new DirectoryScanner();
 2062  
 
 2063  1
         scanner.setBasedir( sourceDirectory );
 2064  
 
 2065  1
         if ( includes != null && includes.length >= 1 )
 2066  
         {
 2067  0
             scanner.setIncludes( includes );
 2068  
         }
 2069  
         else
 2070  
         {
 2071  1
             scanner.setIncludes( new String[] { "**" } );
 2072  
         }
 2073  
 
 2074  1
         if ( excludes != null && excludes.length >= 1 )
 2075  
         {
 2076  0
             scanner.setExcludes( excludes );
 2077  
         }
 2078  
 
 2079  1
         scanner.addDefaultExcludes();
 2080  1
         scanner.scan();
 2081  1
         List<String> includedDirectories = Arrays.asList( scanner.getIncludedDirectories() );
 2082  
 
 2083  1
         for ( String name : includedDirectories )
 2084  
         {
 2085  4
             File source = new File( sourceDirectory, name );
 2086  
 
 2087  4
             if ( source.equals( sourceDirectory ) )
 2088  
             {
 2089  1
                 continue;
 2090  
             }
 2091  
 
 2092  3
             File destination = new File( destinationDirectory, name );
 2093  3
             destination.mkdirs();
 2094  3
         }
 2095  1
     }
 2096  
 
 2097  
     /**
 2098  
      * Copies a entire directory structure.
 2099  
      * <p/>
 2100  
      * Note:
 2101  
      * <ul>
 2102  
      * <li>It will include empty directories.
 2103  
      * <li>The <code>sourceDirectory</code> must exists.
 2104  
      * </ul>
 2105  
      *
 2106  
      * @param sourceDirectory the source dir
 2107  
      * @param destinationDirectory the target dir
 2108  
      * @throws IOException if any
 2109  
      */
 2110  
     public static void copyDirectoryStructure( File sourceDirectory, File destinationDirectory )
 2111  
         throws IOException
 2112  
     {
 2113  3
         copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false );
 2114  2
     }
 2115  
 
 2116  
     /**
 2117  
      * Copies an entire directory structure but only source files with timestamp later than the destinations'.
 2118  
      * <p/>
 2119  
      * Note:
 2120  
      * <ul>
 2121  
      * <li>It will include empty directories.
 2122  
      * <li>The <code>sourceDirectory</code> must exists.
 2123  
      * </ul>
 2124  
      *
 2125  
      * @param sourceDirectory the source dir
 2126  
      * @param destinationDirectory the target dir
 2127  
      * @throws IOException if any
 2128  
      */
 2129  
     public static void copyDirectoryStructureIfModified( File sourceDirectory, File destinationDirectory )
 2130  
         throws IOException
 2131  
     {
 2132  3
         copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, true );
 2133  3
     }
 2134  
 
 2135  
     private static void copyDirectoryStructure( File sourceDirectory, File destinationDirectory,
 2136  
                                                 File rootDestinationDirectory, boolean onlyModifiedFiles )
 2137  
         throws IOException
 2138  
     {
 2139  22
         if ( sourceDirectory == null )
 2140  
         {
 2141  0
             throw new IOException( "source directory can't be null." );
 2142  
         }
 2143  
 
 2144  22
         if ( destinationDirectory == null )
 2145  
         {
 2146  0
             throw new IOException( "destination directory can't be null." );
 2147  
         }
 2148  
 
 2149  22
         if ( sourceDirectory.equals( destinationDirectory ) )
 2150  
         {
 2151  1
             throw new IOException( "source and destination are the same directory." );
 2152  
         }
 2153  
 
 2154  21
         if ( !sourceDirectory.exists() )
 2155  
         {
 2156  0
             throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
 2157  
         }
 2158  
 
 2159  21
         File[] files = sourceDirectory.listFiles();
 2160  
 
 2161  21
         String sourcePath = sourceDirectory.getAbsolutePath();
 2162  
 
 2163  49
         for ( File file : files )
 2164  
         {
 2165  28
             if ( file.equals( rootDestinationDirectory ) )
 2166  
             {
 2167  
                 // We don't copy the destination directory in itself
 2168  0
                 continue;
 2169  
             }
 2170  
 
 2171  28
             String dest = file.getAbsolutePath();
 2172  
 
 2173  28
             dest = dest.substring( sourcePath.length() + 1 );
 2174  
 
 2175  28
             File destination = new File( destinationDirectory, dest );
 2176  
 
 2177  28
             if ( file.isFile() )
 2178  
             {
 2179  12
                 destination = destination.getParentFile();
 2180  
 
 2181  12
                 if ( onlyModifiedFiles )
 2182  
                 {
 2183  9
                     copyFileToDirectoryIfModified( file, destination );
 2184  
                 }
 2185  
                 else
 2186  
                 {
 2187  3
                     copyFileToDirectory( file, destination );
 2188  
                 }
 2189  
             }
 2190  16
             else if ( file.isDirectory() )
 2191  
             {
 2192  16
                 if ( !destination.exists() && !destination.mkdirs() )
 2193  
                 {
 2194  0
                     throw new IOException( "Could not create destination directory '" + destination.getAbsolutePath()
 2195  
                         + "'." );
 2196  
                 }
 2197  
 
 2198  16
                 copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles );
 2199  
             }
 2200  
             else
 2201  
             {
 2202  0
                 throw new IOException( "Unknown file type: " + file.getAbsolutePath() );
 2203  
             }
 2204  
         }
 2205  21
     }
 2206  
 
 2207  
     /**
 2208  
      * Renames a file, even if that involves crossing file system boundaries.
 2209  
      * <p/>
 2210  
      * <p>
 2211  
      * This will remove <code>to</code> (if it exists), ensure that <code>to</code>'s parent directory exists and move
 2212  
      * <code>from</code>, which involves deleting <code>from</code> as well.
 2213  
      * </p>
 2214  
      *
 2215  
      * @param from the file to move
 2216  
      * @param to the new file name
 2217  
      * @throws IOException if anything bad happens during this process. Note that <code>to</code> may have been deleted
 2218  
      *             already when this happens.
 2219  
      */
 2220  
     public static void rename( File from, File to )
 2221  
         throws IOException
 2222  
     {
 2223  0
         if ( to.exists() && !to.delete() )
 2224  
         {
 2225  0
             throw new IOException( "Failed to delete " + to + " while trying to rename " + from );
 2226  
         }
 2227  
 
 2228  0
         File parent = to.getParentFile();
 2229  0
         if ( parent != null && !parent.exists() && !parent.mkdirs() )
 2230  
         {
 2231  0
             throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from );
 2232  
         }
 2233  
 
 2234  0
         if ( !from.renameTo( to ) )
 2235  
         {
 2236  0
             copyFile( from, to );
 2237  0
             if ( !from.delete() )
 2238  
             {
 2239  0
                 throw new IOException( "Failed to delete " + from + " while trying to rename it." );
 2240  
             }
 2241  
         }
 2242  0
     }
 2243  
 
 2244  
     /**
 2245  
      * Create a temporary file in a given directory.
 2246  
      * <p/>
 2247  
      * <p>
 2248  
      * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent
 2249  
      * invocation of this method will yield a different file name.
 2250  
      * </p>
 2251  
      * <p/>
 2252  
      * The filename is prefixNNNNNsuffix where NNNN is a random number
 2253  
      * </p>
 2254  
      * <p>
 2255  
      * This method is different to {@link File#createTempFile(String, String, File)} of JDK 1.2 as it doesn't create the
 2256  
      * file itself. It uses the location pointed to by java.io.tmpdir when the parentDir attribute is null.
 2257  
      * </p>
 2258  
      * <p>
 2259  
      * To delete automatically the file created by this method, use the {@link File#deleteOnExit()} method.
 2260  
      * </p>
 2261  
      *
 2262  
      * @param prefix prefix before the random number
 2263  
      * @param suffix file extension; include the '.'
 2264  
      * @param parentDir Directory to create the temporary file in <code>-java.io.tmpdir</code> used if not specificed
 2265  
      * @return a File reference to the new temporary file.
 2266  
      */
 2267  
     public static File createTempFile( String prefix, String suffix, File parentDir )
 2268  
     {
 2269  11
         File result = null;
 2270  11
         String parent = System.getProperty( "java.io.tmpdir" );
 2271  11
         if ( parentDir != null )
 2272  
         {
 2273  0
             parent = parentDir.getPath();
 2274  
         }
 2275  11
         DecimalFormat fmt = new DecimalFormat( "#####" );
 2276  11
         SecureRandom secureRandom = new SecureRandom();
 2277  11
         long secureInitializer = secureRandom.nextLong();
 2278  11
         Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() );
 2279  11
         synchronized ( rand )
 2280  
         {
 2281  
             do
 2282  
             {
 2283  11
                 result = new File( parent, prefix + fmt.format( Math.abs( rand.nextInt() ) ) + suffix );
 2284  
             }
 2285  11
             while ( result.exists() );
 2286  11
         }
 2287  
 
 2288  11
         return result;
 2289  
     }
 2290  
 
 2291  
     /**
 2292  
      * <b>If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified()</b>
 2293  
      *
 2294  
      * @param from the file to copy
 2295  
      * @param to the destination file
 2296  
      * @param encoding the file output encoding (only if wrappers is not empty)
 2297  
      * @param wrappers array of {@link FilterWrapper}
 2298  
      * @throws IOException if an IO error occurs during copying or filtering
 2299  
      */
 2300  
     public static void copyFile( File from, File to, String encoding, FilterWrapper[] wrappers )
 2301  
         throws IOException
 2302  
     {
 2303  4
         copyFile( from, to, encoding, wrappers, false );
 2304  4
     }
 2305  
 
 2306  1
     public static abstract class FilterWrapper
 2307  
     {
 2308  
         public abstract Reader getReader( Reader fileReader );
 2309  
     }
 2310  
 
 2311  
     /**
 2312  
      * <b>If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if
 2313  
      * overwrite is true</b>
 2314  
      *
 2315  
      * @param from the file to copy
 2316  
      * @param to the destination file
 2317  
      * @param encoding the file output encoding (only if wrappers is not empty)
 2318  
      * @param wrappers array of {@link FilterWrapper}
 2319  
      * @param overwrite if true and f wrappers is null or empty, the file will be copy even if to.lastModified() <
 2320  
      *            from.lastModified()
 2321  
      * @throws IOException if an IO error occurs during copying or filtering
 2322  
      * @since 1.5.2
 2323  
      */
 2324  
     public static void copyFile( File from, File to, String encoding, FilterWrapper[] wrappers, boolean overwrite )
 2325  
         throws IOException
 2326  
     {
 2327  5
         if ( wrappers != null && wrappers.length > 0 )
 2328  
         {
 2329  
             // buffer so it isn't reading a byte at a time!
 2330  1
             Reader fileReader = null;
 2331  1
             Writer fileWriter = null;
 2332  
             try
 2333  
             {
 2334  1
                 if ( encoding == null || encoding.length() < 1 )
 2335  
                 {
 2336  0
                     fileReader = new BufferedReader( new FileReader( from ) );
 2337  0
                     fileWriter = new FileWriter( to );
 2338  
                 }
 2339  
                 else
 2340  
                 {
 2341  1
                     FileInputStream instream = new FileInputStream( from );
 2342  
 
 2343  1
                     FileOutputStream outstream = new FileOutputStream( to );
 2344  
 
 2345  1
                     fileReader = new BufferedReader( new InputStreamReader( instream, encoding ) );
 2346  
 
 2347  1
                     fileWriter = new OutputStreamWriter( outstream, encoding );
 2348  
                 }
 2349  
 
 2350  1
                 Reader reader = fileReader;
 2351  2
                 for ( FilterWrapper wrapper : wrappers )
 2352  
                 {
 2353  1
                     reader = wrapper.getReader( reader );
 2354  
                 }
 2355  
 
 2356  1
                 IOUtil.copy( reader, fileWriter );
 2357  1
                 fileWriter.close();
 2358  1
                 fileWriter = null;
 2359  1
                 fileReader.close();
 2360  1
                 fileReader = null;
 2361  
             }
 2362  
             finally
 2363  
             {
 2364  1
                 IOUtil.close( fileReader );
 2365  1
                 IOUtil.close( fileWriter );
 2366  1
             }
 2367  1
         }
 2368  
         else
 2369  
         {
 2370  4
             if ( to.lastModified() < from.lastModified() || overwrite )
 2371  
             {
 2372  3
                 copyFile( from, to );
 2373  
             }
 2374  
         }
 2375  5
     }
 2376  
 
 2377  
     /**
 2378  
      * Note: the file content is read with platform encoding
 2379  
      *
 2380  
      * @param file the file
 2381  
      * @return a List containing every every line not starting with # and not empty
 2382  
      * @throws IOException if any
 2383  
      */
 2384  
     public static List<String> loadFile( File file )
 2385  
         throws IOException
 2386  
     {
 2387  0
         final List<String> lines = new ArrayList<String>();
 2388  0
         BufferedReader reader = null;
 2389  
         try
 2390  
         {
 2391  0
             if ( file.exists() )
 2392  
             {
 2393  0
                 reader = new BufferedReader( new FileReader( file ) );
 2394  
 
 2395  0
                 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
 2396  
                 {
 2397  0
                     line = line.trim();
 2398  
 
 2399  0
                     if ( !line.startsWith( "#" ) && line.length() != 0 )
 2400  
                     {
 2401  0
                         lines.add( line );
 2402  
                     }
 2403  
                 }
 2404  
 
 2405  0
                 reader.close();
 2406  0
                 reader = null;
 2407  
             }
 2408  
         }
 2409  
         finally
 2410  
         {
 2411  0
             IOUtil.close( reader );
 2412  0
         }
 2413  
 
 2414  0
         return lines;
 2415  
     }
 2416  
 
 2417  
     /**
 2418  
      * For Windows OS, check if the file name contains any of the following characters:
 2419  
      * <code>":", "*", "?", "\"", "<", ">", "|"</code>
 2420  
      *
 2421  
      * @param f not null file
 2422  
      * @return <code>false</code> if the file path contains any of forbidden Windows characters, <code>true</code> if
 2423  
      *         the Os is not Windows or if the file path respect the Windows constraints.
 2424  
      * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
 2425  
      * @since 1.5.2
 2426  
      */
 2427  
     public static boolean isValidWindowsFileName( File f )
 2428  
     {
 2429  1
         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
 2430  
         {
 2431  0
             if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 )
 2432  
             {
 2433  0
                 return false;
 2434  
             }
 2435  
 
 2436  0
             File parentFile = f.getParentFile();
 2437  0
             if ( parentFile != null )
 2438  
             {
 2439  0
                 return isValidWindowsFileName( parentFile );
 2440  
             }
 2441  
         }
 2442  
 
 2443  1
         return true;
 2444  
     }
 2445  
 }