blob: c5e7b28dbc8d941049b9817631a17cc1abf4c3cc [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.tools.ant.taskdefs.optional.starteam;
import java.util.Hashtable;
import java.util.StringTokenizer;
import com.starbase.starteam.Folder;
import com.starbase.starteam.Label;
import com.starbase.starteam.StarTeamFinder;
import com.starbase.starteam.View;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
/**
* FileBasedTask.java
* This abstract class is the base for any tasks that are tree-based, that
* is, for tasks which iterate over a tree of folders in StarTeam which
* is reflected in a tree of folder the local machine.
*
* This class provides the tree-iteration functionality. Derived classes
* will implement their specific task functionality by the visitor pattern,
* specifically by implementing the method
* <code>visit(Folder rootStarteamFolder, java.io.File rootLocalFolder)</code>
*
* Created: Sat Dec 15 16:55:19 2001
*
* @author <a href="mailto:stevec@ignitesports.com">Steve Cohen</a>
* @version 1.0
* @see <A HREF="http://www.starbase.com/">StarBase Web Site</A>
*/
public abstract class TreeBasedTask extends StarTeamTask {
///////////////////////////////////////////////////////////////
// default values for attributes.
///////////////////////////////////////////////////////////////
/**
* This constant sets the filter to include all files. This default has
* the same result as <CODE>setIncludes("*")</CODE>.
*
* @see #getIncludes()
* @see #setIncludes(String includes)
*/
public final static String DEFAULT_INCLUDESETTING = "*";
/**
* This disables the exclude filter by default. In other words, no files
* are excluded. This setting is equivalent to
* <CODE>setExcludes(null)</CODE>.
*
* @see #getExcludes()
* @see #setExcludes(String excludes)
*/
public final static String DEFAULT_EXCLUDESETTING = null;
//ATTRIBUTES settable from ant.
/**
* The root folder of the operation in StarTeam.
*/
private String rootStarteamFolder = "/";
/**
* The local folder corresponding to starteamFolder. If not specified
* the Star Team defalt folder will be used.
*/
private String rootLocalFolder = null;
/**
* All files that fit this pattern are checked out.
*/
private String includes = DEFAULT_INCLUDESETTING;
/**
* All files fitting this pattern are ignored.
*/
private String excludes = DEFAULT_EXCLUDESETTING;
/**
* StarTeam label on which to perform task.
*/
private String label = null;
/**
* Set recursion to false to check out files in only the given folder
* and not in its subfolders.
*/
private boolean recursive = true;
/**
* If forced set to true, files in the target directory will
* be processed regardless of status in the repository.
* Usually this should be true if rootlocalfolder is set
* because status will be relative to the default folder, not
* to the one being processed.
*/
private boolean forced = false;
///////////////////////////////////////////////////////////////
// GET/SET methods.
// Setters, of course are where ant user passes in values.
///////////////////////////////////////////////////////////////
/**
* Set the root folder in the Starteam repository for this operation
* @param rootStarteamFolder the root folder
*/
public void setRootStarteamFolder(String rootStarteamFolder) {
this.rootStarteamFolder = rootStarteamFolder;
}
/**
* returns the root folder in the Starteam repository
* used for this operation
* @return the root folder in use
*/
public String getRootStarteamFolder() {
return this.rootStarteamFolder;
}
/**
* Set the local folder corresponding to the
* starteam folder for this operation.
* If not specified, the StarTeam default will be used
* the default is used.
* @param rootLocalFolder the local folder that will mirror
* this.rootStarteamFolder
*/
public void setRootLocalFolder(String rootLocalFolder) {
this.rootLocalFolder = rootLocalFolder;
}
/**
* Returns the local folder specified by the user,
* corresponding to the starteam folder for this operation.
* or null if not specified
* @return the local folder that mirrors this.rootStarteamFolder
*/
public String getRootLocalFolder() {
return this.rootLocalFolder;
}
/**
* sets the pattern of files to be included. See setExcludes() for a
* description
* @param includes A string of filter patterns to include. Separate the
* patterns by spaces.
* @see #getIncludes()
* @see #setExcludes(String excludes)
* @see #getExcludes()
*/
public void setIncludes(String includes) {
this.includes = includes;
}
/**
* Gets the patterns from the include filter. Rather that duplicate the
* details of AntStarTeanCheckOut's filtering here, refer to these
* links:
*
* @return A string of filter patterns separated by spaces.
* @see #setIncludes(String includes)
* @see #setExcludes(String excludes)
* @see #getExcludes()
*/
public String getIncludes() {
return includes;
}
/**
* Sets the exclude filter. When filtering files, AntStarTeamCheckOut
* uses an unmodified version of <CODE>DirectoryScanner</CODE>'s
* <CODE>match</CODE> method, so here are the patterns straight from the
* Ant source code:
* <BR><BR>
* Matches a string against a pattern. The pattern contains two special
* characters:
* <BR>'*' which means zero or more characters,
* <BR>'?' which means one and only one character.
* <BR><BR>
* For example, if you want to check out all files except .XML and
* .HTML files, you would put the following line in your program:
* <CODE>setExcludes("*.XML,*.HTML");</CODE>
* Finally, note that filters have no effect on the <B>directories</B>
* that are scanned; you could not skip over all files in directories
* whose names begin with "project," for instance.
* <BR><BR>
* Treatment of overlapping inlcudes and excludes: To give a simplistic
* example suppose that you set your include filter to "*.htm *.html"
* and your exclude filter to "index.*". What happens to index.html?
* AntStarTeamCheckOut will not check out index.html, as it matches an
* exclude filter ("index.*"), even though it matches the include
* filter, as well.
* <BR><BR>
* Please also read the following sections before using filters:
*
* @param excludes A string of filter patterns to exclude. Separate the
* patterns by spaces.
* @see #setIncludes(String includes)
* @see #getIncludes()
* @see #getExcludes()
*/
public void setExcludes(String excludes) {
this.excludes = excludes;
}
/**
* Gets the patterns from the exclude filter. Rather that duplicate the
* details of AntStarTeanCheckOut's filtering here, refer to these
* links:
*
* @return A string of filter patterns separated by spaces.
* @see #setExcludes(String excludes)
* @see #setIncludes(String includes)
* @see #getIncludes()
*/
public String getExcludes() {
return excludes;
}
/**
* protected function to allow subclasses to set the label (or not).
* sets the StarTeam label
*
* @param label name of the StarTeam label to be set
*/
protected void _setLabel(String label) {
if (null != label) {
label = label.trim();
if (label.length() > 0) {
this.label = label;
}
}
}
/**
* Get the value of recursive.
* @return value of recursive.
*/
public boolean isRecursive() {
return this.recursive;
}
/**
* Set the value of recursive.
* @param v Value to assign to recursive.
*/
public void setRecursive(boolean v) {
this.recursive = v;
}
/**
* Get the value of forced.
* @return value of forced.
*/
public boolean isForced() {
return this.forced;
}
/**
* Set the value of forced.
* @param v Value to assign to forced.
*/
public void setForced(boolean v) {
this.forced = v;
}
///////////////////////////////////////////////////////////////
// INCLUDE-EXCLUDE processing
///////////////////////////////////////////////////////////////
/**
* Look if the file should be processed by the task.
* Don't process it if it fits no include filters or if
* it fits an exclude filter.
* @param pName the item name to look for being included.
* @return whether the file should be checked out or not.
*/
protected boolean shouldProcess(String pName) {
boolean includeIt = matchPatterns(getIncludes(), pName);
boolean excludeIt = matchPatterns(getExcludes(), pName);
return (includeIt && !excludeIt);
}
/**
* Convenience method to see if a string match a one pattern
* in given set of space-separated patterns.
* @param patterns the space-separated list of patterns.
* @param pName the name to look for matching.
* @return whether the name match at least one pattern.
*/
protected boolean matchPatterns(String patterns, String pName) {
if (patterns == null) {
return false;
}
StringTokenizer exStr = new StringTokenizer(patterns, ",");
while (exStr.hasMoreTokens()) {
if (DirectoryScanner.match(exStr.nextToken(), pName)) {
return true;
}
}
return false;
}
/**
* This method does the work of opening the supplied Starteam view and
* calling the <code>visit()</code> method to perform the task.
*
* @exception BuildException if any error occurs in the processing
* @see <code>visit()</code>
*/
public void execute() throws BuildException {
try {
testPreconditions();
View snapshot = openView();
// find the starteam folder specified to be the root of the
// operation. Throw if it can't be found.
Folder starteamrootfolder =
StarTeamFinder.findFolder(snapshot.getRootFolder(),
this.rootStarteamFolder);
if (null == starteamrootfolder) {
throw new BuildException(
"Unable to find root folder in repository.");
}
// set the local folder.
java.io.File localrootfolder;
if (null == this.rootLocalFolder) {
// use Star Team's default
localrootfolder =
new java.io.File(starteamrootfolder.getPath());
} else {
// force StarTeam to use our folder
localrootfolder = new java.io.File(getRootLocalFolder());
log("overriding local folder to " + localrootfolder);
}
// Inspect everything in the root folder and then recursively
visit(starteamrootfolder, localrootfolder);
} catch (Exception e) {
throw new BuildException(e);
}
}
/**
* Helper method calls on the StarTeam API to retrieve an ID number
* for the specified view, corresponding to this.label.
* @param v the <code>View</code> in which to search for <code>this.label</code>
* @return the ID number corresponding to <code>this.label</code> or -1 if
* no label was provided.
* @exception BuildException if <code>this.label</code> does not correspond
* to any label in the supplied view
*/
protected int getLabelID(View v) throws BuildException {
if (null != this.label) {
Label[] allLabels = v.getLabels();
for (int i = 0; i < allLabels.length; i++) {
if (allLabels[i].getName().equals(this.label)) {
return allLabels[i].getID();
}
}
throw new BuildException("Error: label "
+ this.label
+ " does not exist in view");
}
return -1;
}
/**
* Derived classes must override this class to define actual processing
* to be performed on each folder in the tree defined for the task
*
* @param rootStarteamFolder the StarTeam folderto be visited
* @param rootLocalFolder the local mapping of rootStarteamFolder
*/
protected abstract void visit(Folder rootStarteamFolder,
java.io.File rootLocalFolder)
throws BuildException;
/**
* Derived classes must override this method to define tests for
* any preconditons required by the task. This method is called at
* the beginning of the execute() method.
*
* @exception BuildException throw if any fatal error exists in the
* parameters supplied. If there is a non-fatal condition, just writing
* to the log may be appropriate.
* @see <code>execute()</code>
*/
protected abstract void testPreconditions() throws BuildException;
/**
* Gets the collection of the local file names in the supplied directory.
* We need to check this collection against what we find in Starteam to
* understand what we need to do in order to synch with the repository.
*
* @param localFolder - the local folder to scan
* @return an "identity" hashtable whose keys each represent a file or
* directory in localFolder.
*/
protected static Hashtable listLocalFiles(java.io.File localFolder) {
Hashtable localFileList = new Hashtable();
// we can't use java 2 collections so we will use an identity
// Hashtable to hold the file names. We only care about the keys,
// not the values (which will all be "").
if (localFolder.exists()) {
String[] localFiles = localFolder.list();
for (int i = 0; i < localFiles.length; i++) {
localFileList.put(localFolder.toString() +
java.io.File.separatorChar + localFiles[i], "");
}
}
return localFileList;
}
/**
* Removes from the collection of the local file names
* the supplied name of a processed file. When we are done, only
* files not in StarTeam will remain in localFiles.
* @param localFiles a <code>Hashtable</code> value
* @param thisfile file to remove from list.
* @return true if file was removed, false if it wasn't found.
*/
protected boolean delistLocalFile(Hashtable localFiles, java.io.File thisfile) {
return null != localFiles.remove(thisfile.toString());
}
}