001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.taskdefs.optional.starteam;
019:
020: import com.starbase.starteam.Folder;
021: import com.starbase.starteam.Label;
022: import com.starbase.starteam.PropertyNames;
023: import com.starbase.starteam.StarTeamFinder;
024: import com.starbase.starteam.View;
025: import com.starbase.starteam.ViewConfiguration;
026: import com.starbase.util.OLEDate;
027: import java.text.ParseException;
028: import java.text.SimpleDateFormat;
029: import java.util.Date;
030: import java.util.Hashtable;
031: import java.util.StringTokenizer;
032: import org.apache.tools.ant.BuildException;
033: import org.apache.tools.ant.util.DateUtils;
034: import org.apache.tools.ant.DirectoryScanner;
035: import org.apache.tools.ant.Project;
036:
037: /**
038: * TreeBasedTask.java
039: * This abstract class is the base for any tasks that are tree-based, that
040: * is, for tasks which iterate over a tree of folders in StarTeam which
041: * is reflected in a tree of folder the local machine.
042: *
043: * This class provides the tree-iteration functionality. Derived classes
044: * will implement their specific task functionality by the visitor pattern,
045: * specifically by implementing the method
046: * <code>visit(Folder rootStarteamFolder, java.io.File rootLocalFolder)</code>
047: *
048: * Created: Sat Dec 15 16:55:19 2001
049: *
050: * @see <a href="http://www.borland.com/us/products/starteam/index.html"
051: * >borland StarTeam Web Site</a>
052: */
053:
054: public abstract class TreeBasedTask extends StarTeamTask {
055:
056: ///////////////////////////////////////////////////////////////
057: // default values for attributes.
058: ///////////////////////////////////////////////////////////////
059: /**
060: * This constant sets the filter to include all files. This default has
061: * the same result as <code>setIncludes("*")</code>.
062: *
063: * @see #getIncludes()
064: * @see #setIncludes(String includes)
065: */
066: public static final String DEFAULT_INCLUDESETTING = "*";
067:
068: /**
069: * This disables the exclude filter by default. In other words, no files
070: * are excluded. This setting is equivalent to
071: * <code>setExcludes(null)</code>.
072: *
073: * @see #getExcludes()
074: * @see #setExcludes(String excludes)
075: */
076: public static final String DEFAULT_EXCLUDESETTING = null;
077:
078: //ATTRIBUTES settable from ant.
079:
080: /**
081: * The root folder of the operation in StarTeam.
082: */
083: private String rootStarteamFolder = "/";
084:
085: /**
086: * The local folder corresponding to starteamFolder. If not specified
087: * the Star Team default folder will be used.
088: */
089: private String rootLocalFolder = null;
090:
091: /**
092: * All files that fit this pattern are checked out.
093: */
094: private String includes = DEFAULT_INCLUDESETTING;
095:
096: /**
097: * All files fitting this pattern are ignored.
098: */
099: private String excludes = DEFAULT_EXCLUDESETTING;
100:
101: /**
102: * StarTeam label on which to perform task.
103: */
104: private String label = null;
105:
106: /**
107: * Set recursion to false to check out files in only the given folder
108: * and not in its subfolders.
109: */
110: private boolean recursive = true;
111:
112: /**
113: * Set preloadFileInformation to true to load all file information from the server
114: * at once. Increases performance significantly for projects with many files and/or folders.
115: */
116: private boolean preloadFileInformation = true;
117:
118: /**
119: * If forced set to true, files in the target directory will
120: * be processed regardless of status in the repository.
121: * Usually this should be true if rootlocalfolder is set
122: * because status will be relative to the default folder, not
123: * to the one being processed.
124: */
125: private boolean forced = false;
126:
127: private Label labelInUse = null;
128:
129: /**
130: * holder for the asofdate attribute
131: */
132: private String asOfDate = null;
133:
134: /**
135: * holder for the asofdateformat attribute
136: */
137: private String asOfDateFormat = null;
138:
139: ///////////////////////////////////////////////////////////////
140: // GET/SET methods.
141: // Setters, of course are where ant user passes in values.
142: ///////////////////////////////////////////////////////////////
143:
144: /**
145: * Set the root of the subtree in the StarTeam repository from which to
146: * work; optional. Defaults to the root folder of the view ('/').
147: * @param rootStarteamFolder the root folder
148: */
149: public void setRootStarteamFolder(String rootStarteamFolder) {
150: this .rootStarteamFolder = rootStarteamFolder;
151: }
152:
153: /**
154: * returns the root folder in the Starteam repository
155: * used for this operation
156: * @return the root folder in use
157: */
158: public String getRootStarteamFolder() {
159: return this .rootStarteamFolder;
160: }
161:
162: /**
163: * Set the local folder that will be the root of the tree
164: * to which files are checked out; optional.
165: * If this is not supplied, then the StarTeam "default folder"
166: * associated with <tt>rootstarteamfolder</tt> is used.
167: *
168: * @param rootLocalFolder
169: * the local folder that will mirror
170: * this.rootStarteamFolder
171: */
172: public void setRootLocalFolder(String rootLocalFolder) {
173: this .rootLocalFolder = rootLocalFolder;
174: }
175:
176: /**
177: * Returns the local folder specified by the user,
178: * corresponding to the starteam folder for this operation
179: * or null if not specified.
180: *
181: * @return the local folder that mirrors this.rootStarteamFolder
182: */
183: public String getRootLocalFolder() {
184: return this .rootLocalFolder;
185: }
186:
187: /**
188: * Declare files to include using standard <tt>includes</tt> patterns; optional.
189: * @param includes A string of filter patterns to include. Separate the
190: * patterns by spaces.
191: * @see #getIncludes()
192: * @see #setExcludes(String excludes)
193: * @see #getExcludes()
194: */
195: public void setIncludes(String includes) {
196: this .includes = includes;
197: }
198:
199: /**
200: * Gets the patterns from the include filter. Rather that duplicate the
201: * details of AntStarTeamCheckOut's filtering here, refer to these
202: * links:
203: *
204: * @return A string of filter patterns separated by spaces.
205: * @see #setIncludes(String includes)
206: * @see #setExcludes(String excludes)
207: * @see #getExcludes()
208: */
209: public String getIncludes() {
210: return includes;
211: }
212:
213: /**
214: * if excludes have been specified, emit the list to the log
215: */
216: protected void logIncludes() {
217: if (DEFAULT_INCLUDESETTING != this .includes) {
218: log(" Includes specified: " + this .includes);
219: }
220: }
221:
222: /**
223: * Declare files to exclude using standard <tt>excludes</tt> patterns; optional.
224: * When filtering files, AntStarTeamCheckOut
225: * uses an unmodified version of <code>DirectoryScanner</code>'s
226: * <code>match</code> method, so here are the patterns straight from the
227: * Ant source code:
228: * <br/>
229: * Matches a string against a pattern. The pattern contains two special
230: * characters:
231: * <br/>'*' which means zero or more characters,
232: * <br/>'?' which means one and only one character.
233: * <br/>
234: * For example, if you want to check out all files except .XML and
235: * .HTML files, you would put the following line in your program:
236: * <code>setExcludes("*.XML,*.HTML");</code>
237: * Finally, note that filters have no effect on the <b>directories</b>
238: * that are scanned; you could not skip over all files in directories
239: * whose names begin with "project," for instance.
240: * <br/>
241: * Treatment of overlapping inlcudes and excludes: To give a simplistic
242: * example suppose that you set your include filter to "*.htm *.html"
243: * and your exclude filter to "index.*". What happens to index.html?
244: * AntStarTeamCheckOut will not check out index.html, as it matches an
245: * exclude filter ("index.*"), even though it matches the include
246: * filter, as well.
247: * <br/>
248: * Please also read the following sections before using filters:
249: *
250: * @param excludes A string of filter patterns to exclude. Separate the
251: * patterns by spaces.
252: * @see #setIncludes(String includes)
253: * @see #getIncludes()
254: * @see #getExcludes()
255: */
256: public void setExcludes(String excludes) {
257: this .excludes = excludes;
258: }
259:
260: /**
261: * Gets the patterns from the exclude filter. Rather that duplicate the
262: * details of AntStarTeanCheckOut's filtering here, refer to these
263: * links:
264: *
265: * @return A string of filter patterns separated by spaces.
266: * @see #setExcludes(String excludes)
267: * @see #setIncludes(String includes)
268: * @see #getIncludes()
269: */
270: public String getExcludes() {
271: return excludes;
272: }
273:
274: /**
275: * if excludes have been specified, emit the list to the log
276: */
277: protected void logExcludes() {
278: if (DEFAULT_EXCLUDESETTING != this .excludes) {
279: log(" Excludes specified: " + this .excludes);
280: }
281: }
282:
283: // CheckStyle:MethodNameCheck OFF - bc
284:
285: /**
286: * protected function to allow subclasses to set the label (or not).
287: * sets the StarTeam label
288: *
289: * @param label name of the StarTeam label to be set
290: */
291: protected void _setLabel(String label) {
292: if (null != label) {
293: label = label.trim();
294: if (label.length() > 0) {
295: this .label = label;
296: }
297: }
298: }
299:
300: /**
301: * non-public method callable only by derived classes that implement
302: * setAsOfDate (so that derived tasks that do not accept this
303: * parameter will fail if user attempts to use it.
304: *
305: * @param asOfDate asOfDate entered by user.
306: * @since Ant 1.6
307: */
308: protected void _setAsOfDate(String asOfDate) {
309: if (asOfDate != null && asOfDate.length() > 0) {
310: this .asOfDate = asOfDate;
311: }
312: }
313:
314: /**
315: * non-public method callable only by derived classes that implement
316: * setAsOfDateFormat (so that derived tasks that do not accept this
317: * parameter will fail if user attempts to use it.
318: *
319: * @param asOfDateFormat asOfDate format entered by user.
320: * @since Ant 1.6
321: */
322: protected void _setAsOfDateFormat(String asOfDateFormat) {
323: if (asOfDateFormat != null && asOfDateFormat.length() > 0) {
324: this .asOfDateFormat = asOfDateFormat;
325: }
326: }
327:
328: // CheckStyle:VisibilityModifier ON
329:
330: /**
331: * return the asOfDate entered by the user for internal use by derived
332: * classes.
333: *
334: * @return the asOfDate entered by the user
335: * @since Ant 1.6
336: */
337: protected String getAsOfDate() {
338: return this .asOfDate;
339: }
340:
341: /**
342: * If an asofDate parameter has been supplied by the user return a
343: * StarTeam view based on the configuration of the StarTeam view
344: * specified the user as of the date specified in the parameter.
345: * If no asofDate has been specified, return null.
346: *
347: * This method is meant to be called from within implementations of the
348: * <code>createSnapshotView</code> abstract method.
349: *
350: * @param raw the raw view to be configured as of the supplied date
351: *
352: * @return the view as configured.
353: * @exception BuildException
354: * thrown if the date is not parsable by the default or
355: * supplied format patterns.
356: * @since Ant 1.6
357: */
358: protected View getViewConfiguredByDate(View raw)
359: throws BuildException {
360: if (this .asOfDate == null) {
361: return null;
362: }
363: Date asOfDate = null;
364: SimpleDateFormat fmt = null;
365: if (this .asOfDateFormat != null) {
366: fmt = new SimpleDateFormat(this .asOfDateFormat);
367: try {
368: asOfDate = fmt.parse(this .asOfDate);
369: } catch (ParseException px) {
370: throw new BuildException("AsOfDate " + this .asOfDate
371: + " not parsable by supplied format "
372: + this .asOfDateFormat);
373: }
374: } else {
375: try {
376: asOfDate = DateUtils
377: .parseIso8601DateTimeOrDate(this .asOfDate);
378: } catch (ParseException px) {
379: throw new BuildException("AsOfDate " + this .asOfDate
380: + " not parsable by default"
381: + " ISO8601 formats");
382: }
383: }
384: return new View(raw, ViewConfiguration
385: .createFromTime(new OLEDate(asOfDate)));
386: }
387:
388: /**
389: * return the label passed to the task by the user as a string
390: *
391: * @return the label passed to the task by the user as a string
392: */
393: protected String getLabel() {
394: return this .label;
395: }
396:
397: /**
398: * Get the value of recursive.
399: * @return value of recursive.
400: */
401: public boolean isRecursive() {
402: return this .recursive;
403: }
404:
405: /**
406: * Flag to set to include files in subfolders in the operation; optional,
407: * default true.
408: * @param v Value to assign to recursive.
409: */
410: public void setRecursive(boolean v) {
411: this .recursive = v;
412: }
413:
414: /**
415: * Get the value of preloadFileInformation.
416: * @return value of preloadFileInformation.
417: */
418: public boolean isPreloadFileInformation() {
419: return this .preloadFileInformation;
420: }
421:
422: /**
423: * Flag to set to preload file information from the server; optional,
424: * default true.
425: * Increases performance significantly for projects with many files
426: * and/or folders.
427: * @param v Value to assign to preloadFileInformation.
428: */
429: public void setPreloadFileInformation(boolean v) {
430: this .preloadFileInformation = v;
431: }
432:
433: /**
434: * Get the value of forced.
435: * @return value of forced.
436: */
437: public boolean isForced() {
438: return this .forced;
439: }
440:
441: /**
442: * Flag to force actions regardless of the status
443: * that StarTeam is maintaining for the file; optional, default false.
444: * If <tt>rootlocalfolder</tt> is set then
445: * this should be set "true" as otherwise the checkout will be based on statuses
446: * which do not relate to the target folder.
447: * @param v Value to assign to forced.
448: */
449: public void setForced(boolean v) {
450: this .forced = v;
451: }
452:
453: /**
454: * returns true if a label has been specified and it is a view label.
455: *
456: * @return true if a label has been specified and it is a view label
457: */
458: protected boolean isUsingViewLabel() {
459: return null != this .labelInUse && this .labelInUse.isViewLabel();
460: }
461:
462: /**
463: * returns true if a label has been specified and it is a revision label.
464: *
465: * @return true if a label has been specified and it is a revision label
466: */
467: protected boolean isUsingRevisionLabel() {
468: return null != this .labelInUse
469: && this .labelInUse.isRevisionLabel();
470: }
471:
472: /**
473: * returns the label being used
474: *
475: * @return the label being used
476: */
477: protected Label getLabelInUse() {
478: return this .labelInUse;
479: }
480:
481: /**
482: * show the label in the log and its type.
483: */
484: protected void logLabel() {
485: if (this .isUsingViewLabel()) {
486: log(" Using view label " + getLabel());
487: } else if (this .isUsingRevisionLabel()) {
488: log(" Using revision label " + getLabel());
489: }
490: }
491:
492: /**
493: * show the asofDate in the log
494: * @since Ant 1.6
495: */
496: protected void logAsOfDate() {
497: if (null != this .asOfDate) {
498: log(" Using view as of date " + getAsOfDate());
499: }
500: }
501:
502: ///////////////////////////////////////////////////////////////
503: // INCLUDE-EXCLUDE processing
504: ///////////////////////////////////////////////////////////////
505:
506: /**
507: * Look if the file should be processed by the task.
508: * Don't process it if it fits no include filters or if
509: * it fits an exclude filter.
510: *
511: * @param pName the item name to look for being included.
512: *
513: * @return whether the file should be processed or not.
514: */
515: protected boolean shouldProcess(String pName) {
516: boolean includeIt = matchPatterns(getIncludes(), pName);
517: boolean excludeIt = matchPatterns(getExcludes(), pName);
518: return (includeIt && !excludeIt);
519: }
520:
521: /**
522: * Convenience method to see if a string match a one pattern
523: * in given set of space-separated patterns.
524: * @param patterns the space-separated list of patterns.
525: * @param pName the name to look for matching.
526: * @return whether the name match at least one pattern.
527: */
528: protected boolean matchPatterns(String patterns, String pName) {
529: if (patterns == null) {
530: return false;
531: }
532: StringTokenizer exStr = new StringTokenizer(patterns, ",");
533: while (exStr.hasMoreTokens()) {
534: if (DirectoryScanner.match(exStr.nextToken(), pName)) {
535: return true;
536: }
537: }
538: return false;
539: }
540:
541: /**
542: * Finds and opens the root starteam folder of the operation specified
543: * by this task. This will be one of the following cases:
544: *
545: * @return Starteam's root folder for the operation.
546: * @exception BuildException
547: * if the root folder cannot be found in the repository
548: */
549: private Folder configureRootStarteamFolder() throws BuildException {
550: Folder starteamrootfolder = null;
551: try {
552: // no root local mapping has been specified.
553: View snapshot = openView();
554:
555: // find the starteam folder specified to be the root of the
556: // operation. Throw if it can't be found.
557:
558: starteamrootfolder = StarTeamFinder.findFolder(snapshot
559: .getRootFolder(), this .rootStarteamFolder);
560:
561: if (this .isPreloadFileInformation()) {
562: PropertyNames pn = getServer().getPropertyNames();
563: String[] props = new String[] { pn.FILE_NAME,
564: pn.FILE_PATH, pn.FILE_STATUS, pn.MODIFIED_TIME,
565: pn.FILE_FILE_TIME_AT_CHECKIN,
566: pn.MODIFIED_USER_ID, pn.FILE_SIZE,
567: pn.FILE_ENCODING };
568:
569: int depth = this .isRecursive() ? -1 : 0;
570: starteamrootfolder.populateNow(getServer()
571: .getTypeNames().FILE, props, depth);
572: }
573:
574: } catch (BuildException e) {
575: throw e;
576: } catch (Exception e) {
577: StringBuffer msg = new StringBuffer(
578: "Unable to find root folder ").append(
579: this .rootStarteamFolder).append(
580: " in repository at ").append(getURL());
581: if (this .label != null) {
582: msg.append(" using specified label ")
583: .append(this .label);
584: }
585: if (this .asOfDate != null) {
586: msg.append(" as of specified date ").append(
587: this .asOfDate);
588: }
589: throw new BuildException(msg.toString(), e);
590:
591: }
592:
593: if (null == starteamrootfolder) {
594: throw new BuildException("Unable to find root folder "
595: + this .rootStarteamFolder + " in repository at "
596: + getURL());
597: }
598:
599: return starteamrootfolder;
600: }
601:
602: /**
603: * Returns the local folder mapped to the given StarTeam root folder
604: * of the operation. There are two cases here, depending on whether
605: * <code>rootLocalFolder</code> is defined.
606: * If <code>rootLocalFolder</code> is defined, it will be used to
607: * establish a root mapping. Otherwise, the repository's default root
608: * folder will be used.
609: *
610: * @param starteamrootfolder
611: * root Starteam folder initialized for the operation
612: *
613: * @return the local folder corresponding to the root Starteam folder.
614: * @see findRootStarteamFolder
615: */
616: private java.io.File getLocalRootMapping(Folder starteamrootfolder) {
617: // set the local folder.
618: String localrootfolder;
619: if (null != this .rootLocalFolder) {
620: localrootfolder = rootLocalFolder;
621: } else {
622: // either use default path or root local mapping,
623: // which is now embedded in the root folder
624: localrootfolder = starteamrootfolder.getPathFragment();
625: }
626:
627: return new java.io.File(localrootfolder);
628:
629: }
630:
631: /**
632: * extenders should emit to the log an entry describing the parameters
633: * that will be used by this operation.
634: *
635: * @param starteamrootFolder
636: * root folder in StarTeam for the operation
637: * @param targetrootFolder
638: * root local folder for the operation (whether specified by the user or not.
639: */
640: protected abstract void logOperationDescription(
641: Folder starteamrootFolder, java.io.File targetrootFolder);
642:
643: /**
644: * This method does the work of opening the supplied Starteam view and
645: * calling the <code>visit()</code> method to perform the task.
646: * Derived classes can customize the called methods
647: * <code>testPreconditions()</code> and <code>visit()</code>.
648: *
649: * @exception BuildException if any error occurs in the processing
650: * @see <code>testPreconditions()</code>
651: * @see <code>visit()</code>
652: */
653:
654: public final void execute() throws BuildException {
655: try {
656:
657: Folder starteamrootfolder = configureRootStarteamFolder();
658:
659: // set the local folder.
660: java.io.File localrootfolder = getLocalRootMapping(starteamrootfolder);
661:
662: testPreconditions();
663:
664: // Tell user what he is doing
665: logOperationDescription(starteamrootfolder, localrootfolder);
666:
667: // Inspect everything in the root folder and then recursively
668: visit(starteamrootfolder, localrootfolder);
669:
670: } catch (Exception e) {
671: throw new BuildException(e);
672: } finally {
673: disconnectFromServer();
674: }
675: }
676:
677: private void findLabel(View v) throws BuildException {
678: Label[] allLabels = v.getLabels();
679: for (int i = 0; i < allLabels.length; i++) {
680: Label stLabel = allLabels[i];
681: log("checking label " + stLabel.getName(),
682: Project.MSG_DEBUG);
683: if (stLabel != null && !stLabel.isDeleted()
684: && stLabel.getName().equals(this .label)) {
685: if (!stLabel.isRevisionLabel()
686: && !stLabel.isViewLabel()) {
687: throw new BuildException("Unexpected label type.");
688: }
689: log("using label " + stLabel.getName(),
690: Project.MSG_VERBOSE);
691: this .labelInUse = stLabel;
692: return;
693: }
694: }
695: throw new BuildException("Error: label " + this .label
696: + " does not exist in view " + v.getFullName());
697:
698: }
699:
700: /**
701: * Helper method calls on the StarTeam API to retrieve an ID number
702: * for the specified view, corresponding to this.label.
703: * @param v the <code>View</code> in which to search for <code>this.label</code>
704: * @return the ID number corresponding to <code>this.label</code> or -1 if
705: * no label was provided.
706: * @exception BuildException if <code>this.label</code> does not correspond
707: * to any label in the supplied view
708: */
709: protected int getLabelID(View v) throws BuildException {
710: if (null != this .label) {
711: findLabel(v);
712: return this .labelInUse.getID();
713: }
714: return -1;
715: }
716:
717: /**
718: * Get the id of the label in use.
719: * @return id of the label in use, if labelinuse is present,
720: * otherwise return null
721: */
722: protected int getIDofLabelInUse() {
723: if (null != this .labelInUse) {
724: return this .labelInUse.getID();
725: }
726: return -1;
727: }
728:
729: /**
730: * Derived classes must override this class to define actual processing
731: * to be performed on each folder in the tree defined for the task
732: *
733: * @param rootStarteamFolder
734: * the StarTeam folderto be visited
735: * @param rootLocalFolder
736: * the local mapping of rootStarteamFolder
737: *
738: * @throws BuildException on error
739: */
740: protected abstract void visit(Folder rootStarteamFolder,
741: java.io.File rootLocalFolder) throws BuildException;
742:
743: /**
744: * Derived classes must override this method to define tests for
745: * any preconditons required by the task. This method is called at
746: * the beginning of the execute() method.
747: *
748: * @exception BuildException throw if any fatal error exists in the
749: * parameters supplied. If there is a non-fatal condition, just writing
750: * to the log may be appropriate.
751: * @see <code>execute()</code>
752: */
753: protected abstract void testPreconditions() throws BuildException;
754:
755: /**
756: * Return the full repository path name of a file. Surprisingly there's
757: * no method in com.starbase.starteam.File to provide this.
758: *
759: * @param remotefile the Star Team file whose path is to be returned
760: *
761: * @return the full repository path name of a file.
762: */
763: public static String getFullRepositoryPath(
764: com.starbase.starteam.File remotefile) {
765: StringBuffer sb = new StringBuffer();
766: sb.append(remotefile.getParentFolderHierarchy()).append(
767: remotefile.getName());
768: return sb.toString();
769: }
770:
771: /**
772: * This class implements a map of existing local files to possibly
773: * existing repository files. The map is created by a TreeBasedTask
774: * upon recursing into a directory. Each local item is mapped to an
775: * unattached StarTeam object of the proper type, File->File and
776: * Directory->Folder.
777: *
778: * As the TreeBased does its work, it deletes from the map all items
779: * it has processed.
780: *
781: * When the TreeBased task processes all the items from the repository,
782: * whatever items left in the UnmatchedFileMap are uncontrolled items
783: * and can be processed as appropriate to the task. In the case of
784: * Checkouts, they can be optionally deleted from the local tree. In the
785: * case of Checkins they can optionally be added to the repository.
786: */
787: protected abstract class UnmatchedFileMap extends Hashtable {
788:
789: /**
790: * initializes the UnmatchedFileMap with entries from the local folder
791: * These will be mapped to the corresponding StarTeam entry even though
792: * it will not, in fact, exist in the repository. But through it, it
793: * can be added, listed, etc.
794: *
795: * @param localFolder
796: * the local folder from which the mappings will be made.
797: * @param remoteFolder
798: * the corresponding StarTeam folder which will be processed.
799: */
800: UnmatchedFileMap init(java.io.File localFolder,
801: Folder remoteFolder) {
802: if (!localFolder.exists()) {
803: return this ;
804: }
805:
806: String[] localFiles = localFolder.list();
807:
808: for (int i = 0; i < localFiles.length; i++) {
809: String fn = localFiles[i];
810: java.io.File localFile = new java.io.File(localFolder,
811: localFiles[i]).getAbsoluteFile();
812:
813: log("adding " + localFile + " to UnmatchedFileMap",
814: Project.MSG_DEBUG);
815:
816: if (localFile.isDirectory()) {
817: this .put(localFile,
818: new Folder(remoteFolder, fn, fn));
819: } else {
820: com.starbase.starteam.File remoteFile = new com.starbase.starteam.File(
821: remoteFolder);
822: remoteFile.setName(fn);
823: this .put(localFile, remoteFile);
824: }
825: }
826: return this ;
827: }
828:
829: /**
830: * remove an item found to be controlled from the map.
831: *
832: * @param localFile the local item found to be controlled.
833: */
834: void removeControlledItem(java.io.File localFile) {
835: if (isActive()) {
836: log("removing processed " + localFile.getAbsoluteFile()
837: + " from UnmatchedFileMap", Project.MSG_DEBUG);
838: this .remove(localFile.getAbsoluteFile());
839: }
840: }
841:
842: /**
843: * override will perform the action appropriate for its task to perform
844: * on items which are on the local tree but not in StarTeam. It is
845: * assumed that this method will not be called until all the items in
846: * the corresponding folder have been processed, and that the internal
847: * map * will contain only uncontrolled items.
848: */
849: abstract void processUncontrolledItems() throws BuildException;
850:
851: /**
852: * overrides must define this to declare how this method knows if it
853: * is active. This presents extra clock cycles when the functionality
854: * is not called for.
855: *
856: * @return True if this object is to perform its functionality.
857: */
858: protected abstract boolean isActive();
859:
860: }
861:
862: }
|