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 "./" or * "../" 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()); } /** - * "normalize" 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); + } + + /** + * "Normalize" 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]