KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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.ArrayList JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Hashtable JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Set JavaDoc;
20
21 import org.eclipse.core.resources.*;
22 import org.eclipse.core.runtime.*;
23 import org.eclipse.jface.action.IAction;
24 import org.eclipse.jface.dialogs.MessageDialog;
25 import org.eclipse.jface.operation.IRunnableWithProgress;
26 import org.eclipse.osgi.util.NLS;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.swt.widgets.Shell;
29 import org.eclipse.team.core.RepositoryProvider;
30 import org.eclipse.team.core.TeamException;
31 import org.eclipse.team.internal.ccvs.core.*;
32 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
33 import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
34 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
35 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
36 import org.eclipse.team.internal.ccvs.core.util.Util;
37 import org.eclipse.team.internal.ccvs.ui.CVSUIMessages;
38 import org.eclipse.team.internal.ccvs.ui.Policy;
39 import org.eclipse.team.internal.ui.actions.TeamAction;
40 import org.eclipse.team.internal.ui.dialogs.IPromptCondition;
41 import org.eclipse.team.internal.ui.dialogs.PromptingDialog;
42
43 /**
44  * This class represents an action performed on a local CVS workspace
45  */

46 public abstract class WorkspaceAction extends CVSAction {
47
48     public interface IProviderAction {
49         public IStatus execute(CVSTeamProvider provider, IResource[] resources, IProgressMonitor monitor) throws CVSException;
50     }
51     
52     /**
53      * @see org.eclipse.team.internal.ccvs.ui.actions.CVSAction#beginExecution(IAction)
54      */

55     protected boolean beginExecution(IAction action) throws TeamException {
56         if (super.beginExecution(action)) {
57             // Ensure that the required sync info is loaded
58
if (requiresLocalSyncInfo()) {
59                 // There is a possibility of the selection containing an orphaned subtree.
60
// If it does, they will be purged and enablement rechecked before the
61
// operation is performed.
62
handleOrphanedSubtrees();
63                 // Check enablement just in case the sync info wasn't loaded
64
if (!isEnabled()) {
65                     MessageDialog.openInformation(getShell(), CVSUIMessages.CVSAction_disabledTitle, CVSUIMessages.CVSAction_disabledMessage); //
66
return false;
67                 }
68             }
69             return true;
70         } else {
71             return false;
72         }
73     }
74
75     /*
76      * Determine if any of the selected resources are deascendants of
77      * an orphaned CVS subtree and if they are, purge the CVS folders.
78      */

79     private boolean handleOrphanedSubtrees() {
80         // invoke the inherited method so that overlaps are maintained
81
IResource[] resources = getSelectedResources();
82         for (int i = 0; i < resources.length; i++) {
83             IResource resource = resources[i];
84             handleOrphanedSubtree(resource);
85         }
86         return false;
87     }
88
89     /*
90      * Determine if the resource is a descendant of an orphaned subtree.
91      * If it is, purge the CVS folders of the subtree.
92      */

93     private void handleOrphanedSubtree(IResource resource) {
94         try {
95             if (!CVSWorkspaceRoot.isSharedWithCVS(resource)) return ;
96             ICVSFolder folder;
97             if (resource.getType() == IResource.FILE) {
98                 folder = CVSWorkspaceRoot.getCVSFolderFor(resource.getParent());
99             } else {
100                 folder = CVSWorkspaceRoot.getCVSFolderFor((IContainer)resource);
101             }
102             handleOrphanedSubtree(folder);
103         } catch (CVSException e) {
104             CVSProviderPlugin.log(e);
105         }
106     }
107     
108     /*
109      * Recursively check for and handle orphaned CVS folders
110      */

111     private void handleOrphanedSubtree(final ICVSFolder folder) throws CVSException {
112         if (folder.getIResource().getType() == IResource.PROJECT) return;
113         if (CVSWorkspaceRoot.isOrphanedSubtree((IContainer)folder.getIResource())) {
114             try {
115                 run(new IRunnableWithProgress() {
116                     public void run(IProgressMonitor monitor) throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
117                         try {
118                             folder.unmanage(null);
119                         } catch (CVSException e) {
120                             CVSProviderPlugin.log(e);
121                         }
122                     }
123                 }, true, PROGRESS_BUSYCURSOR);
124             } catch (InvocationTargetException JavaDoc e) {
125                 // Ignore this since we logged the one we care about above
126
} catch (InterruptedException JavaDoc e) {
127                 throw new OperationCanceledException();
128             }
129         }
130         handleOrphanedSubtree(folder.getParent());
131     }
132     
133     /**
134      * Return true if the sync info is loaded for all selected resources.
135      * The purpose of this method is to allow enablement code to be as fast
136      * as possible. If the sync info is not loaded, the menu should be enabled
137      * and, if choosen, the action will verify that it is indeed enabled before
138      * performing the associated operation
139      */

140     protected boolean isSyncInfoLoaded(IResource[] resources) throws CVSException {
141         return EclipseSynchronizer.getInstance().isSyncInfoLoaded(resources, getEnablementDepth());
142     }
143
144     /**
145      * Returns the resource depth of the action for use in determining if the required
146      * sync info is loaded. The default is IResource.DEPTH_INFINITE. Sunclasses can override
147      * as required.
148      */

149     protected int getActionDepth() {
150         return IResource.DEPTH_INFINITE;
151     }
152
153     /**
154      * Returns the resource depth of the action enablement for use in determining if the required
155      * sync info is loaded. The default is IResource.DEPTH_ZERO. Sunclasses can override
156      * as required.
157      */

158     protected int getEnablementDepth() {
159         return IResource.DEPTH_ZERO;
160     }
161     
162     /**
163      * Ensure that the sync info for all the provided resources has been loaded.
164      * If an out-of-sync resource is found, prompt to refresh all the projects involved.
165      */

166     protected boolean ensureSyncInfoLoaded(IResource[] resources) throws CVSException {
167         boolean keepTrying = true;
168         while (keepTrying) {
169             try {
170                 EclipseSynchronizer.getInstance().ensureSyncInfoLoaded(resources, getActionDepth());
171                 keepTrying = false;
172             } catch (CVSException e) {
173                 if (e.getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) {
174                     // determine the projects of the resources involved
175
Set JavaDoc projects = new HashSet JavaDoc();
176                     for (int i = 0; i < resources.length; i++) {
177                         IResource resource = resources[i];
178                         projects.add(resource.getProject());
179                     }
180                     // prompt to refresh
181
if (promptToRefresh(getShell(), (IResource[]) projects.toArray(new IResource[projects.size()]), e.getStatus())) {
182                         for (Iterator JavaDoc iter = projects.iterator();iter.hasNext();) {
183                             IProject project = (IProject) iter.next();
184                             try {
185                                 project.refreshLocal(IResource.DEPTH_INFINITE, null);
186                             } catch (CoreException coreException) {
187                                 throw CVSException.wrapException(coreException);
188                             }
189                         }
190                     } else {
191                         return false;
192                     }
193                 } else {
194                     throw e;
195                 }
196             }
197         }
198         return true;
199     }
200     
201     /**
202      * Override to ensure that the sync info is available before performing the
203      * real <code>isEnabled()</code> test.
204      *
205      * @see org.eclipse.team.internal.ui.actions.TeamAction#setActionEnablement(IAction)
206      */

207     protected void setActionEnablement(IAction action) {
208         try {
209             boolean requires = requiresLocalSyncInfo();
210             if (!requires || (requires && isSyncInfoLoaded(getSelectedResources()))) {
211                 super.setActionEnablement(action);
212             } else {
213                 // If the sync info is not loaded, enable the menu item
214
// Performing the action will ensure that the action should really
215
// be enabled before anything else is done
216
action.setEnabled(true);
217             }
218         } catch (CVSException e) {
219             // We couldn't determine if the sync info was loaded.
220
// Enable the action so that performing the action will
221
// reveal the error to the user.
222
action.setEnabled(true);
223         }
224     }
225
226     /**
227      * Return true if the action requires the sync info for the selected resources.
228      * If the sync info is required, the real enablement code will only be run if
229      * the sync info is loaded from disc. Otherwise, the action is enabled and
230      * performing the action will load the sync info and verify that the action is truely
231      * enabled before doing anything else.
232      *
233      * This implementation returns <code>true</code>. Subclasses must override if they do
234      * not require the sync info of the selected resources.
235      *
236      * @return boolean
237      */

238     protected boolean requiresLocalSyncInfo() {
239         return true;
240     }
241
242     protected boolean promptToRefresh(final Shell shell, final IResource[] resources, final IStatus status) {
243         final boolean[] result = new boolean[] { false};
244         Runnable JavaDoc runnable = new Runnable JavaDoc() {
245             public void run() {
246                 Shell shellToUse = shell;
247                 if (shell == null) {
248                     shellToUse = new Shell(Display.getCurrent());
249                 }
250                 String JavaDoc question;
251                 if (resources.length == 1) {
252                     question = NLS.bind(CVSUIMessages.CVSAction_refreshQuestion, new String JavaDoc[] { status.getMessage(), resources[0].getFullPath().toString() });
253                 } else {
254                     question = NLS.bind(CVSUIMessages.CVSAction_refreshMultipleQuestion, new String JavaDoc[] { status.getMessage() });
255                 }
256                 result[0] = MessageDialog.openQuestion(shellToUse, CVSUIMessages.CVSAction_refreshTitle, question);
257             }
258         };
259         Display.getDefault().syncExec(runnable);
260         return result[0];
261     }
262
263     /**
264      * Most CVS workspace actions modify the workspace and thus should
265      * save dirty editors.
266      * @see org.eclipse.team.internal.ccvs.ui.actions.CVSAction#needsToSaveDirtyEditors()
267      */

268     protected boolean needsToSaveDirtyEditors() {
269         return true;
270     }
271
272     /**
273      * The action is enabled for the appropriate resources. This method checks
274      * that:
275      * <ol>
276      * <li>there is no overlap between a selected file and folder (overlapping
277      * folders is allowed because of logical vs. physical mapping problem in
278      * views)
279      * <li>the state of the resources match the conditions provided by:
280      * <ul>
281      * <li>isEnabledForIgnoredResources()
282      * <li>isEnabledForManagedResources()
283      * <li>isEnabledForUnManagedResources() (i.e. not ignored and not managed)
284      * </ul>
285      * </ol>
286      * @see TeamAction#isEnabled()
287      */

288     public boolean isEnabled() {
289         
290         // allow the super to decide enablement. if the super doesn't know it will return false.
291
boolean enabled = super.isEnabled();
292         if(enabled) return true;
293         
294         // invoke the inherited method so that overlaps are maintained
295
IResource[] resources = getSelectedResourcesWithOverlap();
296         
297         // disable if no resources are selected
298
if(resources.length==0) return false;
299         
300         // disable properly for single resource enablement
301
if (!isEnabledForMultipleResources() && resources.length != 1) return false;
302         
303         // validate enabled for each resource in the selection
304
List JavaDoc folderPaths = new ArrayList JavaDoc();
305         List JavaDoc filePaths = new ArrayList JavaDoc();
306         for (int i = 0; i < resources.length; i++) {
307             IResource resource = resources[i];
308             
309             // only enable for accessible resources
310
if(resource.getType() == IResource.PROJECT) {
311                 if (! resource.isAccessible()) return false;
312             }
313             
314             // no CVS actions are enabled if the selection contains a linked resource
315
if (CVSWorkspaceRoot.isLinkedResource(resource)) return false;
316             
317             // only enable for resources in a project shared with CVS
318
if(RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId()) == null) {
319                 return false;
320             }
321             
322             // collect files and folders separately to check for overlap later
323
IPath resourceFullPath = resource.getFullPath();
324             if(resource.getType() == IResource.FILE) {
325                 filePaths.add(resourceFullPath);
326             } else {
327                 folderPaths.add(resourceFullPath);
328             }
329             
330             // ensure that resource management state matches what the action requires
331
ICVSResource cvsResource = getCVSResourceFor(resource);
332             try {
333                 if (!isEnabledForCVSResource(cvsResource)) {
334                     return false;
335                 }
336             } catch (CVSException e) {
337                 if (!isEnabledForException(e))
338                     return false;
339             }
340         }
341         // Ensure that there is no overlap between files and folders
342
// NOTE: folder overlap must be allowed because of logical vs. physical
343
if(!folderPaths.isEmpty()) {
344             for (Iterator JavaDoc fileIter = filePaths.iterator(); fileIter.hasNext();) {
345                 IPath resourcePath = (IPath) fileIter.next();
346                 for (Iterator JavaDoc it = folderPaths.iterator(); it.hasNext();) {
347                     IPath folderPath = (IPath) it.next();
348                     if (folderPath.isPrefixOf(resourcePath)) {
349                         return false;
350                     }
351                 }
352             }
353         }
354         return true;
355     }
356
357     /**
358      * Method isEnabledForCVSResource.
359      * @param cvsResource
360      * @return boolean
361      */

362     protected boolean isEnabledForCVSResource(ICVSResource cvsResource) throws CVSException {
363         boolean managed = false;
364         boolean ignored = false;
365         boolean added = false;
366         if (cvsResource.isIgnored()) {
367             ignored = true;
368         } else if (cvsResource.isFolder()) {
369             managed = ((ICVSFolder)cvsResource).isCVSFolder();
370         } else {
371             ResourceSyncInfo info = cvsResource.getSyncInfo();
372             managed = info != null;
373             if (managed) added = info.isAdded();
374         }
375         if (managed && ! isEnabledForManagedResources()) return false;
376         if ( ! managed && ! isEnabledForUnmanagedResources()) return false;
377         if ( ignored && ! isEnabledForIgnoredResources()) return false;
378         if (added && ! isEnabledForAddedResources()) return false;
379         if ( ! cvsResource.exists() && ! isEnabledForNonExistantResources()) return false;
380         return true;
381     }
382     
383     /**
384      * Method isEnabledForIgnoredResources.
385      * @return boolean
386      */

387     protected boolean isEnabledForIgnoredResources() {
388         return false;
389     }
390     
391     /**
392      * Method isEnabledForUnmanagedResources.
393      * @return boolean
394      */

395     protected boolean isEnabledForUnmanagedResources() {
396         return false;
397     }
398     
399     /**
400      * Method isEnabledForManagedResources.
401      * @return boolean
402      */

403     protected boolean isEnabledForManagedResources() {
404         return true;
405     }
406
407     /**
408      * Method isEnabledForAddedResources.
409      * @return boolean
410      */

411     protected boolean isEnabledForAddedResources() {
412         return true;
413     }
414     
415     /**
416      * Method isEnabledForAddedResources.
417      * @return boolean
418      */

419     protected boolean isEnabledForMultipleResources() {
420         return true;
421     }
422     
423     /**
424      * Method isEnabledForNonExistantResources.
425      * @return boolean
426      */

427     protected boolean isEnabledForNonExistantResources() {
428         return false;
429     }
430
431     protected void executeProviderAction(IProviderAction action, IResource[] resources, IProgressMonitor monitor) throws InvocationTargetException JavaDoc {
432         Hashtable JavaDoc table = getProviderMapping(resources);
433         Set JavaDoc keySet = table.keySet();
434         monitor.beginTask(null, keySet.size() * 1000);
435         Iterator JavaDoc iterator = keySet.iterator();
436
437         while (iterator.hasNext()) {
438             IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
439             CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
440             List JavaDoc list = (List JavaDoc)table.get(provider);
441             IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
442             try {
443                 addStatus(action.execute(provider, providerResources, subMonitor));
444             } catch (CVSException e) {
445                 throw new InvocationTargetException JavaDoc(e);
446             }
447
448         }
449     }
450     
451     protected void executeProviderAction(IProviderAction action, IProgressMonitor monitor) throws InvocationTargetException JavaDoc {
452         executeProviderAction(action, getSelectedResources(), monitor);
453     }
454
455     /**
456      * Given the current selection this method returns a text label that can
457      * be shown to the user that reflects the tags in the current selection.
458      * These can be used in the <b>Compare With</b> and <b>Replace With</b> actions.
459      */

460     protected String JavaDoc calculateActionTagValue() {
461         try {
462             IResource[] resources = getSelectedResources();
463             CVSTag commonTag = null;
464             boolean sameTagType = true;
465             boolean multipleSameNames = true;
466             
467             for (int i = 0; i < resources.length; i++) {
468                 ICVSResource cvsResource = getCVSResourceFor(resources[i]);
469                 CVSTag tag = null;
470                 if(cvsResource.isFolder()) {
471                     FolderSyncInfo info = ((ICVSFolder)cvsResource).getFolderSyncInfo();
472                     if(info != null) {
473                         tag = info.getTag();
474                     }
475                     if (tag != null && tag.getType() == CVSTag.BRANCH) {
476                         tag = Util.getAccurateFolderTag(resources[i], tag);
477                     }
478                 } else {
479                     tag = Util.getAccurateFileTag(cvsResource);
480                 }
481                 if(tag == null) {
482                     tag = new CVSTag();
483                 }
484                 if(commonTag == null) {
485                     commonTag = tag;
486                 } else if(!commonTag.equals(tag)) {
487                     if(commonTag.getType() != tag.getType()) {
488                         sameTagType = false;
489                     }
490                     if(!commonTag.getName().equals(tag.getName())) {
491                         multipleSameNames = false;
492                     }
493                 }
494             }
495             
496             // set text to default
497
String JavaDoc actionText = CVSUIMessages.ReplaceWithLatestAction_multipleTags;
498             if(commonTag != null) {
499                 int tagType = commonTag.getType();
500                 String JavaDoc tagName = commonTag.getName();
501                 // multiple tag names but of the same type
502
if(sameTagType && !multipleSameNames) {
503                     if(tagType == CVSTag.BRANCH) {
504                         actionText = CVSUIMessages.ReplaceWithLatestAction_multipleBranches; //
505
} else {
506                         actionText = CVSUIMessages.ReplaceWithLatestAction_multipleVersions;
507                     }
508                 // same tag names and types
509
} else if(sameTagType && multipleSameNames) {
510                     if(tagType == CVSTag.BRANCH) {
511                         actionText = NLS.bind(CVSUIMessages.ReplaceWithLatestAction_singleBranch, new String JavaDoc[] { tagName }); //
512
} else if(tagType == CVSTag.VERSION){
513                         actionText = NLS.bind(CVSUIMessages.ReplaceWithLatestAction_singleVersion, new String JavaDoc[] { tagName });
514                     } else if(tagType == CVSTag.HEAD) {
515                         actionText = NLS.bind(CVSUIMessages.ReplaceWithLatestAction_singleHEAD, new String JavaDoc[] { tagName });
516                     }
517                 }
518             }
519             
520             return actionText;
521         } catch (CVSException e) {
522             // silently ignore
523
return CVSUIMessages.ReplaceWithLatestAction_multipleTags; //
524
}
525     }
526
527     protected IResource[] checkOverwriteOfDirtyResources(IResource[] resources, IProgressMonitor monitor) throws CVSException, InterruptedException JavaDoc {
528         List JavaDoc dirtyResources = new ArrayList JavaDoc();
529         IResource[] selectedResources = getSelectedResources();
530         
531         try {
532             monitor = Policy.monitorFor(monitor);
533             monitor.beginTask(null, selectedResources.length * 100);
534             monitor.setTaskName(CVSUIMessages.ReplaceWithAction_calculatingDirtyResources);
535             for (int i = 0; i < selectedResources.length; i++) {
536                 IResource resource = selectedResources[i];
537                 ICVSResource cvsResource = getCVSResourceFor(resource);
538                 if(cvsResource.isModified(Policy.subMonitorFor(monitor, 100))) {
539                     dirtyResources.add(resource);
540                 }
541             }
542         } finally {
543             monitor.done();
544         }
545         
546         PromptingDialog dialog = new PromptingDialog(getShell(), selectedResources,
547                 getPromptCondition((IResource[]) dirtyResources.toArray(new IResource[dirtyResources.size()])), CVSUIMessages.ReplaceWithAction_confirmOverwrite);
548         return dialog.promptForMultiple();
549     }
550
551     /**
552      * This is a helper for the CVS UI automated tests. It allows the tests to ignore prompting dialogs.
553      * @param resources
554      */

555     protected IPromptCondition getPromptCondition(IResource[] resources) {
556         return getOverwriteLocalChangesPrompt(resources);
557     }
558 }
559
Popular Tags