diff --git a/src/main/org/apache/tools/ant/taskdefs/Echo.java b/src/main/org/apache/tools/ant/taskdefs/Echo.java index 29b6428f5..341b750ef 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Echo.java +++ b/src/main/org/apache/tools/ant/taskdefs/Echo.java @@ -20,20 +20,18 @@ package org.apache.tools.ant.taskdefs; import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.io.Writer; -import java.io.BufferedWriter; -import java.io.OutputStreamWriter; -import java.io.FileOutputStream; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; -import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.ResourceUtils; +import org.apache.tools.ant.util.StringUtils; import org.apache.tools.ant.types.LogLevel; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.resources.FileProvider; import org.apache.tools.ant.types.resources.FileResource; +import org.apache.tools.ant.types.resources.LogOutputResource; +import org.apache.tools.ant.types.resources.StringResource; /** * Writes a message to the Ant logging facilities. @@ -62,23 +60,14 @@ public class Echo extends Task { * @exception BuildException if something goes wrong with the build */ public void execute() throws BuildException { - if (output == null) { - log(message, logLevel); - } else { - Writer out = null; - try { - OutputStream os = output instanceof FileProvider ? os = new FileOutputStream( - ((FileProvider) output).getFile(), append) : output.getOutputStream(); - OutputStreamWriter osw = (encoding == null || "".equals(encoding)) ? new OutputStreamWriter( - os) - : new OutputStreamWriter(os, encoding); - out = new BufferedWriter(osw); - out.write(message, 0, message.length()); - } catch (IOException ioe) { - throw new BuildException(ioe, getLocation()); - } finally { - FileUtils.close(out); - } + final String msg = "".equals(message) ? StringUtils.LINE_SEP : message; + try { + ResourceUtils + .copyResource(new StringResource(msg), output == null ? new LogOutputResource( + this, logLevel) : output, null, null, false, false, append, null, "" + .equals(encoding) ? null : encoding, getProject()); + } catch (IOException ioe) { + throw new BuildException(ioe, getLocation()); } } @@ -88,7 +77,7 @@ public class Echo extends Task { * @param msg Sets the value for the message variable. */ public void setMessage(String msg) { - this.message = msg; + this.message = msg == null ? "" : msg; } /** @@ -97,7 +86,6 @@ public class Echo extends Task { * standard output */ public void setFile(File file) { - this.file = file; setOutput(new FileResource(getProject(), file)); } diff --git a/src/main/org/apache/tools/ant/types/resources/Appendable.java b/src/main/org/apache/tools/ant/types/resources/Appendable.java new file mode 100644 index 000000000..14f4711a4 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/resources/Appendable.java @@ -0,0 +1,35 @@ +/* + * 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.types.resources; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface to be implemented by "appendable" resources. + * @since Ant 1.8 + */ +public interface Appendable { + + /** + * Get an appending OutputStream. + * @return OutputStream + * @throws IOException if anything goes wrong + */ + OutputStream getAppendOutputStream() throws IOException; +} diff --git a/src/main/org/apache/tools/ant/types/resources/FileResource.java b/src/main/org/apache/tools/ant/types/resources/FileResource.java index a82a77f6f..56e5803c0 100644 --- a/src/main/org/apache/tools/ant/types/resources/FileResource.java +++ b/src/main/org/apache/tools/ant/types/resources/FileResource.java @@ -36,7 +36,7 @@ import org.apache.tools.ant.types.ResourceFactory; * @since Ant 1.7 */ public class FileResource extends Resource implements Touchable, FileProvider, - ResourceFactory { + ResourceFactory, Appendable { private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); private static final int NULL_FILE @@ -209,11 +209,25 @@ public class FileResource extends Resource implements Touchable, FileProvider, */ public OutputStream getOutputStream() throws IOException { if (isReference()) { - return ((Resource) getCheckedRef()).getOutputStream(); + return ((FileResource) getCheckedRef()).getOutputStream(); } + return getOutputStream(false); + } + + /** + * {@inheritDoc} + */ + public OutputStream getAppendOutputStream() throws IOException { + if (isReference()) { + return ((FileResource) getCheckedRef()).getAppendOutputStream(); + } + return getOutputStream(true); + } + + private OutputStream getOutputStream(boolean append) throws IOException { File f = getNotNullFile(); if (f.exists()) { - if (f.isFile()) { + if (f.isFile() && !append) { f.delete(); } } else { @@ -222,7 +236,7 @@ public class FileResource extends Resource implements Touchable, FileProvider, p.mkdirs(); } } - return new FileOutputStream(f); + return append ? new FileOutputStream(f.getAbsolutePath(), true) : new FileOutputStream(f); } /** diff --git a/src/main/org/apache/tools/ant/types/resources/LogOutputResource.java b/src/main/org/apache/tools/ant/types/resources/LogOutputResource.java new file mode 100644 index 000000000..cd19c9c76 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/resources/LogOutputResource.java @@ -0,0 +1,68 @@ +/* + * 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.types.resources; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.tools.ant.ProjectComponent; +import org.apache.tools.ant.taskdefs.LogOutputStream; +import org.apache.tools.ant.types.Resource; + +/** + * Output-only Resource that always appends to Ant's log. + * @since Ant 1.8 + */ +public class LogOutputResource extends Resource implements Appendable { + private static final String NAME = "[Ant log]"; + + private LogOutputStream outputStream; + + /** + * Create a new LogOutputResource. + * @param managingComponent + */ + public LogOutputResource(ProjectComponent managingComponent) { + super(NAME); + outputStream = new LogOutputStream(managingComponent); + } + + /** + * Create a new LogOutputResource. + * @param managingComponent owning log content + * @param level log level + */ + public LogOutputResource(ProjectComponent managingComponent, int level) { + super(NAME); + outputStream = new LogOutputStream(managingComponent, level); + } + + /** + * {@inheritDoc} + */ + public OutputStream getAppendOutputStream() throws IOException { + return outputStream; + } + + /** + * {@inheritDoc} + */ + public OutputStream getOutputStream() throws IOException { + return outputStream; + } +} diff --git a/src/main/org/apache/tools/ant/util/FileUtils.java b/src/main/org/apache/tools/ant/util/FileUtils.java index df6c4e740..98bd7d921 100644 --- a/src/main/org/apache/tools/ant/util/FileUtils.java +++ b/src/main/org/apache/tools/ant/util/FileUtils.java @@ -469,15 +469,52 @@ public class FileUtils { * * @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 overwrite, boolean preserveLastModified, boolean append, String inputEncoding, String outputEncoding, Project project) throws IOException { - ResourceUtils.copyResource( - new FileResource(sourceFile), new FileResource(destFile), - filters, filterChains, overwrite, preserveLastModified, - inputEncoding, outputEncoding, project); + ResourceUtils.copyResource(new FileResource(sourceFile), new FileResource(destFile), + filters, filterChains, overwrite, preserveLastModified, append, inputEncoding, + outputEncoding, project); } // CheckStyle:ParameterNumberCheck ON diff --git a/src/main/org/apache/tools/ant/util/ResourceUtils.java b/src/main/org/apache/tools/ant/util/ResourceUtils.java index 9e9b2da61..4b342ee71 100644 --- a/src/main/org/apache/tools/ant/util/ResourceUtils.java +++ b/src/main/org/apache/tools/ant/util/ResourceUtils.java @@ -39,6 +39,7 @@ import org.apache.tools.ant.types.TimeComparison; import org.apache.tools.ant.types.ResourceFactory; import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.FilterSetCollection; +import org.apache.tools.ant.types.resources.Appendable; import org.apache.tools.ant.types.resources.FileProvider; import org.apache.tools.ant.types.resources.FileResource; import org.apache.tools.ant.types.resources.Union; @@ -265,12 +266,46 @@ public class ResourceUtils { String inputEncoding, String outputEncoding, Project project) throws IOException { - if (!overwrite) { - long slm = source.getLastModified(); - if (dest.isExists() && slm != 0 - && dest.getLastModified() > slm) { - return; - } + copyResource(source, dest, filters, filterChains, overwrite, preserveLastModified, false, inputEncoding, outputEncoding, project); + } + + // CheckStyle:ParameterNumberCheck OFF - bc + /** + * Convenience method to copy content from one Resource to another + * specifying whether token filtering must be used, whether filter chains + * must be used, whether newer destination files may be overwritten and + * whether the last modified time of dest file should be made + * equal to the last modified time of source. + * + * @param source the Resource to copy from. + * Must not be null. + * @param dest the Resource 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 Resource should be + * overwritten if it already exists. + * @param preserveLastModified Whether or not the last modified time of + * the destination Resource should be set to that + * of the source. + * @param append Whether to append to an Appendable Resource. + * @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 static void copyResource(Resource source, Resource dest, + FilterSetCollection filters, Vector filterChains, + boolean overwrite, boolean preserveLastModified, boolean append, + String inputEncoding, String outputEncoding, + Project project) + throws IOException { + if (!(overwrite || SelectorUtils.isOutOfDate(source, dest, FileUtils.getFileUtils() + .getFileTimestampGranularity()))) { + return; } final boolean filterSetsAvailable = (filters != null && filters.hasFilters()); @@ -288,12 +323,12 @@ public class ResourceUtils { inputEncoding); } in = new BufferedReader(isr); - OutputStreamWriter osw = null; + OutputStream os = getOutputStream(dest, append, project); + OutputStreamWriter osw; if (outputEncoding == null) { - osw = new OutputStreamWriter(dest.getOutputStream()); + osw = new OutputStreamWriter(os); } else { - osw = new OutputStreamWriter(dest.getOutputStream(), - outputEncoding); + osw = new OutputStreamWriter(os, outputEncoding); } out = new BufferedWriter(osw); if (filterChainsAvailable) { @@ -339,12 +374,12 @@ public class ResourceUtils { inputEncoding); } in = new BufferedReader(isr); - OutputStreamWriter osw = null; + OutputStream os = getOutputStream(dest, append, project); + OutputStreamWriter osw; if (outputEncoding == null) { - osw = new OutputStreamWriter(dest.getOutputStream()); + osw = new OutputStreamWriter(os); } else { - osw = new OutputStreamWriter(dest.getOutputStream(), - outputEncoding); + osw = new OutputStreamWriter(os, outputEncoding); } out = new BufferedWriter(osw); if (filterChainsAvailable) { @@ -373,7 +408,7 @@ public class ResourceUtils { OutputStream out = null; try { in = source.getInputStream(); - out = dest.getOutputStream(); + out = getOutputStream(dest, append, project); byte[] buffer = new byte[FileUtils.BUF_SIZE]; int count = 0; @@ -587,4 +622,15 @@ public class ResourceUtils { } } + private static OutputStream getOutputStream(Resource resource, boolean append, Project project) + throws IOException { + if (append) { + if (resource instanceof Appendable) { + return ((Appendable) resource).getAppendOutputStream(); + } + project.log("Appendable OutputStream not available for non-appendable resource " + + resource + "; using plain OutputStream", Project.MSG_VERBOSE); + } + return resource.getOutputStream(); + } } diff --git a/src/tests/antunit/taskdefs/echo-test.xml b/src/tests/antunit/taskdefs/echo-test.xml index f206edefd..76cf4fb11 100644 --- a/src/tests/antunit/taskdefs/echo-test.xml +++ b/src/tests/antunit/taskdefs/echo-test.xml @@ -86,6 +86,9 @@ Simple text Appended + + +