KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > starteam > StarTeamCheckout


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs.optional.starteam;
19
20 import com.starbase.starteam.Folder;
21 import com.starbase.starteam.Item;
22 import com.starbase.starteam.Status;
23 import com.starbase.starteam.View;
24 import com.starbase.starteam.ViewConfiguration;
25 import java.io.IOException JavaDoc;
26 import java.io.File JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import org.apache.tools.ant.BuildException;
30 import org.apache.tools.ant.Project;
31
32
33 /**
34  * Checks out files from a StarTeam project.
35  * It also creates all working directories on the
36  * local directory if appropriate. Ant Usage:
37  * <pre>
38  * &lt;taskdef name="starteamcheckout"
39  * classname="org.apache.tools.ant.taskdefs.StarTeamCheckout"/&gt;
40  * &lt;starteamcheckout username="BuildMaster" password="ant" starteamFolder="Source"
41  * starteamurl="servername:portnum/project/view"
42  * createworkingdirectories="true"/&gt;
43  * </pre>
44  *
45  * @version 1.1
46  * @see <a HREF="http://www.borland.com/us/products/starteam/index.html"
47  * >borland StarTeam Web Site</a>
48  *
49  * @ant.task name="stcheckout" category="scm"
50  */

51 public class StarTeamCheckout extends TreeBasedTask {
52
53     /**
54      * holder for the createDirs attribute
55      */

56     private boolean createDirs = true;
57
58     /**
59      * holder for the deleteUncontrolled attribute. If true,
60      * all local files not in StarTeam will be deleted.
61      */

62     private boolean deleteUncontrolled = true;
63
64     /**
65      * holder for the deleteUncontrolled attribute. If true,
66      * (default) local non-binary files will be checked out using the local
67      * platform's EOL convention. If false, checkouts will preserve the
68      * server's EOL convention.
69      */

70     private boolean convertEOL = true;
71
72
73     /**
74      * flag (defaults to true) to create all directories
75      * that are in the Starteam repository even if they are empty.
76      *
77      * @param value the value to set the attribute to.
78      */

79     public void setCreateWorkingDirs(boolean value) {
80         this.createDirs = value;
81     }
82
83     /**
84      * Whether or not all local files <i>not<i> in StarTeam should be deleted.
85      * Optional, defaults to <code>true</code>.
86      * @param value the value to set the attribute to.
87      */

88     public void setDeleteUncontrolled(boolean value) {
89         this.deleteUncontrolled = value;
90     }
91
92     /**
93      * Set whether or not files should be checked out using the
94      * local machine's EOL convention.
95      * Optional, defaults to <code>true</code>.
96      * @param value the value to set the attribute to.
97      */

98     public void setConvertEOL(boolean value) {
99         this.convertEOL = value;
100     }
101
102     /**
103      * Sets the label StarTeam is to use for checkout; defaults to the most recent file.
104      * The label must exist in starteam or an exception will be thrown.
105      * @param label the label to be used
106      */

107     public void setLabel(String JavaDoc label) {
108         _setLabel(label);
109     }
110
111     /**
112      * This attribute tells whether to do a locked checkout, an unlocked
113      * checkout or to leave the checkout status alone (default). A locked
114      * checkout locks all other users out from making changes. An unlocked
115      * checkout reverts all local files to their previous repository status
116      * and removes the lock.
117      * @see #setLocked(boolean)
118      * @see #setUnlocked(boolean)
119      */

120     private int lockStatus = Item.LockType.UNCHANGED;
121
122     /**
123      * Set to do a locked checkout; optional default is false.
124      * @param v True to do a locked checkout, false to checkout without
125      * changing status/.
126      * @exception BuildException if both locked and unlocked are set true
127      */

128     public void setLocked(boolean v) throws BuildException {
129         setLockStatus(v, Item.LockType.EXCLUSIVE);
130     }
131
132
133     /**
134      * Set to do an unlocked checkout. Default is false;
135      * @param v True to do an unlocked checkout, false to checkout without
136      * changing status.
137      * @exception BuildException if both locked and unlocked are set true
138      */

139     public void setUnlocked(boolean v) throws BuildException {
140         setLockStatus(v, Item.LockType.UNLOCKED);
141     }
142
143     private void setLockStatus(boolean v, int newStatus)
144             throws BuildException {
145         if (v) {
146             if (this.lockStatus == Item.LockType.UNCHANGED) {
147                 this.lockStatus = newStatus;
148             } else if (this.lockStatus != newStatus) {
149                 throw new BuildException(
150                         "Error: cannot set locked and unlocked both true.");
151             }
152         }
153     }
154
155     /**
156      * should checked out files get the timestamp from the repository
157      * or the time they are checked out. True means use the repository
158      * timestamp.
159      */

160     private boolean useRepositoryTimeStamp = false;
161
162     /**
163      * sets the useRepositoryTimestmp member.
164      *
165      * @param useRepositoryTimeStamp
166      * true means checked out files will get the repository timestamp.
167      * false means the checked out files will be timestamped at the time
168      * of checkout.
169      */

170     public void setUseRepositoryTimeStamp(boolean useRepositoryTimeStamp) {
171         this.useRepositoryTimeStamp = useRepositoryTimeStamp;
172     }
173
174     /**
175      * returns the value of the useRepositoryTimestamp member
176      *
177      * @return the value of the useRepositoryTimestamp member
178      */

179     public boolean getUseRepositoryTimeStamp() {
180         return this.useRepositoryTimeStamp;
181     }
182
183     /**
184      * List files, dates, and statuses as of this date; optional.
185      * If not specified, the most recent version of each file will be listed.
186      *
187      * @param asOfDateParam the date as of which the listing to be made
188      * @since Ant 1.6
189      */

190     public void setAsOfDate(String JavaDoc asOfDateParam) {
191         _setAsOfDate(asOfDateParam);
192     }
193
194     /**
195      * Date Format with which asOfDate parameter to be parsed; optional.
196      * Must be a SimpleDateFormat compatible string.
197      * If not specified, and asOfDateParam is specified, parse will use ISO8601
198      * datetime and date formats.
199      *
200      * @param asOfDateFormat the SimpleDateFormat-compatible format string
201      * @since Ant 1.6
202      */

203     public void setAsOfDateFormat(String JavaDoc asOfDateFormat) {
204         _setAsOfDateFormat(asOfDateFormat);
205     }
206
207     /**
208      * Override of base-class abstract function creates an
209      * appropriately configured view for checkouts - either
210      * the current view or a view from this.label or the raw
211      * view itself in the case of a revision label.
212      *
213      * @param raw the unconfigured <code>View</code>
214      *
215      * @return the snapshot <code>View</code> appropriately configured.
216      * @exception BuildException on error
217      */

218     protected View createSnapshotView(View raw) throws BuildException {
219
220         int labelID = getLabelID(raw);
221
222         // if a label has been supplied and it is a view label, use it
223
// to configure the view
224
if (this.isUsingViewLabel()) {
225             return new View(raw, ViewConfiguration.createFromLabel(labelID));
226         // if a label has been supplied and it is a revision label, use the raw
227
// the view as the snapshot
228
} else if (this.isUsingRevisionLabel()) {
229             return raw;
230         }
231         // if a date has been supplied use a view configured to the date.
232
View view = getViewConfiguredByDate(raw);
233         if (view != null) {
234             return view;
235         // otherwise, use this view configured as the tip.
236
} else {
237             return new View(raw, ViewConfiguration.createTip());
238         }
239     }
240
241
242     /**
243      * Implements base-class abstract function to define tests for
244      * any preconditons required by the task.
245      *
246      * @exception BuildException thrown if both rootLocalFolder
247      * and viewRootLocalFolder are defined
248      */

249     protected void testPreconditions() throws BuildException {
250         if (this.isUsingRevisionLabel() && this.createDirs) {
251             log("Ignoring createworkingdirs while using a revision label."
252                 + " Folders will be created only as needed.",
253                 Project.MSG_WARN);
254             this.createDirs = false;
255         }
256         if (lockStatus != Item.LockType.UNCHANGED) {
257             boolean lockStatusBad = false;
258             if (null != getLabel()) {
259                 log("Neither locked nor unlocked may be true"
260                     + " when checking out a labeled version.",
261                     Project.MSG_ERR);
262                 lockStatusBad = true;
263             } else if (null != getAsOfDate()) {
264                 log("Neither locked nor unlocked may be true"
265                     + " when checking out by date.",
266                     Project.MSG_ERR);
267                 lockStatusBad = true;
268             }
269             if (lockStatusBad) {
270                 throw new BuildException(
271                     "Lock status may not be changed"
272                     + " when checking out a non-current version.");
273             }
274         }
275         if (null != getLabel() && null != getAsOfDate()) {
276             throw new BuildException(
277                 "Both label and asOfDate specified. "
278                 + "Unable to process request.");
279         }
280
281     }
282
283     /**
284      * extenders should emit to the log an entry describing the parameters
285      * that will be used by this operation.
286      *
287      * @param starteamrootFolder
288      * root folder in StarTeam for the operation
289      * @param targetrootFolder
290      * root local folder for the operation (whether specified
291      * by the user or not.
292      */

293
294     protected void logOperationDescription(
295         Folder starteamrootFolder, java.io.File JavaDoc targetrootFolder) {
296         log((this.isRecursive() ? "Recursive" : "Non-recursive")
297             + " Checkout from: " + starteamrootFolder.getFolderHierarchy());
298
299         log(" Checking out to"
300             + (null == getRootLocalFolder() ? "(default): " : ": ")
301             + targetrootFolder.getAbsolutePath());
302
303
304         logLabel();
305         logAsOfDate();
306         logIncludes();
307         logExcludes();
308
309         if (this.lockStatus == Item.LockType.EXCLUSIVE) {
310             log(" Items will be checked out with Exclusive locks.");
311         } else if (this.lockStatus == Item.LockType.UNLOCKED) {
312             log(" Items will be checked out unlocked "
313                  + "(even if presently locked).");
314         } else {
315             log(" Items will be checked out with no change in lock status.");
316         }
317         log(" Items will be checked out with "
318             + (this.useRepositoryTimeStamp ? "repository timestamps."
319                                         : "the current timestamp."));
320         log(" Items will be checked out "
321             + (this.isForced() ? "regardless of" : "in accordance with")
322             + " repository status.");
323         if (this.deleteUncontrolled) {
324             log(" Local items not found in the repository will be deleted.");
325         }
326         log(" Items will be checked out "
327             + (this.convertEOL ? "using the local machine's EOL convention"
328              : "without changing the EOL convention used on the server"));
329         log(" Directories will be created"
330             + (this.createDirs ? " wherever they exist in the repository, even if empty."
331              : " only where needed to check out files."));
332
333     }
334
335
336     /**
337      * Implements base-class abstract function to perform the checkout
338      * operation on the files in each folder of the tree.
339      *
340      * @param starteamFolder the StarTeam folder from which files to be
341      * checked out
342      * @param targetFolder the local mapping of rootStarteamFolder
343      * @exception BuildException if any error occurs
344      */

345     protected void visit(Folder starteamFolder, java.io.File JavaDoc targetFolder)
346             throws BuildException {
347         try {
348
349
350             if (null != getRootLocalFolder()) {
351                 starteamFolder.setAlternatePathFragment(
352                     targetFolder.getAbsolutePath());
353             }
354
355             if (!targetFolder.exists()) {
356                 if (!this.isUsingRevisionLabel()) {
357                     if (this.createDirs) {
358                         if (targetFolder.mkdirs()) {
359                             log("Creating folder: " + targetFolder);
360                         } else {
361                             throw new BuildException(
362                                 "Failed to create local folder " + targetFolder);
363                         }
364                     }
365                 }
366             }
367
368
369             Folder[] foldersList = starteamFolder.getSubFolders();
370             Item[] filesList = starteamFolder.getItems(getTypeNames().FILE);
371
372             if (this.isUsingRevisionLabel()) {
373
374                 // prune away any files not belonging to the revision label
375
// this is one ugly API from Starteam SDK
376

377                 Hashtable JavaDoc labelItems = new Hashtable JavaDoc(filesList.length);
378                 int s = filesList.length;
379                 int[] ids = new int[s];
380                 for (int i = 0; i < s; i++) {
381                     ids[i] = filesList[i].getItemID();
382                     labelItems.put(new Integer JavaDoc(ids[i]), new Integer JavaDoc(i));
383                 }
384                 int[] foundIds = getLabelInUse().getLabeledItemIDs(ids);
385                 s = foundIds.length;
386                 Item[] labeledFiles = new Item[s];
387                 for (int i = 0; i < s; i++) {
388                     Integer JavaDoc id = new Integer JavaDoc(foundIds[i]);
389                     labeledFiles[i] =
390                         filesList[((Integer JavaDoc) labelItems.get(id)).intValue()];
391                 }
392                 filesList = labeledFiles;
393             }
394
395
396             // note, it's important to scan the items BEFORE we make the
397
// Unmatched file map because that creates a bunch of NEW
398
// folders and files (unattached to repository) and we
399
// don't want to include those in our traversal.
400

401             UnmatchedFileMap ufm =
402                 new CheckoutMap().
403                     init(targetFolder.getAbsoluteFile(), starteamFolder);
404
405
406
407             for (int i = 0; i < foldersList.length; i++) {
408                 Folder stFolder = foldersList[i];
409
410                 java.io.File JavaDoc subfolder =
411                      new java.io.File JavaDoc(targetFolder, stFolder.getName());
412
413                  ufm.removeControlledItem(subfolder);
414
415                  if (isRecursive()) {
416                          visit(stFolder, subfolder);
417                      }
418                  }
419
420             for (int i = 0; i < filesList.length; i++) {
421                 com.starbase.starteam.File stFile =
422                     (com.starbase.starteam.File) filesList[i];
423                 processFile(stFile, targetFolder);
424
425                 ufm.removeControlledItem(
426                     new java.io.File JavaDoc(targetFolder, stFile.getName()));
427             }
428             if (this.deleteUncontrolled) {
429                 ufm.processUncontrolledItems();
430             }
431         } catch (IOException JavaDoc e) {
432             throw new BuildException(e);
433         }
434     }
435
436
437     /**
438      * provides a string showing from and to full paths for logging
439      *
440      * @param remotefile the Star Team file being processed.
441      *
442      * @return a string showing from and to full paths
443      */

444     private String JavaDoc describeCheckout(com.starbase.starteam.File remotefile,
445                                     java.io.File JavaDoc localFile) {
446         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
447         sb.append(getFullRepositoryPath(remotefile))
448           .append(" --> ");
449         if (null == localFile) {
450             sb.append(remotefile.getFullName());
451         } else {
452             sb.append(localFile);
453         }
454         return sb.toString();
455     }
456     private String JavaDoc describeCheckout(com.starbase.starteam.File remotefile) {
457         return describeCheckout(remotefile, null);
458     }
459     /**
460      * Processes (checks out) <code>stFiles</code>files from StarTeam folder.
461      *
462      * @param eachFile repository file to process
463      * @param targetFolder a java.io.File (Folder) to work
464      * @throws IOException when StarTeam API fails to work with files
465      */

466     private void processFile(com.starbase.starteam.File eachFile,
467                              File JavaDoc targetFolder)
468     throws IOException JavaDoc {
469         String JavaDoc filename = eachFile.getName();
470
471         java.io.File JavaDoc localFile = new java.io.File JavaDoc(targetFolder, filename);
472
473         // If the file doesn't pass the include/exclude tests, skip it.
474
if (!shouldProcess(filename)) {
475             log("Excluding " + getFullRepositoryPath(eachFile),
476                 Project.MSG_INFO);
477                 return;
478         }
479
480         if (this.isUsingRevisionLabel()) {
481             if (!targetFolder.exists()) {
482                 if (targetFolder.mkdirs()) {
483                     log("Creating folder: " + targetFolder);
484                 } else {
485                     throw new BuildException(
486                         "Failed to create local folder " + targetFolder);
487                 }
488             }
489             boolean success = eachFile.checkoutByLabelID(
490                 localFile,
491                 getIDofLabelInUse(),
492                 this.lockStatus,
493                 !this.useRepositoryTimeStamp,
494                 true,
495                 false);
496             if (success) {
497                 log("Checked out " + describeCheckout(eachFile, localFile));
498             }
499         } else {
500             boolean checkout = true;
501
502             // Just a note: StarTeam has a status for NEW which implies
503
// that there is an item on your local machine that is not
504
// in the repository. These are the items that show up as
505
// NOT IN VIEW in the Starteam GUI.
506
// One would think that we would want to perhaps checkin the
507
// NEW items (not in all cases! - Steve Cohen 15 Dec 2001)
508
// Unfortunately, the sdk doesn't really work, and we can't
509
// actually see anything with a status of NEW. That is why
510
// we can just check out everything here without worrying
511
// about losing anything.
512

513             int fileStatus = (eachFile.getStatus());
514
515             // We try to update the status once to give StarTeam
516
// another chance.
517

518             if (fileStatus == Status.MERGE
519                 || fileStatus == Status.UNKNOWN) {
520                 eachFile.updateStatus(true, true);
521                 fileStatus = (eachFile.getStatus());
522             }
523
524             log(eachFile.toString() + " has status of "
525                 + Status.name(fileStatus), Project.MSG_DEBUG);
526
527
528             switch (fileStatus) {
529             case Status.OUTOFDATE:
530             case Status.MISSING:
531                 log("Checking out: " + describeCheckout(eachFile));
532                 break;
533             default:
534                 if (isForced() && fileStatus != Status.CURRENT) {
535                     log("Forced checkout of "
536                         + describeCheckout(eachFile)
537                         + " over status " + Status.name(fileStatus));
538                 } else {
539                     log("Skipping: " + getFullRepositoryPath(eachFile)
540                         + " - status: " + Status.name(fileStatus));
541                     checkout = false;
542                 }
543             }
544
545             if (checkout) {
546                 if (!targetFolder.exists()) {
547                     if (targetFolder.mkdirs()) {
548                         log("Creating folder: " + targetFolder);
549                     } else {
550                         throw new BuildException(
551                             "Failed to create local folder " + targetFolder);
552                     }
553                 }
554                 eachFile.checkout(this.lockStatus,
555                                  !this.useRepositoryTimeStamp, this.convertEOL, false);
556             }
557         }
558     }
559     /**
560      * handles the deletion of uncontrolled items
561      */

562     private class CheckoutMap extends UnmatchedFileMap {
563         protected boolean isActive() {
564             return StarTeamCheckout.this.deleteUncontrolled;
565         }
566
567         /**
568          * override of the base class init. It can be much simpler, since
569          * the action to be taken is simply to delete the local files. No
570          * further interaction with the repository is necessary.
571          *
572          * @param localFolder
573          * the local folder from which the mappings will be made.
574          * @param remoteFolder
575          * not used in this implementation
576          */

577         UnmatchedFileMap init(java.io.File JavaDoc localFolder, Folder remoteFolder) {
578             if (!localFolder.exists()) {
579                 return this;
580             }
581
582             String JavaDoc[] localFiles = localFolder.list();
583             // PR 31965 says that it can return null
584
if (localFiles == null) {
585                 return this;
586             }
587             for (int i = 0; i < localFiles.length; i++) {
588                 java.io.File JavaDoc localFile =
589                     new java.io.File JavaDoc(localFolder, localFiles[i]).getAbsoluteFile();
590
591                 log("adding " + localFile + " to UnmatchedFileMap",
592                     Project.MSG_DEBUG);
593
594                 if (localFile.isDirectory()) {
595                     this.put(localFile, "");
596                 } else {
597                     this.put(localFile, "");
598                 }
599             }
600             return this;
601         }
602
603
604
605         /**
606          * deletes uncontrolled items from the local tree. It is assumed
607          * that this method will not be called until all the items in the
608          * corresponding folder have been processed, and that the internal map
609          * will contain only uncontrolled items.
610          */

611         void processUncontrolledItems() throws BuildException {
612             if (this.isActive()) {
613                 Enumeration JavaDoc e = this.keys();
614                 while (e.hasMoreElements()) {
615                     java.io.File JavaDoc local = (java.io.File JavaDoc) e.nextElement();
616                     delete(local);
617                 }
618             }
619         }
620
621         /**
622          * deletes all files and if the file is a folder recursively deletes
623          * everything in it.
624          *
625          * @param local The local file or folder to be deleted.
626          */

627         void delete(java.io.File JavaDoc local) {
628             // once we find a folder that isn't in the repository,
629
// anything below it can be deleted.
630
if (local.isDirectory() && isRecursive()) {
631                 String JavaDoc[] contents = local.list();
632                 for (int i = 0; i < contents.length; i++) {
633                     java.io.File JavaDoc file = new java.io.File JavaDoc(local, contents[i]);
634                     delete(file);
635                 }
636             }
637             local.delete();
638             log("Deleted uncontrolled item " + local.getAbsolutePath());
639         }
640     }
641
642
643 }
644
Popular Tags