Author: bodewig
Date: Thu Dec 18 07:42:27 2008
New Revision: 727750
URL: http://svn.apache.org/viewvc?rev=727750&view=rev
Log:
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.
Modified:
ant/core/trunk/WHATSNEW
ant/core/trunk/docs/manual/CoreTasks/sync.html
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Sync.java
ant/core/trunk/src/tests/antunit/taskdefs/sync-test.xml
Modified: ant/core/trunk/WHATSNEW
URL:
http://svn.apache.org/viewvc/ant/core/trunk/WHATSNEW?rev=727750&r1=727749&r2=727750&view=diff
==============================================================================
--- ant/core/trunk/WHATSNEW (original)
+++ ant/core/trunk/WHATSNEW Thu Dec 18 07:42:27 2008
@@ -620,6 +620,10 @@
build fail if the task didn't do anything.
Bugzilla Report 21064.
+ * <sync>'s <preserveInTarget> 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
=============================================
Modified: ant/core/trunk/docs/manual/CoreTasks/sync.html
URL:
http://svn.apache.org/viewvc/ant/core/trunk/docs/manual/CoreTasks/sync.html?rev=727750&r1=727749&r2=727750&view=diff
==============================================================================
--- ant/core/trunk/docs/manual/CoreTasks/sync.html (original)
+++ ant/core/trunk/docs/manual/CoreTasks/sync.html Thu Dec 18 07:42:27 2008
@@ -62,7 +62,9 @@
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.
</td>
<td valign="top" align="center">No; defaults to false.</td>
</tr>
@@ -112,6 +114,26 @@
support the dir attribute and the usedefaultexcludes attribute
defaults to false.</p>
+<h5>Additional Parameters</h5>
+<table border="1" cellpadding="2" cellspacing="0">
+ <tr>
+ <td valign="top"><b>Attribute</b></td>
+ <td valign="top"><b>Description</b></td>
+ <td align="center" valign="top"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td valign="top">preserveEmptyDirs</td>
+ <td valign="top">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".</td>
+ <td align="center" valign="top">No, defaults to the value of the
+ task's includeemptydirs attribute</td>
+ </tr>
+</table>
+
<h3>Examples</h3>
<blockquote><pre>
Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Sync.java
URL:
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Sync.java?rev=727750&r1=727749&r2=727750&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Sync.java (original)
+++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Sync.java Thu Dec 18
07:42:27 2008
@@ -22,6 +22,7 @@
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -113,17 +114,33 @@
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 @@
*
* @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 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 @@
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 @@
* @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 @@
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>
@@ -438,6 +504,8 @@
*/
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 @@
+ "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;
+ }
}
/**
Modified: ant/core/trunk/src/tests/antunit/taskdefs/sync-test.xml
URL:
http://svn.apache.org/viewvc/ant/core/trunk/src/tests/antunit/taskdefs/sync-test.xml?rev=727750&r1=727749&r2=727750&view=diff
==============================================================================
--- ant/core/trunk/src/tests/antunit/taskdefs/sync-test.xml (original)
+++ ant/core/trunk/src/tests/antunit/taskdefs/sync-test.xml Thu Dec 18 07:42:27
2008
@@ -56,4 +56,35 @@
<au:assertFileExists file="${output}/b/baz.txt"/>
<au:assertFileDoesntExist file="${output}/b/c"/>
</target>
+
+ <target name="testPreserveEmptyOverridesDefault" depends="setUp">
+
+ <sync todir="${output}">
+ <fileset dir="${input}"/>
+ <preserveintarget preserveEmptyDirs="true">
+ <include name="**/b/**"/>
+ </preserveintarget>
+ </sync>
+
+ <au:assertFileDoesntExist file="${output}/a/bar.txt"/>
+ <au:assertFileExists file="${output}/a/foo.txt"/>
+ <au:assertFileExists file="${output}/b/baz.txt"/>
+ <au:assertFileExists file="${output}/b/c"/>
+ </target>
+
+ <target name="xtestPreserveEmptyOverrulesIncludeEmpty" depends="setUp">
+
+ <sync todir="${output}" includeEmptyDirs="true">
+ <fileset dir="${input}"/>
+ <preserveintarget preserveEmptyDirs="false">
+ <include name="**/b/**"/>
+ </preserveintarget>
+ </sync>
+
+ <au:assertFileDoesntExist file="${output}/a/bar.txt"/>
+ <au:assertFileExists file="${output}/a/foo.txt"/>
+ <au:assertFileExists file="${output}/b/baz.txt"/>
+ <au:assertFileDoesntExist file="${output}/b/c"/>
+ </target>
+
</project>