conor 2003/02/13 05:59:53
Modified: src/main/org/apache/tools/ant/taskdefs/optional/starteam Tag: ANT_15_BRANCH StarTeamCheckin.java StarTeamCheckout.java StarTeamLabel.java StarTeamList.java StarTeamTask.java TreeBasedTask.java Log: Merge of StarTeam fixes Revision Changes Path No revision No revision 1.5.2.3 +190 -113 ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java Index: StarTeamCheckin.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java,v retrieving revision 1.5.2.2 retrieving revision 1.5.2.3 diff -u -w -u -r1.5.2.2 -r1.5.2.3 --- StarTeamCheckin.java 10 Feb 2003 14:25:20 -0000 1.5.2.2 +++ StarTeamCheckin.java 13 Feb 2003 13:59:53 -0000 1.5.2.3 @@ -53,18 +53,17 @@ */ package org.apache.tools.ant.taskdefs.optional.starteam; -import java.io.FileInputStream; -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.TypeNames; import com.starbase.starteam.View; import com.starbase.starteam.ViewConfiguration; - +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -72,8 +71,6 @@ * Checks files into a StarTeam project. * Optionally adds files and in the local tree that * are not managed by the repository to its control. - * - * * Created: Sat Dec 15 20:26:07 2001 * * @author <a href="mailto:[EMAIL PROTECTED]">Steve Cohen</a> @@ -181,15 +178,50 @@ /** * Implements base-class abstract function to define tests for - * any preconditons required by the task + * any preconditons required by the task. * - * @exception BuildException not thrown in this implementation + * @exception BuildException thrown if both rootLocalFolder + * and viewRootLocalFolder are defined */ 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 emit to the log an + * entry describing the parameters that will be used by this operation. + * + * @param starteamrootFolder + * root folder in StarTeam for the operation + * @param targetrootFolder + * root local folder for the operation + * (whether specified by the user or not). + */ + protected void logOperationDescription( + Folder starteamrootFolder, java.io.File targetrootFolder) + { + log((this.isRecursive() ? "Recursive" : "Non-recursive") + +" Checkin from" + + (null == getRootLocalFolder() ? " (default): " : ": ") + + targetrootFolder.getAbsolutePath()); + + log("Checking in to: " + starteamrootFolder.getFolderHierarchy()); + logIncludes(); + logExcludes(); + + if (this.lockStatus == Item.LockType.UNLOCKED) { + log(" Items will be checked in unlocked."); + } + else { + log(" Items will be checked in with no change in lock status."); + } + + if (this.isForced()) { + log(" Items will be checked in in accordance with repository status and regardless of lock status."); + } + else { + log(" Items will be checked in regardless of repository status only if locked." ); + } + + } /** @@ -202,129 +234,174 @@ * @exception BuildException if any error occurs */ protected void visit(Folder starteamFolder, java.io.File targetFolder) - throws BuildException { + throws BuildException + { try { - Hashtable localFiles = listLocalFiles(targetFolder); + if (null != getRootLocalFolder()) { + starteamFolder.setAlternatePathFragment( + targetFolder.getAbsolutePath()); + } - // If we have been told to create the working folders - // 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); + Folder[] foldersList = starteamFolder.getSubFolders(); + Item[] stFiles = starteamFolder.getItems(getTypeNames().FILE); - delistLocalFile(localFiles, localFile); + // note, it's important to scan the items BEFORE we make the + // UnmatchedFileMap because that creates a bunch of NEW + // folders and files (unattached to repository) and we + // don't want to include those in our traversal. - // If the file doesn't pass the include/exclude tests, skip it. - if (!shouldProcess(filename)) { - log("Skipping " + eachFile.toString(), Project.MSG_INFO); - continue; - } + UnmatchedFileMap ufm = + new CheckinMap().init( + targetFolder.getAbsoluteFile(), starteamFolder); - // 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. + for (int i = 0, size = foldersList.length; i < size; i++) { + Folder stFolder = foldersList[i]; + java.io.File subfolder = + new java.io.File(targetFolder, stFolder.getName()); - if (!isForced()) { - int fileStatus = (eachFile.getStatus()); + ufm.removeControlledItem(subfolder); - // 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; + if (isRecursive()) { + visit(stFolder, subfolder); } } - // Check in anything else. - log("Checking In: " + (localFile.toString()), Project.MSG_INFO); - eachFile.checkinFrom(localFile, this.comment, - this.lockStatus, - true, true, true); - } + for (int i = 0, size = stFiles.length; i < size; i++) { + com.starbase.starteam.File stFile = + (com.starbase.starteam.File) stFiles[i]; + processFile(stFile); - // 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); - } + ufm.removeControlledItem( + new java.io.File(targetFolder, stFile.getName())); } + if (this.addUncontrolled) { - addUncontrolledItems(localFiles, starteamFolder); + ufm.processUncontrolledItems(); } - } catch (IOException e) { throw new BuildException(e); } + } /** - * Adds to the StarTeam repository everything on the local machine that - * is not currently in the repository. - * @param folder - StarTeam folder to which these items are to be added. + * provides a string showing from and to full paths for logging + * + * @param remotefile the Star Team file being processed. + * + * @return a string showing from and to full paths + */ + private String describeCheckin(com.starbase.starteam.File remotefile) + { + StringBuffer sb = new StringBuffer(); + sb.append(remotefile.getFullName()) + .append(" --> ") + .append(getFullRepositoryPath(remotefile)); + return sb.toString(); + } + + /** + * Processes (checks-out) <code>stFiles</code>files from StarTeam folder. + * + * @param eachFile repository file to process + * @param targetFolder a java.io.File (Folder) to work + * @throws IOException when StarTeam API fails to work with files */ - private void addUncontrolledItems(Hashtable localFiles, Folder folder) + private void processFile(com.starbase.starteam.File eachFile) throws IOException { - try { - Enumeration e = localFiles.keys(); - while (e.hasMoreElements()) { - java.io.File file = - new java.io.File(e.nextElement().toString()); - add(folder, file); + String filename = eachFile.getName(); + + // If the file doesn't pass the include/exclude tests, skip it. + if (!shouldProcess(filename)) { + log("Excluding " + getFullRepositoryPath(eachFile)); + return; } - } catch (SecurityException e) { - log("Error adding file: " + e, Project.MSG_ERR); + + boolean checkin = true; + 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.MODIFIED) { + log("Checking in: " + describeCheckin(eachFile)); + } + else if (fileStatus == Status.MISSING) { + log("Local file missing: " + describeCheckin(eachFile)); + checkin = false; + } + else { + if (isForced()) { + log("Forced checkin of " + describeCheckin(eachFile) + + " over status " + Status.name(fileStatus)); + } else { + log("Skipping: " + getFullRepositoryPath(eachFile) + + " - status: " + Status.name(fileStatus)); + checkin = false; + } + } + if (checkin) { + eachFile.checkin(this.comment, this.lockStatus, + this.isForced(), true, true); } } /** - * 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. + * handles the deletion of uncontrolled items */ - private void add(Folder parentFolder, java.io.File file) - throws IOException { - // If the current file is a Directory, we need to process all - // of its children as well. - if (file.isDirectory()) { - log("Adding new folder to repository: " + file.getAbsolutePath(), - Project.MSG_INFO); - Folder newFolder = new Folder(parentFolder); - newFolder.setName(file.getName()); - newFolder.update(); + private class CheckinMap extends UnmatchedFileMap { + protected boolean isActive() { + return StarTeamCheckin.this.addUncontrolled; + } - // now visit this new folder to take care of adding any files - // or subfolders within it. + + /** + * This override adds all its members to the repository. It is assumed + * that this method will not be called until all the items in the + * corresponding folder have been processed, and that the internal map + * will contain only uncontrolled items. + */ + void processUncontrolledItems() throws BuildException { + if (this.isActive()) { + Enumeration e = this.keys(); + while (e.hasMoreElements()) { + java.io.File local = (java.io.File) e.nextElement(); + Item remoteItem = (Item) this.get(local); + remoteItem.update(); + + // once we find a folder that isn't in the repository, + // we know we can add it. + if (local.isDirectory()) { + Folder folder = (Folder) remoteItem; + log("Added uncontrolled folder " + + folder.getFolderHierarchy() + + " from " + local.getAbsoluteFile()); if (isRecursive()) { - visit(newFolder, file); + UnmatchedFileMap submap = + new CheckinMap().init(local, folder); + submap.processUncontrolledItems(); } } else { - log("Adding new file to repository: " + file.getAbsolutePath(), - Project.MSG_INFO); - File newFile = new File(parentFolder); - newFile.addFromStream(new FileInputStream(file), - file.getName(), - null, this.comment, 3, true); + com.starbase.starteam.File remoteFile = + (com.starbase.starteam.File) remoteItem; + log("Added uncontrolled file " + + TreeBasedTask.getFullRepositoryPath(remoteFile) + + " from " + local.getAbsoluteFile()); + + } + } } } } + +} + + 1.9.2.4 +379 -127 ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java Index: StarTeamCheckout.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java,v retrieving revision 1.9.2.3 retrieving revision 1.9.2.4 diff -u -w -u -r1.9.2.3 -r1.9.2.4 --- StarTeamCheckout.java 10 Feb 2003 14:25:20 -0000 1.9.2.3 +++ StarTeamCheckout.java 13 Feb 2003 13:59:53 -0000 1.9.2.4 @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2001-2002 The Apache Software Foundation. All rights + * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,17 +53,16 @@ */ 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.TypeNames; import com.starbase.starteam.View; import com.starbase.starteam.ViewConfiguration; - +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -112,8 +111,8 @@ } /** - * Should all all local files <i>not<i> in StarTeam be deleted? - * Optional, defaults to "true". + * Whether or not all local files <i>not<i> in StarTeam should be deleted. + * Optional, defaults to <code>true</code>. * @param value the value to set the attribute to. */ public void setDeleteUncontrolled(boolean value) { @@ -174,40 +173,132 @@ } /** + * should checked out files get the timestamp from the repository + * or the time they are checked out. True means use the repository + * timestamp. + */ + private boolean useRepositoryTimeStamp = false; + + /** + * sets the useRepositoryTimestmp member. + * + * @param useRepositoryTimeStamp + * true means checked out files will get the repository timestamp. + * false means the checked out files will be timestamped at the time + * of checkout. + */ + public void setUseRepositoryTimeStamp(boolean useRepositoryTimeStamp) + { + this.useRepositoryTimeStamp = useRepositoryTimeStamp; + } + + /** + * returns the value of the useRepositoryTimestamp member + * + * @return the value of the useRepositoryTimestamp member + */ + public boolean getUseRepositoryTimeStamp() { + return this.useRepositoryTimeStamp; + } + /** * Override of base-class abstract function creates an * appropriately configured view for checkouts - either - * the current view or a view from this.label. + * the current view or a view from this.label or the raw + * view itself in the case of a revision label. * * @param raw the unconfigured <code>View</code> + * * @return the snapshot <code>View</code> appropriately configured. + * @exception BuildException */ - protected View createSnapshotView(View raw) { + protected View createSnapshotView(View raw) + throws BuildException + { int labelID = getLabelID(raw); - // if a label has been supplied, use it to configure the view - // otherwise use current view - if (labelID >= 0) { + // if a label has been supplied and it is a view label, use it + // to configure the view + if (this.isUsingViewLabel()) { return new View(raw, ViewConfiguration.createFromLabel(labelID)); - } else { + } + // if a label has been supplied and it is a revision label, use the raw + // the view as the snapshot + else if (this.isUsingRevisionLabel()) { + return raw; + } + // otherwise, use this view configured as the tip. + else { return new View(raw, ViewConfiguration.createTip()); } } /** * Implements base-class abstract function to define tests for - * any preconditons required by the task + * any preconditons required by the task. * - * @exception BuildException not thrown in this implementation + * @exception BuildException thrown if both rootLocalFolder + * and viewRootLocalFolder are defined */ protected void testPreconditions() throws BuildException { - if (null != getRootLocalFolder() && !isForced()) { - log("Warning: rootLocalFolder specified, but forcing off.", + if (this.isUsingRevisionLabel() && this.createDirs) { + log("Ignoring createworkingdirs while using a revision label." + + " Folders will be created only as needed.", Project.MSG_WARN); + this.createDirs=false; } } /** + * extenders should emit to the log an entry describing the parameters + * that will be used by this operation. + * + * @param starteamrootFolder + * root folder in StarTeam for the operation + * @param targetrootFolder + * root local folder for the operation (whether specified + * by the user or not. + */ + + protected void logOperationDescription( + Folder starteamrootFolder, java.io.File targetrootFolder) + { + log((this.isRecursive() ? "Recursive" : "Non-recursive") + + " Checkout from: " + starteamrootFolder.getFolderHierarchy()); + + log(" Checking out to" + + (null == getRootLocalFolder() ? "(default): " : ": ") + + targetrootFolder.getAbsolutePath()); + + + logLabel(); + logIncludes(); + logExcludes(); + + if (this.lockStatus == Item.LockType.EXCLUSIVE) { + log(" Items will be checked out with Exclusive locks."); + } + else if (this.lockStatus == Item.LockType.UNLOCKED) { + log(" Items will be checked out unlocked (even if presently locked)."); + } + else { + log(" Items will be checked out with no change in lock status."); + } + log(" Items will be checked out with " + + (this.useRepositoryTimeStamp ? "repository timestamps." + : "the current timestamp.")); + log(" Items will be checked out " + + (this.isForced() ? "regardless of" : "in accordance with") + + " repository status."); + if (this.deleteUncontrolled) { + log(" Local items not found in the repository will be deleted."); + } + log(" Directories will be created"+ + (this.createDirs ? " wherever they exist in the repository, even if empty." + : " only where needed to check out files.")); + + } + /** * Implements base-class abstract function to perform the checkout * operation on the files in each folder of the tree. * @@ -217,59 +308,166 @@ * @exception BuildException if any error occurs */ protected void visit(Folder starteamFolder, java.io.File targetFolder) - throws BuildException { + 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 (null != getRootLocalFolder()) { + starteamFolder.setAlternatePathFragment( + targetFolder.getAbsolutePath()); + } + if (!targetFolder.exists()) { - targetFolder.mkdir(); + if (!this.isUsingRevisionLabel()) { + if (this.createDirs) { + if (targetFolder.mkdirs()) { + log("Creating folder: " + targetFolder); + } else { + throw new BuildException( + "Failed to create local folder " + targetFolder); + } + } } } - // 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); + Folder[] foldersList = starteamFolder.getSubFolders(); + Item[] filesList = starteamFolder.getItems(getTypeNames().FILE); - // If the file doesn't pass the include/exclude tests, skip it. - if (!shouldProcess(filename)) { - log("Skipping " + eachFile.toString(), Project.MSG_INFO); - continue; + if (this.isUsingRevisionLabel()) { + + // prune away any files not belonging to the revision label + // this is one ugly API from Starteam SDK + + Hashtable labelItems = new Hashtable(filesList.length); + int s = filesList.length; + int[] ids = new int[s]; + for (int i=0; i < s; i++) { + ids[i]=filesList[i].getItemID(); + labelItems.put(new Integer(ids[i]), new Integer(i)); + } + int[] foundIds = getLabelInUse().getLabeledItemIDs(ids); + s = foundIds.length; + Item[] labeledFiles = new Item[s]; + for (int i=0; i < s; i++) { + Integer ID = new Integer(foundIds[i]); + labeledFiles[i] = + filesList[((Integer) labelItems.get(ID)).intValue()]; + } + filesList = labeledFiles; } - // 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. + // note, it's important to scan the items BEFORE we make the + // Unmatched file map because that creates a bunch of NEW + // folders and files (unattached to repository) and we + // don't want to include those in our traversal. - if (!isForced()) { - int fileStatus = (eachFile.getStatus()); + UnmatchedFileMap ufm = + new CheckoutMap(). + init(targetFolder.getAbsoluteFile(), starteamFolder); - // 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()); + + + for (int i = 0; i < foldersList.length; i++) { + Folder stFolder = foldersList[i]; + + java.io.File subfolder = + new java.io.File(targetFolder, stFolder.getName()); + + ufm.removeControlledItem(subfolder); + + if (isRecursive()) { + visit(stFolder, subfolder); } - if (fileStatus == Status.CURRENT) { - log("Not processing " + eachFile.toString() - + " as it is current.", - Project.MSG_INFO); - continue; + } + + for (int i = 0; i < filesList.length; i++) { + com.starbase.starteam.File stFile = + (com.starbase.starteam.File) filesList[i]; + processFile( stFile, targetFolder); + + ufm.removeControlledItem( + new java.io.File(targetFolder, stFile.getName())); + } + if (this.deleteUncontrolled) { + ufm.processUncontrolledItems(); + } + } catch (IOException e) { + throw new BuildException(e); } } - // Check out anything else. + /** + * provides a string showing from and to full paths for logging + * + * @param remotefile the Star Team file being processed. + * + * @return a string showing from and to full paths + */ + private String describeCheckout(com.starbase.starteam.File remotefile, + java.io.File localFile) + { + StringBuffer sb = new StringBuffer(); + sb.append(getFullRepositoryPath(remotefile)) + .append(" --> "); + if (null == localFile) { + sb.append(remotefile.getFullName()); + } else { + sb.append(localFile); + } + return sb.toString(); + } + private String describeCheckout(com.starbase.starteam.File remotefile) { + return describeCheckout(remotefile,null); + } + /** + * Processes (checks out) <code>stFiles</code>files from StarTeam folder. + * + * @param eachFile repository file to process + * @param targetFolder a java.io.File (Folder) to work + * @throws IOException when StarTeam API fails to work with files + */ + private void processFile(com.starbase.starteam.File eachFile, + java.io.File targetFolder ) + throws IOException + { + String filename = eachFile.getName(); + + java.io.File localFile = new java.io.File(targetFolder, filename); + + // If the file doesn't pass the include/exclude tests, skip it. + if (!shouldProcess(filename)) { + log("Excluding " + getFullRepositoryPath(eachFile), + Project.MSG_INFO); + return; + } + + if (this.isUsingRevisionLabel()) { + if (!targetFolder.exists()) { + if (targetFolder.mkdirs()) { + log("Creating folder: " + targetFolder); + } else { + throw new BuildException( + "Failed to create local folder " + targetFolder); + } + } + boolean success = eachFile.checkoutByLabelID( + localFile, + getIDofLabelInUse(), + this.lockStatus, + !this.useRepositoryTimeStamp, + true, + false); + if (success) { + log("Checked out " + describeCheckout(eachFile, localFile)); + } + } + else { + boolean checkout = true; + // 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 @@ -281,79 +479,133 @@ // 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); + 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()); } - // 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); + log(eachFile.toString() + " has status of " + + Status.name(fileStatus), Project.MSG_DEBUG); + + + switch (fileStatus) { + case Status.OUTOFDATE: + case Status.MISSING: + log("Checking out: " + describeCheckout(eachFile)); + break; + default: + if (isForced()) { + log("Forced checkout of " + + describeCheckout(eachFile) + + " over status " + Status.name(fileStatus)); + } else { + log("Skipping: " + getFullRepositoryPath(eachFile) + + " - status: " + Status.name(fileStatus)); + checkout = false; } } - if (this.deleteUncontrolled) { - deleteUncontrolledItems(localFiles); + if (checkout) { + if (!targetFolder.exists()) { + if (targetFolder.mkdirs()) { + log("Creating folder: " + targetFolder); + } else { + throw new BuildException( + "Failed to create local folder " + targetFolder); + } + } + eachFile.checkout(this.lockStatus, + !this.useRepositoryTimeStamp, true, true); + } + } + } + /** + * handles the deletion of uncontrolled items + */ + private class CheckoutMap extends UnmatchedFileMap { + protected boolean isActive() { + return StarTeamCheckout.this.deleteUncontrolled; } - } catch (IOException e) { - throw new BuildException(e); + /** + * override of the base class init. It can be much simpler, since + * the action to be taken is simply to delete the local files. No + * further interaction with the repository is necessary. + * + * @param localFolder + * the local folder from which the mappings will be made. + * @param remoteFolder + * not used in this implementation + */ + UnmatchedFileMap init(java.io.File localFolder, Folder remoteFolder) { + if (!localFolder.exists()) { + return this; + } + + String[] localFiles = localFolder.list(); + + for (int i=0; i < localFiles.length; i++) { + java.io.File localFile = + new java.io.File(localFolder, localFiles[i]).getAbsoluteFile(); + + log("adding " + localFile + " to UnmatchedFileMap", + Project.MSG_DEBUG); + + if (localFile.isDirectory()) { + this.put(localFile, ""); + } + else { + this.put(localFile, ""); } } + return this; + } + /** - * Deletes everything on the local machine that is not in the repository. - * - * @param localFiles the list of filenames whose elements are to be deleted + * deletes uncontrolled items from the local tree. It is assumed + * that this method will not be called until all the items in the + * corresponding folder have been processed, and that the internal map + * will contain only uncontrolled items. */ - private void deleteUncontrolledItems(Hashtable localFiles) { - try { - Enumeration e = localFiles.keys(); + void processUncontrolledItems() throws BuildException { + if (this.isActive()) { + Enumeration e = this.keys(); while (e.hasMoreElements()) { - java.io.File file = - new java.io.File(e.nextElement().toString()); - delete(file); + java.io.File local = (java.io.File) e.nextElement(); + delete(local); } - } 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. + * deletes all files and if the file is a folder recursively deletes + * everything in it. + * + * @param local The local file or folder to be deleted. */ - 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]); + void delete(java.io.File local) { + // once we find a folder that isn't in the repository, + // anything below it can be deleted. + if (local.isDirectory() && isRecursive()) { + String[] contents = local.list(); + for (int i=0; i< contents.length; i++) { + java.io.File file = new java.io.File(local, contents[i]); + delete(file); } } - - log("Deleting: " + file.getAbsolutePath(), Project.MSG_INFO); - return file.delete(); + local.delete(); + log("Deleted uncontrolled item " + local.getAbsolutePath()); } - - } - - - - - - - - +} 1.9.2.4 +72 -8 ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java Index: StarTeamLabel.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java,v retrieving revision 1.9.2.3 retrieving revision 1.9.2.4 diff -u -w -u -r1.9.2.3 -r1.9.2.4 --- StarTeamLabel.java 10 Feb 2003 14:25:20 -0000 1.9.2.3 +++ StarTeamLabel.java 13 Feb 2003 13:59:53 -0000 1.9.2.4 @@ -53,15 +53,13 @@ */ package org.apache.tools.ant.taskdefs.optional.starteam; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - import com.starbase.starteam.Label; import com.starbase.starteam.View; import com.starbase.starteam.ViewConfiguration; import com.starbase.util.OLEDate; - +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import org.apache.tools.ant.BuildException; /** @@ -96,6 +94,19 @@ private String description; /** + * If true, this will be a build label. If false, it will be a build + * label. The default is false. Has no effect if revision label is + * true. + */ + private boolean buildlabel = false; + + /** + * If true, this will be a revision label. If false, it will be a build + * label. The default is false. + */ + private boolean revisionlabel = false; + + /** * The time of the last successful. The new label will be a snapshot of the * repository at this time. String should be formatted as "yyyyMMddHHmmss" */ @@ -120,6 +131,30 @@ } /** + * set the type of label based on the supplied value - if true, this + * label will be a revision label, if false, a build label. + * + * @param revision If true this will be a revision label; if false, + * a build label + */ + public void setBuildLabel( boolean buildlabel ) { + this.buildlabel = buildlabel; + } + + /** + * set the type of label based on the supplied value - if true, this + * label will be a revision label, if false, a build label. + * + * @param revision If true this will be a revision label; if false, + * a build label + */ + public void setRevisionLabel( boolean revisionlabel ) { + this.revisionlabel = revisionlabel; + } + + + + /** * The timestamp of the build that will be stored with the label; required. * Must be formatted <code>yyyyMMddHHmmss</code> */ @@ -128,7 +163,8 @@ Date lastBuildTime = DATE_FORMAT.parse(lastbuild); this.lastBuild = new OLEDate(lastBuildTime); } catch (ParseException e) { - throw new BuildException("Unable to parse the date '" + lastbuild + "'", e); + throw new BuildException("Unable to parse the date '" + + lastbuild + "'", e); } } @@ -139,11 +175,33 @@ */ public void execute() throws BuildException { + if (this.revisionlabel && this.buildlabel) { + throw new BuildException( + "'revisionlabel' and 'buildlabel' both specified. " + + "A revision label cannot be a build label."); + } + View snapshot = openView(); // Create the new label and update the repository - new Label(snapshot, labelName, description, this.lastBuild, true).update(); - log("Created Label " + labelName); + + if (this.revisionlabel) { + new Label(snapshot, this.labelName, this.description).update(); + log("Created Revision Label " + this.labelName); + } + else if (null != lastBuild){ + new Label(snapshot, this.labelName, this.description,this.lastBuild, + this.buildlabel).update(); + log("Created View Label (" + +(this.buildlabel ? "" : "non-") + "build) " + this.labelName + +" as of " + this.lastBuild.toString()); + } + else { + new Label(snapshot, this.labelName, this.description, + this.buildlabel).update(); + log("Created View Label (" + +(this.buildlabel ? "" : "non-") + "build) " + this.labelName); + } } /** @@ -155,7 +213,13 @@ * @return the snapshot <code>View</code> appropriately configured. */ protected View createSnapshotView(View raw) { + /* + if (this.revisionlabel) { + return raw; + } return new View(raw, ViewConfiguration.createFromTime(this.lastBuild)); + */ + return raw; } } 1.6.2.4 +125 -21 ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java Index: StarTeamList.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java,v retrieving revision 1.6.2.3 retrieving revision 1.6.2.4 diff -u -w -u -r1.6.2.3 -r1.6.2.4 --- StarTeamList.java 10 Feb 2003 14:25:20 -0000 1.6.2.3 +++ StarTeamList.java 13 Feb 2003 13:59:53 -0000 1.6.2.4 @@ -53,17 +53,18 @@ */ package org.apache.tools.ant.taskdefs.optional.starteam; -import java.io.IOException; -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 java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Enumeration; +import java.util.Hashtable; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; /** * Produces a listing of the contents of the StarTeam repository @@ -78,6 +79,7 @@ */ public class StarTeamList extends TreeBasedTask { + private boolean listUncontrolled = true; /** * List files, dates, and statuses as of this label; optional. * The label must exist in starteam or an exception will be thrown. @@ -120,6 +122,29 @@ } /** + * extenders should emit to the log an entry describing the parameters + * that will be used by this operation. + * + * @param starteamrootFolder + * root folder in StarTeam for the operation + * @param targetrootFolder + * root local folder for the operation (whether specified by the user or not. + */ + protected void logOperationDescription(Folder starteamrootFolder, java.io.File targetrootFolder) { + log((this.isRecursive() ? "Recursive" : "Non-recursive") + + " Listing of: " + starteamrootFolder.getFolderHierarchy()); + + log("Listing against local folder" + + (null == getRootLocalFolder() ? " (default): " : ": ") + + targetrootFolder.getAbsolutePath(), + Project.MSG_INFO); + logLabel(); + logIncludes(); + logExcludes(); + + + } + /** * Implements base-class abstract function to perform the checkout * operation on the files in each folder of the tree. * @@ -130,26 +155,35 @@ protected void visit(Folder starteamFolder, java.io.File targetFolder) throws BuildException { try { - if (null == getRootLocalFolder()) { - log("Folder: " + starteamFolder.getName() + " (Default folder: " + targetFolder + ")"); - } else { - log("Folder: " + starteamFolder.getName() + " (Local folder: " + targetFolder + ")"); + if (null != getRootLocalFolder()) { + starteamFolder.setAlternatePathFragment( + targetFolder.getAbsolutePath()); + } - Hashtable localFiles = listLocalFiles(targetFolder); + Folder[] subFolders = starteamFolder.getSubFolders(); + Item[] files = starteamFolder.getItems(getTypeNames().FILE); + + UnmatchedFileMap ufm = + new UnmatchedListingMap().init( + targetFolder.getAbsoluteFile(), starteamFolder); + + log(""); + log("Listing StarTeam folder " + + starteamFolder.getFolderHierarchy()); + log(" against local folder " + + targetFolder.getAbsolutePath()); + // 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); - - + ufm.removeControlledItem(localFile); // If the file doesn't pass the include/exclude tests, skip it. if (!shouldProcess(filename)) { @@ -162,32 +196,40 @@ // 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); + ufm.removeControlledItem(targetSubfolder); if (isRecursive()) { visit(subFolders[i], targetSubfolder); } } + if (this.listUncontrolled) { + ufm.processUncontrolledItems(); + } } catch (IOException e) { throw new BuildException(e); } } + private static final SimpleDateFormat SDF = + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss zzz"); + protected void list(File reposFile, java.io.File localFile) throws IOException { StringBuffer b = new StringBuffer(); - if (null == getRootLocalFolder()) { - // status is irrelevant to us if we have specified a - // root local folder. - b.append(pad(Status.name(reposFile.getStatus()), 12)).append(' '); + int status = reposFile.getStatus(); + java.util.Date displayDate = null; + if (status==Status.NEW) { + displayDate = new java.util.Date(localFile.lastModified()); + } else { + displayDate = reposFile.getModifiedTime().createDate(); } + b.append(pad(Status.name(status), 12)).append(' '); b.append(pad(getUserName(reposFile.getLocker()), 20)) .append(' ') - .append(reposFile.getModifiedTime().toString()) + .append(SDF.format(displayDate)) .append(rpad(String.valueOf(reposFile.getSize()), 9)) .append(' ') .append(reposFile.getName()); @@ -212,6 +254,68 @@ protected static String rpad(String s, int padlen) { s = blankstr + s; return s.substring(s.length() - padlen); + } + + /** + * handles the list of uncontrolled items + */ + private class UnmatchedListingMap extends UnmatchedFileMap { + + protected boolean isActive() { + return StarTeamList.this.listUncontrolled; + } + + /** + * lists uncontrolled items from the local tree. It is assumed + * that this method will not be called until all the items in the + * corresponding folder have been processed, and that the internal map + * will contain only uncontrolled items. + */ + void processUncontrolledItems() throws BuildException{ + if (this.isActive()) { + Enumeration e = this.keys(); + + // handle the files so they appear first + while (e.hasMoreElements()) { + java.io.File local = (java.io.File) e.nextElement(); + Item remoteItem = (Item) this.get(local); + + // once we find a folder that isn't in the repository, + // we know we can add it. + if (local.isFile()) { + com.starbase.starteam.File remoteFile = + (com.starbase.starteam.File) remoteItem; + try { + list(remoteFile, local); + } catch (IOException ie) { + throw new BuildException("IOError in stlist",ie); + } + } + } + // now do it again for the directories so they appear last. + e = this.keys(); + while (e.hasMoreElements()) { + java.io.File local = (java.io.File) e.nextElement(); + Item remoteItem = (Item) this.get(local); + + // once we find a folder that isn't in the repository, + // we know we can add it. + if (local.isDirectory()) { + Folder folder = (Folder) remoteItem; + if (isRecursive()) { + log("Listing uncontrolled folder " + + folder.getFolderHierarchy() + + " from " + local.getAbsoluteFile()); + UnmatchedFileMap submap = + new UnmatchedListingMap().init(local, folder); + submap.processUncontrolledItems(); + } + } + } + } + } + + } 1.6.2.3 +54 -24 ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java Index: StarTeamTask.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java,v retrieving revision 1.6.2.2 retrieving revision 1.6.2.3 diff -u -w -u -r1.6.2.2 -r1.6.2.3 --- StarTeamTask.java 10 Feb 2003 14:25:20 -0000 1.6.2.2 +++ StarTeamTask.java 13 Feb 2003 13:59:53 -0000 1.6.2.3 @@ -53,14 +53,15 @@ */ package org.apache.tools.ant.taskdefs.optional.starteam; -import java.util.StringTokenizer; - +import com.starbase.starteam.BuildNumber; import com.starbase.starteam.Server; import com.starbase.starteam.StarTeamFinder; +import com.starbase.starteam.TypeNames; import com.starbase.starteam.User; import com.starbase.starteam.View; - +import java.util.StringTokenizer; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; /** @@ -114,6 +115,12 @@ */ private Server server = null; + private void logStarteamVersion() { + log("StarTeam version: "+ + BuildNumber.getDisplayString(), Project.MSG_DEBUG); + } + + ///////////////////////////////////////////////////////// // GET/SET methods. // Setters, of course are where ant user passes in values. @@ -125,7 +132,7 @@ * @param servername a <code>String</code> value * @see #setURL(String) */ - public void setServername(String servername) { + public final void setServername(String servername) { this.servername = servername; } @@ -135,7 +142,7 @@ * @return the name of the StarTeam server * @see #getURL() */ - public String getServername() { + public final String getServername() { return this.servername; } @@ -145,7 +152,7 @@ * @param serverport port number to be set * @see #setURL(String) */ - public void setServerport(String serverport) { + public final void setServerport(String serverport) { this.serverport = serverport; } @@ -155,7 +162,7 @@ * @return the port number of the StarTeam connection * @see #getURL() */ - public String getServerport() { + public final String getServerport() { return this.serverport; } @@ -166,7 +173,7 @@ * @param projectname the name of the StarTeam project to be acted on * @see #setURL(String) */ - public void setProjectname(String projectname) { + public final void setProjectname(String projectname) { this.projectname = projectname; } @@ -176,7 +183,7 @@ * @return the name of the StarTeam project to be acted on * @see #getURL() */ - public String getProjectname() { + public final String getProjectname() { return this.projectname; } @@ -187,7 +194,7 @@ * @param projectname the name of the StarTeam view to be acted on * @see #setURL(String) */ - public void setViewname(String viewname) { + public final void setViewname(String viewname) { this.viewname = viewname; } @@ -197,7 +204,7 @@ * @return the name of the StarTeam view to be acted on * @see #getURL() */ - public String getViewname() { + public final String getViewname() { return this.viewname; } @@ -214,7 +221,7 @@ * @see #setProjectname(String) * @see #setViewname(String) */ - public void setURL(String url) { + public final void setURL(String url) { StringTokenizer t = new StringTokenizer(url, "/"); if (t.hasMoreTokens()) { String unpw = t.nextToken(); @@ -248,7 +255,7 @@ * @see #getProjectname() * @see #getViewname() */ - public String getURL() { + public final String getURL() { return this.servername + ":" + this.serverport + "/" + @@ -257,11 +264,20 @@ } /** + * returns an URL string useful for interacting with many StarTeamFinder + * methods. + * + * @return the URL string for this task. + */ + protected final String getViewURL() { + return getUserName() + ":" + getPassword() + "@" + getURL(); + } + /** * set the name of the StarTeam user, needed for the connection * * @param userName name of the user to be logged in */ - public void setUserName(String userName) { + public final void setUserName(String userName) { this.userName = userName; } @@ -270,7 +286,7 @@ * * @return the name of the StarTeam user */ - public String getUserName() { + public final String getUserName() { return this.userName; } @@ -279,7 +295,7 @@ * * @param password the password to be used for login */ - public void setPassword(String password) { + public final void setPassword(String password) { this.password = password; } @@ -288,7 +304,7 @@ * * @return the password used for login */ - public String getPassword() { + public final String getPassword() { return this.password; } @@ -298,18 +314,27 @@ * * @return a reference to the server */ - protected Server getServer() { + protected final Server getServer() { return this.server; } /** + * returns a list of TypeNames known to the server. + * + * @return a reference to the server's TypeNames + */ + protected final TypeNames getTypeNames() { + return this.server.getTypeNames(); + } + /** * Derived classes must override <code>createSnapshotView</code> * defining the kind of configured view appropriate to its task. * * @param rawview the unconfigured <code>View</code> * @return the snapshot <code>View</code> appropriately configured. */ - protected abstract View createSnapshotView(View rawview); + protected abstract View createSnapshotView(View rawview) + throws BuildException; /** * All subclasses will call on this method to open the view needed for @@ -322,10 +347,15 @@ * @see #getServer() */ protected View openView() throws BuildException { - View view = - StarTeamFinder.openView(getUserName() + ":" - + getPassword() - + "@" + getURL()); + + logStarteamVersion(); + View view = null; + try { + view = StarTeamFinder.openView(getViewURL()); + } catch (Exception e) { + throw new BuildException( + "Failed to connect to " + getURL(), e); + } if (null == view) { throw new BuildException("Cannot find view" + getURL() + @@ -344,7 +374,7 @@ * @param userID a user's ID * @return the name of the user with ID userID */ - protected String getUserName(int userID) { + protected final String getUserName(int userID) { User u = this.server.getUser(userID); if (null == u) { return ""; 1.8.2.3 +330 -82 ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java Index: TreeBasedTask.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java,v retrieving revision 1.8.2.2 retrieving revision 1.8.2.3 diff -u -w -u -r1.8.2.2 -r1.8.2.3 --- TreeBasedTask.java 10 Feb 2003 14:25:20 -0000 1.8.2.2 +++ TreeBasedTask.java 13 Feb 2003 13:59:53 -0000 1.8.2.3 @@ -1,7 +1,7 @@ /* * The Apache Software License, Version 1.1 * - * Copyright (c) 2001-2002 The Apache Software Foundation. All rights + * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,16 +53,18 @@ */ 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.Item; import com.starbase.starteam.Label; import com.starbase.starteam.StarTeamFinder; import com.starbase.starteam.View; - +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; /** * FileBasedTask.java @@ -116,7 +118,7 @@ /** * The local folder corresponding to starteamFolder. If not specified - * the Star Team defalt folder will be used. + * the Star Team default folder will be used. */ private String rootLocalFolder = null; @@ -150,6 +152,7 @@ */ private boolean forced = false; + private Label labelInUse = null; /////////////////////////////////////////////////////////////// // GET/SET methods. @@ -179,23 +182,32 @@ * to which files are checked out; optional. * If this is not supplied, then the StarTeam "default folder" * associated with <tt>rootstarteamfolder</tt> is used. - * @param rootLocalFolder the local folder that will mirror + * + * @param rootLocalFolder + * the local folder that will mirror * this.rootStarteamFolder + * + * @see rootLocalFolder */ 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 + * corresponding to the starteam folder for this operation + * or null if not specified. + * * @return the local folder that mirrors this.rootStarteamFolder + * @see rootLocalFolder */ public String getRootLocalFolder() { return this.rootLocalFolder; } + /** * Declare files to include using standard <tt>includes</tt> patterns; optional. * @param includes A string of filter patterns to include. Separate the @@ -223,6 +235,15 @@ } /** + * if excludes have been specified, emit the list to the log + */ + protected void logIncludes() { + if (this.DEFAULT_INCLUDESETTING != this.includes) { + log(" Includes specified: "+ this.includes); + } + } + + /** * Declare files to exclude using standard <tt>excludes</tt> patterns; optional. * When filtering files, AntStarTeamCheckOut * uses an unmodified version of <CODE>DirectoryScanner</CODE>'s @@ -275,6 +296,16 @@ } /** + * if excludes have been specified, emit the list to the log + */ + protected void logExcludes() { + if (this.DEFAULT_EXCLUDESETTING != this.excludes) { + log(" Excludes specified: "+ this.excludes); + } + } + + + /** * protected function to allow subclasses to set the label (or not). * sets the StarTeam label * @@ -289,6 +320,10 @@ } } + protected String getLabel() { + return this.label; + } + /** * Get the value of recursive. * @return value of recursive. @@ -326,6 +361,49 @@ this.forced = v; } + /** + * returns true if a label has been specified and it is a view label. + * + * @return true if a label has been specified and it is a view label + */ + protected boolean isUsingViewLabel() { + return null != this.labelInUse && + this.labelInUse.isViewLabel(); + } + /** + * returns true if a label has been specified and it is a revision label. + * + * @return true if a label has been specified and it is a revision label + */ + protected boolean isUsingRevisionLabel() { + return null != this.labelInUse && + this.labelInUse.isRevisionLabel(); + } + + /** + * returns the label being used + * + * @return + */ + protected Label getLabelInUse() { + return this.labelInUse; + } + + /** + * show the label in the log and its type. + */ + protected void logLabel() { + if (this.isUsingViewLabel()) { + log(" Using view label " + getLabel()); + } + else if (this.isUsingRevisionLabel()) { + log(" Using revision label " + getLabel()); + } + } + + + + /////////////////////////////////////////////////////////////// // INCLUDE-EXCLUDE processing /////////////////////////////////////////////////////////////// @@ -334,8 +412,10 @@ * 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. + * + * @return whether the file should be processed or not. */ protected boolean shouldProcess(String pName) { boolean includeIt = matchPatterns(getIncludes(), pName); @@ -364,49 +444,144 @@ } /** - * This method does the work of opening the supplied Starteam view and - * calling the <code>visit()</code> method to perform the task. + * Finds and opens the root starteam folder of the operation specified + * by this task. This will be one of the following cases: * - * @exception BuildException if any error occurs in the processing - * @see <code>visit()</code> - */ - - public void execute() throws BuildException { + * @return Starteam's root folder for the operation. + * @exception BuildException + * if the root folder cannot be found in the repository + */ + private final Folder configureRootStarteamFolder() + throws BuildException + { + Folder starteamrootfolder = null; try { - testPreconditions(); - + // no root local mapping has been specified. View snapshot = openView(); // find the starteam folder specified to be the root of the // operation. Throw if it can't be found. - Folder starteamrootfolder = + + starteamrootfolder = StarTeamFinder.findFolder(snapshot.getRootFolder(), this.rootStarteamFolder); + } + catch (BuildException e) { + throw e; + } + catch (Exception e) { + throw new BuildException( + "Unable to find root folder " + this.rootStarteamFolder + + " in repository at " + getURL(), e); + + } + if (null == starteamrootfolder) { throw new BuildException( - "Unable to find root folder in repository."); + "Unable to find root folder " + this.rootStarteamFolder + + " in repository at " + getURL()); } + return starteamrootfolder; + } + + /** + * Returns the local folder mapped to the given StarTeam root folder + * of the operation. There are two cases here, depending on whether + * <code>rootLocalFolder</code> is defined. + * If <code>rootLocalFolder</code> is defined, it will be used to + * establish a root mapping. Otherwise, the repository's default root + * folder will be used. + * + * @param starteamrootfolder + * root Starteam folder initialized for the operation + * + * @return the local folder corresponding to the root Starteam folder. + * @see findRootStarteamFolder + */ + private final java.io.File getLocalRootMapping(Folder starteamrootfolder) { // 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); + String localrootfolder; + if (null != this.rootLocalFolder) { + localrootfolder = rootLocalFolder; + } + else { + // either use default path or root local mapping, + // which is now embedded in the root folder + localrootfolder = starteamrootfolder.getPathFragment(); } + return new java.io.File(localrootfolder); + + } + + /** + * extenders should emit to the log an entry describing the parameters + * that will be used by this operation. + * + * @param starteamrootFolder + * root folder in StarTeam for the operation + * @param targetrootFolder + * root local folder for the operation (whether specified by the user or not. + */ + protected abstract void logOperationDescription( + Folder starteamrootFolder, java.io.File targetrootFolder); + + /** + * This method does the work of opening the supplied Starteam view and + * calling the <code>visit()</code> method to perform the task. + * Derived classes can customize the called methods + * <code>testPreconditions()</code> and <code>visit()</code>. + * + * @exception BuildException if any error occurs in the processing + * @see <code>testPreconditions()</code> + * @see <code>visit()</code> + */ + + public final void execute() throws BuildException { + try { + + Folder starteamrootfolder = configureRootStarteamFolder(); + + // set the local folder. + java.io.File localrootfolder = + getLocalRootMapping(starteamrootfolder); + + testPreconditions(); + + // Tell user what he is doing + logOperationDescription(starteamrootfolder, localrootfolder); + // Inspect everything in the root folder and then recursively visit(starteamrootfolder, localrootfolder); + } catch (Exception e) { throw new BuildException(e); } } + private void findLabel(View v) throws BuildException { + Label[] allLabels = v.getLabels(); + for (int i = 0; i < allLabels.length; i++) { + Label stLabel = allLabels[i]; + log("checking label " + stLabel.getName(), Project.MSG_DEBUG); + if (stLabel.getName().equals(this.label)) { + if (!stLabel.isRevisionLabel() && !stLabel.isViewLabel()) { + throw new BuildException("Unexpected label type."); + } + log("using label " + stLabel.getName(), Project.MSG_DEBUG); + this.labelInUse = stLabel; + return; + } + } + throw new BuildException("Error: label " + + this.label + + " does not exist in view " + + v.getFullName()); + + } + /** * Helper method calls on the StarTeam API to retrieve an ID number * for the specified view, corresponding to this.label. @@ -418,32 +593,37 @@ */ 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(); + findLabel(v); + return this.labelInUse.getID(); } + return -1; } - throw new BuildException("Error: label " - + this.label - + " does not exist in view"); + + protected int getIDofLabelInUse() { + if (null != this.labelInUse) { + return this.labelInUse.getID(); } 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 + * @param rootStarteamFolder + * the StarTeam folderto be visited + * @param rootLocalFolder + * the local mapping of rootStarteamFolder + * + * @exception BuildException */ 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 @@ -456,46 +636,114 @@ */ protected abstract void testPreconditions() throws BuildException; + + /** + * Return the full repository path name of a file. Surprisingly there's + * no method in com.starbase.starteam.File to provide this. + * + * @param remotefile the Star Team file whose path is to be returned + * + * @return the full repository path name of a file. + */ + public static String getFullRepositoryPath( + com.starbase.starteam.File remotefile) + { + StringBuffer sb = new StringBuffer(); + sb.append(remotefile.getParentFolderHierarchy()) + .append(remotefile.getName()); + return sb.toString(); + } + /** - * 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 ""). + * This class implements a map of existing local files to possibly + * existing repository files. The map is created by a TreeBasedTask + * upon recursing into a directory. Each local item is mapped to an + * unattached StarTeam object of the proper type, File->File and + * Directory->Folder. + * + * As the TreeBased does its work, it deletes from the map all items + * it has processed. + * + * When the TreeBased task processes all the items from the repository, + * whatever items left in the UnmatchedFileMap are uncontrolled items + * and can be processed as appropriate to the task. In the case of + * Checkouts, they can be optionally deleted from the local tree. In the + * case of Checkins they can optionally be added to the resository. + */ + protected abstract class UnmatchedFileMap extends Hashtable { + + /** + * initializes the UnmatchedFileMap with entries from the local folder + * These will be mapped to the corresponding StarTeam entry even though + * it will not, in fact, exist in the repository. But through it, it + * can be added, listed, etc. + * + * @param localFolder + * the local folder from which the mappings will be made. + * @param remoteFolder + * the corresponding StarTeam folder which will be processed. + */ + UnmatchedFileMap init(java.io.File localFolder, Folder remoteFolder) { + if (!localFolder.exists()) { + return this; + } - 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], ""); + String fn = localFiles[i]; + java.io.File localFile = + new java.io.File(localFolder, localFiles[i]).getAbsoluteFile(); + + log("adding " + localFile + " to UnmatchedFileMap", + Project.MSG_DEBUG); + + if (localFile.isDirectory()) { + this.put(localFile, new Folder( remoteFolder, fn, fn)); } + else { + com.starbase.starteam.File remoteFile = + new com.starbase.starteam.File(remoteFolder); + remoteFile.setName(fn); + this.put(localFile, remoteFile); } - return localFileList; + } + return this; } /** - * 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. + * remove an item found to be controlled from the map. + * + * @param localFile the local item found to be controlled. */ - protected boolean delistLocalFile(Hashtable localFiles, java.io.File thisfile) { - return null != localFiles.remove(thisfile.toString()); + void removeControlledItem(java.io.File localFile) { + if (isActive()) { + log("removing processed " + localFile.getAbsoluteFile() + + " from UnmatchedFileMap", Project.MSG_DEBUG); + this.remove(localFile.getAbsoluteFile()); } } + /** + * override will perform the action appropriate for its task to perform + * on items which are on the local tree but not in StarTeam. It is + * assumed that this method will not be called until all the items in + * the corresponding folder have been processed, and that the internal + * map * will contain only uncontrolled items. + */ + abstract void processUncontrolledItems() throws BuildException; + + /** + * overrides must define this to declare how this method knows if it + * is active. This presents extra clock cycles when the functionality + * is not called for. + * + * @return True if this object is to perform its functionality. + */ + abstract protected boolean isActive(); + } +}