mbenson     2005/01/26 09:31:04

  Modified:    src/main/org/apache/tools/ant/util Tag: ANT_16_BRANCH
                        FileUtils.java
  Log:
  Sync with HEAD, minus two deprecation warnings.
  
  Revision  Changes    Path
  No                   revision
  No                   revision
  1.56.2.8  +406 -294  ant/src/main/org/apache/tools/ant/util/FileUtils.java
  
  Index: FileUtils.java
  ===================================================================
  RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/util/FileUtils.java,v
  retrieving revision 1.56.2.7
  retrieving revision 1.56.2.8
  diff -u -r1.56.2.7 -r1.56.2.8
  --- FileUtils.java    9 Mar 2004 17:01:57 -0000       1.56.2.7
  +++ FileUtils.java    26 Jan 2005 17:31:04 -0000      1.56.2.8
  @@ -1,5 +1,5 @@
   /*
  - * Copyright  2001-2004 The Apache Software Foundation
  + * Copyright  2001-2005 The Apache Software Foundation
    *
    *  Licensed under the Apache License, Version 2.0 (the "License");
    *  you may not use this file except in compliance with the License.
  @@ -30,7 +30,8 @@
   import java.io.InputStreamReader;
   import java.io.OutputStreamWriter;
   import java.io.Reader;
  -import java.lang.reflect.Method;
  +import java.io.Writer;
  +import java.io.OutputStream;
   import java.net.MalformedURLException;
   import java.net.URL;
   import java.text.CharacterIterator;
  @@ -49,19 +50,25 @@
   
   /**
    * This class also encapsulates methods which allow Files to be
  - * refered to using abstract path names which are translated to native
  + * referred to using abstract path names which are translated to native
    * system file paths at runtime as well as copying files or setting
  - * there last modification time.
  + * their last modification time.
    *
    * @version $Revision$
    */
   
   public class FileUtils {
  -    private static Random rand = new Random(System.currentTimeMillis());
  -    private static Object lockReflection = new Object();
  -    private static java.lang.reflect.Method setLastModified = null;
   
  -    private boolean onNetWare = Os.isFamily("netware");
  +    private static final FileUtils PRIMARY_INSTANCE = new FileUtils();
  +
  +    //get some non-crypto-grade randomness from various places.
  +    private static Random rand = new Random(System.currentTimeMillis()
  +            + Runtime.getRuntime().freeMemory());
  +
  +    private static boolean onNetWare = Os.isFamily("netware");
  +    private static boolean onDos = Os.isFamily("dos");
  +
  +    private static final int BUF_SIZE = 8192;
   
       // for toURI
       private static boolean[] isSpecial = new boolean[256];
  @@ -69,10 +76,15 @@
       private static char[] escapedChar2 = new char[256];
   
       /**
  -     * the granularity of timestamps under FAT
  +     * The granularity of timestamps under FAT.
        */
       public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000;
   
  +    /**
  +     * The granularity of timestamps under Unix.
  +     */
  +    public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000;
  +
   
       // stolen from FilePathToURI of the Xerces-J team
       static {
  @@ -106,16 +118,26 @@
       }
   
       /**
  +     * Method to retrieve The FileUtils, which is shared by all users of this
  +     * method.
  +     * @return an instance of FileUtils.
  +     * @since Ant 1.6.3
  +     */
  +    public static FileUtils getFileUtils() {
  +        return PRIMARY_INSTANCE;
  +    }
  +
  +    /**
        * Empty constructor.
        */
       protected FileUtils() {
       }
   
       /**
  -     * Get the URL for a file taking into account # characters
  +     * Get the URL for a file taking into account # characters.
        *
        * @param file the file whose URL representation is required.
  -     * @return The FileURL value
  +     * @return The FileURL value.
        * @throws MalformedURLException if the URL representation cannot be
        *      formed.
        */
  @@ -124,7 +146,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a destination.
  +     * Convenience method to copy a file from a source to a destination.
        * No filtering is performed.
        *
        * @param sourceFile Name of file to copy from.
  @@ -132,7 +154,7 @@
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(String sourceFile, String destFile)
           throws IOException {
  @@ -140,16 +162,16 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a destination
  +     * Convenience method to copy a file from a source to a destination
        * specifying if token filtering must be used.
        *
        * @param sourceFile Name of file to copy from.
        *                   Must not be <code>null</code>.
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(String sourceFile, String destFile,
                            FilterSetCollection filters)
  @@ -159,7 +181,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used and if
        * source files may overwrite newer destination files.
        *
  @@ -167,11 +189,11 @@
        *                   Must not be <code>null</code>.
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(String sourceFile, String destFile, 
FilterSetCollection filters,
                            boolean overwrite) throws IOException {
  @@ -180,7 +202,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * source files may overwrite newer destination files and the
        * last modified time of <code>destFile</code> file should be made equal
  @@ -190,14 +212,14 @@
        *                   Must not be <code>null</code>.
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
        * @param preserveLastModified Whether or not the last modified time of
        *                             the resulting file should be set to that
        *                             of the source file.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(String sourceFile, String destFile, 
FilterSetCollection filters,
                            boolean overwrite, boolean preserveLastModified)
  @@ -207,7 +229,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * source files may overwrite newer destination files and the
        * last modified time of <code>destFile</code> file should be made equal
  @@ -217,7 +239,7 @@
        *                   Must not be <code>null</code>.
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
        * @param preserveLastModified Whether or not the last modified time of
  @@ -225,7 +247,7 @@
        *                             of the source file.
        * @param encoding the encoding used to read and write the files.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        *
        * @since Ant 1.5
        */
  @@ -238,7 +260,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * filter chains must be used, if source files may overwrite
        * newer destination files and the last modified time of
  @@ -249,7 +271,7 @@
        *                   Must not be <code>null</code>.
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param filterChains filterChains to apply during the copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
  @@ -257,9 +279,9 @@
        *                             the resulting file should be set to that
        *                             of the source file.
        * @param encoding the encoding used to read and write the files.
  -     * @param project the project instance
  +     * @param project the project instance.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        *
        * @since Ant 1.5
        */
  @@ -274,7 +296,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * filter chains must be used, if source files may overwrite
        * newer destination files and the last modified time of
  @@ -285,7 +307,7 @@
        *                   Must not be <code>null</code>.
        * @param destFile Name of file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param filterChains filterChains to apply during the copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
  @@ -294,9 +316,9 @@
        *                             of the source file.
        * @param inputEncoding the encoding used to read the files.
        * @param outputEncoding the encoding used to write the files.
  -     * @param project the project instance
  +     * @param project the project instance.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        *
        * @since Ant 1.6
        */
  @@ -312,7 +334,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a destination.
  +     * Convenience method to copy a file from a source to a destination.
        * No filtering is performed.
        *
        * @param sourceFile the file to copy from.
  @@ -320,23 +342,23 @@
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(File sourceFile, File destFile) throws IOException {
           copyFile(sourceFile, destFile, null, false, false);
       }
   
       /**
  -     * Convienence method to copy a file from a source to a destination
  +     * Convenience method to copy a file from a source to a destination
        * specifying if token filtering must be used.
        *
        * @param sourceFile the file to copy from.
        *                   Must not be <code>null</code>.
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(File sourceFile, File destFile, FilterSetCollection 
filters)
           throws IOException {
  @@ -344,7 +366,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used and if
        * source files may overwrite newer destination files.
        *
  @@ -352,11 +374,11 @@
        *                   Must not be <code>null</code>.
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(File sourceFile, File destFile, FilterSetCollection 
filters,
                            boolean overwrite) throws IOException {
  @@ -364,7 +386,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * source files may overwrite newer destination files and the
        * last modified time of <code>destFile</code> file should be made equal
  @@ -374,14 +396,14 @@
        *                   Must not be <code>null</code>.
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
        * @param preserveLastModified Whether or not the last modified time of
        *                             the resulting file should be set to that
        *                             of the source file.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        */
       public void copyFile(File sourceFile, File destFile, FilterSetCollection 
filters,
                            boolean overwrite, boolean preserveLastModified)
  @@ -391,7 +413,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * source files may overwrite newer destination files, the last
        * modified time of <code>destFile</code> file should be made
  @@ -402,7 +424,7 @@
        *                   Must not be <code>null</code>.
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
        * @param preserveLastModified Whether or not the last modified time of
  @@ -410,7 +432,7 @@
        *                             of the source file.
        * @param encoding the encoding used to read and write the files.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        *
        * @since Ant 1.5
        */
  @@ -423,7 +445,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * filter chains must be used, if source files may overwrite
        * newer destination files and the last modified time of
  @@ -434,7 +456,7 @@
        *                   Must not be <code>null</code>.
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param filterChains filterChains to apply during the copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
  @@ -442,9 +464,9 @@
        *                             the resulting file should be set to that
        *                             of the source file.
        * @param encoding the encoding used to read and write the files.
  -     * @param project the project instance
  +     * @param project the project instance.
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        *
        * @since Ant 1.5
        */
  @@ -458,7 +480,7 @@
       }
   
       /**
  -     * Convienence method to copy a file from a source to a
  +     * Convenience method to copy a file from a source to a
        * destination specifying if token filtering must be used, if
        * filter chains must be used, if source files may overwrite
        * newer destination files and the last modified time of
  @@ -469,7 +491,7 @@
        *                   Must not be <code>null</code>.
        * @param destFile the file to copy to.
        *                 Must not be <code>null</code>.
  -     * @param filters the collection of filters to apply to this copy
  +     * @param filters the collection of filters to apply to this copy.
        * @param filterChains filterChains to apply during the copy.
        * @param overwrite Whether or not the destination file should be
        *                  overwritten if it already exists.
  @@ -478,10 +500,10 @@
        *                             of the source file.
        * @param inputEncoding the encoding used to read the files.
        * @param outputEncoding the encoding used to write the files.
  -     * @param project the project instance
  +     * @param project the project instance.
        *
        *
  -     * @throws IOException if the copying fails
  +     * @throws IOException if the copying fails.
        *
        * @since Ant 1.6
        */
  @@ -498,23 +520,19 @@
               if (destFile.exists() && destFile.isFile()) {
                   destFile.delete();
               }
  -
               // ensure that parent dir of dest file exists!
               // not using getParentFile method to stay 1.1 compat
  -            File parent = getParentFile(destFile);
  +            File parent = destFile.getParentFile();
               if (parent != null && !parent.exists()) {
                   parent.mkdirs();
               }
  -
               final boolean filterSetsAvailable = (filters != null
                                                    && filters.hasFilters());
               final boolean filterChainsAvailable = (filterChains != null
                                                      && filterChains.size() > 
0);
  -
               if (filterSetsAvailable) {
                   BufferedReader in = null;
                   BufferedWriter out = null;
  -
                   try {
                       if (inputEncoding == null) {
                           in = new BufferedReader(new FileReader(sourceFile));
  @@ -524,7 +542,6 @@
                                                       inputEncoding);
                           in = new BufferedReader(isr);
                       }
  -
                       if (outputEncoding == null) {
                           out = new BufferedWriter(new FileWriter(destFile));
                       } else {
  @@ -533,17 +550,15 @@
                                                        outputEncoding);
                           out = new BufferedWriter(osw);
                       }
  -
                       if (filterChainsAvailable) {
                           ChainReaderHelper crh = new ChainReaderHelper();
  -                        crh.setBufferSize(8192);
  +                        crh.setBufferSize(BUF_SIZE);
                           crh.setPrimaryReader(in);
                           crh.setFilterChains(filterChains);
                           crh.setProject(project);
                           Reader rdr = crh.getAssembledReader();
                           in = new BufferedReader(rdr);
                       }
  -
                       LineTokenizer lineTokenizer = new LineTokenizer();
                       lineTokenizer.setIncludeDelims(true);
                       String newline = null;
  @@ -560,12 +575,8 @@
                           line = lineTokenizer.getToken(in);
                       }
                   } finally {
  -                    if (out != null) {
  -                        out.close();
  -                    }
  -                    if (in != null) {
  -                        in.close();
  -                    }
  +                    close(out);
  +                    close(in);
                   }
               } else if (filterChainsAvailable
                          || (inputEncoding != null
  @@ -573,7 +584,6 @@
                          || (inputEncoding == null && outputEncoding != null)) 
{
                   BufferedReader in = null;
                   BufferedWriter out = null;
  -
                    try {
                        if (inputEncoding == null) {
                            in = new BufferedReader(new FileReader(sourceFile));
  @@ -584,7 +594,6 @@
                                        new FileInputStream(sourceFile),
                                        inputEncoding));
                        }
  -
                        if (outputEncoding == null) {
                            out = new BufferedWriter(new FileWriter(destFile));
                        } else {
  @@ -594,17 +603,16 @@
                                        new FileOutputStream(destFile),
                                        outputEncoding));
                        }
  -
                        if (filterChainsAvailable) {
                            ChainReaderHelper crh = new ChainReaderHelper();
  -                         crh.setBufferSize(8192);
  +                         crh.setBufferSize(BUF_SIZE);
                            crh.setPrimaryReader(in);
                            crh.setFilterChains(filterChains);
                            crh.setProject(project);
                            Reader rdr = crh.getAssembledReader();
                            in = new BufferedReader(rdr);
                        }
  -                     char[] buffer = new char[1024 * 8];
  +                     char[] buffer = new char[BUF_SIZE];
                        while (true) {
                            int nRead = in.read(buffer, 0, buffer.length);
                            if (nRead == -1) {
  @@ -612,13 +620,9 @@
                            }
                            out.write(buffer, 0, nRead);
                         }
  -                  } finally {
  -                      if (out != null) {
  -                         out.close();
  -                     }
  -                     if (in != null) {
  -                         in.close();
  -                     }
  +                 } finally {
  +                     close(out);
  +                     close(in);
                    }
               } else {
                   FileInputStream in = null;
  @@ -627,22 +631,17 @@
                       in = new FileInputStream(sourceFile);
                       out = new FileOutputStream(destFile);
   
  -                    byte[] buffer = new byte[8 * 1024];
  +                    byte[] buffer = new byte[BUF_SIZE];
                       int count = 0;
                       do {
                           out.write(buffer, 0, count);
                           count = in.read(buffer, 0, buffer.length);
                       } while (count != -1);
                   } finally {
  -                    if (out != null) {
  -                        out.close();
  -                    }
  -                    if (in != null) {
  -                        in.close();
  -                    }
  +                    close(out);
  +                    close(in);
                   }
               }
  -
               if (preserveLastModified) {
                   setFileLastModified(destFile, sourceFile.lastModified());
               }
  @@ -650,63 +649,19 @@
       }
   
       /**
  -     * see whether we have a setLastModified method in File and return it.
  -     *
  -     * @return a method to setLastModified.
  -     */
  -    protected final Method getSetLastModified() {
  -        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
  -            return null;
  -        }
  -        synchronized (lockReflection) {
  -            if (setLastModified == null) {
  -                try {
  -                    setLastModified =
  -                        java.io.File.class.getMethod("setLastModified",
  -                                                     new Class[] 
{Long.TYPE});
  -                } catch (NoSuchMethodException nse) {
  -                    throw new BuildException("File.setlastModified not in 
JDK > 1.1?",
  -                                             nse);
  -                }
  -            }
  -        }
  -        return setLastModified;
  -    }
  -
  -    /**
  -     * Calls File.setLastModified(long time) in a Java 1.1 compatible way.
  +     * Calls File.setLastModified(long time). Originally written to
  +     * to dynamically bind to that call on Java1.2+.
        *
        * @param file the file whose modified time is to be set
        * @param time the time to which the last modified time is to be set.
  -     *
  -     * @throws BuildException if the time cannot be set.
  +     *             if this is -1, the current time is used.
        */
  -    public void setFileLastModified(File file, long time)
  -        throws BuildException {
  -        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
  -            return;
  -        }
  -        Long[] times = new Long[1];
  -        if (time < 0) {
  -            times[0] = new Long(System.currentTimeMillis());
  -        } else {
  -            times[0] = new Long(time);
  -        }
  -
  -        try {
  -            getSetLastModified().invoke(file, times);
  -        } catch (java.lang.reflect.InvocationTargetException ite) {
  -            Throwable nested = ite.getTargetException();
  -            throw new BuildException("Exception setting the modification 
time "
  -                                     + "of " + file, nested);
  -        } catch (Throwable other) {
  -            throw new BuildException("Exception setting the modification 
time "
  -                                     + "of " + file, other);
  -        }
  +    public void setFileLastModified(File file, long time) {
  +        file.setLastModified((time < 0) ? System.currentTimeMillis() : time);
       }
   
       /**
  -     * Interpret the filename as a file relative to the given file -
  +     * Interpret the filename as a file relative to the given file
        * unless the filename already represents an absolute filename.
        *
        * @param file the "reference" file for relative paths. This
  @@ -715,7 +670,7 @@
        * of /).  If it is null, this call is equivalent to
        * <code>new java.io.File(filename)</code>.
        *
  -     * @param filename a file name
  +     * @param filename a file name.
        *
        * @return an absolute file that doesn't contain &quot;./&quot; or
        * &quot;../&quot; sequences and uses the correct separator for
  @@ -726,34 +681,18 @@
               .replace('\\', File.separatorChar);
   
           // deal with absolute files
  -        if (!onNetWare) {
  -            if (filename.startsWith(File.separator)
  -                || (filename.length() >= 2
  -                    && Character.isLetter(filename.charAt(0))
  -                    && filename.charAt(1) == ':')) {
  -                return normalize(filename);
  -            }
  -        } else {
  -            // the assumption that the : will appear as the second character 
in
  -            // the path name breaks down when NetWare is a supported 
platform.
  -            // Netware volumes are of the pattern: "data:\";
  -            int colon = filename.indexOf(":");
  -            if (filename.startsWith(File.separator)
  -                || (colon > -1)) {
  -                return normalize(filename);
  -            }
  +        if (isAbsolutePath(filename)) {
  +            return normalize(filename);
           }
  -
           if (file == null) {
               return new File(filename);
           }
  -
           File helpFile = new File(file.getAbsolutePath());
           StringTokenizer tok = new StringTokenizer(filename, File.separator);
           while (tok.hasMoreTokens()) {
               String part = tok.nextToken();
               if (part.equals("..")) {
  -                helpFile = getParentFile(helpFile);
  +                helpFile = helpFile.getParentFile();
                   if (helpFile == null) {
                       String msg = "The file or path you specified ("
                           + filename + ") is invalid relative to "
  @@ -766,25 +705,46 @@
                   helpFile = new File(helpFile, part);
               }
           }
  -
           return new File(helpFile.getAbsolutePath());
       }
   
       /**
  -     * &quot;normalize&quot; the given absolute path.
  +     * Verifies if the filename represents is an absolute path.
  +     * @param filename the file name to be checked for being an absolute 
path.
  +     * @return true if the filename represents an absolute path.
  +     */
  +    private static boolean isAbsolutePath(String filename) {
  +        if (filename.startsWith(File.separator)) {
  +            // common for all os
  +            return true;
  +        }
  +        if (onDos && filename.length() >= 2
  +            && Character.isLetter(filename.charAt(0))
  +            && filename.charAt(1) == ':') {
  +            // Actually on windows the : must be followed by a \ for
  +            // the path to be absolute, else the path is relative
  +            // to the current working directory on that drive.
  +            // (Every drive may have another current working directory)
  +            return true;
  +        }
  +        return (onNetWare && filename.indexOf(":") > -1);
  +    }
  +
  +    /**
  +     * &quot;Normalize&quot; the given absolute path.
        *
        * <p>This includes:
        * <ul>
        *   <li>Uppercase the drive letter if there is one.</li>
        *   <li>Remove redundant slashes after the drive spec.</li>
  -     *   <li>resolve all ./, .\, ../ and ..\ sequences.</li>
  +     *   <li>Resolve all ./, .\, ../ and ..\ sequences.</li>
        *   <li>DOS style paths that start with a drive letter will have
        *     \ as the separator.</li>
        * </ul>
  -     * Unlike <code>File#getCanonicalPath()</code> it specifically doesn't
  -     * resolve symbolic links.
  +     * Unlike <code>File#getCanonicalPath()</code> this method
  +     * specifically does not resolve symbolic links.
        *
  -     * @param path the path to be normalized
  +     * @param path the path to be normalized.
        * @return the normalized version of the path.
        *
        * @throws java.lang.NullPointerException if the file path is
  @@ -799,26 +759,14 @@
           // make sure we are dealing with an absolute path
           int colon = path.indexOf(":");
   
  -        if (!onNetWare) {
  -            if (!path.startsWith(File.separator)
  -                && !(path.length() >= 2
  -                    && Character.isLetter(path.charAt(0))
  -                    && colon == 1)) {
  -                String msg = path + " is not an absolute path";
  -                throw new BuildException(msg);
  -            }
  -        } else {
  -            if (!path.startsWith(File.separator)
  -                && (colon == -1)) {
  -                String msg = path + " is not an absolute path";
  -                throw new BuildException(msg);
  -            }
  +        if (!isAbsolutePath(path)) {
  +            String msg = path + " is not an absolute path";
  +            throw new BuildException(msg);
           }
  -
           boolean dosWithDrive = false;
           String root = null;
           // Eliminate consecutive slashes after the drive spec
  -        if ((!onNetWare && path.length() >= 2
  +        if ((onDos && path.length() >= 2
                   && Character.isLetter(path.charAt(0))
                   && path.charAt(1) == ':')
               || (onNetWare && colon > -1)) {
  @@ -845,7 +793,6 @@
                   }
               }
               path = sbPath.toString().replace('\\', File.separatorChar);
  -
           } else {
               if (path.length() == 1) {
                   root = File.separator;
  @@ -859,7 +806,6 @@
                   path = path.substring(1);
               }
           }
  -
           Stack s = new Stack();
           s.push(root);
           StringTokenizer tok = new StringTokenizer(path, File.separator);
  @@ -877,7 +823,6 @@
                   s.push(thisToken);
               }
           }
  -
           StringBuffer sb = new StringBuffer();
           for (int i = 0; i < s.size(); i++) {
               if (i > 1) {
  @@ -887,8 +832,6 @@
               }
               sb.append(s.elementAt(i));
           }
  -
  -
           path = sb.toString();
           if (dosWithDrive) {
               path = path.replace('/', '\\');
  @@ -912,8 +855,8 @@
           String name = f.getName();
           boolean isAbsolute = path.charAt(0) == File.separatorChar;
           // treat directories specified using .DIR syntax as files
  -        boolean isDirectory = f.isDirectory() &&
  -            !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4);
  +        boolean isDirectory = f.isDirectory()
  +            && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4);
   
           String device = null;
           StringBuffer directory = null;
  @@ -949,9 +892,9 @@
           if (!isAbsolute && directory != null) {
               directory.insert(0, '.');
           }
  -        osPath = ((device != null) ? device + ":" : "") +
  -            ((directory != null) ? "[" + directory + "]" : "") +
  -            ((file != null) ? file : "");
  +        osPath = ((device != null) ? device + ":" : "")
  +            + ((directory != null) ? "[" + directory + "]" : "")
  +            + ((file != null) ? file : "");
           return osPath;
       }
   
  @@ -961,16 +904,17 @@
        * <p>The file denoted by the returned abstract pathname did not
        * exist before this method was invoked, any subsequent invocation
        * of this method will yield a different file name.</p>
  -     *
  -     * <p>This method is different to File.createTempFile of JDK 1.2
  -     * as it doesn't create the file itself.
  -     * It uses the location pointed to by java.io.tmpdir
  -     * when the parentDir attribute is
  -     * null.</p>
  -     *
  -     * @param parentDir Directory to create the temporary file in -
  -     * current working directory will be assumed if this parameter is
  -     * null.
  +     * <p>
  +     * The filename is prefixNNNNNsuffix where NNNN is a random number.
  +     * </p>
  +     * <p>This method is different from File.createTempFile() of JDK 1.2
  +     * as it doesn't create the file itself.  It uses the location pointed
  +     * to by java.io.tmpdir when the parentDir attribute is null.</p>
  +     *
  +     * @param prefix prefix before the random number.
  +     * @param suffix file extension; include the '.'.
  +     * @param parentDir Directory to create the temporary file in;
  +     * java.io.tmpdir used if not specified.
        *
        * @return a File reference to the new temporary file.
        * @since ant 1.5
  @@ -996,40 +940,65 @@
       /**
        * Compares the contents of two files.
        *
  -     * <p>simple but sub-optimal comparision algorithm.  written for
  -     * working rather than fast. Better would be a block read into
  -     * buffers followed by long comparisions apart from the final 1-7
  -     * bytes.</p>
  -     *
        * @param f1 the file whose content is to be compared.
        * @param f2 the other file whose content is to be compared.
        *
        * @return true if the content of the files is the same.
        *
        * @throws IOException if the files cannot be read.
  -     *
  -     * @since 1.9
        */
       public boolean contentEquals(File f1, File f2) throws IOException {
  +        return contentEquals(f1, f2, false);
  +    }
  +
  +    /**
  +     * Compares the contents of two files.
  +     *
  +     * @param f1 the file whose content is to be compared.
  +     * @param f2 the other file whose content is to be compared.
  +     * @param textfile true if the file is to be treated as a text file and
  +     *        differences in kind of line break are to be ignored.
  +     *
  +     * @return true if the content of the files is the same.
  +     *
  +     * @throws IOException if the files cannot be read.
  +     * @since Ant 1.6.3
  +     */
  +    public boolean contentEquals(File f1, File f2, boolean textfile) throws 
IOException {
           if (f1.exists() != f2.exists()) {
               return false;
           }
  -
           if (!f1.exists()) {
               // two not existing files are equal
               return true;
           }
  -
  +        // should the following two be switched?  If f1 and f2 refer to the 
same file,
  +        // isn't their content equal regardless of whether that file is a 
directory?
           if (f1.isDirectory() || f2.isDirectory()) {
               // don't want to compare directory contents for now
               return false;
           }
  -
           if (fileNameEquals(f1, f2)) {
               // same filename => true
               return true;
           }
  +        return textfile ? textEquals(f1, f2) : binaryEquals(f1, f2);
  +    }
   
  +    /**
  +     * Binary compares the contents of two files.
  +     * <p>
  +     * simple but sub-optimal comparision algorithm. written for working
  +     * rather than fast. Better would be a block read into buffers followed
  +     * by long comparisions apart from the final 1-7 bytes.
  +     * </p>
  +     *
  +     * @param f1 the file whose content is to be compared.
  +     * @param f2 the other file whose content is to be compared.
  +     * @return true if the content of the files is the same.
  +     * @throws IOException if the files cannot be read.
  +     */
  +    private boolean binaryEquals(File f1, File f2) throws IOException {
           if (f1.length() != f2.length()) {
               // different size =>false
               return false;
  @@ -1053,61 +1022,76 @@
               }
               return true;
           } finally {
  -            if (in1 != null) {
  -                try {
  -                    in1.close();
  -                } catch (IOException e) {
  -                    // ignore
  +            close(in1);
  +            close(in2);
  +        }
  +    }
  +
  +    /**
  +     * Text compares the contents of two files.
  +     *
  +     * Ignores different kinds of line endings.
  +     *
  +     * @param f1 the file whose content is to be compared.
  +     * @param f2 the other file whose content is to be compared.
  +     * @return true if the content of the files is the same.
  +     * @throws IOException if the files cannot be read.
  +     */
  +    private boolean textEquals(File f1, File f2) throws IOException {
  +        BufferedReader in1 = null;
  +        BufferedReader in2 = null;
  +        try {
  +            in1 = new BufferedReader(new FileReader(f1));
  +            in2 = new BufferedReader(new FileReader(f2));
  +
  +            String expected = in1.readLine();
  +            while (expected != null) {
  +                if (!expected.equals(in2.readLine())) {
  +                    return false;
                   }
  +                expected = in1.readLine();
               }
  -            if (in2 != null) {
  -                try {
  -                    in2.close();
  -                } catch (IOException e) {
  -                    // ignore
  -                }
  +            if (in2.readLine() != null) {
  +                return false;
               }
  +            return true;
  +        } finally {
  +            close(in1);
  +            close(in2);
           }
       }
   
       /**
  -     * Emulation of File.getParentFile for JDK 1.1
  -     *
  -     *
  +     * This was originally an emulation of [EMAIL PROTECTED] 
File#getParentFile} for JDK 1.1,
  +     * but it is now implemented using that method (Ant 1.6.3 onwards).
        * @param f the file whose parent is required.
        * @return the given file's parent, or null if the file does not have a
        *         parent.
        * @since 1.10
        */
       public File getParentFile(File f) {
  -        if (f != null) {
  -            String p = f.getParent();
  -            if (p != null) {
  -                return new File(p);
  -            }
  -        }
  -        return null;
  +        return (f == null) ? null : f.getParentFile();
       }
   
       /**
  -     * Read from reader till EOF
  +     * Read from reader till EOF.
        * @param rdr the reader from which to read.
  -     * @return the contents read out of the given reader
  +     * @return the contents read out of the given reader.
        *
        * @throws IOException if the contents could not be read out from the
        *         reader.
        */
       public static final String readFully(Reader rdr) throws IOException {
  -        return readFully(rdr, 8192);
  +        return readFully(rdr, BUF_SIZE);
       }
   
       /**
  -     * Read from reader till EOF
  +     * Read from reader till EOF.
        *
        * @param rdr the reader from which to read.
  -     * @param bufferSize the buffer size to use when reading
  +     * @param bufferSize the buffer size to use when reading.
        *
  -     * @return the contents read out of the given reader
  +     * @return the contents read out of the given reader.
        *
        * @throws IOException if the contents could not be read out from the
        *         reader.
  @@ -1120,73 +1104,68 @@
           }
           final char[] buffer = new char[bufferSize];
           int bufferLength = 0;
  -        String text = null;
           StringBuffer textBuffer = null;
           while (bufferLength != -1) {
               bufferLength = rdr.read(buffer);
  -            if (bufferLength != -1) {
  -                if (textBuffer == null) {
  -                    textBuffer = new StringBuffer(
  -                                                  new String(buffer, 0, 
bufferLength));
  -                } else {
  -                    textBuffer.append(new String(buffer, 0, bufferLength));
  -                }
  +            if (bufferLength > 0) {
  +                textBuffer = (textBuffer == null) ? new StringBuffer() : 
textBuffer;
  +                textBuffer.append(new String(buffer, 0, bufferLength));
               }
           }
  -        if (textBuffer != null) {
  -            text = textBuffer.toString();
  -        }
  -        return text;
  +        return (textBuffer == null) ? null : textBuffer.toString();
       }
   
       /**
  -     * Emulation of File.createNewFile for JDK 1.1.
  +     * This was originally an emulation of File.createNewFile for JDK 1.1,
  +     * but it is now implemented using that method (Ant 1.6.3 onwards).
        *
  -     * <p>This method does <strong>not</strong> guarantee that the
  -     * operation is atomic.</p>
  +     * <p>This method has historically <strong>not</strong> guaranteed that 
the
  +     * operation was atomic. In its current implementation it is.
        *
  -     * @param f the file to be created
  +     * @param f the file to be created.
        * @return true if the file did not exist already.
  +     * @throws IOException on error.
        * @since Ant 1.5
        */
       public boolean createNewFile(File f) throws IOException {
  -        if (f != null) {
  -            if (f.exists()) {
  -                return false;
  -            }
  -
  -            FileOutputStream fos = null;
  -            try {
  -                fos = new FileOutputStream(f);
  -                fos.write(new byte[0]);
  -            } finally {
  -                if (fos != null) {
  -                    fos.close();
  -                }
  -            }
  +        return f.createNewFile();
  +    }
   
  -            return true;
  +    /**
  +     * Create a new file, optionally creating parent directories.
  +     *
  +     * @param f the file to be created.
  +     * @param mkdirs <code>boolean</code> whether to create parent 
directories.
  +     * @return true if the file did not exist already.
  +     * @throws IOException on error.
  +     * @since Ant 1.6.3
  +     */
  +    public boolean createNewFile(File f, boolean mkdirs) throws IOException {
  +        File parent = f.getParentFile();
  +        if (mkdirs && !(parent.exists())) {
  +            parent.mkdirs();
           }
  -        return false;
  +        return f.createNewFile();
       }
   
       /**
        * Checks whether a given file is a symbolic link.
        *
        * <p>It doesn't really test for symbolic links but whether the
  -     * canonical and absolute paths of the file are identical - this
  +     * canonical and absolute paths of the file are identical--this
        * may lead to false positives on some platforms.</p>
        *
        * @param parent the parent directory of the file to test
        * @param name the name of the file to test.
        *
        * @return true if the file is a symbolic link.
  +     * @throws IOException on error.
        * @since Ant 1.5
        */
       public boolean isSymbolicLink(File parent, String name)
           throws IOException {
  -        File resolvedParent = new File(parent.getCanonicalPath());
  -        File toTest = new File(resolvedParent, name);
  +        File toTest = new File(((parent == null)
  +            ? null : parent.getCanonicalPath()), name);
           return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath());
       }
   
  @@ -1197,7 +1176,7 @@
        * @param path The path to remove from, must not be null, must be 
absolute.
        *
        * @return path's normalized absolute if it doesn't start with
  -     * leading, path's path with leading's path removed otherwise.
  +     * leading; path's path with leading's path removed otherwise.
        *
        * @since Ant 1.5
        */
  @@ -1213,12 +1192,7 @@
           if (!l.endsWith(File.separator)) {
               l += File.separator;
           }
  -
  -        if (p.startsWith(l)) {
  -            return p.substring(l.length());
  -        } else {
  -            return p;
  -        }
  +        return (p.startsWith(l)) ? p.substring(l.length()) : p;
       }
   
       /**
  @@ -1229,7 +1203,7 @@
        *
        * <p>This code doesn't handle non-ASCII characters properly.</p>
        *
  -     * @param path the path in the local file system
  +     * @param path the path in the local file system.
        * @return the URI version of the local path.
        * @since Ant 1.6
        */
  @@ -1246,7 +1220,6 @@
               if (!path.startsWith(File.separator)) {
                   sb.append("/");
               }
  -
           } catch (BuildException e) {
               // relative path
           }
  @@ -1321,13 +1294,13 @@
        * <code>from</code>, which involves deleting <code>from</code> as
        * well.</p>
        *
  +     * @param from the file to move.
  +     * @param to the new file name.
  +     *
        * @throws IOException if anything bad happens during this
        * process.  Note that <code>to</code> may have been deleted
        * already when this happens.
        *
  -     * @param from the file to move
  -     * @param to the new file name
  -     *
        * @since Ant 1.6
        */
       public void rename(File from, File to) throws IOException {
  @@ -1335,13 +1308,11 @@
               throw new IOException("Failed to delete " + to
                                     + " while trying to rename " + from);
           }
  -
  -        File parent = getParentFile(to);
  +        File parent = to.getParentFile();
           if (parent != null && !parent.exists() && !parent.mkdirs()) {
               throw new IOException("Failed to create directory " + parent
                                     + " while trying to rename " + from);
           }
  -
           if (!from.renameTo(to)) {
               copyFile(from, to);
               if (!from.delete()) {
  @@ -1351,11 +1322,152 @@
           }
       }
   
  +    /**
  +     * Get the granularity of file timestamps.
  +     * The choice is made based on OS, which is incorrect--it should really 
be
  +     * by filesystem. We do not have an easy way to probe for file systems,
  +     * however.
  +     * @return the difference, in milliseconds, which two file timestamps 
must have
  +     * in order for the two files to be given a creation order.
  +     */
       public long getFileTimestampGranularity() {
  -        if (Os.isFamily("dos")) {
  -            return FAT_FILE_TIMESTAMP_GRANULARITY;
  -        } else {
  -            return 0;
  +        return onDos
  +            ? FAT_FILE_TIMESTAMP_GRANULARITY : 
UNIX_FILE_TIMESTAMP_GRANULARITY;
  +    }
  +
  +    /**
  +     * Returns true if the source is older than the dest.
  +     * If the dest file does not exist, then the test returns false; it is
  +     * implicitly not up do date.
  +     * @param source source file (should be the older).
  +     * @param dest dest file (should be the newer).
  +     * @param granularity an offset added to the source time.
  +     * @return true if the source is older than the dest after accounting
  +     *              for granularity.
  +     * @since Ant 1.6.3
  +     */
  +    public boolean isUpToDate(File source, File dest, long granularity) {
  +        //do a check for the destination file existing
  +        if (!dest.exists()) {
  +            //if it does not, then the file is not up to date.
  +            return false;
  +        }
  +        long sourceTime = source.lastModified();
  +        long destTime = dest.lastModified();
  +        return isUpToDate(sourceTime, destTime, granularity);
  +    }
  +
  +
  +    /**
  +     * Returns true if the source is older than the dest.
  +     * @param source source file (should be the older).
  +     * @param dest dest file (should be the newer).
  +     * @return true if the source is older than the dest, taking the 
granularity into account.
  +     * @since Ant 1.6.3
  +     */
  +    public boolean isUpToDate(File source, File dest) {
  +        return isUpToDate(source, dest, getFileTimestampGranularity());
  +    }
  +
  +    /**
  +     * Compare two timestamps for being up to date using
  +     * the specified granularity.
  +     *
  +     * @param sourceTime timestamp of source file.
  +     * @param destTime timestamp of dest file.
  +     * @param granularity os/filesys granularity.
  +     * @return true if the dest file is considered up to date.
  +     */
  +    public boolean isUpToDate(long sourceTime, long destTime, long 
granularity) {
  +        if (destTime == -1) {
  +            return false;
  +        }
  +        return destTime >= sourceTime + granularity;
  +    }
  +
  +    /**
  +     * Compare two timestamps for being up to date using the
  +     * current granularity.
  +     *
  +     * @param sourceTime  timestamp of source file.
  +     * @param destTime    timestamp of dest file.
  +     * @return true if the dest file is considered up to date.
  +     */
  +    public boolean isUpToDate(long sourceTime, long destTime) {
  +        return isUpToDate(sourceTime, destTime, 
getFileTimestampGranularity());
  +    }
  +
  +    /**
  +     * Close a Writer without throwing any exception if something went wrong.
  +     * Do not attempt to close it if the argument is null.
  +     * @param device output writer, can be null.
  +     */
  +    public static void close(Writer device) {
  +        if (device != null) {
  +            try {
  +                device.close();
  +            } catch (IOException ioex) {
  +                //ignore
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Close a stream without throwing any exception if something went wrong.
  +     * Do not attempt to close it if the argument is null.
  +     *
  +     * @param device Reader, can be null.
  +     */
  +    public static void close(Reader device) {
  +        if (device != null) {
  +            try {
  +                device.close();
  +            } catch (IOException ioex) {
  +                //ignore
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Close a stream without throwing any exception if something went wrong.
  +     * Do not attempt to close it if the argument is null.
  +     *
  +     * @param device stream, can be null.
  +     */
  +    public static void close(OutputStream device) {
  +        if (device != null) {
  +            try {
  +                device.close();
  +            } catch (IOException ioex) {
  +                //ignore
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Close a stream without throwing any exception if something went wrong.
  +     * Do not attempt to close it if the argument is null.
  +     *
  +     * @param device stream, can be null.
  +     */
  +    public static void close(InputStream device) {
  +        if (device != null) {
  +            try {
  +                device.close();
  +            } catch (IOException ioex) {
  +                //ignore
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Delete the file with [EMAIL PROTECTED] File#delete()} if the argument 
is not null.
  +     * Do nothing on a null argument.
  +     * @param file file to delete.
  +     */
  +    public static void delete(File file) {
  +        if (file != null) {
  +            file.delete();
           }
       }
   }
  
  
  

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

Reply via email to