aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java860
1 files changed, 860 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java
new file mode 100644
index 00000000..6397f714
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ResourceUtils.java
@@ -0,0 +1,860 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.TimeComparison;
+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.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.StringResource;
+import org.apache.tools.ant.types.resources.Touchable;
+import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.types.resources.selectors.Date;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
+// CheckStyle:HideUtilityClassConstructorCheck OFF - bc
+
+/**
+ * This class provides utility methods to process Resources.
+ *
+ * @since Ant 1.5.2
+ */
+public class ResourceUtils {
+
+ /** Utilities used for file operations */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /**
+ * Name of charset "ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1".
+ *
+ * @since Ant 1.8.1
+ */
+ public static final String ISO_8859_1 = "ISO-8859-1";
+
+ private static final long MAX_IO_CHUNK_SIZE = 16*1024*1024; // 16 MB
+
+ /**
+ * Tells which source files should be reprocessed based on the
+ * last modification date of target files.
+ * @param logTo where to send (more or less) interesting output.
+ * @param source array of resources bearing relative path and last
+ * modification date.
+ * @param mapper filename mapper indicating how to find the target
+ * files.
+ * @param targets object able to map as a resource a relative path
+ * at <b>destination</b>.
+ * @return array containing the source files which need to be
+ * copied or processed, because the targets are out of date or do
+ * not exist.
+ */
+ public static Resource[] selectOutOfDateSources(final ProjectComponent logTo,
+ final Resource[] source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets) {
+ return selectOutOfDateSources(logTo, source, mapper, targets,
+ FILE_UTILS.getFileTimestampGranularity());
+ }
+
+ /**
+ * Tells which source files should be reprocessed based on the
+ * last modification date of target files.
+ * @param logTo where to send (more or less) interesting output.
+ * @param source array of resources bearing relative path and last
+ * modification date.
+ * @param mapper filename mapper indicating how to find the target
+ * files.
+ * @param targets object able to map as a resource a relative path
+ * at <b>destination</b>.
+ * @param granularity The number of milliseconds leeway to give
+ * before deciding a target is out of date.
+ * @return array containing the source files which need to be
+ * copied or processed, because the targets are out of date or do
+ * not exist.
+ * @since Ant 1.6.2
+ */
+ public static Resource[] selectOutOfDateSources(final ProjectComponent logTo,
+ final Resource[] source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets,
+ final long granularity) {
+ final Union u = new Union();
+ u.addAll(Arrays.asList(source));
+ final ResourceCollection rc
+ = selectOutOfDateSources(logTo, u, mapper, targets, granularity);
+ return rc.size() == 0 ? new Resource[0] : ((Union) rc).listResources();
+ }
+
+ /**
+ * Tells which sources should be reprocessed based on the
+ * last modification date of targets.
+ * @param logTo where to send (more or less) interesting output.
+ * @param source ResourceCollection.
+ * @param mapper filename mapper indicating how to find the target Resources.
+ * @param targets object able to map a relative path as a Resource.
+ * @param granularity The number of milliseconds leeway to give
+ * before deciding a target is out of date.
+ * @return ResourceCollection.
+ * @since Ant 1.7
+ */
+ public static ResourceCollection selectOutOfDateSources(final ProjectComponent logTo,
+ final ResourceCollection source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets,
+ final long granularity) {
+ logFuture(logTo, source, granularity);
+ final ResourceSelectorProvider p =
+ new ResourceSelectorProvider() {
+ public ResourceSelector
+ getTargetSelectorForSource(final Resource sr) {
+ return new ResourceSelector() {
+ public boolean isSelected(final Resource target) {
+ /* Extra I/O, probably wasted:
+ if (target.isDirectory()) {
+ return false;
+ }
+ */
+ return SelectorUtils.isOutOfDate(sr, target,
+ granularity);
+ }
+ };
+ }
+ };
+ return selectSources(logTo, source, mapper, targets, p);
+ }
+
+ /**
+ * Tells which sources should be reprocessed because the given
+ * selector selects at least one target.
+ *
+ * @param logTo where to send (more or less) interesting output.
+ * @param source ResourceCollection.
+ * @param mapper filename mapper indicating how to find the target Resources.
+ * @param targets object able to map a relative path as a Resource.
+ * @param selector returns a selector that is applied to target
+ * files. If it selects at least one target the source will be
+ * added to the returned collection.
+ * @return ResourceCollection.
+ * @since Ant 1.8.0
+ */
+ public static ResourceCollection selectSources(final ProjectComponent logTo,
+ ResourceCollection source,
+ final FileNameMapper mapper,
+ final ResourceFactory targets,
+ final ResourceSelectorProvider selector) {
+ if (source.size() == 0) {
+ logTo.log("No sources found.", Project.MSG_VERBOSE);
+ return Resources.NONE;
+ }
+ source = Union.getInstance(source);
+
+ final Union result = new Union();
+ for (final Resource sr : source) {
+ String srName = sr.getName();
+ srName = srName == null
+ ? srName : srName.replace('/', File.separatorChar);
+
+ String[] targetnames = null;
+ try {
+ targetnames = mapper.mapFileName(srName);
+ } catch (final Exception e) {
+ logTo.log("Caught " + e + " mapping resource " + sr,
+ Project.MSG_VERBOSE);
+ }
+ if (targetnames == null || targetnames.length == 0) {
+ logTo.log(sr + " skipped - don\'t know how to handle it",
+ Project.MSG_VERBOSE);
+ continue;
+ }
+ for (int i = 0; i < targetnames.length; i++) {
+ if (targetnames[i] == null) {
+ targetnames[i] = "(no name)";
+ }
+ }
+ final Union targetColl = new Union();
+ for (int i = 0; i < targetnames.length; i++) {
+ targetColl.add(targets.getResource(
+ targetnames[i].replace(File.separatorChar, '/')));
+ }
+ //find the out-of-date targets:
+ final Restrict r = new Restrict();
+ r.add(selector.getTargetSelectorForSource(sr));
+ r.add(targetColl);
+ if (r.size() > 0) {
+ result.add(sr);
+ final Resource t = r.iterator().next();
+ logTo.log(sr.getName() + " added as " + t.getName()
+ + (t.isExists() ? " is outdated." : " doesn\'t exist."),
+ Project.MSG_VERBOSE);
+ continue;
+ }
+ //log uptodateness of all targets:
+ logTo.log(sr.getName()
+ + " omitted as " + targetColl.toString()
+ + (targetColl.size() == 1 ? " is" : " are ")
+ + " up to date.", Project.MSG_VERBOSE);
+ }
+ return result;
+ }
+
+ /**
+ * Convenience method to copy content from one Resource to another.
+ * No filtering is performed.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public static void copyResource(final Resource source, final Resource dest) throws IOException {
+ copyResource(source, dest, null);
+ }
+
+ /**
+ * Convenience method to copy content from one Resource to another.
+ * No filtering is performed.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public static void copyResource(final Resource source, final Resource dest, final Project project)
+ throws IOException {
+ copyResource(source, dest, null, null, false,
+ false, null, null, 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 <code>dest</code> file should be made
+ * equal to the last modified time of <code>source</code>.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @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 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.7
+ */
+ public static void copyResource(final Resource source, final Resource dest,
+ final FilterSetCollection filters, final Vector filterChains,
+ final boolean overwrite, final boolean preserveLastModified,
+ final String inputEncoding, final String outputEncoding,
+ final Project project)
+ throws IOException {
+ 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 <code>dest</code> file should be made
+ * equal to the last modified time of <code>source</code>.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @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(final Resource source, final Resource dest,
+ final FilterSetCollection filters, final Vector filterChains,
+ final boolean overwrite, final boolean preserveLastModified,
+ final boolean append,
+ final String inputEncoding, final String outputEncoding,
+ final Project project)
+ throws IOException {
+ copyResource(source, dest, filters, filterChains, overwrite,
+ preserveLastModified, append, inputEncoding,
+ outputEncoding, project, /* force: */ false);
+ }
+
+ /**
+ * 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 <code>dest</code> file should be made
+ * equal to the last modified time of <code>source</code>.
+ *
+ * @param source the Resource to copy from.
+ * Must not be <code>null</code>.
+ * @param dest the Resource to copy to.
+ * Must not be <code>null</code>.
+ * @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.
+ * @param force whether read-only target files will be overwritten
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.8.2
+ */
+ public static void copyResource(final Resource source, final Resource dest,
+ final FilterSetCollection filters, final Vector filterChains,
+ final boolean overwrite, final boolean preserveLastModified,
+ final boolean append,
+ final String inputEncoding, final String outputEncoding,
+ final Project project, final boolean force)
+ throws IOException {
+ if (!(overwrite || SelectorUtils.isOutOfDate(source, dest, FileUtils.getFileUtils()
+ .getFileTimestampGranularity()))) {
+ return;
+ }
+ final boolean filterSetsAvailable = (filters != null
+ && filters.hasFilters());
+ final boolean filterChainsAvailable = (filterChains != null
+ && filterChains.size() > 0);
+ String effectiveInputEncoding = null;
+ if (source instanceof StringResource) {
+ effectiveInputEncoding = ((StringResource) source).getEncoding();
+ } else {
+ effectiveInputEncoding = inputEncoding;
+ }
+ File destFile = null;
+ if (dest.as(FileProvider.class) != null) {
+ destFile = dest.as(FileProvider.class).getFile();
+ }
+ if (destFile != null && destFile.isFile() && !destFile.canWrite()) {
+ if (!force) {
+ throw new ReadOnlyTargetFileException(destFile);
+ } else if (!FILE_UTILS.tryHardToDelete(destFile)) {
+ throw new IOException("failed to delete read-only "
+ + "destination file " + destFile);
+ }
+ }
+
+ if (filterSetsAvailable) {
+ copyWithFilterSets(source, dest, filters, filterChains,
+ filterChainsAvailable, append,
+ effectiveInputEncoding, outputEncoding,
+ project);
+ } else if (filterChainsAvailable
+ || (effectiveInputEncoding != null
+ && !effectiveInputEncoding.equals(outputEncoding))
+ || (effectiveInputEncoding == null && outputEncoding != null)) {
+ copyWithFilterChainsOrTranscoding(source, dest, filterChains,
+ filterChainsAvailable, append,
+ effectiveInputEncoding,
+ outputEncoding, project);
+ } else {
+ boolean copied = false;
+ if (source.as(FileProvider.class) != null
+ && destFile != null && !append) {
+ final File sourceFile =
+ source.as(FileProvider.class).getFile();
+ try {
+ copyUsingFileChannels(sourceFile, destFile);
+ copied = true;
+ } catch (final IOException ex) {
+ String msg = "Attempt to copy " + sourceFile
+ + " to " + destFile + " using NIO Channels"
+ + " failed due to '" + ex.getMessage()
+ + "'. Falling back to streams.";
+ if (project != null) {
+ project.log(msg, Project.MSG_WARN);
+ } else {
+ System.err.println(msg);
+ }
+ }
+ }
+ if (!copied) {
+ copyUsingStreams(source, dest, append, project);
+ }
+ }
+ if (preserveLastModified) {
+ final Touchable t = dest.as(Touchable.class);
+ if (t != null) {
+ setLastModified(t, source.getLastModified());
+ }
+ }
+ }
+ // CheckStyle:ParameterNumberCheck ON
+
+ /**
+ * Set the last modified time of an object implementing
+ * org.apache.tools.ant.types.resources.Touchable .
+ *
+ * @param t the Touchable whose modified time is to be set.
+ * @param time the time to which the last modified time is to be set.
+ * if this is -1, the current time is used.
+ * @since Ant 1.7
+ */
+ public static void setLastModified(final Touchable t, final long time) {
+ t.touch((time < 0) ? System.currentTimeMillis() : time);
+ }
+
+ /**
+ * Compares the contents of two Resources.
+ *
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @param text true if the content is to be treated as text and
+ * differences in kind of line break are to be ignored.
+ *
+ * @return true if the content of the Resources is the same.
+ *
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ public static boolean contentEquals(final Resource r1, final Resource r2, final boolean text) throws IOException {
+ if (r1.isExists() != r2.isExists()) {
+ return false;
+ }
+ if (!r1.isExists()) {
+ // two not existing files are equal
+ return true;
+ }
+ // should the following two be switched? If r1 and r2 refer to the same file,
+ // isn't their content equal regardless of whether that file is a directory?
+ if (r1.isDirectory() || r2.isDirectory()) {
+ // don't want to compare directory contents for now
+ return false;
+ }
+ if (r1.equals(r2)) {
+ return true;
+ }
+ if (!text) {
+ final long s1 = r1.getSize();
+ final long s2 = r2.getSize();
+ if (s1 != Resource.UNKNOWN_SIZE && s2 != Resource.UNKNOWN_SIZE
+ && s1 != s2) {
+ return false;
+ }
+ }
+ return compareContent(r1, r2, text) == 0;
+ }
+
+ /**
+ * Compare the content of two Resources. A nonexistent Resource's
+ * content is "less than" that of an existing Resource; a directory-type
+ * Resource's content is "less than" that of a file-type Resource.
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @param text true if the content is to be treated as text and
+ * differences in kind of line break are to be ignored.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ public static int compareContent(final Resource r1, final Resource r2, final boolean text) throws IOException {
+ if (r1.equals(r2)) {
+ return 0;
+ }
+ final boolean e1 = r1.isExists();
+ final boolean e2 = r2.isExists();
+ if (!(e1 || e2)) {
+ return 0;
+ }
+ if (e1 != e2) {
+ return e1 ? 1 : -1;
+ }
+ final boolean d1 = r1.isDirectory();
+ final boolean d2 = r2.isDirectory();
+ if (d1 && d2) {
+ return 0;
+ }
+ if (d1 || d2) {
+ return d1 ? -1 : 1;
+ }
+ return text ? textCompare(r1, r2) : binaryCompare(r1, r2);
+ }
+
+ /**
+ * Convenience method to turn any fileProvider into a basic
+ * FileResource with the file's immediate parent as the basedir,
+ * for tasks that need one.
+ * @param fileProvider input
+ * @return fileProvider if it is a FileResource instance, or a new
+ * FileResource with fileProvider's file.
+ * @since Ant 1.8
+ */
+ public static FileResource asFileResource(final FileProvider fileProvider) {
+ if (fileProvider instanceof FileResource || fileProvider == null) {
+ return (FileResource) fileProvider;
+ }
+ final FileResource result = new FileResource(fileProvider.getFile());
+ result.setProject(Project.getProject(fileProvider));
+ return result;
+ }
+
+ /**
+ * Binary compares the contents of two Resources.
+ * <p>
+ * simple but sub-optimal comparison algorithm. written for working
+ * rather than fast. Better would be a block read into buffers followed
+ * by long comparisons apart from the final 1-7 bytes.
+ * </p>
+ *
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ private static int binaryCompare(final Resource r1, final Resource r2) throws IOException {
+ InputStream in1 = null;
+ InputStream in2 = null;
+ try {
+ in1 = new BufferedInputStream(r1.getInputStream());
+ in2 = new BufferedInputStream(r2.getInputStream());
+
+ for (int b1 = in1.read(); b1 != -1; b1 = in1.read()) {
+ final int b2 = in2.read();
+ if (b1 != b2) {
+ return b1 > b2 ? 1 : -1;
+ }
+ }
+ return in2.read() == -1 ? 0 : -1;
+ } finally {
+ FileUtils.close(in1);
+ FileUtils.close(in2);
+ }
+ }
+
+ /**
+ * Text compares the contents of two Resources.
+ * Ignores different kinds of line endings.
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.7
+ */
+ private static int textCompare(final Resource r1, final Resource r2) throws IOException {
+ BufferedReader in1 = null;
+ BufferedReader in2 = null;
+ try {
+ in1 = new BufferedReader(new InputStreamReader(r1.getInputStream()));
+ in2 = new BufferedReader(new InputStreamReader(r2.getInputStream()));
+
+ String expected = in1.readLine();
+ while (expected != null) {
+ final String actual = in2.readLine();
+ if (!expected.equals(actual)) {
+ if (actual == null) {
+ return 1;
+ }
+ return expected.compareTo(actual);
+ }
+ expected = in1.readLine();
+ }
+ return in2.readLine() == null ? 0 : -1;
+ } finally {
+ FileUtils.close(in1);
+ FileUtils.close(in2);
+ }
+ }
+
+ /**
+ * Log which Resources (if any) have been modified in the future.
+ * @param logTo the ProjectComponent to do the logging.
+ * @param rc the collection of Resources to check.
+ * @param granularity the timestamp granularity to use.
+ * @since Ant 1.7
+ */
+ private static void logFuture(final ProjectComponent logTo,
+ final ResourceCollection rc, final long granularity) {
+ final long now = System.currentTimeMillis() + granularity;
+ final Date sel = new Date();
+ sel.setMillis(now);
+ sel.setWhen(TimeComparison.AFTER);
+ final Restrict future = new Restrict();
+ future.add(sel);
+ future.add(rc);
+ for (final Resource r : future) {
+ logTo.log("Warning: " + r.getName() + " modified in the future.", Project.MSG_WARN);
+ }
+ }
+
+ private static void copyWithFilterSets(final Resource source, final Resource dest,
+ final FilterSetCollection filters,
+ final Vector filterChains,
+ final boolean filterChainsAvailable,
+ final boolean append, final String inputEncoding,
+ final String outputEncoding,
+ final Project project)
+ throws IOException {
+ BufferedReader in = null;
+ BufferedWriter out = null;
+ try {
+ InputStreamReader isr = null;
+ if (inputEncoding == null) {
+ isr = new InputStreamReader(source.getInputStream());
+ } else {
+ isr = new InputStreamReader(source.getInputStream(),
+ inputEncoding);
+ }
+ in = new BufferedReader(isr);
+ final OutputStream os = getOutputStream(dest, append, project);
+ OutputStreamWriter osw;
+ if (outputEncoding == null) {
+ osw = new OutputStreamWriter(os);
+ } else {
+ osw = new OutputStreamWriter(os, outputEncoding);
+ }
+ out = new BufferedWriter(osw);
+ if (filterChainsAvailable) {
+ final ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setBufferSize(FileUtils.BUF_SIZE);
+ crh.setPrimaryReader(in);
+ crh.setFilterChains(filterChains);
+ crh.setProject(project);
+ final Reader rdr = crh.getAssembledReader();
+ in = new BufferedReader(rdr);
+ }
+ final LineTokenizer lineTokenizer = new LineTokenizer();
+ lineTokenizer.setIncludeDelims(true);
+ String newline = null;
+ String line = lineTokenizer.getToken(in);
+ while (line != null) {
+ if (line.length() == 0) {
+ // this should not happen, because the lines are
+ // returned with the end of line delimiter
+ out.newLine();
+ } else {
+ newline = filters.replaceTokens(line);
+ out.write(newline);
+ }
+ line = lineTokenizer.getToken(in);
+ }
+ } finally {
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static void copyWithFilterChainsOrTranscoding(final Resource source,
+ final Resource dest,
+ final Vector filterChains,
+ final boolean filterChainsAvailable,
+ final boolean append,
+ final String inputEncoding,
+ final String outputEncoding,
+ final Project project)
+ throws IOException {
+ BufferedReader in = null;
+ BufferedWriter out = null;
+ try {
+ InputStreamReader isr = null;
+ if (inputEncoding == null) {
+ isr = new InputStreamReader(source.getInputStream());
+ } else {
+ isr = new InputStreamReader(source.getInputStream(),
+ inputEncoding);
+ }
+ in = new BufferedReader(isr);
+ final OutputStream os = getOutputStream(dest, append, project);
+ OutputStreamWriter osw;
+ if (outputEncoding == null) {
+ osw = new OutputStreamWriter(os);
+ } else {
+ osw = new OutputStreamWriter(os, outputEncoding);
+ }
+ out = new BufferedWriter(osw);
+ if (filterChainsAvailable) {
+ final ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setBufferSize(FileUtils.BUF_SIZE);
+ crh.setPrimaryReader(in);
+ crh.setFilterChains(filterChains);
+ crh.setProject(project);
+ final Reader rdr = crh.getAssembledReader();
+ in = new BufferedReader(rdr);
+ }
+ final char[] buffer = new char[FileUtils.BUF_SIZE];
+ while (true) {
+ final int nRead = in.read(buffer, 0, buffer.length);
+ if (nRead == -1) {
+ break;
+ }
+ out.write(buffer, 0, nRead);
+ }
+ } finally {
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static void copyUsingFileChannels(final File sourceFile,
+ final File destFile)
+ throws IOException {
+
+ final File parent = destFile.getParentFile();
+ if (parent != null && !parent.isDirectory()
+ && !(parent.mkdirs() || parent.isDirectory())) {
+ throw new IOException("failed to create the parent directory"
+ + " for " + destFile);
+ }
+
+ FileInputStream in = null;
+ FileOutputStream out = null;
+ FileChannel srcChannel = null;
+ FileChannel destChannel = null;
+
+ try {
+ in = new FileInputStream(sourceFile);
+ out = new FileOutputStream(destFile);
+
+ srcChannel = in.getChannel();
+ destChannel = out.getChannel();
+
+ long position = 0;
+ final long count = srcChannel.size();
+ while (position < count) {
+ final long chunk = Math.min(MAX_IO_CHUNK_SIZE, count - position);
+ position +=
+ destChannel.transferFrom(srcChannel, position, chunk);
+ }
+ } finally {
+ FileUtils.close(srcChannel);
+ FileUtils.close(destChannel);
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static void copyUsingStreams(final Resource source, final Resource dest,
+ final boolean append, final Project project)
+ throws IOException {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = source.getInputStream();
+ out = getOutputStream(dest, append, project);
+
+ final byte[] buffer = new byte[FileUtils.BUF_SIZE];
+ int count = 0;
+ do {
+ out.write(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ } finally {
+ FileUtils.close(out);
+ FileUtils.close(in);
+ }
+ }
+
+ private static OutputStream getOutputStream(final Resource resource, final boolean append, final Project project)
+ throws IOException {
+ if (append) {
+ final Appendable a = resource.as(Appendable.class);
+ if (a != null) {
+ return a.getAppendOutputStream();
+ }
+ String msg = "Appendable OutputStream not available for non-appendable resource "
+ + resource + "; using plain OutputStream";
+ if (project != null) {
+ project.log(msg, Project.MSG_VERBOSE);
+ } else {
+ System.out.println(msg);
+ }
+ }
+ return resource.getOutputStream();
+ }
+
+ public interface ResourceSelectorProvider {
+ ResourceSelector getTargetSelectorForSource(Resource source);
+ }
+
+ /**
+ * @since Ant 1.9.4
+ */
+ public static class ReadOnlyTargetFileException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ public ReadOnlyTargetFileException(final File destFile) {
+ super("can't write to read-only destination file " + destFile);
+ }
+ }
+}