All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tools.ant.util.FileUtils Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.apache.tools.ant.util;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.HttpURLConnection;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channel;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.JarFile;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.PathTokenizer;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.launch.Locator;
import org.apache.tools.ant.taskdefs.condition.Os;
import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.types.resources.FileResource;

/**
 * This class also encapsulates methods which allow Files to be
 * referred to using abstract path names which are translated to native
 * system file paths at runtime as well as copying files or setting
 * their last modification time.
 *
 */
public class FileUtils {
    private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
    private static final int EXPAND_SPACE = 50;
    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 final boolean ON_NETWARE = Os.isFamily("netware");
    private static final boolean ON_DOS = Os.isFamily("dos");
    private static final boolean ON_WIN9X = Os.isFamily("win9x");
    private static final boolean ON_WINDOWS = Os.isFamily("windows");

    static final int BUF_SIZE = 8192;


    /**
     * 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;

    /**
     * The granularity of timestamps under the NT File System.
     * NTFS has a granularity of 100 nanoseconds, which is less
     * than 1 millisecond, so we round this up to 1 millisecond.
     */
    public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1;

    /**
     * A one item cache for fromUri.
     * fromUri is called for each element when parseing ant build
     * files. It is a costly operation. This just caches the result
     * of the last call.
     */
    private Object cacheFromUriLock = new Object();
    private String cacheFromUriRequest = null;
    private String cacheFromUriResponse = null;

    /**
     * Factory method.
     *
     * @return a new instance of FileUtils.
     * @deprecated since 1.7.
     *             Use getFileUtils instead,
     * FileUtils do not have state.
     */
    public static FileUtils newFileUtils() {
        return new FileUtils();
    }

    /**
     * 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.
     *
     * @param file the file whose URL representation is required.
     * @return The FileURL value.
     * @throws MalformedURLException if the URL representation cannot be
     *      formed.
     */
    public URL getFileURL(File file) throws MalformedURLException {
        return new URL(toURI(file.getAbsolutePath()));
    }

    /**
     * Convenience method to copy a file from a source to a destination.
     * No filtering is performed.
     *
     * @param sourceFile Name of file to copy from.
     *                   Must not be null.
     * @param destFile Name of file to copy to.
     *                 Must not be null.
     *
     * @throws IOException if the copying fails.
     */
    public void copyFile(String sourceFile, String destFile) throws IOException {
        copyFile(new File(sourceFile), new File(destFile), null, false, false);
    }

    /**
     * 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 null.
     * @param destFile Name of file to copy to.
     *                 Must not be null.
     * @param filters the collection of filters to apply to this copy.
     *
     * @throws IOException if the copying fails.
     */
    public void copyFile(String sourceFile, String destFile, FilterSetCollection filters)
            throws IOException {
        copyFile(new File(sourceFile), new File(destFile), filters, false, false);
    }

    /**
     * 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.
     *
     * @param sourceFile Name of file to copy from. Must not be null.
     * @param destFile Name of file to copy to. Must not be null.
     * @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.
     */
    public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
                         boolean overwrite) throws IOException {
        copyFile(new File(sourceFile), new File(destFile), filters, overwrite, false);
    }

    /**
     * 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 destFile file should be made equal to
     * the last modified time
     * of sourceFile.
     *
     * @param sourceFile Name of file to copy from. Must not be null.
     * @param destFile Name of file to copy to. Must not be null.
     * @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.
     */
    public void copyFile(String sourceFile, String destFile,
                         FilterSetCollection filters,
                         boolean overwrite, boolean preserveLastModified)
        throws IOException {
        copyFile(new File(sourceFile), new File(destFile), filters, overwrite,
                 preserveLastModified);
    }

    /**
     * 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 destFile file should be made equal to the last modified time
     * of sourceFile.
     *
     * @param sourceFile Name of file to copy from. Must not be null.
     * @param destFile Name of file to copy to. Must not be null.
     * @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.
     * @param encoding the encoding used to read and write the files.
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.5
     */
    public void copyFile(String sourceFile, String destFile,
                         FilterSetCollection filters, boolean overwrite,
                         boolean preserveLastModified, String encoding) throws IOException {
        copyFile(new File(sourceFile), new File(destFile), filters,
                 overwrite, preserveLastModified, encoding);
    }

    // CheckStyle:ParameterNumberCheck OFF - bc
    /**
     * 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
     * destFile file should be made equal
     * to the last modified time of sourceFile.
     *
     * @param sourceFile Name of file to copy from.
     *                   Must not be null.
     * @param destFile Name of file to copy to.
     *                 Must not be null.
     * @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.
     * @param preserveLastModified Whether or not the last modified time of
     *                             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.
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.5
     */
    public void copyFile(String sourceFile, String destFile,
                         FilterSetCollection filters, Vector filterChains,
                         boolean overwrite, boolean preserveLastModified,
                         String encoding, Project project) throws IOException {
        copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite,
                preserveLastModified, encoding, project);
    }

    /**
     * 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 destFile file should be made
     * equal to the last modified time of sourceFile.
     *
     * @param sourceFile Name of file to copy from. Must not be null.
     * @param destFile Name of file to copy to. Must not be null.
     * @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.
     * @param preserveLastModified Whether or not the last modified time of the resulting file
     *            should be set to that 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.
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.6
     */
    public void copyFile(String sourceFile, String destFile,
                         FilterSetCollection filters, Vector filterChains,
                         boolean overwrite, boolean preserveLastModified,
                         String inputEncoding, String outputEncoding,
                         Project project) throws IOException {
        copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite,
                preserveLastModified, inputEncoding, outputEncoding, project);
    }

    /**
     * Convenience method to copy a file from a source to a destination. No filtering is performed.
     *
     * @param sourceFile the file to copy from. Must not be null.
     * @param destFile the file to copy to. Must not be null.
     *
     * @throws IOException if the copying fails.
     */
    public void copyFile(File sourceFile, File destFile) throws IOException {
        copyFile(sourceFile, destFile, null, false, false);
    }

    /**
     * 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 null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @param filters the collection of filters to apply to this copy.
     *
     * @throws IOException if the copying fails.
     */
    public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
            throws IOException {
        copyFile(sourceFile, destFile, filters, false, false);
    }

    /**
     * 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.
     *
     * @param sourceFile the file to copy from.
     *                   Must not be null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @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.
     */
    public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
                         boolean overwrite) throws IOException {
        copyFile(sourceFile, destFile, filters, overwrite, false);
    }

    /**
     * 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 destFile file should be made equal
     * to the last modified time of sourceFile.
     *
     * @param sourceFile the file to copy from.
     *                   Must not be null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @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.
     */
    public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
                         boolean overwrite, boolean preserveLastModified) throws IOException {
        copyFile(sourceFile, destFile, filters, overwrite, preserveLastModified, null);
    }

    /**
     * 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 destFile file should be made equal to the last modified time
     * of sourceFile and which character encoding to assume.
     *
     * @param sourceFile the file to copy from. Must not be null.
     * @param destFile the file to copy to. Must not be null.
     * @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.
     * @param encoding the encoding used to read and write the files.
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.5
     */
    public void copyFile(File sourceFile, File destFile,
                         FilterSetCollection filters, boolean overwrite,
                         boolean preserveLastModified, String encoding) throws IOException {
        copyFile(sourceFile, destFile, filters, null, overwrite,
                 preserveLastModified, encoding, null);
    }

    /**
     * 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
     * destFile file should be made equal
     * to the last modified time of sourceFile.
     *
     * @param sourceFile the file to copy from.
     *                   Must not be null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @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.
     * @param preserveLastModified Whether or not the last modified time of
     *                             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.
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.5
     */
    public void copyFile(File sourceFile, File destFile,
                         FilterSetCollection filters, Vector filterChains,
                         boolean overwrite, boolean preserveLastModified,
                         String encoding, Project project) throws IOException {
        copyFile(sourceFile, destFile, filters, filterChains,
                 overwrite, preserveLastModified, encoding, encoding, project);
    }

    /**
     * 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
     * destFile file should be made equal
     * to the last modified time of sourceFile.
     *
     * @param sourceFile the file to copy from.
     *                   Must not be null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @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.
     * @param preserveLastModified Whether or not the last modified time of
     *                             the resulting file should be set to that
     *                             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.
     *
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.6
     */
    public void copyFile(File sourceFile, File destFile,
            FilterSetCollection filters, Vector filterChains,
            boolean overwrite, boolean preserveLastModified,
            String inputEncoding, String outputEncoding,
            Project project) throws IOException {
        copyFile(sourceFile, destFile, filters, filterChains, overwrite, preserveLastModified,
                false, inputEncoding, outputEncoding, project);
    }

    /**
     * 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
     * destFile file should be made equal
     * to the last modified time of sourceFile.
     *
     * @param sourceFile the file to copy from.
     *                   Must not be null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @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.
     * @param preserveLastModified Whether or not the last modified time of
     *                             the resulting file should be set to that
     *                             of the source file.
     * @param append whether to append to the destination file.
     * @param inputEncoding the encoding used to read the files.
     * @param outputEncoding the encoding used to write the files.
     * @param project the project instance.
     *
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.8
     */
    public void copyFile(File sourceFile, File destFile,
                         FilterSetCollection filters, Vector filterChains,
                         boolean overwrite, boolean preserveLastModified,
                         boolean append,
                         String inputEncoding, String outputEncoding,
                         Project project) throws IOException {
        copyFile(sourceFile, destFile, filters, filterChains, overwrite,
                 preserveLastModified, append, inputEncoding, outputEncoding,
                 project, /* force: */ false);
    }

    /**
     * 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
     * destFile file should be made equal
     * to the last modified time of sourceFile.
     *
     * @param sourceFile the file to copy from.
     *                   Must not be null.
     * @param destFile the file to copy to.
     *                 Must not be null.
     * @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.
     * @param preserveLastModified Whether or not the last modified time of
     *                             the resulting file should be set to that
     *                             of the source file.
     * @param append whether to append to the destination 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 force whether to overwrite read-only destination files.
     *
     * @throws IOException if the copying fails.
     *
     * @since Ant 1.8.2
     */
    public void copyFile(File sourceFile, File destFile,
                         FilterSetCollection filters, Vector filterChains,
                         boolean overwrite, boolean preserveLastModified,
                         boolean append,
                         String inputEncoding, String outputEncoding,
                         Project project, boolean force) throws IOException {
        ResourceUtils.copyResource(new FileResource(sourceFile),
                                   new FileResource(destFile),
                                   filters, filterChains, overwrite,
                                   preserveLastModified, append, inputEncoding,
                                   outputEncoding, project, force);
    }

    // CheckStyle:ParameterNumberCheck ON

    /**
     * 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.
     *             if this is -1, the current time is used.
     */
    public void setFileLastModified(File file, long time) {
        ResourceUtils.setLastModified(new FileResource(file), time);
    }

    /**
     * Interpret the filename as a file relative to the given file
     * unless the filename already represents an absolute filename.
     * Differs from new File(file, filename) in that
     * the resulting File's path will always be a normalized,
     * absolute pathname.  Also, if it is determined that
     * filename is context-relative, file
     * will be discarded and the reference will be resolved using
     * available context/state information about the filesystem.
     *
     * @param file the "reference" file for relative paths. This
     * instance must be an absolute file and must not contain
     * "./" or "../" sequences (same for \ instead
     * of /).  If it is null, this call is equivalent to
     * new java.io.File(filename).getAbsoluteFile().
     *
     * @param filename a file name.
     *
     * @return an absolute file.
     * @throws java.lang.NullPointerException if filename is null.
     */
    public File resolveFile(File file, String filename) {
        if (!isAbsolutePath(filename)) {
            char sep = File.separatorChar;
            filename = filename.replace('/', sep).replace('\\', sep);
            if (isContextRelativePath(filename)) {
                file = null;
                // on cygwin, our current directory can be a UNC;
                // assume user.dir is absolute or all hell breaks loose...
                String udir = System.getProperty("user.dir");
                if (filename.charAt(0) == sep && udir.charAt(0) == sep) {
                    filename = dissect(udir)[0] + filename.substring(1);
                }
            }
            filename = new File(file, filename).getAbsolutePath();
        }
        return normalize(filename);
    }

    /**
     * On DOS and NetWare, the evaluation of certain file
     * specifications is context-dependent.  These are filenames
     * beginning with a single separator (relative to current root directory)
     * and filenames with a drive specification and no intervening separator
     * (relative to current directory of the specified root).
     * @param filename the filename to evaluate.
     * @return true if the filename is relative to system context.
     * @throws java.lang.NullPointerException if filename is null.
     * @since Ant 1.7
     */
    public static boolean isContextRelativePath(String filename) {
        if (!(ON_DOS || ON_NETWARE) || filename.length() == 0) {
            return false;
        }
        char sep = File.separatorChar;
        filename = filename.replace('/', sep).replace('\\', sep);
        char c = filename.charAt(0);
        int len = filename.length();
        return (c == sep && (len == 1 || filename.charAt(1) != sep))
                || (Character.isLetter(c) && len > 1
                && filename.indexOf(':') == 1
                && (len == 2 || filename.charAt(2) != sep));
    }

    /**
     * Verifies that the specified filename represents an absolute path.
     * Differs from new java.io.File("filename").isAbsolute() in that a path
     * beginning with a double file separator--signifying a Windows UNC--must
     * at minimum match "\\a\b" to be considered an absolute path.
     * @param filename the filename to be checked.
     * @return true if the filename represents an absolute path.
     * @throws java.lang.NullPointerException if filename is null.
     * @since Ant 1.6.3
     */
    public static boolean isAbsolutePath(String filename) {
        int len = filename.length();
        if (len == 0) {
            return false;
        }
        char sep = File.separatorChar;
        filename = filename.replace('/', sep).replace('\\', sep);
        char c = filename.charAt(0);
        if (!(ON_DOS || ON_NETWARE)) {
            return (c == sep);
        }
        if (c == sep) {
            // CheckStyle:MagicNumber OFF
            if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) {
                return false;
            }
            // CheckStyle:MagicNumber ON
            int nextsep = filename.indexOf(sep, 2);
            return nextsep > 2 && nextsep + 1 < len;
        }
        int colon = filename.indexOf(':');
        return (Character.isLetter(c) && colon == 1
                && filename.length() > 2 && filename.charAt(2) == sep)
                || (ON_NETWARE && colon > 0);
    }

    /**
     * Translate a path into its native (platform specific) format.
     * 

* This method uses PathTokenizer to separate the input path * into its components. This handles DOS style paths in a relatively * sensible way. The file separators are then converted to their platform * specific versions. * * @param toProcess The path to be translated. * May be null. * * @return the native version of the specified path or * an empty string if the path is null or empty. * * @since ant 1.7 * @see PathTokenizer */ public static String translatePath(String toProcess) { if (toProcess == null || toProcess.length() == 0) { return ""; } StringBuffer path = new StringBuffer(toProcess.length() + EXPAND_SPACE); PathTokenizer tokenizer = new PathTokenizer(toProcess); while (tokenizer.hasMoreTokens()) { String pathComponent = tokenizer.nextToken(); pathComponent = pathComponent.replace('/', File.separatorChar); pathComponent = pathComponent.replace('\\', File.separatorChar); if (path.length() != 0) { path.append(File.pathSeparatorChar); } path.append(pathComponent); } return path.toString(); } /** * "Normalize" the given absolute path. * *

This includes: *

    *
  • Uppercase the drive letter if there is one.
  • *
  • Remove redundant slashes after the drive spec.
  • *
  • Resolve all ./, .\, ../ and ..\ sequences.
  • *
  • DOS style paths that start with a drive letter will have * \ as the separator.
  • *
* Unlike {@link File#getCanonicalPath()} this method * specifically does not resolve symbolic links. * * @param path the path to be normalized. * @return the normalized version of the path. * * @throws java.lang.NullPointerException if path is null. */ public File normalize(final String path) { Stack s = new Stack(); String[] dissect = dissect(path); s.push(dissect[0]); StringTokenizer tok = new StringTokenizer(dissect[1], File.separator); while (tok.hasMoreTokens()) { String thisToken = tok.nextToken(); if (".".equals(thisToken)) { continue; } if ("..".equals(thisToken)) { if (s.size() < 2) { // Cannot resolve it, so skip it. return new File(path); } s.pop(); } else { // plain component s.push(thisToken); } } StringBuffer sb = new StringBuffer(); final int size = s.size(); for (int i = 0; i < size; i++) { if (i > 1) { // not before the filesystem root and not after it, since root // already contains one sb.append(File.separatorChar); } sb.append(s.elementAt(i)); } return new File(sb.toString()); } /** * Dissect the specified absolute path. * @param path the path to dissect. * @return String[] {root, remaining path}. * @throws java.lang.NullPointerException if path is null. * @since Ant 1.7 */ public String[] dissect(String path) { char sep = File.separatorChar; path = path.replace('/', sep).replace('\\', sep); // make sure we are dealing with an absolute path if (!isAbsolutePath(path)) { throw new BuildException(path + " is not an absolute path"); } String root = null; int colon = path.indexOf(':'); if (colon > 0 && (ON_DOS || ON_NETWARE)) { int next = colon + 1; root = path.substring(0, next); char[] ca = path.toCharArray(); root += sep; //remove the initial separator; the root has it. next = (ca[next] == sep) ? next + 1 : next; StringBuffer sbPath = new StringBuffer(); // Eliminate consecutive slashes after the drive spec: for (int i = next; i < ca.length; i++) { if (ca[i] != sep || ca[i - 1] != sep) { sbPath.append(ca[i]); } } path = sbPath.toString(); } else if (path.length() > 1 && path.charAt(1) == sep) { // UNC drive int nextsep = path.indexOf(sep, 2); nextsep = path.indexOf(sep, nextsep + 1); root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path; path = path.substring(root.length()); } else { root = File.separator; path = path.substring(1); } return new String[] {root, path}; } /** * Returns a VMS String representation of a File object. * This is useful since the JVM by default internally converts VMS paths * to Unix style. * The returned String is always an absolute path. * * @param f The File to get the VMS path for. * @return The absolute VMS path to f. */ public String toVMSPath(File f) { // format: "DEVICE:[DIR.SUBDIR]FILE" String osPath; String path = normalize(f.getAbsolutePath()).getPath(); String name = f.getName(); boolean isAbsolute = path.charAt(0) == File.separatorChar; // treat directories specified using .DIR syntax as files // CheckStyle:MagicNumber OFF boolean isDirectory = f.isDirectory() && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4); // CheckStyle:MagicNumber ON String device = null; StringBuffer directory = null; String file = null; int index = 0; if (isAbsolute) { index = path.indexOf(File.separatorChar, 1); if (index == -1) { return path.substring(1) + ":[000000]"; } device = path.substring(1, index++); } if (isDirectory) { directory = new StringBuffer(path.substring(index).replace(File.separatorChar, '.')); } else { int dirEnd = path.lastIndexOf(File.separatorChar, path.length()); if (dirEnd == -1 || dirEnd < index) { file = path.substring(index); } else { directory = new StringBuffer(path.substring(index, dirEnd). replace(File.separatorChar, '.')); index = dirEnd + 1; if (path.length() > index) { file = path.substring(index); } } } if (!isAbsolute && directory != null) { directory.insert(0, '.'); } osPath = ((device != null) ? device + ":" : "") + ((directory != null) ? "[" + directory + "]" : "") + ((file != null) ? file : ""); return osPath; } /** * Create a File object for a temporary file in a given directory. Without * actually creating the file. * *

* 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. *

*

* The filename is prefixNNNNNsuffix where NNNN is a random number. *

* * @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. * * @deprecated since ant 1.7.1 use createTempFile(String, String, File, * boolean, boolean) instead. * @return a File reference to the new, nonexistent temporary file. */ public File createTempFile(String prefix, String suffix, File parentDir) { return createTempFile(prefix, suffix, parentDir, false, false); } private static final String NULL_PLACEHOLDER = "null"; /** * Create a temporary file in a given directory. * *

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.

* * @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. * @param deleteOnExit whether to set the tempfile for deletion on * normal VM exit. * @param createFile true if the file must actually be created. If false * chances exist that a file with the same name is created in the time * between invoking this method and the moment the file is actually created. * If possible set to true. * * @return a File reference to the new temporary file. * @since Ant 1.7.1 */ public File createTempFile(String prefix, String suffix, File parentDir, boolean deleteOnExit, boolean createFile) { File result = null; String parent = (parentDir == null) ? System.getProperty("java.io.tmpdir") : parentDir.getPath(); if (prefix == null) { prefix = NULL_PLACEHOLDER; } if (suffix == null) { suffix = NULL_PLACEHOLDER; } if (createFile) { try { result = File.createTempFile(prefix, suffix, new File(parent)); } catch (IOException e) { throw new BuildException("Could not create tempfile in " + parent, e); } } else { DecimalFormat fmt = new DecimalFormat("#####"); synchronized (rand) { do { result = new File(parent, prefix + fmt.format(rand.nextInt(Integer.MAX_VALUE)) + suffix); } while (result.exists()); } } if (deleteOnExit) { result.deleteOnExit(); } return result; } /** * Create a File object for a temporary file in a given directory. Without * actually creating the file. * *

* 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. *

*

* The filename is prefixNNNNNsuffix where NNNN is a random number. *

* * @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. * @param deleteOnExit * whether to set the tempfile for deletion on normal VM exit. * * @deprecated since ant 1.7.1 use createTempFile(String, String, File, * boolean, boolean) instead. * @return a File reference to the new, nonexistent temporary file. */ public File createTempFile(String prefix, String suffix, File parentDir, boolean deleteOnExit) { return createTempFile(prefix, suffix, parentDir, deleteOnExit, 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. * * @return true if the content of the files is the same. * * @throws IOException if the files cannot be read. */ 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 { return ResourceUtils.contentEquals(new FileResource(f1), new FileResource(f2), textfile); } /** * This was originally an emulation of {@link 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 * @deprecated since 1.7. Just use {@link File#getParentFile} directly. */ public File getParentFile(File f) { return (f == null) ? null : f.getParentFile(); } /** * Read from reader till EOF. * @param rdr the reader from which to read. * @return the contents read out of the given reader. * * @throws IOException if the contents could not be read out from the * reader. */ public static String readFully(Reader rdr) throws IOException { return readFully(rdr, BUF_SIZE); } /** * Read from reader till EOF. * * @param rdr the reader from which to read. * @param bufferSize the buffer size to use when reading. * * @return the contents read out of the given reader. * * @throws IOException if the contents could not be read out from the * reader. */ public static String readFully(Reader rdr, int bufferSize) throws IOException { if (bufferSize <= 0) { throw new IllegalArgumentException("Buffer size must be greater " + "than 0"); } final char[] buffer = new char[bufferSize]; int bufferLength = 0; StringBuffer textBuffer = null; while (bufferLength != -1) { bufferLength = rdr.read(buffer); if (bufferLength > 0) { textBuffer = (textBuffer == null) ? new StringBuffer() : textBuffer; textBuffer.append(new String(buffer, 0, bufferLength)); } } return (textBuffer == null) ? null : textBuffer.toString(); } /** * Safe read fully - do not return a null for an empty reader. * @param reader the input to read from. * @return the string. * @throws IOException if unable to read from reader. * @since Ant 1.7.1 */ public static String safeReadFully(Reader reader) throws IOException { String ret = readFully(reader); return ret == null ? "" : ret; } /** * 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). * *

This method has historically not guaranteed that the * operation was atomic. In its current implementation it is. * * @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 { return f.createNewFile(); } /** * Create a new file, optionally creating parent directories. * * @param f the file to be created. * @param mkdirs boolean 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 f.createNewFile(); } /** * Checks whether a given file is a symbolic link. * *

It doesn't really test for symbolic links but whether the * canonical and absolute paths of the file are identical--this * may lead to false positives on some platforms.

* * @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 * @deprecated use SymbolicLinkUtils instead */ public boolean isSymbolicLink(File parent, String name) throws IOException { SymbolicLinkUtils u = SymbolicLinkUtils.getSymbolicLinkUtils(); if (parent == null) { return u.isSymbolicLink(name); } return u.isSymbolicLink(parent, name); } /** * Removes a leading path from a second path. * * @param leading The leading path, must not be null, must be absolute. * @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. * * @since Ant 1.5 */ public String removeLeadingPath(File leading, File path) { String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); String p = normalize(path.getAbsolutePath()).getAbsolutePath(); if (l.equals(p)) { return ""; } // ensure that l ends with a / // so we never think /foo was a parent directory of /foobar if (!l.endsWith(File.separator)) { l += File.separator; } return (p.startsWith(l)) ? p.substring(l.length()) : p; } /** * Learn whether one path "leads" another. * @param leading The leading path, must not be null, must be absolute. * @param path The path to remove from, must not be null, must be absolute. * @return true if path starts with leading; false otherwise. * @since Ant 1.7 */ public boolean isLeadingPath(File leading, File path) { String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); String p = normalize(path.getAbsolutePath()).getAbsolutePath(); if (l.equals(p)) { return true; } // ensure that l ends with a / // so we never think /foo was a parent directory of /foobar if (!l.endsWith(File.separator)) { l += File.separator; } return p.startsWith(l); } /** * Constructs a file: URI that represents the * external form of the given pathname. * *

Will be an absolute URI if the given path is absolute.

* *

This code encodes non ASCII characters too.

* *

The coding of the output is the same as what File.toURI().toASCIIString() produces

* * See dt-sysid * which makes some mention of how * characters not supported by URI Reference syntax should be escaped. * * @param path the path in the local file system. * @return the URI version of the local path. * @since Ant 1.6 */ public String toURI(String path) { return new File(path).getAbsoluteFile().toURI().toASCIIString(); } /** * Constructs a file path from a file: URI. * *

Will be an absolute path if the given URI is absolute.

* *

Swallows '%' that are not followed by two characters, * doesn't deal with non-ASCII characters.

* * @param uri the URI designating a file in the local filesystem. * @return the local file system path for the file. * @since Ant 1.6 */ public String fromURI(String uri) { synchronized (cacheFromUriLock) { if (uri.equals(cacheFromUriRequest)) { return cacheFromUriResponse; } String path = Locator.fromURI(uri); String ret = isAbsolutePath(path) ? normalize(path).getAbsolutePath() : path; cacheFromUriRequest = uri; cacheFromUriResponse = ret; return ret; } } /** * Compares two filenames. * *

Unlike java.io.File#equals this method will try to compare * the absolute paths and "normalize" the filenames * before comparing them.

* * @param f1 the file whose name is to be compared. * @param f2 the other file whose name is to be compared. * * @return true if the file are for the same file. * * @since Ant 1.5.3 */ public boolean fileNameEquals(File f1, File f2) { return normalize(f1.getAbsolutePath()).getAbsolutePath().equals( normalize(f2.getAbsolutePath()).getAbsolutePath()); } /** * Are the two File instances pointing to the same object on the * file system? * @since Ant 1.8.2 */ public boolean areSame(File f1, File f2) throws IOException { if (f1 == null && f2 == null) { return true; } if (f1 == null || f2 == null) { return false; } File f1Normalized = normalize(f1.getAbsolutePath()); File f2Normalized = normalize(f2.getAbsolutePath()); return f1Normalized.equals(f2Normalized) || f1Normalized.getCanonicalFile().equals(f2Normalized .getCanonicalFile()); } /** * Renames a file, even if that involves crossing file system boundaries. * *

This will remove to (if it exists), ensure that * to's parent directory exists and move * from, which involves deleting from as * well.

* * @param from the file to move. * @param to the new file name. * * @throws IOException if anything bad happens during this * process. Note that to may have been deleted * already when this happens. * * @since Ant 1.6 */ public void rename(File from, File to) throws IOException { // identical logic lives in Move.renameFile(): from = normalize(from.getAbsolutePath()).getCanonicalFile(); to = normalize(to.getAbsolutePath()); if (!from.exists()) { System.err.println("Cannot rename nonexistent file " + from); return; } if (from.getAbsolutePath().equals(to.getAbsolutePath())) { System.err.println("Rename of " + from + " to " + to + " is a no-op."); return; } if (to.exists() && !(areSame(from, to) || tryHardToDelete(to))) { throw new IOException("Failed to delete " + to + " while trying to rename " + from); } File parent = to.getParentFile(); if (parent != null && !parent.exists() && !parent.mkdirs()) { throw new IOException("Failed to create directory " + parent + " while trying to rename " + from); } if (!from.renameTo(to)) { copyFile(from, to); if (!tryHardToDelete(from)) { throw new IOException("Failed to delete " + from + " while trying to rename it."); } } } /** * 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, so this heuristic gives us a decent default. * * @return the difference, in milliseconds, which two file timestamps must have in order for the * two files to be considered to have different timestamps. */ public long getFileTimestampGranularity() { if (ON_WIN9X) { return FAT_FILE_TIMESTAMP_GRANULARITY; } if (ON_WINDOWS) { return NTFS_FILE_TIMESTAMP_GRANULARITY; } if (ON_DOS) { return FAT_FILE_TIMESTAMP_GRANULARITY; } return UNIX_FILE_TIMESTAMP_GRANULARITY; } /** * test whether a file or directory exists, with an error in the * upper/lower case spelling of the name. * Using this method is only interesting on case insensitive file systems * (Windows).
* It will return true only if 3 conditions are met : *
*
    *
  • operating system is case insensitive
  • *
  • file exists
  • *
  • actual name from directory reading is different from the * supplied argument
  • *
*
* the purpose is to identify files or directories on case-insensitive * filesystems whose case is not what is expected.
* Possibly to rename them afterwards to the desired upper/lowercase * combination. *
* @param localFile file to test * @return true if the file exists and the case of the actual file * is not the case of the parameter * @since Ant 1.7.1 */ public boolean hasErrorInCase(File localFile) { localFile = normalize(localFile.getAbsolutePath()); if (!localFile.exists()) { return false; } final String localFileName = localFile.getName(); FilenameFilter ff = new FilenameFilter () { public boolean accept(File dir, String name) { return name.equalsIgnoreCase(localFileName) && (!name.equals(localFileName)); } }; String[] names = localFile.getParentFile().list(ff); return names != null && names.length == 1; } /** * 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) { return destTime != -1 && 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 (null != device) { try { device.close(); } catch (IOException e) { //ignore } } } /** * Close a Reader 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 (null != device) { try { device.close(); } catch (IOException e) { //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 (null != device) { try { device.close(); } catch (IOException e) { //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 (null != device) { try { device.close(); } catch (IOException e) { //ignore } } } /** * Close a Channel without throwing any exception if something went wrong. * Do not attempt to close it if the argument is null. * * @param device channel, can be null. * @since Ant 1.8.0 */ public static void close(Channel device) { if (null != device) { try { device.close(); } catch (IOException e) { //ignore } } } /** * Closes an URLConnection if its concrete implementation provides * a way to close it that Ant knows of. * * @param conn connection, can be null * @since Ant 1.8.0 */ public static void close(URLConnection conn) { if (conn != null) { try { if (conn instanceof JarURLConnection) { JarURLConnection juc = (JarURLConnection) conn; JarFile jf = juc.getJarFile(); jf.close(); jf = null; } else if (conn instanceof HttpURLConnection) { ((HttpURLConnection) conn).disconnect(); } } catch (IOException exc) { //ignore } } } /** * Delete the file with {@link 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(); } } /** * Accommodate Windows bug encountered in both Sun and IBM JDKs. * Others possible. If the delete does not work, call System.gc(), * wait a little and try again. * * @return whether deletion was successful * @since Ant 1.8.0 */ public boolean tryHardToDelete(File f) { return tryHardToDelete(f, ON_WINDOWS); } /** * If delete does not work, call System.gc() if asked to, wait a * little and try again. * * @return whether deletion was successful * @since Ant 1.8.3 */ public boolean tryHardToDelete(File f, boolean runGC) { if (!f.delete()) { if (runGC) { System.gc(); } try { Thread.sleep(DELETE_RETRY_SLEEP_MILLIS); } catch (InterruptedException ex) { // Ignore Exception } return f.delete(); } return true; } /** * Calculates the relative path between two files. *

* Implementation note:
This function may throw an IOException if an I/O error occurs * because its use of the canonical pathname may require filesystem queries. *

* * @param fromFile the File to calculate the path from * @param toFile the File to calculate the path to * @return the relative path between the files * @throws Exception for undocumented reasons * @see File#getCanonicalPath() * * @since Ant 1.7 */ public static String getRelativePath(File fromFile, File toFile) throws Exception { String fromPath = fromFile.getCanonicalPath(); String toPath = toFile.getCanonicalPath(); // build the path stack info to compare String[] fromPathStack = getPathStack(fromPath); String[] toPathStack = getPathStack(toPath); if (0 < toPathStack.length && 0 < fromPathStack.length) { if (!fromPathStack[0].equals(toPathStack[0])) { // not the same device (would be "" on Linux/Unix) return getPath(Arrays.asList(toPathStack)); } } else { // no comparison possible return getPath(Arrays.asList(toPathStack)); } int minLength = Math.min(fromPathStack.length, toPathStack.length); int same = 1; // Used outside the for loop // get index of parts which are equal for (; same < minLength && fromPathStack[same].equals(toPathStack[same]); same++) { // Do nothing } List relativePathStack = new ArrayList(); // if "from" part is longer, fill it up with ".." // to reach path which is equal to both paths for (int i = same; i < fromPathStack.length; i++) { relativePathStack.add(".."); } // fill it up path with parts which were not equal for (int i = same; i < toPathStack.length; i++) { relativePathStack.add(toPathStack[i]); } return getPath(relativePathStack); } /** * Gets all names of the path as an array of Strings. * * @param path to get names from * @return Strings, never null * * @since Ant 1.7 */ public static String[] getPathStack(String path) { String normalizedPath = path.replace(File.separatorChar, '/'); return normalizedPath.split("/"); } /** * Gets path from a List of Strings. * * @param pathStack List of Strings to be concatenated as a path. * @return String, never null * * @since Ant 1.7 */ public static String getPath(List pathStack) { // can safely use '/' because Windows understands '/' as separator return getPath(pathStack, '/'); } /** * Gets path from a List of Strings. * * @param pathStack List of Strings to be concated as a path. * @param separatorChar char to be used as separator between names in path * @return String, never null * * @since Ant 1.7 */ public static String getPath(final List pathStack, final char separatorChar) { final StringBuffer buffer = new StringBuffer(); final Iterator iter = pathStack.iterator(); if (iter.hasNext()) { buffer.append(iter.next()); } while (iter.hasNext()) { buffer.append(separatorChar); buffer.append(iter.next()); } return buffer.toString(); } /** * Get the default encoding. * This is done by opening an InputStreamReader on * a dummy InputStream and getting the encoding. * Could use System.getProperty("file.encoding"), but cannot * see where this is documented. * @return the default file encoding. */ public String getDefaultEncoding() { InputStreamReader is = new InputStreamReader( new InputStream() { public int read() { return -1; } }); try { return is.getEncoding(); } finally { close(is); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy