From 7b84ae34e5dda8ff45d94e73e2537b369d1dedf1 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Thu, 18 Dec 2008 15:42:27 +0000 Subject: [PATCH] partial implementation of a new preserveemptydirs attribute for sync's preservintarget. PR 43159. Unfortunately DirectorScanner.slowScan doesn't work as expected and I'm running out of time before a meeting, so I'm commiting it unfinished. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@727750 13f79535-47bb-0310-9956-ffa450edef68 --- WHATSNEW | 4 + docs/manual/CoreTasks/sync.html | 24 +++- .../org/apache/tools/ant/taskdefs/Sync.java | 109 ++++++++++++++++-- src/tests/antunit/taskdefs/sync-test.xml | 31 +++++ 4 files changed, 156 insertions(+), 12 deletions(-) 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. + * 's has a new attribute that conrols + whether empty directories should be kept. + Bugzilla Report 43159. + Changes from Ant 1.7.0 TO Ant 1.7.1 ============================================= diff --git a/docs/manual/CoreTasks/sync.html b/docs/manual/CoreTasks/sync.html index b97c81890..9b45e305a 100644 --- a/docs/manual/CoreTasks/sync.html +++ b/docs/manual/CoreTasks/sync.html @@ -62,7 +62,9 @@ dir, it will get removed from the target.

nested <preserveintarget> element. If this attribute is false (the default) empty directories that only exist in the target directory will be removed even if they are matched by - the patterns of <preserveintarget>. + the patterns of <preserveintarget>. This can be + overridden by <preserveintarget>'s + preserveEmptyDirs attribute. No; defaults to false. @@ -112,6 +114,26 @@ href="../CoreTypes/fileset.html">FileSet except that it doesn't support the dir attribute and the usedefaultexcludes attribute defaults to false.

+
Additional Parameters
+ + + + + + + + + + + +
AttributeDescriptionRequired
preserveEmptyDirsOverrules 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
+

Examples

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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +