diff --git a/WHATSNEW b/WHATSNEW
index 6c540aa82..cbdf1b3fc 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -620,6 +620,10 @@ Other changes:
build fail if the task didn't do anything.
Bugzilla Report 21064.
+ *
| Attribute | +Description | +Required | +
| preserveEmptyDirs | +Overrules the includeEmptydirs setting for + directories matched by this element. If you want to preserve + empty directories that are not in your source directory you can + either set the task's includeemptydirs attribute or this one. + If the two attribute values conflict, this attribute + "wins". | +No, defaults to the value of the + task's includeemptydirs attribute | +
diff --git a/src/main/org/apache/tools/ant/taskdefs/Sync.java b/src/main/org/apache/tools/ant/taskdefs/Sync.java
index 51549a3e6..8fd8d393c 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Sync.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Sync.java
@@ -22,6 +22,7 @@ import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -113,17 +114,33 @@ public class Sync extends Task {
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);
+ 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()) {
+ if (!myCopy.getIncludeEmptyDirs()
+ || (syncTarget != null
+ && syncTarget.getPreserveEmptyDirs() == Boolean.FALSE)) {
log("PASS#3: Removing empty directories from " + toDir,
Project.MSG_DEBUG);
- int removedDirCount = removeEmptyDirectories(toDir, false);
+
+ 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");
}
}
@@ -155,11 +172,16 @@ public class Sync extends Task {
*
* @param nonOrphans the table of all non-orphan Files.
* @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 either preserveEmptyDirs is set or
+ * includeEmptyDirs is true.
* @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) {
+ 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]);
@@ -216,11 +238,22 @@ public class Sync extends Task {
for (int i = dirs.length - 1; i >= 0; --i) {
File f = new File(toDir, dirs[i]);
if (f.list().length < 1) {
- log("Removing orphan directory: " + f, Project.MSG_DEBUG);
- f.delete();
- ++removedCount[0];
+ log("Removing orphan directory: " + f, Project.MSG_DEBUG);
+ f.delete();
+ ++removedCount[0];
}
}
+
+ if (syncTarget != null) {
+ if (syncTarget.getPreserveEmptyDirs() != null
+ || myCopy.getIncludeEmptyDirs()) {
+ String[] preservedDirs = ds.getExcludedDirectories();
+ for (int i = preservedDirs.length - 1; i >= 0; --i) {
+ preservedDirectories.add(new File(toDir, preservedDirs[i]));
+ }
+ }
+ }
+
return removedCount;
}
@@ -238,17 +271,23 @@ public class Sync extends Task {
* @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) {
+ private int removeEmptyDirectories(File dir, boolean removeIfEmpty,
+ Set preservedEmptyDirectories) {
int removedCount = 0;
- if (dir.isDirectory()) {
+ if (!preservedEmptyDirectories.contains(dir) && 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);
+ if (!preservedEmptyDirectories.contains(file)
+ && file.isDirectory()) {
+ removedCount +=
+ removeEmptyDirectories(file, true,
+ preservedEmptyDirectories);
}
}
if (children.length > 0) {
@@ -265,6 +304,33 @@ public class Sync extends Task {
return removedCount;
}
+ /**
+ * Removes all empty directories preserved by preserveInTarget in
+ * the preserveEmptyDirs == FALSE case.
+ *
+ * Relies on the set to be ordered in reversed lexicographic
+ * order so that directories will be removed depth-first.
+ *
+ * @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 passed thru to
@@ -438,6 +504,8 @@ public class Sync extends Task {
*/
public static class SyncTarget extends AbstractFileSet {
+ private Boolean preserveEmptyDirs;
+
/**
* Constructor for SyncTarget.
* This just changes the default value of "defaultexcludes" from
@@ -458,6 +526,25 @@ public class Sync extends Task {
+ "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;
+ }
}
/**
diff --git a/src/tests/antunit/taskdefs/sync-test.xml b/src/tests/antunit/taskdefs/sync-test.xml
index 056edf1a8..1eab7a409 100644
--- a/src/tests/antunit/taskdefs/sync-test.xml
+++ b/src/tests/antunit/taskdefs/sync-test.xml
@@ -56,4 +56,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+