blob: 1d85b91c92b9a9cea2ab1e9ccf2738e737a95e44 [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.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import com.starbase.starteam.File;
import com.starbase.starteam.Folder;
import com.starbase.starteam.Item;
import com.starbase.starteam.Status;
import com.starbase.starteam.View;
import com.starbase.starteam.ViewConfiguration;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
/**
* This class logs into StarTeam checks out any changes that have occurred since
* the last successful build. It also creates all working directories on the
* local directory if appropriate. Ant Usage:
* <pre>
* &lt;taskdef name="starteamcheckout"
* classname="org.apache.tools.ant.taskdefs.StarTeamCheckout"/&gt;
* &lt;starteamcheckout username="BuildMaster" password="ant" starteamFolder="Source"
* starteamurl="servername:portnum/project/view"
* createworkingdirectories="true"/&gt;
* </pre>
*
* @author Christopher Charlier, ThoughtWorks, Inc. 2001
* @author <a href="mailto:jcyip@thoughtworks.com">Jason Yip</a>
* @author Jason Pettiss
* @author <a href="mailto:stevec@ignitesports.com">Steve Cohen</a>
* @version 1.1
* @see <A HREF="http://www.starbase.com/">StarBase Web Site</A>
*
* @ant.task name="stcheckout" category="scm"
*/
public class StarTeamCheckout extends TreeBasedTask {
/**
* holder for the createDirs attribute
*/
private boolean createDirs = true;
/**
* holder for the deleteUncontrolled attribute. If true,
* all local files not in StarTeam will be deleted.
*/
private boolean deleteUncontrolled = true;
/**
* Set the attribute that tells ant if we want to create all directories
* that are in the Starteam repository regardless if they are empty.
*
* @param value the value to set the attribute to.
*/
public void setCreateWorkingDirs(boolean value) {
this.createDirs = value;
}
/**
* Sets the attribute that tells ant whether or not to remove local files
* that are NOT found in the Starteam repository to the supplied value.
*
* @param value the value to set the attribute to.
*/
public void setDeleteUncontrolled(boolean value) {
this.deleteUncontrolled = value;
}
/**
* Sets the label StarTeam is to use for checkout.
*
* @param label the label to be used
*/
public void setLabel(String label) {
_setLabel(label);
}
/**
* This attribute tells whether to do a locked checkout, an unlocked
* checkout or to leave the checkout status alone (default). A locked
* checkout locks all other users out from making changes. An unlocked
* checkout reverts all local files to their previous repository status
* and removes the lock.
* @see #setLocked(boolean)
* @see #setUnlocked(boolean)
*/
private int lockStatus = Item.LockType.UNCHANGED;
/**
* Set to do a locked checkout. Default is false.
* @param v True to do a locked checkout, false to checkout without
* changing status/.
* @exception BuildException if both locked and unlocked are set true
*/
public void setLocked(boolean v) throws BuildException {
setLockStatus(v, Item.LockType.EXCLUSIVE);
}
/**
* Set to do an unlocked checkout. Default is false;
* @param v True to do an unlocked checkout, false to checkout without
* changing status.
* @exception BuildException if both locked and unlocked are set true
*/
public void setUnlocked(boolean v) throws BuildException {
setLockStatus(v, Item.LockType.UNLOCKED);
}
private void setLockStatus(boolean v, int newStatus)
throws BuildException {
if (v) {
if (this.lockStatus == Item.LockType.UNCHANGED) {
this.lockStatus = newStatus;
} else if (this.lockStatus != newStatus) {
throw new BuildException(
"Error: cannot set locked and unlocked both true.");
}
}
}
/**
* Override of base-class abstract function creates an
* appropriately configured view for checkouts - either
* the current view or a view from this.label.
*
* @param raw the unconfigured <code>View</code>
* @return the snapshot <code>View</code> appropriately configured.
*/
protected View createSnapshotView(View raw) {
int labelID = getLabelID(raw);
// if a label has been supplied, use it to configure the view
// otherwise use current view
if (labelID >= 0) {
return new View(raw, ViewConfiguration.createFromLabel(labelID));
} else {
return new View(raw, ViewConfiguration.createTip());
}
}
/**
* Implements base-class abstract function to define tests for
* any preconditons required by the task
*
* @exception BuildException not thrown in this implementation
*/
protected void testPreconditions() throws BuildException {
if (null != getRootLocalFolder() && !isForced()) {
log("Warning: rootLocalFolder specified, but forcing off.",
Project.MSG_WARN);
}
}
/**
* Implements base-class abstract function to perform the checkout
* operation on the files in each folder of the tree.
*
* @param starteamFolder the StarTeam folder from which files to be
* checked out
* @param targetFolder the local mapping of rootStarteamFolder
* @exception BuildException if any error occurs
*/
protected void visit(Folder starteamFolder, java.io.File targetFolder)
throws BuildException {
try {
Hashtable localFiles = listLocalFiles(targetFolder);
// If we have been told to create the working folders
if (createDirs) {
// Create if it doesn't exist
if (!targetFolder.exists()) {
targetFolder.mkdir();
}
}
// For all Files in this folder, we need to check
// if there have been modifications.
Item[] files = starteamFolder.getItems("File");
for (int i = 0; i < files.length; i++) {
File eachFile = (File) files[i];
String filename = eachFile.getName();
java.io.File localFile =
new java.io.File(targetFolder, filename);
delistLocalFile(localFiles, localFile);
// If the file doesn't pass the include/exclude tests, skip it.
if (!shouldProcess(filename)) {
log("Skipping " + eachFile.toString(), Project.MSG_INFO);
continue;
}
// If forced is not set then we may save ourselves some work by
// looking at the status flag.
// Otherwise, we care nothing about these statuses.
if (!isForced()) {
int fileStatus = (eachFile.getStatus());
// We try to update the status once to give StarTeam
// another chance.
if (fileStatus == Status.MERGE || fileStatus == Status.UNKNOWN) {
eachFile.updateStatus(true, true);
fileStatus = (eachFile.getStatus());
}
if (fileStatus == Status.CURRENT) {
log("Not processing " + eachFile.toString()
+ " as it is current.",
Project.MSG_INFO);
continue;
}
}
// Check out anything else.
// Just a note: StarTeam has a status for NEW which implies
// that there is an item on your local machine that is not
// in the repository. These are the items that show up as
// NOT IN VIEW in the Starteam GUI.
// One would think that we would want to perhaps checkin the
// NEW items (not in all cases! - Steve Cohen 15 Dec 2001)
// Unfortunately, the sdk doesn't really work, and we can't
// actually see anything with a status of NEW. That is why
// we can just check out everything here without worrying
// about losing anything.
log("Checking Out: " + (localFile.toString()), Project.MSG_INFO);
eachFile.checkoutTo(localFile, this.lockStatus,
true, true, true);
}
// Now we recursively call this method on all sub folders in this
// folder unless recursive attribute is off.
Folder[] subFolders = starteamFolder.getSubFolders();
for (int i = 0; i < subFolders.length; i++) {
java.io.File targetSubfolder =
new java.io.File(targetFolder, subFolders[i].getName());
delistLocalFile(localFiles, targetSubfolder);
if (isRecursive()) {
visit(subFolders[i], targetSubfolder);
}
}
if (this.deleteUncontrolled) {
deleteUncontrolledItems(localFiles);
}
} catch (IOException e) {
throw new BuildException(e);
}
}
/**
* Deletes everything on the local machine that is not in the repository.
*
* @param localFiles the list of filenames whose elements are to be deleted
*/
private void deleteUncontrolledItems(Hashtable localFiles) {
try {
Enumeration e = localFiles.keys();
while (e.hasMoreElements()) {
java.io.File file =
new java.io.File(e.nextElement().toString());
delete(file);
}
} catch (SecurityException e) {
log("Error deleting file: " + e, Project.MSG_ERR);
}
}
/**
* Deletes the file from the local drive.
* @param file the file or directory to delete.
* @return true if the file was successfully deleted otherwise false.
*/
private boolean delete(java.io.File file) {
// If the current file is a Directory, we need to delete all
// of its children as well.
if (file.isDirectory()) {
java.io.File[] children = file.listFiles();
for (int i = 0; i < children.length; i++) {
delete(children[i]);
}
}
log("Deleting: " + file.getAbsolutePath(), Project.MSG_INFO);
return file.delete();
}
}