From 43e62b81ff4d8554b4a548d26e0f907318ed6f1d Mon Sep 17 00:00:00 2001 From: Magesh Umasankar Date: Thu, 28 Feb 2002 21:59:04 +0000 Subject: [PATCH] 1. ExpandProperties filter introduced as envisioned by Steve. 2. String readFully(Reader) added to FileUtils.java (main trunk revision 1.13) 3. Doc changes 4. Removed the attribute evaluateproperties from as it is no longer necessary. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271617 13f79535-47bb-0310-9956-ffa450edef68 --- .../docs/manual/CoreTasks/loadfile.html | 12 +- .../tools/ant/filters/BaseFilterReader.java | 27 + .../tools/ant/filters/ExpandProperties.java | 142 ++++ .../ant/filters/util/ChainReaderHelper.java | 45 +- .../apache/tools/ant/taskdefs/LoadFile.java | 18 +- .../tools/ant/taskdefs/LoadProperties.java | 1 + .../apache/tools/ant/types/FilterChain.java | 5 + .../org/apache/tools/ant/util/FileUtils.java | 628 ++++++++++++++++++ 8 files changed, 832 insertions(+), 46 deletions(-) create mode 100644 proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/ExpandProperties.java create mode 100644 proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java diff --git a/proposal/sandbox/filterreaders/docs/manual/CoreTasks/loadfile.html b/proposal/sandbox/filterreaders/docs/manual/CoreTasks/loadfile.html index e909de3cf..06a2f0550 100644 --- a/proposal/sandbox/filterreaders/docs/manual/CoreTasks/loadfile.html +++ b/proposal/sandbox/filterreaders/docs/manual/CoreTasks/loadfile.html @@ -41,11 +41,6 @@ Whether to halt the build on failure No, default "true" - - evaluateProperties - flag to enable property evalation in the file - No, default "false" -

The LoadFile task supports nested @@ -75,7 +70,7 @@ Load a file, don't fail if it is missing (a message is printed, though) property="mail.recipients" srcFile="recipientlist.txt"> <filterchain> - <filterreader classname="org.apache.tools.ant.filters.StripLineBreaks" /> + <striplinebreaks/> </filterchaint> </loadfile> @@ -84,8 +79,9 @@ merging lines to ensure this happens.

    <loadfile
       property="system.configuration.xml"
-      srcFile="configuration.xml"
-      evaluateProperties="true" />
+      srcFile="configuration.xml">
+        <expandproperties/>
+    </loadfile>
 
Load an XML file into a property, expanding all properties declared in the file in the process. diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/BaseFilterReader.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/BaseFilterReader.java index 514313c4a..5ce041edb 100644 --- a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/BaseFilterReader.java +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/BaseFilterReader.java @@ -58,6 +58,9 @@ import java.io.IOException; import java.io.Reader; import java.io.StringReader; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.util.FileUtils; + /** * Base class for core filter readers. * @@ -69,6 +72,9 @@ public abstract class BaseFilterReader /** Have the parameters passed been interpreted? */ private boolean initialized = false; + /** The Ant project */ + private Project project = null; + /** * This constructor is a dummy constructor and is * not meant to be used by any class other than Ant's @@ -162,6 +168,20 @@ public abstract class BaseFilterReader return initialized; } + /** + * Set the project to work with + */ + public final void setProject(final Project project) { + this.project = project; + } + + /** + * Get the project + */ + protected final Project getProject() { + return project; + } + /** * Read till EOL */ @@ -177,4 +197,11 @@ public abstract class BaseFilterReader } return line; } + + /** + * Read till EOF + */ + protected final String readFully() throws IOException { + return FileUtils.readFully(in, 8192); + } } diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/ExpandProperties.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/ExpandProperties.java new file mode 100644 index 000000000..13dfc54cc --- /dev/null +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/ExpandProperties.java @@ -0,0 +1,142 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 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 + * . + */ +package org.apache.tools.ant.filters; + +import java.io.IOException; +import java.io.Reader; + +import org.apache.tools.ant.Project; + +/** + * Attach a prefix to every line + * + * Example: + * ======= + * + * <prefixlines prefix="Foo"/> + * + * Or: + * + * <filterreader classname="org.apache.tools.ant.filters.PrefixLines"> + * <param name="prefix" value="Foo"/> + * </filterreader> + * + * @author Magesh Umasankar + */ +public final class ExpandProperties + extends BaseFilterReader + implements ChainableReader +{ + /** Data that must be read from, if not null. */ + private String queuedData = null; + + /** + * This constructor is a dummy constructor and is + * not meant to be used by any class other than Ant's + * introspection mechanism. This will close the filter + * that is created making it useless for further operations. + */ + public ExpandProperties() { + super(); + } + + /** + * Create a new filtered reader. + * + * @param in a Reader object providing the underlying stream. + */ + public ExpandProperties(final Reader in) { + super(in); + } + + /** + * Prefix lines with user defined prefix. + */ + public final int read() throws IOException { + + int ch = -1; + + if (queuedData != null && queuedData.length() == 0) { + queuedData = null; + } + + if (queuedData != null) { + ch = queuedData.charAt(0); + queuedData = queuedData.substring(1); + if (queuedData.length() == 0) { + queuedData = null; + } + } else { + queuedData = readFully(); + if (queuedData == null) { + ch = -1; + } else { + Project project = getProject(); + queuedData = project.replaceProperties(queuedData); + return read(); + } + } + return ch; + } + + /** + * Create a new PrefixLines using the passed in + * Reader for instantiation. + */ + public final Reader chain(final Reader rdr) { + ExpandProperties newFilter = new ExpandProperties(rdr); + newFilter.setProject(getProject()); + return newFilter; + } +} diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java index 1546ed310..384ba17b6 100644 --- a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java @@ -56,12 +56,14 @@ package org.apache.tools.ant.filters.util; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; +import org.apache.tools.ant.filters.BaseFilterReader; import org.apache.tools.ant.filters.ChainableReader; import org.apache.tools.ant.types.AntFilterReader; import org.apache.tools.ant.types.FilterChain; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Parameter; import org.apache.tools.ant.types.Parameterizable; +import org.apache.tools.ant.util.FileUtils; import java.io.*; import java.lang.reflect.Constructor; @@ -84,13 +86,16 @@ public final class ChainReaderHelper { /** * The size of the buffer to be used. */ - public int bufferSize = 4096; + public int bufferSize = 8192; /** * Chain of filters */ public Vector filterChains = new Vector(); + /** The Ant project */ + private Project project = null; + /** * Sets the primary reader */ @@ -98,6 +103,20 @@ public final class ChainReaderHelper { primaryReader = rdr; } + /** + * Set the project to work with + */ + public final void setProject(final Project project) { + this.project = project; + } + + /** + * Get the project + */ + public final Project getProject() { + return project; + } + /** * Sets the buffer size to be used. Defaults to 4096, * if this method is not invoked. @@ -194,6 +213,9 @@ public final class ChainReaderHelper { } } else if (o instanceof ChainableReader && o instanceof Reader) { + if (project != null && o instanceof BaseFilterReader) { + ((BaseFilterReader) o).setProject(project); + } instream = ((ChainableReader) o).chain(instream); } } @@ -207,25 +229,6 @@ public final class ChainReaderHelper { */ public final String readFully(Reader rdr) throws IOException { - - 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 (textBuffer != null) { - text = textBuffer.toString(); - } - return text; + return FileUtils.readFully(rdr, bufferSize); } } diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadFile.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadFile.java index 7f6222440..60215517d 100644 --- a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadFile.java +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadFile.java @@ -92,11 +92,6 @@ public final class LoadFile extends Task { */ private String property = null; - /** - * flag to control whether props get evaluated or not - */ - private boolean evaluateProperties=false; - /** * Holds FilterChains */ @@ -148,15 +143,6 @@ public final class LoadFile extends Task { } - /** - * setter to eval properties. - * @since 1.6 - */ - public final void setEvaluateProperties(final boolean evaluateProperties) { - this.evaluateProperties=evaluateProperties; - } - - /** * read in a source file to a property * @@ -194,14 +180,12 @@ public final class LoadFile extends Task { crh.setBufferSize(size); crh.setPrimaryReader(instream); crh.setFilterChains(filterChains); + crh.setProject(project); instream = crh.getAssembledReader(); String text = crh.readFully(instream); if (text != null) { - if(evaluateProperties) { - text = project.replaceProperties(text); - } project.setNewProperty(property, text); log("loaded " + text.length() + " characters",Project.MSG_VERBOSE); log(property+" := "+text,Project.MSG_DEBUG); diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java index ca5b07527..9e6982f38 100644 --- a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java @@ -128,6 +128,7 @@ public final class LoadProperties extends Task { crh.setBufferSize(size); crh.setPrimaryReader(instream); crh.setFilterChains(filterChains); + crh.setProject(project); instream = crh.getAssembledReader(); String text = crh.readFully(instream); diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/types/FilterChain.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/types/FilterChain.java index a0ad2e870..441ead33a 100644 --- a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/types/FilterChain.java +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/types/FilterChain.java @@ -55,6 +55,7 @@ package org.apache.tools.ant.types; import java.util.Vector; +import org.apache.tools.ant.filters.ExpandProperties; import org.apache.tools.ant.filters.HeadFilter; import org.apache.tools.ant.filters.LineContains; import org.apache.tools.ant.filters.PrefixLines; @@ -82,6 +83,10 @@ public final class FilterChain { return filterReaders; } + public final void addExpandProperties(final ExpandProperties expandProperties) { + filterReaders.addElement(expandProperties); + } + public final void addHeadFilter(final HeadFilter headFilter) { filterReaders.addElement(headFilter); } diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java new file mode 100644 index 000000000..e72540108 --- /dev/null +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java @@ -0,0 +1,628 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2002 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 + * . + */ + +package org.apache.tools.ant.util; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import java.lang.reflect.Method; +import java.text.DecimalFormat; +import java.util.Random; +import java.util.Stack; +import java.util.StringTokenizer; + +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 Conor MacNeill + * @author Stefan Bodewig + * + * @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; + + /** + * 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 destFile file should be made equal + * to the last modified time of sourceFile. + * + * @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 destFile file should be made equal + * to the last modified time of sourceFile. + * + * @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 = getParentFile(destFile); + 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 + * new java.io.File(filename). + * + * @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("..")) { + helpFile = getParentFile(helpFile); + if (helpFile == null) { + String msg = "The file or path you specified (" + + filename + ") is invalid relative to " + + file.getPath(); + throw new BuildException(msg); + } + } else if (part.equals(".")) { + // Do nothing here + } else { + helpFile = new File(helpFile, part); + } + } + + return new File(helpFile.getAbsolutePath()); + } + + /** + * "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.
  • + *
+ * + * @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 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); + } + + /** + * 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.

+ * + *

This method is different to File.createTempFile of JDK 1.2 + * as it doesn't create the file itself and doesn't use platform + * specific temporary directory when the parentDir attribute is + * null.

+ * + * @param parentDir Directory to create the temporary file in - + * current working directory will be assumed if this parameter is + * null. + * + * @since 1.8 + */ + public File createTempFile(String prefix, String suffix, File parentDir) { + + File result = null; + String parent = null; + if (parentDir != null) { + parent = parentDir.getPath(); + } + DecimalFormat fmt = new DecimalFormat("#####"); + synchronized (rand) { + do { + result = new File(parent, + prefix + fmt.format(rand.nextInt()) + + suffix); + } while (result.exists()); + } + return result; + } + + /** + * Compares the contents of two files. + * + *

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.

+ * + * @since 1.9 + */ + public boolean contentEquals(File f1, File f2) throws IOException { + if (f1.exists() != f2.exists()) { + return false; + } + + if (!f1.exists()) { + // two not existing files are equal + return true; + } + + if (f1.isDirectory() || f2.isDirectory()) { + // don't want to compare directory contents for now + return false; + } + + if (f1.equals(f2)) { + // same filename => true + return true; + } + + if (f1.length() != f2.length()) { + // different size =>false + return false; + } + + InputStream in1 = null; + InputStream in2 = null; + try { + in1 = new BufferedInputStream(new FileInputStream(f1)); + in2 = new BufferedInputStream(new FileInputStream(f2)); + + int expectedByte = in1.read(); + while (expectedByte != -1) { + if (expectedByte != in2.read()) { + return false; + } + expectedByte = in1.read(); + } + if (in2.read() != -1) { + return false; + } + return true; + } finally { + if (in1 != null) { + try { + in1.close(); + } catch (IOException e) {} + } + if (in2 != null) { + try { + in2.close(); + } catch (IOException e) {} + } + } + } + + /** + * Emulation of File.getParentFile for JDK 1.1 + * + * @since 1.10 + */ + public File getParentFile(File f) { + if (f != null) { + String p = f.getParent(); + if (p != null) { + return new File(p); + } + } + return null; + } + + /** + * Read from reader till EOF + */ + public static final String readFully(Reader rdr) throws IOException { + return readFully(rdr, 8192); + } + + /** + * Read from reader till EOF + */ + public static final String readFully(Reader rdr, int bufferSize) throws IOException { + 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 (textBuffer != null) { + text = textBuffer.toString(); + } + return text; + } +} +