diff --git a/docs/manual/OptionalTasks/starteam.html b/docs/manual/OptionalTasks/starteam.html index 3a3ec6ce6..6c531d2bb 100644 --- a/docs/manual/OptionalTasks/starteam.html +++ b/docs/manual/OptionalTasks/starteam.html @@ -1,7 +1,7 @@ -StartTeam Tasks +StarTeam Tasks

StarTeam Support

@@ -98,7 +98,10 @@ See also the required common StarTeam parameters. rootstarteamfolder The root of the subtree in the StarTeam repository from which to - check out files. Defaults to the root folder of the view ('/'). + check out files. Defaults to the root folder of the view ('/'). + If supplied, this should always be an "absolute" path, that is, it should begin with a '/'. + Relative paths have little meaning in this context and confuse StarTeam. + no @@ -141,9 +144,7 @@ See also the required common StarTeam parameters. forced If true, checkouts will occur regardless of the status -that StarTeam is maintaining for the file. If rootlocalfolder is set then -this should be set "true" as otherwise the checkout will be based on statuses -which do not relate to the target folder. Defaults to "false". +that StarTeam is maintaining for the file. If false, status will be used to determine which files to check out. Defaults to "false". no @@ -156,8 +157,16 @@ users. If false (default) has no effect. unlocked If true, file will be unlocked so that other users may change it. This is a way to reverse changes that have not yet been checked in. -If false (default) has no effect. +If false (default) has no effect. + + userepositorytimestamp + true means checked out files will get the repository timestamp. +false(default) means the checked out files will be timestamped at the time +of checkout. no + + +

Examples

@@ -290,7 +299,9 @@ See also the required common StarTeam parameters. rootstarteamfolder The root of the subtree in the StarTeam repository into which to - files will be checked. Defaults to the root folder of the view ('/'). + files will be checked. Defaults to the root folder of the view ('/'). + If supplied, this should always be an "absolute" path, that is, it should begin with a '/'. + Relative paths have little meaning in this context and confuse StarTeam. no @@ -330,9 +341,7 @@ added to the repository. Defaults to "false". forced If true, checkins will occur regardless of the status -that StarTeam is maintaining for the file. If rootlocalfolder is set then -this should be set "true" as otherwise the checkin will be based on statuses -which do not relate to the target folder. Defaults to "false". +that StarTeam is maintaining for the file. If false, checkins will use this status to decide which files to update. Defaults to "false". no unlocked @@ -499,10 +508,24 @@ See also the required common StarTeam parameters.The name to be given to the label yes + + revisionlabel + Yes means that the label attribute is to be saved as a "revision label". No (default) means that it will be saved as a "view label" + no + + + buildlabel + Yes (default) means that the label attribute is to be saved as a "build label". + This means that Change Requests which have an "AddressedIn" field value of "next build" + will have this label assigned to that field when the label is created. + No means that no CRs will have this label assigned to them. This will have no effect if revisionlabel is also true. + no + + lastbuild The timestamp of the build that will be stored with the label. Must be formatted yyyyMMddHHmmss - yes + no description @@ -513,8 +536,8 @@ See also the required common StarTeam parameters.Examples -This example shows the use of this tag. It will create a label named Version 6.2 with -"Thorough description" as its description. +This example shows the use of this tag. It will create a View label that is a build label named Version 6.2 with +"Thorough description" as its description.
   <tstamp>
     <format property="nowstamp" pattern="yyyyMMddHHmmss" locale="en"/>
@@ -527,6 +550,35 @@ This example shows the use of this tag.  It will create a label named Version
            description="Thorough description"
   />
 
+This example creates a non-build View label named Version 6.3 with +"Thorough description" as its description. +
+  <tstamp>
+    <format property="nowstamp" pattern="yyyyMMddHHmmss" locale="en"/>
+  </tstamp>  
+  <stlabel URL="STARTEAM:49201/Aproject/AView"
+           username="auser"
+           password="secret"
+           label="Version 6.3"
+           lastbuild="${nowstamp}"
+           description="Thorough description"
+           buildlabel="false"
+  />
+
+This example will create a Revision label that is a build label named Version 6.2.00.001 with +"revision label" as its description. +
+  <tstamp>
+    <format property="nowstamp" pattern="yyyyMMddHHmmss" locale="en"/>
+  </tstamp>  
+  <stlabel URL="STARTEAM:49201/Aproject/AView"
+           username="auser"
+           password="secret"
+           label="Version 6.2.00.001"
+           description="revision label"
+           revisionlabel="true"
+  />
+

@@ -534,7 +586,7 @@ This example shows the use of this tag. It will create a label named Version

Description

-Produces a listing of the contents of the StarTeam repository at the specified view and StarTeamFolder. The listing will contain the name of the user, if any, who has the file locked, the size of the file, its lastModifiedDate in the repository, and the name of the file. Unless the rootLocalFolder is specified, listing will also show the status of the local file in the default local directory relative to the repository. +Produces a listing of the contents of the StarTeam repository at the specified view and StarTeamFolder. The listing will contain the name of the user, if any, who has the file locked, the size of the file, its lastModifiedDate in the repository, the name of the file, and the status of the local file in the default local directory relative to the repository.

Parameters

See also
the required common StarTeam parameters.

@@ -547,7 +599,8 @@ See also the required common StarTeam parameters. rootstarteamfolder - The root of the subtree in the StarTeam repository to be listed. Defaults to the root folder of the view ('/'). + The root of the subtree in the StarTeam repository to be listed. Defaults to the root folder of the view ('/'). If supplied, this should always be an "absolute" path, that is, it should begin with a '/'. + Relative paths have little meaning in this context and confuse StarTeam. no @@ -576,6 +629,13 @@ See also the required common StarTeam parameters.Indicates if subfolders should be searched for files to list. Defaults to "true". no + + listuncontrolled + if true, any files or folders NOT in StarTeam will be included in the listing. + If false, they won't. Defaults to "true". + no + +

Examples

diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java index c7531c2cb..2cf5850e5 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java @@ -57,6 +57,7 @@ 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; @@ -70,8 +71,6 @@ import org.apache.tools.ant.Project; * 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 Steve Cohen @@ -179,15 +178,50 @@ public class StarTeamCheckin extends TreeBasedTask { /** * 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." ); + } + + } /** @@ -200,129 +234,176 @@ public class StarTeamCheckin extends TreeBasedTask { * @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 - // 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); + if (null != getRootLocalFolder()) { + starteamFolder.setAlternatePathFragment( + targetFolder.getAbsolutePath()); + } - delistLocalFile(localFiles, localFile); + Folder[] foldersList = starteamFolder.getSubFolders(); + Item[] stFiles = starteamFolder.getItems(getTypeNames().FILE); + + // 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); } - // 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); - } + + for (int i = 0, size = stFiles.length; i < size; i++) { + com.starbase.starteam.File stFile = + (com.starbase.starteam.File) stFiles[i]; + processFile( stFile, targetFolder); + + 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 void addUncontrolledItems(Hashtable localFiles, Folder folder) - throws IOException { - try { - Enumeration e = localFiles.keys(); - while (e.hasMoreElements()) { - java.io.File file = - new java.io.File(e.nextElement().toString()); - add(folder, file); + 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) stFilesfiles 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(); + + // If the file doesn't pass the include/exclude tests, skip it. + if (!shouldProcess(filename)) { + log("Excluding " + getFullRepositoryPath(eachFile)); + return; + } + + 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; } - } catch (SecurityException e) { - log("Error adding file: " + e, Project.MSG_ERR); + } + 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(); - - // now visit this new folder to take care of adding any files - // or subfolders within it. - if (isRecursive()) { - visit(newFolder, file); + private class CheckinMap extends UnmatchedFileMap { + protected boolean isActive() { + return StarTeamCheckin.this.addUncontrolled; + } + + + /** + * 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()) { + UnmatchedFileMap submap = + new CheckinMap().init(local, folder); + submap.processUncontrolledItems(); + } + } else { + com.starbase.starteam.File remoteFile = + (com.starbase.starteam.File) remoteItem; + log("Added uncontrolled file " + + TreeBasedTask.getFullRepositoryPath(remoteFile) + + " from " + local.getAbsoluteFile()); + + } + } } - } 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); } } + } + + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java index 7335e3137..3d98daaeb 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java @@ -57,6 +57,7 @@ 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; @@ -171,40 +172,127 @@ public class StarTeamCheckout extends TreeBasedTask { } } + /** + * 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. - * - * @param raw the unconfigured View + * 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 View + * * @return the snapshot View 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.", - Project.MSG_WARN); - } + //intentionally do nothing } + /** + * 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(" Working directories will "+ + (this.createDirs ? "be created as needed." + : "not be created.")); + + } /** * Implements base-class abstract function to perform the checkout * operation on the files in each folder of the tree. @@ -215,91 +303,64 @@ public class StarTeamCheckout extends TreeBasedTask { * @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 (!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); + if (null != getRootLocalFolder()) { + starteamFolder.setAlternatePathFragment( + targetFolder.getAbsolutePath()); } - - // 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); - } + + + Folder[] foldersList = starteamFolder.getSubFolders(); + Item[] filesList = starteamFolder.getItems(getTypeNames().FILE); + + + // 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. + + UnmatchedFileMap ufm = + new CheckoutMap(). + init(targetFolder.getAbsoluteFile(), starteamFolder); + + + + 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()) { + if (!subfolder.exists()) { + if (this.createDirs) { + log("Creating folder: " + subfolder); + subfolder.mkdirs(); + } + } + if (subfolder.exists()) { + visit(stFolder, subfolder); + } + } } + 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) { - deleteUncontrolledItems(localFiles); + ufm.processUncontrolledItems(); } - } catch (IOException e) { throw new BuildException(e); } @@ -307,51 +368,195 @@ public class StarTeamCheckout extends TreeBasedTask { /** - * Deletes everything on the local machine that is not in the repository. - * - * @param localFiles the list of filenames whose elements are to be deleted + * 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 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); + 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); } - /** - * 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. + * Processes (checks out) stFilesfiles 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 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]); - } + 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; } - log("Deleting: " + file.getAbsolutePath(), Project.MSG_INFO); - return file.delete(); - } - - -} - - + if (this.isUsingRevisionLabel()) { + 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 + // 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. + + 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()); + } + 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 (checkout) { + 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; + } + /** + * 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 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(); + while (e.hasMoreElements()) { + java.io.File local = (java.io.File) e.nextElement(); + delete(local); + } + } + } + + /** + * 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. + */ + 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); + } + } + local.delete(); + log("Deleted uncontrolled item " + local.getAbsolutePath()); + } + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java index f68ca013c..1e1ce4777 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java @@ -93,6 +93,19 @@ public class StarTeamLabel extends StarTeamTask { */ 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" @@ -117,6 +130,30 @@ public class StarTeamLabel extends StarTeamTask { this.description = description; } + /** + * 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 yyyyMMddHHmmss @@ -126,7 +163,8 @@ public class StarTeamLabel extends StarTeamTask { 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); } } @@ -137,11 +175,33 @@ public class StarTeamLabel extends StarTeamTask { */ 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); + } } /** @@ -153,7 +213,13 @@ public class StarTeamLabel extends StarTeamTask { * @return the snapshot View appropriately configured. */ protected View createSnapshotView(View raw) { + /* + if (this.revisionlabel) { + return raw; + } return new View(raw, ViewConfiguration.createFromTime(this.lastBuild)); + */ + return raw; } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java index a91e64968..0a12b6d8b 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java @@ -60,8 +60,11 @@ 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 @@ -76,6 +79,7 @@ import org.apache.tools.ant.BuildException; */ 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. @@ -90,7 +94,7 @@ public class StarTeamList extends TreeBasedTask { /** * Override of base-class abstract function creates an * appropriately configured view for checkoutlists - either - * the current view or a view from this.label. + * the current view or a view from this.label. * * @param raw the unconfigured View * @return the snapshot View appropriately configured. @@ -117,6 +121,29 @@ public class StarTeamList extends TreeBasedTask { //intentionally do nothing. } + /** + * 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. @@ -128,26 +155,35 @@ public class StarTeamList extends TreeBasedTask { 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)) { @@ -160,32 +196,40 @@ public class StarTeamList extends TreeBasedTask { // 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 +256,68 @@ public class StarTeamList extends TreeBasedTask { 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(); + } + } + } + } + } + + + } + }// StarTeamList diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java index 669e8dd3e..0d16deeed 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java @@ -53,12 +53,15 @@ */ package org.apache.tools.ant.taskdefs.optional.starteam; +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; /** @@ -112,6 +115,12 @@ public abstract class StarTeamTask extends Task { */ 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. @@ -123,7 +132,7 @@ public abstract class StarTeamTask extends Task { * @param servername a String value * @see #setURL(String) */ - public void setServername(String servername) { + public final void setServername(String servername) { this.servername = servername; } @@ -133,7 +142,7 @@ public abstract class StarTeamTask extends Task { * @return the name of the StarTeam server * @see #getURL() */ - public String getServername() { + public final String getServername() { return this.servername; } @@ -143,7 +152,7 @@ public abstract class StarTeamTask extends Task { * @param serverport port number to be set * @see #setURL(String) */ - public void setServerport(String serverport) { + public final void setServerport(String serverport) { this.serverport = serverport; } @@ -153,7 +162,7 @@ public abstract class StarTeamTask extends Task { * @return the port number of the StarTeam connection * @see #getURL() */ - public String getServerport() { + public final String getServerport() { return this.serverport; } @@ -164,7 +173,7 @@ public abstract class StarTeamTask extends Task { * @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; } @@ -174,7 +183,7 @@ public abstract class StarTeamTask extends Task { * @return the name of the StarTeam project to be acted on * @see #getURL() */ - public String getProjectname() { + public final String getProjectname() { return this.projectname; } @@ -185,7 +194,7 @@ public abstract class StarTeamTask extends Task { * @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; } @@ -195,7 +204,7 @@ public abstract class StarTeamTask extends Task { * @return the name of the StarTeam view to be acted on * @see #getURL() */ - public String getViewname() { + public final String getViewname() { return this.viewname; } @@ -212,7 +221,7 @@ public abstract class StarTeamTask extends Task { * @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(); @@ -246,7 +255,7 @@ public abstract class StarTeamTask extends Task { * @see #getProjectname() * @see #getViewname() */ - public String getURL() { + public final String getURL() { return this.servername + ":" + this.serverport + "/" + @@ -254,12 +263,21 @@ public abstract class StarTeamTask extends Task { ((null == this.viewname) ? "" : this.viewname); } + /** + * 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; } @@ -268,7 +286,7 @@ public abstract class StarTeamTask extends Task { * * @return the name of the StarTeam user */ - public String getUserName() { + public final String getUserName() { return this.userName; } @@ -277,7 +295,7 @@ public abstract class StarTeamTask extends Task { * * @param password the password to be used for login */ - public void setPassword(String password) { + public final void setPassword(String password) { this.password = password; } @@ -286,7 +304,7 @@ public abstract class StarTeamTask extends Task { * * @return the password used for login */ - public String getPassword() { + public final String getPassword() { return this.password; } @@ -296,10 +314,18 @@ public abstract class StarTeamTask extends Task { * * @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 createSnapshotView * defining the kind of configured view appropriate to its task. @@ -307,7 +333,8 @@ public abstract class StarTeamTask extends Task { * @param rawview the unconfigured View * @return the snapshot View 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 @@ -320,10 +347,15 @@ public abstract class StarTeamTask extends Task { * @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() + @@ -342,7 +374,7 @@ public abstract class StarTeamTask extends Task { * @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 ""; diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java index c3d3a8b5f..2ec2cae35 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java @@ -54,13 +54,17 @@ package org.apache.tools.ant.taskdefs.optional.starteam; 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 @@ -114,7 +118,7 @@ public abstract class TreeBasedTask extends StarTeamTask { /** * 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; @@ -148,6 +152,14 @@ public abstract class TreeBasedTask extends StarTeamTask { */ private boolean forced = false; + private Label labelInUse = null; + + /** + * holds a list of local files against which files in the repository + * will be compared for addition on checkins or deletion on checkouts if + * the proper options have been set. + */ + private Hashtable localFileList = new Hashtable(); /////////////////////////////////////////////////////////////// // GET/SET methods. @@ -173,27 +185,36 @@ public abstract class TreeBasedTask extends StarTeamTask { } /** - * Set the local folder that will be the root of the tree + * Set the local folder that will be the root of the tree * to which files are checked out; optional. - * If this is not supplied, then the StarTeam "default folder" + * If this is not supplied, then the StarTeam "default folder" * associated with rootstarteamfolder is used. - * @param rootLocalFolder the local folder that will mirror - * this.rootStarteamFolder + * + * @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 includes patterns; optional. * @param includes A string of filter patterns to include. Separate the @@ -220,6 +241,15 @@ public abstract class TreeBasedTask extends StarTeamTask { return includes; } + /** + * 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 excludes patterns; optional. * When filtering files, AntStarTeamCheckOut @@ -272,6 +302,16 @@ public abstract class TreeBasedTask extends StarTeamTask { return excludes; } + /** + * 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 @@ -287,6 +327,10 @@ public abstract class TreeBasedTask extends StarTeamTask { } } + protected String getLabel() { + return this.label; + } + /** * Get the value of recursive. * @return value of recursive. @@ -324,6 +368,40 @@ public abstract class TreeBasedTask extends StarTeamTask { 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(); + } + + /** + * 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 /////////////////////////////////////////////////////////////// @@ -332,8 +410,10 @@ public abstract class TreeBasedTask extends StarTeamTask { * 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. + * + * @param pName the item name to look for being included. + * + * @return whether the file should be processed or not. */ protected boolean shouldProcess(String pName) { boolean includeIt = matchPatterns(getIncludes(), pName); @@ -362,49 +442,159 @@ public abstract class TreeBasedTask extends StarTeamTask { } /** - * This method does the work of opening the supplied Starteam view and - * calling the visit() method to perform the task. - * - * @exception BuildException if any error occurs in the processing - * @see visit() + * gets a url representing the root of the view. + * + * @return a url corresponding to the root of the starteam view tree, useful for + * starteam lookups */ + private final String getRootStarteamURL() { + StringBuffer buf = new StringBuffer(getViewURL()); + if (!this.rootStarteamFolder.startsWith("/")) { + buf.append("/"); + } + buf.append(this.rootStarteamFolder); + return buf.toString(); - public void execute() throws BuildException { - try { - testPreconditions(); + } + /** + * Finds and opens the root starteam folder of the operation specified + * by this task. This will be one of the following cases: + * + * @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 { + // 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); - if (null == starteamrootfolder) { - throw new BuildException( - "Unable to find root folder in repository."); - } + } + 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 " + 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 + * rootLocalFolder is defined. + * If rootLocalFolder 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. + 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 visit() method to perform the task. + * Derived classes can customize the called methods + * testPreconditions() and visit(). + * + * @exception BuildException if any error occurs in the processing + * @see testPreconditions() + * @see visit() + */ + + public final void execute() throws BuildException { + try { + testPreconditions(); + + Folder starteamrootfolder = configureRootStarteamFolder(); // 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); - } + java.io.File localrootfolder = + getLocalRootMapping(starteamrootfolder); + // 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. @@ -416,32 +606,37 @@ public abstract class TreeBasedTask extends StarTeamTask { */ 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"); + findLabel(v); + return this.labelInUse.getID(); } return -1; } + 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 @@ -454,46 +649,114 @@ public abstract class TreeBasedTask extends StarTeamTask { */ 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. + * 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. */ - protected static Hashtable listLocalFiles(java.io.File localFolder) { + public static String getFullRepositoryPath( + com.starbase.starteam.File remotefile) + { + StringBuffer sb = new StringBuffer(); + sb.append(remotefile.getParentFolderHierarchy()) + .append(remotefile.getName()); + return sb.toString(); + } - 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], ""); + + for (int i=0; i < localFiles.length; 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 this; } - return localFileList; + + /** + * remove an item found to be controlled from the map. + * + * @param localFile the local item found to be controlled. + */ + 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(); + } - /** - * 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 Hashtable 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()); - } } - -