|
Ant example source code file (Sync.java)
The Sync.java source code
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant.taskdefs;
import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
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;
import org.apache.tools.ant.types.PatternSet;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.selectors.FileSelector;
import org.apache.tools.ant.types.selectors.NoneSelector;
/**
* Synchronize a local target directory from the files defined
* in one or more filesets.
*
* <p>Uses a <copy> task internally, but forbidding the use of
* mappers and filter chains. Files of the destination directory not
* present in any of the source fileset are removed.</p>
*
* @since Ant 1.6
*
* revised by <a href="mailto:daniel.armbrust@mayo.edu">Dan Armbrust
* to remove orphaned directories.
*
* @ant.task category="filesystem"
*/
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
/**
* Initialize the sync task.
* @throws BuildException if there is a problem.
* @see Task#init()
*/
public void init()
throws BuildException {
// Instantiate it
myCopy = new MyCopy();
configureTask(myCopy);
// Default config of <mycopy> for our purposes.
myCopy.setFiltering(false);
myCopy.setIncludeEmptyDirs(false);
myCopy.setPreserveLastModified(true);
}
private void configureTask(Task helper) {
helper.setProject(getProject());
helper.setTaskName(getTaskName());
helper.setOwningTarget(getOwningTarget());
helper.init();
}
// Override Task#execute
/**
* Execute the sync task.
* @throws BuildException if there is an error.
* @see Task#execute()
*/
public void execute()
throws BuildException {
// The destination of the files to copy
File toDir = myCopy.getToDir();
// The complete list of files to copy
Set allFiles = myCopy.nonOrphans;
// If the destination directory didn't already exist,
// or was empty, then no previous file removal is necessary!
boolean noRemovalNecessary = !toDir.exists() || toDir.list().length < 1;
// Copy all the necessary out-of-date files
log("PASS#1: Copying files to " + toDir, Project.MSG_DEBUG);
myCopy.execute();
// Do we need to perform further processing?
if (noRemovalNecessary) {
log("NO removing necessary in " + toDir, Project.MSG_DEBUG);
return; // nope ;-)
}
// 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);
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()) {
log("PASS#3: Removing empty directories from " + toDir,
Project.MSG_DEBUG);
int removedDirCount = removeEmptyDirectories(toDir, false);
logRemovedCount(removedDirCount, "empty director", "y", "ies");
}
}
private void logRemovedCount(int count, String prefix,
String singularSuffix, String pluralSuffix) {
File toDir = myCopy.getToDir();
String what = (prefix == null) ? "" : prefix;
what += (count < 2) ? singularSuffix : pluralSuffix;
if (count > 0) {
log("Removed " + count + " " + what + " from " + toDir,
Project.MSG_INFO);
} else {
log("NO " + what + " to remove from " + toDir,
Project.MSG_VERBOSE);
}
}
/**
* Removes all files and folders not found as keys of a table
* (used as a set!).
*
* <p>If the provided file is a directory, it is recursively
* scanned for orphaned files which will be removed as well.</p>
*
* <p>If the directory is an orphan, it will also be removed.
*
* @param nonOrphans the table of all non-orphan <code>Files.
* @param file the initial file or directory to scan or test.
* @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) {
int[] removedCount = new int[] {0, 0};
String[] excls =
(String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]);
// want to keep toDir itself
excls[nonOrphans.size()] = "";
DirectoryScanner ds = null;
if (syncTarget != null) {
FileSet fs = new FileSet();
fs.setDir(toDir);
fs.setCaseSensitive(syncTarget.isCaseSensitive());
fs.setFollowSymlinks(syncTarget.isFollowSymlinks());
// preserveInTarget would find all files we want to keep,
// but we need to find all that we want to delete - so the
// meaning of all patterns and selectors must be inverted
PatternSet ps = syncTarget.mergePatterns(getProject());
fs.appendExcludes(ps.getIncludePatterns(getProject()));
fs.appendIncludes(ps.getExcludePatterns(getProject()));
fs.setDefaultexcludes(!syncTarget.getDefaultexcludes());
// selectors are implicitly ANDed in DirectoryScanner. To
// revert their logic we wrap them into a <none> selector
// instead.
FileSelector[] s = syncTarget.getSelectors(getProject());
if (s.length > 0) {
NoneSelector ns = new NoneSelector();
for (int i = 0; i < s.length; i++) {
ns.appendSelector(s[i]);
}
fs.appendSelector(ns);
}
ds = fs.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++) {
File f = new File(toDir, files[i]);
log("Removing orphan file: " + f, Project.MSG_DEBUG);
f.delete();
++removedCount[1];
}
String[] dirs = ds.getIncludedDirectories();
// ds returns the directories in lexicographic order.
// iterating through the array backwards means we are deleting
// leaves before their parent nodes - thus making sure (well,
// more likely) that the directories are empty when we try to
// delete them.
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];
}
}
return removedCount;
}
/**
* Removes all empty directories from a directory.
*
* <p>Note that a directory that contains only empty
* directories, directly or not, will be removed!</em>
*
* <p>Recurses depth-first to find the leaf directories
* which are empty and removes them, then unwinds the
* recursion stack, removing directories which have
* become empty themselves, etc...</p>
*
* @param dir the root directory to scan for empty directories.
* @param removeIfEmpty whether to remove the root directory
* itself if it becomes empty.
* @return the number of empty directories actually removed.
*/
private int removeEmptyDirectories(File dir, boolean removeIfEmpty) {
int removedCount = 0;
if (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 (children.length > 0) {
// This directory may have become empty...
// We need to re-query its children list!
children = dir.listFiles();
}
if (children.length < 1 && removeIfEmpty) {
log("Removing empty directory: " + dir, Project.MSG_DEBUG);
dir.delete();
++removedCount;
}
}
return removedCount;
}
//
// Various copy attributes/subelements of <copy> passed thru to
Other Ant examples (source code examples)Here is a short list of links related to this Ant Sync.java source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.