-
Notifications
You must be signed in to change notification settings - Fork 155
/
CloseUnrelatedProjectsAction.java
257 lines (237 loc) · 8.79 KB
/
CloseUnrelatedProjectsAction.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*******************************************************************************
* Copyright (c) 2006, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Dina Sayed, dsayed@eg.ibm.com, IBM - bug 269844
* Andrey Loskutov <loskutov@gmx.de> - generified interface, bug 462760
* Mickael Istria (Red Hat Inc.) - Bug 486901
* Lucas Bullen (Red Hat Inc.) - Bug 522096 - "Close Projects" on working set
*******************************************************************************/
package org.eclipse.ui.actions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.internal.ide.IDEInternalPreferences;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
import org.eclipse.ui.internal.ide.misc.DisjointSet;
/**
* This action closes all projects that are unrelated to the selected projects. A
* project is unrelated if it is not directly or transitively referenced by one
* of the selected projects, and does not directly or transitively reference
* one of the selected projects.
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*
* @see IDEActionFactory#CLOSE_UNRELATED_PROJECTS
* @since 3.3
*/
public class CloseUnrelatedProjectsAction extends CloseResourceAction {
/**
* The id of this action.
*/
@SuppressWarnings("hiding")
public static final String ID = PlatformUI.PLUGIN_ID + ".CloseUnrelatedProjectsAction"; //$NON-NLS-1$
private List<IResource> projectsToClose = new ArrayList<>();
private boolean selectionDirty = true;
private List<? extends IResource> oldSelection = Collections.emptyList();
/**
* Builds the connected component set for the input projects.
* The result is a DisjointSet where all related projects belong
* to the same set.
*/
private static DisjointSet<IProject> buildConnectedComponents(IProject[] projects) {
//initially each vertex is in a set by itself
DisjointSet<IProject> set = new DisjointSet<>();
for (IProject project : projects) {
set.makeSet(project);
}
for (IProject project : projects) {
try {
IProject[] references = project.getReferencedProjects();
//each reference represents an edge in the project reference
//digraph from projects[i] -> references[j]
for (IProject reference : references) {
IProject setOne = set.findSet(project);
//note that referenced projects may not exist in the workspace
IProject setTwo = set.findSet(reference);
//these two projects are related, so join their sets
if (setOne != null && setTwo != null && setOne != setTwo) {
set.union(setOne, setTwo);
}
}
} catch (CoreException e) {
//assume inaccessible projects have no references
}
}
return set;
}
/**
* Creates this action.
*
* @param shell
* The shell to use for parenting any dialogs created by this
* action.
*
* @deprecated {@link #CloseUnrelatedProjectsAction(IShellProvider)}
*/
@Deprecated
public CloseUnrelatedProjectsAction(Shell shell) {
super(shell, IDEWorkbenchMessages.CloseUnrelatedProjectsAction_text);
initAction();
}
/**
* Creates this action.
*
* @param provider
* The shell to use for parenting any dialogs created by this
* action.
* @since 3.4
*/
public CloseUnrelatedProjectsAction(IShellProvider provider){
super(provider, IDEWorkbenchMessages.CloseUnrelatedProjectsAction_text,
IDEWorkbenchMessages.CloseUnrelatedProjectsAction_toolTip,
IDEWorkbenchMessages.CloseUnrelatedProjectsAction_text_plural,
IDEWorkbenchMessages.CloseUnrelatedProjectsAction_toolTip_plural);
initAction();
}
@Override
public void run() {
if (promptForConfirmation()) {
super.run();
}
}
/**
* Returns whether to close unrelated projects.
* Consults the preference and prompts the user if necessary.
*
* @return <code>true</code> if unrelated projects should be closed, and
* <code>false</code> otherwise.
*/
private boolean promptForConfirmation() {
IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
if (store.getBoolean(IDEInternalPreferences.CLOSE_UNRELATED_PROJECTS)) {
return true;
}
// get first project name
List<? extends IResource> selection = super.getSelectedResources();
int selectionSize = selection.size();
if (selectionSize == 0) {
return true;
}
String message = null;
if (selectionSize == 1) { // if one project is selected then print its name
IResource firstSelected = selection.get(0);
String projectName = null;
if (firstSelected instanceof IProject) {
projectName = firstSelected.getName();
}
message = NLS.bind(IDEWorkbenchMessages.CloseUnrelatedProjectsAction_confirmMsg1, projectName);
} else // if more then one project is selected then print there number
message = NLS.bind(IDEWorkbenchMessages.CloseUnrelatedProjectsAction_confirmMsgN, selectionSize);
MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(
getShell(), IDEWorkbenchMessages.CloseUnrelatedProjectsAction_toolTip,
message, IDEWorkbenchMessages.CloseUnrelatedProjectsAction_AlwaysClose,
false, null, null);
if (dialog.getReturnCode() != IDialogConstants.OK_ID) {
return false;
}
store.setValue(IDEInternalPreferences.CLOSE_UNRELATED_PROJECTS, dialog.getToggleState());
return true;
}
/**
* Initializes for the constructor.
*/
private void initAction(){
setId(ID);
setToolTipText(IDEWorkbenchMessages.CloseUnrelatedProjectsAction_toolTip);
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IIDEHelpContextIds.CLOSE_UNRELATED_PROJECTS_ACTION);
}
@Override
protected void clearCache() {
super.clearCache();
oldSelection = Collections.emptyList();
selectionDirty = true;
}
/**
* Computes the related projects of the selection.
*/
private List<IResource> computeRelated(List<? extends IResource> selection) {
if (selection.contains(ResourcesPlugin.getWorkspace().getRoot())) {
return new ArrayList<>();
}
//build the connected component set for all projects in the workspace
DisjointSet<IProject> set = buildConnectedComponents(ResourcesPlugin.getWorkspace().getRoot().getProjects());
//remove the connected components that the selected projects are in
for (IResource resource : selection) {
IProject project = resource.getProject();
if (project != null) {
set.removeSet(project);
}
}
//the remainder of the projects in the disjoint set are unrelated to the selection
List<IResource> projects = new ArrayList<>();
set.toList(projects);
return projects;
}
@Override
protected List<? extends IResource> getSelectedResources() {
if (selectionDirty) {
List<? extends IResource> newSelection = super.getSelectedResources();
if (!oldSelection.equals(newSelection)) {
oldSelection = newSelection;
projectsToClose = computeRelated(newSelection);
}
selectionDirty = false;
}
return projectsToClose;
}
/**
* Handles a resource changed event by updating the enablement
* when projects change.
* <p>
* This method overrides the super-type implementation to update
* the selection when the open state or description of any project changes.
*/
@Override
public void resourceChanged(IResourceChangeEvent event) {
// don't bother looking at delta if selection not applicable
if (selectionIsOfType(IResource.PROJECT)) {
IResourceDelta delta = event.getDelta();
if (delta != null) {
IResourceDelta[] projDeltas = delta.getAffectedChildren(IResourceDelta.CHANGED);
for (IResourceDelta projDelta : projDeltas) {
//changing either the description or the open state can affect enablement
if ((projDelta.getFlags() & (IResourceDelta.OPEN | IResourceDelta.DESCRIPTION)) != 0) {
selectionChanged(getStructuredSelection());
return;
}
}
}
}
}
}