aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java606
1 files changed, 606 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java
new file mode 100644
index 00000000..c1c75529
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Sync.java
@@ -0,0 +1,606 @@
+/*
+ * 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.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.AbstractFileSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.Restrict;
+import org.apache.tools.ant.types.resources.selectors.Exists;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+
+/**
+ * Synchronize a local target directory from the files defined
+ * in one or more filesets.
+ *
+ * <p>Uses a &lt;copy&gt; task internally, but forbidding the use of
+ * mappers and filter chains. Files of the destination directory not
+ * present in any of the source fileset are removed.</p>
+ *
+ * @since Ant 1.6
+ *
+ * revised by <a href="mailto:daniel.armbrust@mayo.edu">Dan Armbrust</a>
+ * to remove orphaned directories.
+ *
+ * @ant.task category="filesystem"
+ */
+public class Sync extends Task {
+
+ // Same as regular <copy> task... see at end-of-file!
+ private MyCopy myCopy;
+
+ // Similar to a fileset, but doesn't allow dir attribute to be set
+ private SyncTarget syncTarget;
+
+ private Resources resources = null;
+
+ // Override Task#init
+ /**
+ * Initialize the sync task.
+ * @throws BuildException if there is a problem.
+ * @see Task#init()
+ */
+ @Override
+ public void init()
+ throws BuildException {
+ // Instantiate it
+ myCopy = new MyCopy();
+ configureTask(myCopy);
+
+ // Default config of <mycopy> for our purposes.
+ myCopy.setFiltering(false);
+ myCopy.setIncludeEmptyDirs(false);
+ myCopy.setPreserveLastModified(true);
+ }
+
+ private void configureTask(Task helper) {
+ helper.setProject(getProject());
+ helper.setTaskName(getTaskName());
+ helper.setOwningTarget(getOwningTarget());
+ helper.init();
+ }
+
+ // Override Task#execute
+ /**
+ * Execute the sync task.
+ * @throws BuildException if there is an error.
+ * @see Task#execute()
+ */
+ @Override
+ public void execute()
+ throws BuildException {
+ // The destination of the files to copy
+ File toDir = myCopy.getToDir();
+
+ // The complete list of files to copy
+ Set allFiles = myCopy.nonOrphans;
+
+ // If the destination directory didn't already exist,
+ // or was empty, then no previous file removal is necessary!
+ boolean noRemovalNecessary = !toDir.exists() || toDir.list().length < 1;
+
+ // Copy all the necessary out-of-date files
+ log("PASS#1: Copying files to " + toDir, Project.MSG_DEBUG);
+ myCopy.execute();
+
+ // Do we need to perform further processing?
+ if (noRemovalNecessary) {
+ log("NO removing necessary in " + toDir, Project.MSG_DEBUG);
+ return; // nope ;-)
+ }
+
+ // will hold the directories matched by SyncTarget in reversed
+ // lexicographic order (order is important, that's why we use
+ // a LinkedHashSet
+ Set preservedDirectories = new LinkedHashSet();
+
+ // Get rid of all files not listed in the source filesets.
+ log("PASS#2: Removing orphan files from " + toDir, Project.MSG_DEBUG);
+ int[] removedFileCount = removeOrphanFiles(allFiles, toDir,
+ preservedDirectories);
+ logRemovedCount(removedFileCount[0], "dangling director", "y", "ies");
+ logRemovedCount(removedFileCount[1], "dangling file", "", "s");
+
+ // Get rid of empty directories on the destination side
+ if (!myCopy.getIncludeEmptyDirs()
+ || getExplicitPreserveEmptyDirs() == Boolean.FALSE) {
+ log("PASS#3: Removing empty directories from " + toDir,
+ Project.MSG_DEBUG);
+
+ int removedDirCount = 0;
+ if (!myCopy.getIncludeEmptyDirs()) {
+ removedDirCount =
+ removeEmptyDirectories(toDir, false, preservedDirectories);
+ } else { // must be syncTarget.preserveEmptydirs == FALSE
+ removedDirCount =
+ removeEmptyDirectories(preservedDirectories);
+ }
+ logRemovedCount(removedDirCount, "empty director", "y", "ies");
+ }
+ }
+
+ private void logRemovedCount(int count, String prefix,
+ String singularSuffix, String pluralSuffix) {
+ File toDir = myCopy.getToDir();
+
+ String what = (prefix == null) ? "" : prefix;
+ what += (count < 2) ? singularSuffix : pluralSuffix;
+
+ if (count > 0) {
+ log("Removed " + count + " " + what + " from " + toDir,
+ Project.MSG_INFO);
+ } else {
+ log("NO " + what + " to remove from " + toDir,
+ Project.MSG_VERBOSE);
+ }
+ }
+
+ /**
+ * Removes all files and folders not found as keys of a table
+ * (used as a set!).
+ *
+ * <p>If the provided file is a directory, it is recursively
+ * scanned for orphaned files which will be removed as well.</p>
+ *
+ * <p>If the directory is an orphan, it will also be removed.</p>
+ *
+ * @param nonOrphans the table of all non-orphan <code>File</code>s.
+ * @param file the initial file or directory to scan or test.
+ * @param preservedDirectories will be filled with the directories
+ * matched by preserveInTarget - if any. Will not be
+ * filled unless preserveEmptyDirs and includeEmptyDirs
+ * conflict.
+ * @return the number of orphaned files and directories actually removed.
+ * Position 0 of the array is the number of orphaned directories.
+ * Position 1 of the array is the number or orphaned files.
+ */
+ private int[] removeOrphanFiles(Set nonOrphans, File toDir,
+ Set preservedDirectories) {
+ int[] removedCount = new int[] {0, 0};
+ String[] excls =
+ (String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]);
+ // want to keep toDir itself
+ excls[nonOrphans.size()] = "";
+
+ DirectoryScanner ds = null;
+ if (syncTarget != null) {
+ FileSet fs = syncTarget.toFileSet(false);
+ fs.setDir(toDir);
+
+ // preserveInTarget would find all files we want to keep,
+ // but we need to find all that we want to delete - so the
+ // meaning of all patterns and selectors must be inverted
+ PatternSet ps = syncTarget.mergePatterns(getProject());
+ fs.appendExcludes(ps.getIncludePatterns(getProject()));
+ fs.appendIncludes(ps.getExcludePatterns(getProject()));
+ fs.setDefaultexcludes(!syncTarget.getDefaultexcludes());
+
+ // selectors are implicitly ANDed in DirectoryScanner. To
+ // revert their logic we wrap them into a <none> selector
+ // instead.
+ FileSelector[] s = syncTarget.getSelectors(getProject());
+ if (s.length > 0) {
+ NoneSelector ns = new NoneSelector();
+ for (int i = 0; i < s.length; i++) {
+ ns.appendSelector(s[i]);
+ }
+ fs.appendSelector(ns);
+ }
+ ds = fs.getDirectoryScanner(getProject());
+ } else {
+ ds = new DirectoryScanner();
+ ds.setBasedir(toDir);
+ }
+ ds.addExcludes(excls);
+
+ ds.scan();
+ String[] files = ds.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ File f = new File(toDir, files[i]);
+ log("Removing orphan file: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount[1];
+ }
+ String[] dirs = ds.getIncludedDirectories();
+ // ds returns the directories in lexicographic order.
+ // iterating through the array backwards means we are deleting
+ // leaves before their parent nodes - thus making sure (well,
+ // more likely) that the directories are empty when we try to
+ // delete them.
+ for (int i = dirs.length - 1; i >= 0; --i) {
+ File f = new File(toDir, dirs[i]);
+ String[] children = f.list();
+ if (children == null || children.length < 1) {
+ log("Removing orphan directory: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount[0];
+ }
+ }
+
+ Boolean ped = getExplicitPreserveEmptyDirs();
+ if (ped != null && ped.booleanValue() != myCopy.getIncludeEmptyDirs()) {
+ FileSet fs = syncTarget.toFileSet(true);
+ fs.setDir(toDir);
+ String[] preservedDirs =
+ fs.getDirectoryScanner(getProject()).getIncludedDirectories();
+ for (int i = preservedDirs.length - 1; i >= 0; --i) {
+ preservedDirectories.add(new File(toDir, preservedDirs[i]));
+ }
+ }
+
+ return removedCount;
+ }
+
+ /**
+ * Removes all empty directories from a directory.
+ *
+ * <p><em>Note that a directory that contains only empty
+ * directories, directly or not, will be removed!</em></p>
+ *
+ * <p>Recurses depth-first to find the leaf directories
+ * which are empty and removes them, then unwinds the
+ * recursion stack, removing directories which have
+ * become empty themselves, etc...</p>
+ *
+ * @param dir the root directory to scan for empty directories.
+ * @param removeIfEmpty whether to remove the root directory
+ * itself if it becomes empty.
+ * @param preservedEmptyDirectories directories matched by
+ * syncTarget
+ * @return the number of empty directories actually removed.
+ */
+ private int removeEmptyDirectories(File dir, boolean removeIfEmpty,
+ Set preservedEmptyDirectories) {
+ int removedCount = 0;
+ if (dir.isDirectory()) {
+ File[] children = dir.listFiles();
+ for (int i = 0; i < children.length; ++i) {
+ File file = children[i];
+ // Test here again to avoid method call for non-directories!
+ if (file.isDirectory()) {
+ removedCount +=
+ removeEmptyDirectories(file, true,
+ preservedEmptyDirectories);
+ }
+ }
+ if (children.length > 0) {
+ // This directory may have become empty...
+ // We need to re-query its children list!
+ children = dir.listFiles();
+ }
+ if (children.length < 1 && removeIfEmpty
+ && !preservedEmptyDirectories.contains(dir)) {
+ log("Removing empty directory: " + dir, Project.MSG_DEBUG);
+ dir.delete();
+ ++removedCount;
+ }
+ }
+ return removedCount;
+ }
+
+ /**
+ * Removes all empty directories preserved by preserveInTarget in
+ * the preserveEmptyDirs == FALSE case.
+ *
+ * <p>Relies on the set to be ordered in reversed lexicographic
+ * order so that directories will be removed depth-first.</p>
+ *
+ * @param preservedEmptyDirectories directories matched by
+ * syncTarget
+ * @return the number of empty directories actually removed.
+ *
+ * @since Ant 1.8.0
+ */
+ private int removeEmptyDirectories(Set preservedEmptyDirectories) {
+ int removedCount = 0;
+ for (Iterator iter = preservedEmptyDirectories.iterator();
+ iter.hasNext();) {
+ File f = (File) iter.next();
+ String[] s = f.list();
+ if (s == null || s.length == 0) {
+ log("Removing empty directory: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount;
+ }
+ }
+ return removedCount;
+ }
+
+ //
+ // Various copy attributes/subelements of <copy> passed thru to <mycopy>
+ //
+
+ /**
+ * Sets the destination directory.
+ * @param destDir the destination directory
+ */
+ public void setTodir(File destDir) {
+ myCopy.setTodir(destDir);
+ }
+
+ /**
+ * Used to force listing of all names of copied files.
+ * @param verbose if true force listing of all names of copied files.
+ */
+ public void setVerbose(boolean verbose) {
+ myCopy.setVerbose(verbose);
+ }
+
+ /**
+ * Overwrite any existing destination file(s).
+ * @param overwrite if true overwrite any existing destination file(s).
+ */
+ public void setOverwrite(boolean overwrite) {
+ myCopy.setOverwrite(overwrite);
+ }
+
+ /**
+ * Used to copy empty directories.
+ * @param includeEmpty If true copy empty directories.
+ */
+ public void setIncludeEmptyDirs(boolean includeEmpty) {
+ myCopy.setIncludeEmptyDirs(includeEmpty);
+ }
+
+ /**
+ * If false, note errors to the output but keep going.
+ * @param failonerror true or false
+ */
+ public void setFailOnError(boolean failonerror) {
+ myCopy.setFailOnError(failonerror);
+ }
+
+ /**
+ * Adds a set of files to copy.
+ * @param set a fileset
+ */
+ public void addFileset(FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Adds a collection of filesystem resources to copy.
+ * @param rc a resource collection
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ if (rc instanceof FileSet && rc.isFilesystemOnly()) {
+ // receives special treatment in copy that this task relies on
+ myCopy.add(rc);
+ } else {
+ if (resources == null) {
+ Restrict r = new Restrict();
+ r.add(new Exists());
+ r.add(resources = new Resources());
+ myCopy.add(r);
+ }
+ resources.add(rc);
+ }
+ }
+
+ /**
+ * The number of milliseconds leeway to give before deciding a
+ * target is out of date.
+ *
+ * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p>
+ * @param granularity a <code>long</code> value
+ * @since Ant 1.6.2
+ */
+ public void setGranularity(long granularity) {
+ myCopy.setGranularity(granularity);
+ }
+
+ /**
+ * A container for patterns and selectors that can be used to
+ * specify files that should be kept in the target even if they
+ * are not present in any source directory.
+ *
+ * <p>You must not invoke this method more than once.</p>
+ * @param s a preserveintarget nested element
+ * @since Ant 1.7
+ */
+ public void addPreserveInTarget(SyncTarget s) {
+ if (syncTarget != null) {
+ throw new BuildException("you must not specify multiple "
+ + "preserveintarget elements.");
+ }
+ syncTarget = s;
+ }
+
+ /**
+ * The value of preserveTarget's preserveEmptyDirs attribute if
+ * specified and preserveTarget has been used in the first place.
+ *
+ * @since Ant 1.8.0.
+ */
+ private Boolean getExplicitPreserveEmptyDirs() {
+ return syncTarget == null ? null : syncTarget.getPreserveEmptyDirs();
+ }
+
+ /**
+ * Subclass Copy in order to access it's file/dir maps.
+ */
+ public static class MyCopy extends Copy {
+
+ // List of files that must be copied, irrelevant from the
+ // fact that they are newer or not than the destination.
+ private Set nonOrphans = new HashSet();
+
+ /** Constructor for MyCopy. */
+ public MyCopy() {
+ }
+
+ /**
+ * @see Copy#scan(File, File, String[], String[])
+ */
+ /** {@inheritDoc} */
+ @Override
+ protected void scan(File fromDir, File toDir, String[] files,
+ String[] dirs) {
+ assertTrue("No mapper", mapperElement == null);
+
+ super.scan(fromDir, toDir, files, dirs);
+
+ for (int i = 0; i < files.length; ++i) {
+ nonOrphans.add(files[i]);
+ }
+ for (int i = 0; i < dirs.length; ++i) {
+ nonOrphans.add(dirs[i]);
+ }
+ }
+
+ /**
+ * @see Copy#scan(Resource[], File)
+ */
+ /** {@inheritDoc} */
+ @Override
+ protected Map scan(Resource[] resources, File toDir) {
+ assertTrue("No mapper", mapperElement == null);
+
+ for (int i = 0; i < resources.length; i++) {
+ nonOrphans.add(resources[i].getName());
+ }
+ return super.scan(resources, toDir);
+ }
+
+ /**
+ * Get the destination directory.
+ * @return the destination directory
+ */
+ public File getToDir() {
+ return destDir;
+ }
+
+ /**
+ * Get the includeEmptyDirs attribute.
+ * @return true if emptyDirs are to be included
+ */
+ public boolean getIncludeEmptyDirs() {
+ return includeEmpty;
+ }
+
+ /**
+ * Yes, we can.
+ * @return true always.
+ * @since Ant 1.7
+ */
+ @Override
+ protected boolean supportsNonFileResources() {
+ return true;
+ }
+ }
+
+ /**
+ * Inner class used to hold exclude patterns and selectors to save
+ * stuff that happens to live in the target directory but should
+ * not get removed.
+ *
+ * @since Ant 1.7
+ */
+ public static class SyncTarget extends AbstractFileSet {
+
+ private Boolean preserveEmptyDirs;
+
+ /**
+ * Constructor for SyncTarget.
+ * This just changes the default value of "defaultexcludes" from
+ * true to false.
+ */
+ public SyncTarget() {
+ super();
+ }
+
+ /**
+ * Override AbstractFileSet#setDir(File) to disallow
+ * setting the directory.
+ * @param dir ignored
+ * @throws BuildException always
+ */
+ @Override
+ public void setDir(File dir) throws BuildException {
+ throw new BuildException("preserveintarget doesn't support the dir "
+ + "attribute");
+ }
+
+ /**
+ * Whether empty directories matched by this fileset should be
+ * preserved.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setPreserveEmptyDirs(boolean b) {
+ preserveEmptyDirs = Boolean.valueOf(b);
+ }
+
+ /**
+ * Whether empty directories matched by this fileset should be
+ * preserved.
+ *
+ * @since Ant 1.8.0
+ */
+ public Boolean getPreserveEmptyDirs() {
+ return preserveEmptyDirs;
+ }
+
+ private FileSet toFileSet(boolean withPatterns) {
+ FileSet fs = new FileSet();
+ fs.setCaseSensitive(isCaseSensitive());
+ fs.setFollowSymlinks(isFollowSymlinks());
+ fs.setMaxLevelsOfSymlinks(getMaxLevelsOfSymlinks());
+ fs.setProject(getProject());
+
+ if (withPatterns) {
+ PatternSet ps = mergePatterns(getProject());
+ fs.appendIncludes(ps.getIncludePatterns(getProject()));
+ fs.appendExcludes(ps.getExcludePatterns(getProject()));
+ for (Enumeration e = selectorElements(); e.hasMoreElements();) {
+ fs.appendSelector((FileSelector) e.nextElement());
+ }
+ fs.setDefaultexcludes(getDefaultexcludes());
+ }
+ return fs;
+ }
+ }
+
+ /**
+ * Pseudo-assert method.
+ */
+ private static void assertTrue(String message, boolean condition) {
+ if (!condition) {
+ throw new BuildException("Assertion Error: " + message);
+ }
+ }
+
+}