diff options
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.java | 860 |
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); + } + } +} |