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: + *

+ * + * @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; + } +} +