KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > ui > actions > CVSAction


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.team.internal.ccvs.ui.actions;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.util.*;
15
16 import org.eclipse.core.commands.*;
17 import org.eclipse.core.resources.IResource;
18 import org.eclipse.core.resources.mapping.ResourceMapping;
19 import org.eclipse.core.runtime.*;
20 import org.eclipse.jface.action.IAction;
21 import org.eclipse.jface.dialogs.*;
22 import org.eclipse.jface.operation.IRunnableWithProgress;
23 import org.eclipse.jface.preference.IPreferenceStore;
24 import org.eclipse.jface.text.TextSelection;
25 import org.eclipse.jface.util.IPropertyChangeListener;
26 import org.eclipse.jface.util.PropertyChangeEvent;
27 import org.eclipse.jface.viewers.*;
28 import org.eclipse.osgi.util.NLS;
29 import org.eclipse.swt.custom.BusyIndicator;
30 import org.eclipse.swt.widgets.Display;
31 import org.eclipse.swt.widgets.Shell;
32 import org.eclipse.team.core.TeamException;
33 import org.eclipse.team.internal.ccvs.core.*;
34 import org.eclipse.team.internal.ccvs.core.filehistory.CVSFileRevision;
35 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
36 import org.eclipse.team.internal.ccvs.ui.*;
37 import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager;
38 import org.eclipse.team.internal.ui.*;
39 import org.eclipse.team.internal.ui.actions.TeamAction;
40 import org.eclipse.team.internal.ui.dialogs.IPromptCondition;
41 import org.eclipse.ui.*;
42 import org.eclipse.ui.actions.RetargetAction;
43 import org.eclipse.ui.ide.IDE;
44
45 /**
46  * CVSAction is the common superclass for all CVS actions. It provides
47  * facilities for enablement handling, standard error handling, selection
48  * retrieval and prompting.
49  */

50 abstract public class CVSAction extends TeamAction implements IEditorActionDelegate {
51     
52     private List accumulatedStatus = new ArrayList();
53     private RetargetAction retargetAction;
54     private IAction action;
55     
56     public CVSAction() {
57         super();
58     }
59     
60     /**
61      * Initializes a retarget action that will listen to part changes and allow parts to
62      * override this action's behavior. The retarget action is used if this
63      * action is shown in a top-level menu or toolbar.
64      * @param window the workbench window showing this action
65      * @since 3.1
66      */

67     private void initializeRetargetAction(IWorkbenchWindow window) {
68         // Don't need to specify a the title because it will use this actions
69
// title instead.
70
retargetAction = new RetargetAction(getId(), ""); //$NON-NLS-1$
71
retargetAction.addPropertyChangeListener(new IPropertyChangeListener() {
72             public void propertyChange(PropertyChangeEvent event) {
73                 if (event.getProperty().equals(IAction.ENABLED)) {
74                     Object JavaDoc val = event.getNewValue();
75                     if (val instanceof Boolean JavaDoc && action != null) {
76                         action.setEnabled(((Boolean JavaDoc) val).booleanValue());
77                     }
78                 } else if (event.getProperty().equals(IAction.CHECKED)) {
79                     Object JavaDoc val = event.getNewValue();
80                     if (val instanceof Boolean JavaDoc && action != null) {
81                         action.setChecked(((Boolean JavaDoc) val).booleanValue());
82                     }
83                 } else if (event.getProperty().equals(IAction.TEXT)) {
84                     Object JavaDoc val = event.getNewValue();
85                     if (val instanceof String JavaDoc && action != null) {
86                         action.setText((String JavaDoc) val);
87                     }
88                 } else if (event.getProperty().equals(IAction.TOOL_TIP_TEXT)) {
89                     Object JavaDoc val = event.getNewValue();
90                     if (val instanceof String JavaDoc && action != null) {
91                         action.setToolTipText((String JavaDoc) val);
92                     }
93                 } else if (event.getProperty().equals(SubActionBars.P_ACTION_HANDLERS)) {
94                     if(action != null && retargetAction != null) {
95                         action.setEnabled(retargetAction.isEnabled());
96                     }
97                 }
98             }
99         });
100         window.getPartService().addPartListener(retargetAction);
101         IWorkbenchPart activePart = window.getPartService().getActivePart();
102         if (activePart != null)
103             retargetAction.partActivated(activePart);
104     }
105
106     /**
107      * Common run method for all CVS actions.
108      */

109     final public void run(IAction action) {
110         try {
111             if (!beginExecution(action)) return;
112             // If the action has been replaced by another handler, then
113
// call that one instead.
114
if(retargetAction != null && retargetAction.getActionHandler() != null) {
115                 retargetAction.run();
116             } else {
117                 execute(action);
118             }
119             endExecution();
120         } catch (InvocationTargetException JavaDoc e) {
121             // Handle the exception and any accumulated errors
122
handle(e);
123         } catch (InterruptedException JavaDoc e) {
124             // Show any problems that have occurred so far
125
handle(null);
126         } catch (TeamException e) {
127             // Handle the exception and any accumulated errors
128
handle(e);
129         }
130     }
131     
132     /**
133      * Return the command and retarget action id for this action. This is used to
134      *match retargetable actions and allow key bindings.
135      *
136      * @return the id for this action
137      * @since 3.1
138      */

139     public String JavaDoc getId() {
140         return ""; //$NON-NLS-1$
141
}
142     
143     /**
144      * Called when this action is added to a top-level menu or toolbar (e.g. IWorkbenchWindowDelegate)
145      * @since 3.1
146      */

147     public void init(IWorkbenchWindow window) {
148         super.init(window);
149         initializeRetargetAction(window);
150     }
151     
152     public boolean isEnabled() {
153         if(retargetAction != null && retargetAction.getActionHandler() != null) {
154             return retargetAction.isEnabled();
155         }
156         // don't know so let subclasses decide
157
return false;
158     }
159     
160     public void dispose() {
161         super.dispose();
162         IWorkbenchWindow window = getWindow();
163         if (window != null) {
164             IPartService partService = window.getPartService();
165             if (partService != null)
166                 partService.removePartListener(retargetAction);
167         }
168         
169         if(retargetAction != null) {
170             retargetAction.dispose();
171             retargetAction = null;
172         }
173     }
174     
175     public void selectionChanged(final IAction action, ISelection selection) {
176         if (selection instanceof TextSelection) {
177             // Since we have a text selection, we will assume that the target is the active editor.
178
// Look for the active editor and see it adapts to ResourceMapping or IResource.
179
// See bug 132176
180
IEditorPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
181             if (part != null) {
182                 IEditorInput input = part.getEditorInput();
183                 ResourceMapping mapping = Utils.getResourceMapping(input);
184                 if (mapping != null) {
185                     selection = new StructuredSelection(mapping);
186                 } else {
187                     IResource resource = Utils.getResource(input);
188                     if (resource != null) {
189                         selection = new StructuredSelection(resource);
190                     }
191                 }
192             }
193         }
194         super.selectionChanged(action, selection);
195         this.action = action;
196     }
197     
198     protected void setActionEnablement(IAction action) {
199         if(retargetAction != null && retargetAction.getActionHandler() != null) {
200             action.setEnabled(retargetAction.isEnabled());
201         } else {
202             super.setActionEnablement(action);
203         }
204     }
205
206     /**
207      * This method gets invoked before the <code>CVSAction#execute(IAction)</code>
208      * method. It can perform any prechecking and initialization required before
209      * the action is executed. Subclasses may override but must invoke this
210      * inherited method to ensure proper initialization of this superclass is performed.
211      * These included preparation to accumulate IStatus and checking for dirty editors.
212      */

213     protected boolean beginExecution(IAction action) throws TeamException {
214         accumulatedStatus.clear();
215         if(needsToSaveDirtyEditors()) {
216             if(!saveAllEditors()) {
217                 return false;
218             }
219         }
220         return true;
221     }
222
223     /**
224      * This method gets invoked after <code>CVSAction#execute(IAction)</code>
225      * if no exception occurred. Subclasses may override but should invoke this
226      * inherited method to ensure proper handling of any accumulated IStatus.
227      */

228     protected void endExecution() throws TeamException {
229         if ( ! accumulatedStatus.isEmpty()) {
230             handle(null);
231         }
232     }
233     
234     /**
235      * Add a status to the list of accumulated status.
236      * These will be provided to method handle(Exception, IStatus[])
237      * when the action completes.
238      */

239     protected void addStatus(IStatus status) {
240         accumulatedStatus.add(status);
241     }
242     
243     /**
244      * Return the list of status accumulated so far by the action. This
245      * will include any OK status that were added using addStatus(IStatus)
246      */

247     protected IStatus[] getAccumulatedStatus() {
248         return (IStatus[]) accumulatedStatus.toArray(new IStatus[accumulatedStatus.size()]);
249     }
250     
251     /**
252      * Return the title to be displayed on error dialogs.
253      * Subclasses should override to present a custom message.
254      */

255     protected String JavaDoc getErrorTitle() {
256         return CVSUIMessages.CVSAction_errorTitle;
257     }
258     
259     /**
260      * Return the title to be displayed on error dialogs when warnings occur.
261      * Subclasses should override to present a custom message.
262      */

263     protected String JavaDoc getWarningTitle() {
264         return CVSUIMessages.CVSAction_warningTitle;
265     }
266
267     /**
268      * Return the message to be used for the parent MultiStatus when
269      * multiple errors occur during an action.
270      * Subclasses should override to present a custom message.
271      */

272     protected String JavaDoc getMultiStatusMessage() {
273         return CVSUIMessages.CVSAction_multipleProblemsMessage;
274     }
275     
276     /**
277      * Return the status to be displayed in an error dialog for the given list
278      * of non-OK status.
279      *
280      * This method can be overridden but subclasses. Returning an OK status will
281      * prevent the error dialog from being shown.
282      */

283     protected IStatus getStatusToDisplay(IStatus[] problems) {
284         if (problems.length == 1) {
285             return problems[0];
286         }
287         MultiStatus combinedStatus = new MultiStatus(CVSUIPlugin.ID, 0, getMultiStatusMessage(), null);
288         for (int i = 0; i < problems.length; i++) {
289             combinedStatus.merge(problems[i]);
290         }
291         return combinedStatus;
292     }
293     
294     /**
295      * Method that implements generic handling of an exception.
296      *
297      * This method will also use any accumulated status when determining what
298      * information (if any) to show the user.
299      *
300      * @param exception the exception that occurred (or null if none occurred)
301      * @param status any status accumulated by the action before the end of
302      * the action or the exception occurred.
303      */

304     protected void handle(Exception JavaDoc exception) {
305         // Get the non-OK status
306
List problems = new ArrayList();
307         IStatus[] status = getAccumulatedStatus();
308         if (status != null) {
309             for (int i = 0; i < status.length; i++) {
310                 IStatus iStatus = status[i];
311                 if ( ! iStatus.isOK() || iStatus.getCode() == CVSStatus.SERVER_ERROR) {
312                     problems.add(iStatus);
313                 }
314             }
315         }
316         // Handle the case where there are no problem status
317
if (problems.size() == 0) {
318             if (exception == null) return;
319             handle(exception, getErrorTitle(), null);
320             return;
321         }
322
323         // For now, display both the exception and the problem status
324
// Later, we can determine how to display both together
325
if (exception != null) {
326             handle(exception, getErrorTitle(), null);
327         }
328         
329         String JavaDoc message = null;
330         IStatus statusToDisplay = getStatusToDisplay((IStatus[]) problems.toArray(new IStatus[problems.size()]));
331         if (statusToDisplay.isOK()) return;
332         if (statusToDisplay.isMultiStatus() && statusToDisplay.getChildren().length == 1) {
333             message = statusToDisplay.getMessage();
334             statusToDisplay = statusToDisplay.getChildren()[0];
335         }
336         String JavaDoc title;
337         if (statusToDisplay.getSeverity() == IStatus.ERROR) {
338             title = getErrorTitle();
339         } else {
340             title = getWarningTitle();
341         }
342         CVSUIPlugin.openError(getShell(), title, message, new CVSException(statusToDisplay));
343     }
344
345     /**
346      * Convenience method for running an operation with the appropriate progress.
347      * Any exceptions are propagated so they can be handled by the
348      * <code>CVSAction#run(IAction)</code> error handling code.
349      *
350      * @param runnable the runnable which executes the operation
351      * @param cancelable indicate if a progress monitor should be cancelable
352      * @param progressKind one of PROGRESS_BUSYCURSOR or PROGRESS_DIALOG
353      */

354     final protected void run(final IRunnableWithProgress runnable, boolean cancelable, int progressKind) throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
355         final Exception JavaDoc[] exceptions = new Exception JavaDoc[] {null};
356         
357         // Ensure that no repository view refresh happens until after the action
358
final IRunnableWithProgress innerRunnable = new IRunnableWithProgress() {
359             public void run(IProgressMonitor monitor) throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
360                 getRepositoryManager().run(runnable, monitor);
361             }
362         };
363         
364         switch (progressKind) {
365             case PROGRESS_BUSYCURSOR :
366                 BusyIndicator.showWhile(Display.getCurrent(), new Runnable JavaDoc() {
367                     public void run() {
368                         try {
369                             innerRunnable.run(new NullProgressMonitor());
370                         } catch (InvocationTargetException JavaDoc e) {
371                             exceptions[0] = e;
372                         } catch (InterruptedException JavaDoc e) {
373                             exceptions[0] = e;
374                         }
375                     }
376                 });
377                 break;
378             case PROGRESS_DIALOG :
379             default :
380                 new ProgressMonitorDialog(getShell()).run(cancelable, cancelable, innerRunnable);
381                 break;
382         }
383         if (exceptions[0] != null) {
384             if (exceptions[0] instanceof InvocationTargetException JavaDoc)
385                 throw (InvocationTargetException JavaDoc)exceptions[0];
386             else
387                 throw (InterruptedException JavaDoc)exceptions[0];
388         }
389     }
390     
391     /**
392      * Answers if the action would like dirty editors to saved
393      * based on the CVS preference before running the action. By
394      * default, CVSActions do not save dirty editors.
395      */

396     protected boolean needsToSaveDirtyEditors() {
397         return false;
398     }
399     
400     /**
401      * Returns the selected CVS resources
402      */

403     protected ICVSResource[] getSelectedCVSResources() {
404         ArrayList resources = null;
405         IStructuredSelection selection = getSelection();
406         if (!selection.isEmpty()) {
407             resources = new ArrayList();
408             Iterator elements = selection.iterator();
409             while (elements.hasNext()) {
410                 Object JavaDoc next = elements.next();
411                 if (next instanceof ICVSResource) {
412                     resources.add(next);
413                     continue;
414                 }
415                 if (next instanceof IAdaptable) {
416                     IAdaptable a = (IAdaptable) next;
417                     Object JavaDoc adapter = a.getAdapter(ICVSResource.class);
418                     if (adapter instanceof ICVSResource) {
419                         resources.add(adapter);
420                         continue;
421                     }
422                 }
423             }
424         }
425         if (resources != null && !resources.isEmpty()) {
426             return (ICVSResource[])resources.toArray(new ICVSResource[resources.size()]);
427         }
428         return new ICVSResource[0];
429     }
430
431     /**
432      * Get selected CVS remote folders
433      */

434     protected ICVSRemoteFolder[] getSelectedRemoteFolders() {
435         ArrayList resources = null;
436         IStructuredSelection selection = getSelection();
437         if (!selection.isEmpty()) {
438             resources = new ArrayList();
439             Iterator elements = selection.iterator();
440             while (elements.hasNext()) {
441                 Object JavaDoc next = elements.next();
442                 if (next instanceof ICVSRemoteFolder) {
443                     resources.add(next);
444                     continue;
445                 }
446                 if (next instanceof IAdaptable) {
447                     IAdaptable a = (IAdaptable) next;
448                     Object JavaDoc adapter = a.getAdapter(ICVSRemoteFolder.class);
449                     if (adapter instanceof ICVSRemoteFolder) {
450                         resources.add(adapter);
451                         continue;
452                     }
453                 }
454             }
455         }
456         if (resources != null && !resources.isEmpty()) {
457             return (ICVSRemoteFolder[])resources.toArray(new ICVSRemoteFolder[resources.size()]);
458         }
459         return new ICVSRemoteFolder[0];
460     }
461
462     /**
463      * Returns the selected remote resources
464      */

465     protected ICVSRemoteResource[] getSelectedRemoteResources() {
466         ArrayList resources = null;
467         IStructuredSelection selection = getSelection();
468         if (!selection.isEmpty()) {
469             resources = new ArrayList();
470             Iterator elements = selection.iterator();
471             while (elements.hasNext()) {
472                 Object JavaDoc next = elements.next();
473                 if (next instanceof ICVSRemoteResource) {
474                     resources.add(next);
475                     continue;
476                 }
477                 if (next instanceof CVSFileRevision) {
478                     resources.add(((CVSFileRevision)next).getCVSRemoteFile());
479                     continue;
480                 }
481                 if (next instanceof ILogEntry) {
482                     resources.add(((ILogEntry)next).getRemoteFile());
483                     continue;
484                 }
485                 if (next instanceof IAdaptable) {
486                     IAdaptable a = (IAdaptable) next;
487                     Object JavaDoc adapter = a.getAdapter(ICVSRemoteResource.class);
488                     if (adapter instanceof ICVSRemoteResource) {
489                         resources.add(adapter);
490                         continue;
491                     }
492                 }
493             }
494         }
495         if (resources != null && !resources.isEmpty()) {
496             ICVSRemoteResource[] result = new ICVSRemoteResource[resources.size()];
497             resources.toArray(result);
498             return result;
499         }
500         return new ICVSRemoteResource[0];
501     }
502         
503     /**
504      * A helper prompt condition for prompting for CVS dirty state.
505      */

506     public static IPromptCondition getOverwriteLocalChangesPrompt(final IResource[] dirtyResources) {
507         return new IPromptCondition() {
508             List resources = Arrays.asList(dirtyResources);
509             public boolean needsPrompt(IResource resource) {
510                 return resources.contains(resource);
511             }
512             public String JavaDoc promptMessage(IResource resource) {
513                 return NLS.bind(CVSUIMessages.ReplaceWithAction_localChanges, new String JavaDoc[] { resource.getName() });
514             }
515         };
516     }
517         
518     /**
519      * Checks if a the resources' parent's tags are different then the given tag.
520      * Prompts the user that they are adding mixed tags and returns <code>true</code> if
521      * the user wants to continue or <code>false</code> otherwise.
522      */

523     public static boolean checkForMixingTags(final Shell shell, IResource[] resources, final CVSTag tag) throws CVSException {
524         final IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
525         if(!store.getBoolean(ICVSUIConstants.PREF_PROMPT_ON_MIXED_TAGS)) {
526             return true;
527         };
528         
529         final boolean[] result = new boolean[] { true };
530         
531         for (int i = 0; i < resources.length; i++) {
532             IResource resource = resources[i];
533             if (resource.getType() != IResource.PROJECT) {
534                 ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
535                 CVSTag parentTag = cvsResource.getParent().getFolderSyncInfo().getTag();
536                 // prompt if the tags are not equal
537
// consider BASE to be equal the parent tag since we don't make BASE sticky on replace
538
if (!CVSTag.equalTags(tag, parentTag) && !CVSTag.equalTags(tag, CVSTag.BASE)) {
539                     shell.getDisplay().syncExec(new Runnable JavaDoc() {
540                         public void run() {
541                             MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(shell,
542                                     CVSUIMessages.CVSAction_mixingTagsTitle,
543                                     NLS.bind(CVSUIMessages.CVSAction_mixingTags, new String JavaDoc[] { tag.getName() }),
544                                     CVSUIMessages.CVSAction_doNotShowThisAgain, false,
545                                     store, ICVSUIConstants.PREF_PROMPT_ON_MIXED_TAGS);
546                             result[0] = dialog.getReturnCode() == 0;
547                         }
548                     });
549                     // only prompt once
550
break;
551                 }
552             }
553         }
554         return result[0];
555     }
556     
557     /**
558      * Based on the CVS preference for saving dirty editors this method will either
559      * ignore dirty editors, save them automatically, or prompt the user to save them.
560      *
561      * @return <code>true</code> if the command succeeded, and <code>false</code>
562      * if at least one editor with unsaved changes was not saved
563      */

564     private boolean saveAllEditors() {
565         final int option = CVSUIPlugin.getPlugin().getPreferenceStore().getInt(ICVSUIConstants.PREF_SAVE_DIRTY_EDITORS);
566         final boolean[] okToContinue = new boolean[] {true};
567         if (option != ICVSUIConstants.OPTION_NEVER) {
568             Display.getDefault().syncExec(new Runnable JavaDoc() {
569                 public void run() {
570                     boolean confirm = option == ICVSUIConstants.OPTION_PROMPT;
571                     IResource[] selectedResources = getSelectedResources();
572                     if (selectedResources != null) {
573                         okToContinue[0] = IDE.saveAllEditors(selectedResources, confirm);
574                     }
575                 }
576             });
577         }
578         return okToContinue[0];
579     }
580     /**
581      * @see org.eclipse.team.internal.ui.actions.TeamAction#handle(java.lang.Exception, java.lang.String, java.lang.String)
582      */

583     protected void handle(Exception JavaDoc exception, String JavaDoc title, String JavaDoc message) {
584         CVSUIPlugin.openError(getShell(), title, message, exception, CVSUIPlugin.LOG_NONTEAM_EXCEPTIONS);
585     }
586     
587     protected RepositoryManager getRepositoryManager() {
588         return CVSUIPlugin.getPlugin().getRepositoryManager();
589     }
590
591     /* (non-Javadoc)
592      * @see org.eclipse.team.internal.ui.actions.TeamAction#getSelectedResources()
593      */

594     protected final IResource[] getSelectedResourcesWithOverlap() {
595         IStructuredSelection selection = getSelection();
596         CVSActionSelectionProperties props = CVSActionSelectionProperties.getProperties(getSelection());
597         if (props == null) {
598             return Utils.getContributedResources(selection.toArray());
599         }
600         return props.getAllSelectedResources();
601     }
602     
603     /* (non-Javadoc)
604      * @see org.eclipse.team.internal.ui.actions.TeamAction#getSelectedResources()
605      */

606     protected final IResource[] getSelectedResources() {
607         IStructuredSelection selection = getSelection();
608         CVSActionSelectionProperties props = CVSActionSelectionProperties.getProperties(getSelection());
609         if (props == null) {
610             return CVSActionSelectionProperties.getNonOverlapping(Utils.getContributedResources(selection.toArray()));
611         }
612         return props.getNonoverlappingSelectedResources();
613     }
614     
615     /* (non-Javadoc)
616      * @see org.eclipse.ui.IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart)
617      */

618     public void setActiveEditor(IAction action, IEditorPart targetEditor) {
619     }
620     
621     /**
622      * These handlers won't have any interesting property changes. There is
623      * no need to notify listeners.
624      * @param handlerListener
625      * @since 3.1
626      */

627     public void removeHandlerListener(IHandlerListener handlerListener) {
628     }
629     public void addHandlerListener(IHandlerListener handlerListener) {
630     }
631     
632     /* (non-Javadoc)
633      * @see org.eclipse.core.commands.IHandler#isHandled()
634      */

635     public boolean isHandled() {
636         return true;
637     }
638     
639     protected final ICVSResource getCVSResourceFor(IResource resource) {
640         CVSActionSelectionProperties props = CVSActionSelectionProperties.getProperties(getSelection());
641         if (props == null) {
642             return CVSWorkspaceRoot.getCVSResourceFor(resource);
643         }
644         return props.getCVSResourceFor(resource);
645     }
646 }
647
Popular Tags