Browse Source

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
master
Stefan Bodewig 16 years ago
parent
commit
7b84ae34e5
4 changed files with 156 additions and 12 deletions
  1. +4
    -0
      WHATSNEW
  2. +23
    -1
      docs/manual/CoreTasks/sync.html
  3. +98
    -11
      src/main/org/apache/tools/ant/taskdefs/Sync.java
  4. +31
    -0
      src/tests/antunit/taskdefs/sync-test.xml

+ 4
- 0
WHATSNEW View File

@@ -620,6 +620,10 @@ Other changes:
build fail if the task didn't do anything. build fail if the task didn't do anything.
Bugzilla Report 21064. 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 Changes from Ant 1.7.0 TO Ant 1.7.1
============================================= =============================================




+ 23
- 1
docs/manual/CoreTasks/sync.html View File

@@ -62,7 +62,9 @@ dir, it will get removed from the target.</p>
nested &lt;preserveintarget&gt; element. If this attribute is nested &lt;preserveintarget&gt; element. If this attribute is
false (the default) empty directories that only exist in the false (the default) empty directories that only exist in the
target directory will be removed even if they are matched by target directory will be removed even if they are matched by
the patterns of &lt;preserveintarget&gt;.
the patterns of &lt;preserveintarget&gt;. This can be
overridden by &lt;preserveintarget&gt;'s
preserveEmptyDirs attribute.
</td> </td>
<td valign="top" align="center">No; defaults to false.</td> <td valign="top" align="center">No; defaults to false.</td>
</tr> </tr>
@@ -112,6 +114,26 @@ href="../CoreTypes/fileset.html">FileSet</a> except that it doesn't
support the dir attribute and the usedefaultexcludes attribute support the dir attribute and the usedefaultexcludes attribute
defaults to false.</p> 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> <h3>Examples</h3>


<blockquote><pre> <blockquote><pre>


+ 98
- 11
src/main/org/apache/tools/ant/taskdefs/Sync.java View File

@@ -22,6 +22,7 @@ import java.io.File;


import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;


@@ -113,17 +114,33 @@ public class Sync extends Task {
return; // nope ;-) 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. // Get rid of all files not listed in the source filesets.
log("PASS#2: Removing orphan files from " + toDir, Project.MSG_DEBUG); 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[0], "dangling director", "y", "ies");
logRemovedCount(removedFileCount[1], "dangling file", "", "s"); logRemovedCount(removedFileCount[1], "dangling file", "", "s");


// Get rid of empty directories on the destination side // 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, log("PASS#3: Removing empty directories from " + toDir,
Project.MSG_DEBUG); 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"); logRemovedCount(removedDirCount, "empty director", "y", "ies");
} }
} }
@@ -155,11 +172,16 @@ public class Sync extends Task {
* *
* @param nonOrphans the table of all non-orphan <code>File</code>s. * @param nonOrphans the table of all non-orphan <code>File</code>s.
* @param file the initial file or directory to scan or test. * @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. * @return the number of orphaned files and directories actually removed.
* Position 0 of the array is the number of orphaned directories. * Position 0 of the array is the number of orphaned directories.
* Position 1 of the array is the number or orphaned files. * 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}; int[] removedCount = new int[] {0, 0};
String[] excls = String[] excls =
(String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]); (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) { for (int i = dirs.length - 1; i >= 0; --i) {
File f = new File(toDir, dirs[i]); File f = new File(toDir, dirs[i]);
if (f.list().length < 1) { 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; return removedCount;
} }


@@ -238,17 +271,23 @@ public class Sync extends Task {
* @param dir the root directory to scan for empty directories. * @param dir the root directory to scan for empty directories.
* @param removeIfEmpty whether to remove the root directory * @param removeIfEmpty whether to remove the root directory
* itself if it becomes empty. * itself if it becomes empty.
* @param preservedEmptyDirectories directories matched by
* syncTarget
* @return the number of empty directories actually removed. * @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; int removedCount = 0;
if (dir.isDirectory()) {
if (!preservedEmptyDirectories.contains(dir) && dir.isDirectory()) {
File[] children = dir.listFiles(); File[] children = dir.listFiles();
for (int i = 0; i < children.length; ++i) { for (int i = 0; i < children.length; ++i) {
File file = children[i]; File file = children[i];
// Test here again to avoid method call for non-directories! // 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) { if (children.length > 0) {
@@ -265,6 +304,33 @@ public class Sync extends Task {
return 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> // Various copy attributes/subelements of <copy> passed thru to <mycopy>
@@ -438,6 +504,8 @@ public class Sync extends Task {
*/ */
public static class SyncTarget extends AbstractFileSet { public static class SyncTarget extends AbstractFileSet {


private Boolean preserveEmptyDirs;

/** /**
* Constructor for SyncTarget. * Constructor for SyncTarget.
* This just changes the default value of "defaultexcludes" from * This just changes the default value of "defaultexcludes" from
@@ -458,6 +526,25 @@ public class Sync extends Task {
+ "attribute"); + "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;
}
} }


/** /**


+ 31
- 0
src/tests/antunit/taskdefs/sync-test.xml View File

@@ -56,4 +56,35 @@
<au:assertFileExists file="${output}/b/baz.txt"/> <au:assertFileExists file="${output}/b/baz.txt"/>
<au:assertFileDoesntExist file="${output}/b/c"/> <au:assertFileDoesntExist file="${output}/b/c"/>
</target> </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> </project>

Loading…
Cancel
Save