|
- /*
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2001 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution, if
- * any, must include the following acknowlegement:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowlegement may appear in the software itself,
- * if and wherever such third-party acknowlegements normally appear.
- *
- * 4. The names "The Jakarta Project", "Ant", and "Apache Software
- * Foundation" must not be used to endorse or promote products derived
- * from this software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache"
- * nor may "Apache" appear in their names without prior written
- * permission of the Apache Group.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- */
-
- package org.apache.tools.ant.util;
-
- import java.io.*;
- import java.lang.reflect.Method;
- import java.util.StringTokenizer;
- import java.util.Stack;
-
- import org.apache.tools.ant.BuildException;
- import org.apache.tools.ant.Project;
- import org.apache.tools.ant.types.FilterSetCollection;
-
- /**
- * This class also encapsulates methods which allow Files to be
- * refered 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.
- *
- * @author duncan@x180.com
- * @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
- * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
- */
-
- public class FileUtils {
- private static Object lockReflection = new Object();
- private static java.lang.reflect.Method setLastModified = null;
-
- /**
- * Factory method.
- */
- public static FileUtils newFileUtils() {
- return new FileUtils();
- }
-
- /**
- * Empty constructor.
- */
- protected FileUtils() {}
-
- /**
- * Convienence method to copy a file from a source to a destination.
- * No filtering is performed.
- *
- * @throws IOException
- */
- public void copyFile(String sourceFile, String destFile) throws IOException {
- copyFile(new File(sourceFile), new File(destFile), null, false, false);
- }
-
- /**
- * Convienence method to copy a file from a source to a destination
- * specifying if token filtering must be used.
- *
- * @throws IOException
- */
- public void copyFile(String sourceFile, String destFile, FilterSetCollection filters)
- throws IOException
- {
- copyFile(new File(sourceFile), new File(destFile), filters, false, false);
- }
-
- /**
- * Convienence 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.
- *
- * @throws IOException
- */
- public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
- boolean overwrite) throws IOException {
- copyFile(new File(sourceFile), new File(destFile), filters,
- overwrite, false);
- }
-
- /**
- * Convienence 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
- * to the last modified time of <code>sourceFile</code>.
- *
- * @throws IOException
- */
- 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);
- }
-
- /**
- * Convienence method to copy a file from a source to a destination.
- * No filtering is performed.
- *
- * @throws IOException
- */
- 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
- * specifying if token filtering must be used.
- *
- * @throws IOException
- */
- public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
- throws IOException {
- copyFile(sourceFile, destFile, filters, false, false);
- }
-
- /**
- * Convienence 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.
- *
- * @throws IOException
- */
- public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
- boolean overwrite) throws IOException {
- copyFile(sourceFile, destFile, filters, overwrite, false);
- }
-
- /**
- * Convienence 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
- * to the last modified time of <code>sourceFile</code>.
- *
- * @throws IOException
- */
- public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
- boolean overwrite, boolean preserveLastModified)
- throws IOException {
-
- if (overwrite || !destFile.exists() ||
- destFile.lastModified() < sourceFile.lastModified()) {
-
- 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 = new File(destFile.getParent());
- if (!parent.exists()) {
- parent.mkdirs();
- }
-
- if (filters != null && filters.hasFilters()) {
- BufferedReader in = new BufferedReader(new FileReader(sourceFile));
- BufferedWriter out = new BufferedWriter(new FileWriter(destFile));
-
- int length;
- String newline = null;
- String line = in.readLine();
- while (line != null) {
- if (line.length() == 0) {
- out.newLine();
- } else {
- newline = filters.replaceTokens(line);
- out.write(newline);
- out.newLine();
- }
- line = in.readLine();
- }
-
- out.close();
- in.close();
- } else {
- FileInputStream in = new FileInputStream(sourceFile);
- FileOutputStream out = new FileOutputStream(destFile);
-
- byte[] buffer = new byte[8 * 1024];
- int count = 0;
- do {
- out.write(buffer, 0, count);
- count = in.read(buffer, 0, buffer.length);
- } while (count != -1);
-
- in.close();
- out.close();
- }
-
- if (preserveLastModified) {
- setFileLastModified(destFile, sourceFile.lastModified());
- }
- }
- }
-
- /**
- * see whether we have a setLastModified method in File and return it.
- */
- protected final Method getSetLastModified() {
- if (Project.getJavaVersion() == Project.JAVA_1_1) {
- return null;
- }
- if (setLastModified == 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.
- */
- public void setFileLastModified(File file, long time) throws BuildException {
- if (Project.getJavaVersion() == Project.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);
- }
- }
-
- /**
- * 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
- * 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
- * <code>new java.io.File(filename)</code>.
- *
- * @param filename a file name
- *
- * @return an absolute file that doesn't contain "./" or
- * "../" sequences and uses the correct separator for
- * the current platform.
- */
- public File resolveFile(File file, String filename) {
- filename = filename.replace('/', File.separatorChar)
- .replace('\\', File.separatorChar);
-
- // deal with absolute files
- if (filename.startsWith(File.separator) ||
-
- (filename.length() >= 2 &&
- Character.isLetter(filename.charAt(0)) &&
- filename.charAt(1) == ':')
-
- ) {
- 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("..")) {
- String parentFile = helpFile.getParent();
- if (parentFile == null) {
- String msg = "The file or path you specified ("
- + filename + ") is invalid relative to "
- + file.getPath();
- throw new BuildException(msg);
- }
- helpFile = new File(parentFile);
- } else if (part.equals(".")) {
- // Do nothing here
- } else {
- helpFile = new File(helpFile, part);
- }
- }
-
- return new File(helpFile.getAbsolutePath());
- }
-
- /**
- * "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>DOS style paths that start with a drive letter will have
- * \ as the separator.</li>
- * </ul>
- *
- * @throws java.lang.NullPointerException if the file path is
- * equal to null.
- */
- public File normalize(String path) {
- String orig = path;
-
- path = path.replace('/', File.separatorChar)
- .replace('\\', File.separatorChar);
-
- // make sure we are dealing with an absolute path
- if (!path.startsWith(File.separator) &&
- ! (path.length() >= 2 &&
- Character.isLetter(path.charAt(0)) &&
- path.charAt(1) == ':')
- ) {
- 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 (path.length() >= 2 &&
- Character.isLetter(path.charAt(0)) &&
- path.charAt(1) == ':') {
-
- dosWithDrive = true;
-
- char[] ca = path.replace('/', '\\').toCharArray();
- StringBuffer sb = new StringBuffer();
- sb.append(Character.toUpperCase(ca[0])).append(':');
-
- for (int i = 2; i < ca.length; i++) {
- if ((ca[i] != '\\') ||
- (ca[i] == '\\' && ca[i - 1] != '\\')
- ) {
- sb.append(ca[i]);
- }
- }
-
- path = sb.toString().replace('\\', File.separatorChar);
- if (path.length() == 2) {
- root = path;
- path = "";
- } else {
- root = path.substring(0, 3);
- path = path.substring(3);
- }
-
- } else {
- if (path.length() == 1) {
- root = File.separator;
- path = "";
- } else if (path.charAt(1) == File.separatorChar) {
- // UNC drive
- root = File.separator+File.separator;
- path = path.substring(2);
- } else {
- root = File.separator;
- path = path.substring(1);
- }
- }
-
- Stack s = new Stack();
- s.push(root);
- StringTokenizer tok = new StringTokenizer(path, File.separator);
- while (tok.hasMoreTokens()) {
- String thisToken = tok.nextToken();
- if (".".equals(thisToken)) {
- continue;
- } else if ("..".equals(thisToken)) {
- if (s.size() < 2) {
- throw new BuildException("Cannot resolve path "+orig);
- } else {
- s.pop();
- }
- } else { // plain component
- s.push(thisToken);
- }
- }
-
- StringBuffer sb = new StringBuffer();
- for (int i=0; i<s.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));
- }
-
-
- path = sb.toString();
- if (dosWithDrive) {
- path = path.replace('/', '\\');
- }
- return new File(path);
- }
- }
|