git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277393 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -61,4 +61,18 @@ | |||
| <fileset dir="${src}" excludes="**/d"/> | |||
| </sync> | |||
| </target> | |||
| <target name="copynoremove" depends="setup"> | |||
| <mkdir dir="${src}/a/b/c"/> | |||
| <touch file="${src}/a/b/c/d"/> | |||
| <mkdir dir="${dest}/e"/> | |||
| <touch file="${dest}/e/f"/> | |||
| <sync todir="${dest}"> | |||
| <fileset dir="${src}"/> | |||
| <deletefromtarget> | |||
| <exclude name="e/f"/> | |||
| </deletefromtarget> | |||
| </sync> | |||
| </target> | |||
| </project> | |||
| @@ -584,13 +584,7 @@ public class DirectoryScanner | |||
| } else { | |||
| this.includes = new String[includes.length]; | |||
| for (int i = 0; i < includes.length; i++) { | |||
| String pattern; | |||
| pattern = includes[i].replace('/', File.separatorChar).replace( | |||
| '\\', File.separatorChar); | |||
| if (pattern.endsWith(File.separator)) { | |||
| pattern += "**"; | |||
| } | |||
| this.includes[i] = pattern; | |||
| this.includes[i] = normalizePattern(includes[i]); | |||
| } | |||
| } | |||
| } | |||
| @@ -614,17 +608,60 @@ public class DirectoryScanner | |||
| } else { | |||
| this.excludes = new String[excludes.length]; | |||
| for (int i = 0; i < excludes.length; i++) { | |||
| String pattern; | |||
| pattern = excludes[i].replace('/', File.separatorChar).replace( | |||
| '\\', File.separatorChar); | |||
| if (pattern.endsWith(File.separator)) { | |||
| pattern += "**"; | |||
| this.excludes[i] = normalizePattern(excludes[i]); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Adds to the list of exclude patterns to use. All '/' and '\' | |||
| * characters are replaced by <code>File.separatorChar</code>, so | |||
| * the separator used need not match | |||
| * <code>File.separatorChar</code>. | |||
| * <p> | |||
| * When a pattern ends with a '/' or '\', "**" is appended. | |||
| * | |||
| * @param excludes A list of exclude patterns. | |||
| * May be <code>null</code>, in which case the | |||
| * exclude patterns don't get changed at all. | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public void addExcludes(String[] excludes) { | |||
| if (excludes != null) { | |||
| if (this.excludes != null) { | |||
| String[] tmp = new String[excludes.length | |||
| + this.excludes.length]; | |||
| System.arraycopy(this.excludes, 0, tmp, 0, | |||
| this.excludes.length); | |||
| for (int i = 0; i < excludes.length; i++) { | |||
| tmp[this.excludes.length + i] = | |||
| normalizePattern(excludes[i]); | |||
| } | |||
| this.excludes[i] = pattern; | |||
| this.excludes = tmp; | |||
| } else { | |||
| setExcludes(excludes); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * All '/' and '\' characters are replaced by | |||
| * <code>File.separatorChar</code>, so the separator used need not | |||
| * match <code>File.separatorChar</code>. | |||
| * | |||
| * <p> When a pattern ends with a '/' or '\', "**" is appended. | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| private static String normalizePattern(String p) { | |||
| String pattern = p.replace('/', File.separatorChar) | |||
| .replace('\\', File.separatorChar); | |||
| if (pattern.endsWith(File.separator)) { | |||
| pattern += "**"; | |||
| } | |||
| return pattern; | |||
| } | |||
| /** | |||
| * Sets the selectors that will select the filelist. | |||
| @@ -32,6 +32,7 @@ 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; | |||
| /** | |||
| @@ -55,6 +56,9 @@ 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; | |||
| // Override Task#init | |||
| /** | |||
| * @see Task#init() | |||
| @@ -152,13 +156,21 @@ public class Sync extends Task { | |||
| */ | |||
| private int[] removeOrphanFiles(Set nonOrphans, File toDir) { | |||
| int[] removedCount = new int[] {0, 0}; | |||
| DirectoryScanner ds = new DirectoryScanner(); | |||
| ds.setBasedir(toDir); | |||
| String[] excls = | |||
| (String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]); | |||
| // want to keep toDir itself | |||
| excls[nonOrphans.size()] = ""; | |||
| ds.setExcludes(excls); | |||
| DirectoryScanner ds = null; | |||
| if (syncTarget != null) { | |||
| syncTarget.setTargetDir(toDir); | |||
| ds = syncTarget.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++) { | |||
| @@ -288,6 +300,23 @@ public class Sync extends Task { | |||
| 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> | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public void addDeleteFromTarget(SyncTarget s) { | |||
| if (syncTarget != null) { | |||
| throw new BuildException("you must not specify multiple " | |||
| + "deletefromtaget elements."); | |||
| } | |||
| syncTarget = s; | |||
| } | |||
| /** | |||
| * Subclass Copy in order to access it's file/dir maps. | |||
| */ | |||
| @@ -336,6 +365,31 @@ public class Sync extends Task { | |||
| } | |||
| /** | |||
| * 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 { | |||
| public SyncTarget() { | |||
| super(); | |||
| setDefaultexcludes(false); | |||
| } | |||
| public void setDir(File dir) throws BuildException { | |||
| throw new BuildException("synctarget doesn't support the dir " | |||
| + "attribute"); | |||
| } | |||
| private void setTargetDir(File dir) throws BuildException { | |||
| super.setDir(dir); | |||
| } | |||
| } | |||
| /** | |||
| * Pseudo-assert method. | |||
| */ | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| * Copyright 2004 The Apache Software Foundation | |||
| * Copyright 2004-2005 The Apache Software Foundation | |||
| * | |||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||
| * you may not use this file except in compliance with the License. | |||
| @@ -83,6 +83,15 @@ public class SyncTest extends BuildFileTest { | |||
| assertDebuglogContaining("Removed 2 dangling directories from"); | |||
| } | |||
| public void testCopyNoRemove() { | |||
| executeTarget("copynoremove"); | |||
| String d = getProject().getProperty("dest") + "/a/b/c/d"; | |||
| assertFileIsPresent(d); | |||
| String f = getProject().getProperty("dest") + "/e/f"; | |||
| assertFileIsPresent(f); | |||
| assertTrue(getFullLog().indexOf("Removing orphan file:") == -1); | |||
| } | |||
| public void assertFileIsPresent(String f) { | |||
| assertTrue("Expected file " + f, | |||
| getProject().resolveFile(f).exists()); | |||