]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 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.jdt.ui.actions; | |
12 | ||
13 | import java.lang.reflect.InvocationTargetException; | |
14 | import java.util.Comparator; | |
15 | ||
16 | import com.ibm.icu.text.Collator; | |
17 | ||
18 | import org.eclipse.core.runtime.Assert; | |
19 | ||
20 | import org.eclipse.jface.action.IAction; | |
21 | import org.eclipse.jface.action.IStatusLineManager; | |
22 | import org.eclipse.jface.dialogs.MessageDialog; | |
23 | import org.eclipse.jface.operation.IRunnableContext; | |
24 | import org.eclipse.jface.viewers.ILabelProvider; | |
25 | import org.eclipse.jface.viewers.ISelection; | |
26 | import org.eclipse.jface.viewers.IStructuredSelection; | |
27 | import org.eclipse.jface.viewers.StructuredSelection; | |
28 | import org.eclipse.jface.window.Window; | |
29 | ||
30 | import org.eclipse.jface.text.DocumentEvent; | |
31 | import org.eclipse.jface.text.IEditingSupport; | |
32 | import org.eclipse.jface.text.IEditingSupportRegistry; | |
33 | import org.eclipse.jface.text.IRegion; | |
34 | import org.eclipse.jface.text.IRewriteTarget; | |
35 | import org.eclipse.jface.text.ITextSelection; | |
36 | import org.eclipse.jface.text.source.ISourceViewer; | |
37 | ||
38 | import org.eclipse.ui.IEditorPart; | |
39 | import org.eclipse.ui.IObjectActionDelegate; | |
40 | import org.eclipse.ui.IWorkbenchPart; | |
41 | import org.eclipse.ui.IWorkbenchSite; | |
42 | import org.eclipse.ui.PlatformUI; | |
43 | import org.eclipse.ui.progress.IProgressService; | |
44 | ||
45 | import org.eclipse.jdt.core.ICompilationUnit; | |
46 | import org.eclipse.jdt.core.IJavaElement; | |
47 | import org.eclipse.jdt.core.ISourceRange; | |
48 | import org.eclipse.jdt.core.compiler.IProblem; | |
49 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
50 | import org.eclipse.jdt.core.search.TypeNameMatch; | |
51 | ||
52 | import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; | |
53 | import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation; | |
54 | import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation.IChooseImportQuery; | |
55 | import org.eclipse.jdt.internal.corext.util.History; | |
56 | import org.eclipse.jdt.internal.corext.util.Messages; | |
57 | import org.eclipse.jdt.internal.corext.util.QualifiedTypeNameHistory; | |
58 | ||
59 | import org.eclipse.jdt.ui.JavaUI; | |
60 | import org.eclipse.jdt.ui.SharedASTProvider; | |
61 | ||
62 | import org.eclipse.jdt.internal.ui.IJavaHelpContextIds; | |
63 | import org.eclipse.jdt.internal.ui.actions.ActionMessages; | |
64 | import org.eclipse.jdt.internal.ui.actions.ActionUtil; | |
65 | import org.eclipse.jdt.internal.ui.actions.MultiOrganizeImportAction; | |
66 | import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter; | |
67 | import org.eclipse.jdt.internal.ui.dialogs.MultiElementListSelectionDialog; | |
68 | import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; | |
69 | import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; | |
70 | import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings; | |
71 | import org.eclipse.jdt.internal.ui.util.ElementValidator; | |
72 | import org.eclipse.jdt.internal.ui.util.ExceptionHandler; | |
73 | import org.eclipse.jdt.internal.ui.util.TypeNameMatchLabelProvider; | |
74 | ||
75 | /** | |
76 | * Organizes the imports of a compilation unit. | |
77 | * <p> | |
78 | * The action is applicable to selections containing elements of | |
79 | * type <code>ICompilationUnit</code> or <code>IPackage | |
80 | * </code>. | |
81 | * | |
82 | * <p> | |
83 | * This class may be instantiated; it is not intended to be subclassed. | |
84 | * </p> | |
85 | * | |
86 | * @since 2.0 | |
87 | * | |
88 | * @noextend This class is not intended to be subclassed by clients. | |
89 | */ | |
90 | public class OrganizeImportsAction extends SelectionDispatchAction { | |
91 | ||
92 | private static final OrganizeImportComparator ORGANIZE_IMPORT_COMPARATOR= new OrganizeImportComparator(); | |
93 | ||
94 | private JavaEditor fEditor; | |
95 | /** <code>true</code> if the query dialog is showing. */ | |
96 | private boolean fIsQueryShowing= false; | |
97 | private final MultiOrganizeImportAction fCleanUpDelegate; | |
98 | ||
99 | /* (non-Javadoc) | |
100 | * Class implements IObjectActionDelegate | |
101 | */ | |
102 | public static class ObjectDelegate implements IObjectActionDelegate { | |
103 | private OrganizeImportsAction fAction; | |
104 | public void setActivePart(IAction action, IWorkbenchPart targetPart) { | |
105 | fAction= new OrganizeImportsAction(targetPart.getSite()); | |
106 | } | |
107 | public void run(IAction action) { | |
108 | fAction.run(); | |
109 | } | |
110 | public void selectionChanged(IAction action, ISelection selection) { | |
111 | if (fAction == null) | |
112 | action.setEnabled(false); | |
113 | } | |
114 | } | |
115 | ||
116 | private static final class OrganizeImportComparator implements Comparator<String> { | |
117 | ||
118 | public int compare(String o1, String o2) { | |
119 | if (o1.equals(o2)) | |
120 | return 0; | |
121 | ||
122 | History history= QualifiedTypeNameHistory.getDefault(); | |
123 | ||
124 | int pos1= history.getPosition(o1); | |
125 | int pos2= history.getPosition(o2); | |
126 | ||
127 | if (pos1 == pos2) | |
128 | return Collator.getInstance().compare(o1, o2); | |
129 | ||
130 | if (pos1 > pos2) { | |
131 | return -1; | |
132 | } else { | |
133 | return 1; | |
134 | } | |
135 | } | |
136 | ||
137 | } | |
138 | ||
139 | /** | |
140 | * Creates a new <code>OrganizeImportsAction</code>. The action requires | |
141 | * that the selection provided by the site's selection provider is of type <code> | |
142 | * org.eclipse.jface.viewers.IStructuredSelection</code>. | |
143 | * | |
144 | * @param site the site providing context information for this action | |
145 | */ | |
146 | public OrganizeImportsAction(IWorkbenchSite site) { | |
147 | super(site); | |
148 | ||
149 | fCleanUpDelegate= new MultiOrganizeImportAction(site); | |
150 | ||
151 | setText(ActionMessages.OrganizeImportsAction_label); | |
152 | setToolTipText(ActionMessages.OrganizeImportsAction_tooltip); | |
153 | setDescription(ActionMessages.OrganizeImportsAction_description); | |
154 | ||
155 | PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.ORGANIZE_IMPORTS_ACTION); | |
156 | } | |
157 | ||
158 | /** | |
159 | * Note: This constructor is for internal use only. Clients should not call this constructor. | |
160 | * @param editor the Java editor | |
161 | * | |
162 | * @noreference This constructor is not intended to be referenced by clients. | |
163 | */ | |
164 | public OrganizeImportsAction(JavaEditor editor) { | |
165 | super(editor.getEditorSite()); | |
166 | ||
167 | fEditor= editor; | |
168 | fCleanUpDelegate= new MultiOrganizeImportAction(editor); | |
169 | ||
170 | setText(ActionMessages.OrganizeImportsAction_label); | |
171 | setToolTipText(ActionMessages.OrganizeImportsAction_tooltip); | |
172 | setDescription(ActionMessages.OrganizeImportsAction_description); | |
173 | ||
174 | PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.ORGANIZE_IMPORTS_ACTION); | |
175 | ||
176 | setEnabled(fCleanUpDelegate.isEnabled()); | |
177 | } | |
178 | ||
179 | /* (non-Javadoc) | |
180 | * Method declared on SelectionDispatchAction. | |
181 | */ | |
182 | @Override | |
183 | public void selectionChanged(ITextSelection selection) { | |
184 | fCleanUpDelegate.selectionChanged(selection); | |
185 | setEnabled(fCleanUpDelegate.isEnabled()); | |
186 | } | |
187 | ||
188 | /* (non-Javadoc) | |
189 | * Method declared on SelectionDispatchAction. | |
190 | */ | |
191 | @Override | |
192 | public void selectionChanged(IStructuredSelection selection) { | |
193 | fCleanUpDelegate.selectionChanged(selection); | |
194 | setEnabled(fCleanUpDelegate.isEnabled()); | |
195 | } | |
196 | ||
197 | /* (non-Javadoc) | |
198 | * Method declared on SelectionDispatchAction. | |
199 | */ | |
200 | @Override | |
201 | public void run(ITextSelection selection) { | |
202 | ICompilationUnit cu= getCompilationUnit(fEditor); | |
203 | if (cu != null) { | |
204 | run(cu); | |
205 | } | |
206 | } | |
207 | ||
208 | private static ICompilationUnit getCompilationUnit(JavaEditor editor) { | |
209 | IJavaElement element= JavaUI.getEditorInputJavaElement(editor.getEditorInput()); | |
210 | if (!(element instanceof ICompilationUnit)) | |
211 | return null; | |
212 | ||
213 | return (ICompilationUnit)element; | |
214 | } | |
215 | ||
216 | /* (non-Javadoc) | |
217 | * Method declared on SelectionDispatchAction. | |
218 | */ | |
219 | @Override | |
220 | public void run(IStructuredSelection selection) { | |
221 | ICompilationUnit[] cus= fCleanUpDelegate.getCompilationUnits(selection); | |
222 | if (cus.length == 0) { | |
223 | MessageDialog.openInformation(getShell(), ActionMessages.OrganizeImportsAction_EmptySelection_title, ActionMessages.OrganizeImportsAction_EmptySelection_description); | |
224 | } else if (cus.length == 1) { | |
225 | run(cus[0]); | |
226 | } else { | |
227 | fCleanUpDelegate.run(selection); | |
228 | } | |
229 | } | |
230 | ||
231 | /** | |
232 | * Perform organize import on multiple compilation units. No editors are opened. | |
233 | * @param cus The compilation units to run on | |
234 | */ | |
235 | public void runOnMultiple(final ICompilationUnit[] cus) { | |
236 | if (cus.length == 0) | |
237 | return; | |
238 | ||
239 | fCleanUpDelegate.run(new StructuredSelection(cus)); | |
240 | } | |
241 | ||
242 | /** | |
243 | * Runs the organize import action on a single compilation unit | |
244 | * | |
245 | * @param cu The compilation unit to process | |
246 | */ | |
247 | public void run(ICompilationUnit cu) { | |
248 | ||
249 | JavaEditor editor= null; | |
250 | if (fEditor != null) { | |
251 | editor= fEditor; | |
252 | ||
253 | //organize imports from within editor -> editor has focus | |
254 | if (!ElementValidator.check(cu, getShell(), ActionMessages.OrganizeImportsAction_error_title, true)) | |
255 | return; | |
256 | } else { | |
257 | IEditorPart openEditor= EditorUtility.isOpenInEditor(cu); | |
258 | if (!(openEditor instanceof JavaEditor)) { | |
259 | fCleanUpDelegate.run(new StructuredSelection(cu)); | |
260 | return; | |
261 | } | |
262 | ||
263 | editor= (JavaEditor) openEditor; | |
264 | //organize imports from package explorer -> editor does not have focus | |
265 | if (!ElementValidator.check(cu, getShell(), ActionMessages.OrganizeImportsAction_error_title, false)) | |
266 | return; | |
267 | } | |
268 | ||
269 | Assert.isNotNull(editor); | |
270 | if (!ActionUtil.isEditable(editor, getShell(), cu)) | |
271 | return; | |
272 | ||
273 | CompilationUnit astRoot= SharedASTProvider.getAST(cu, SharedASTProvider.WAIT_ACTIVE_ONLY, null); | |
274 | ||
275 | CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(cu.getJavaProject()); | |
276 | OrganizeImportsOperation op= new OrganizeImportsOperation(cu, astRoot, settings.importIgnoreLowercase, !cu.isWorkingCopy(), true, createChooseImportQuery(editor)); | |
277 | ||
278 | IRewriteTarget target= (IRewriteTarget) editor.getAdapter(IRewriteTarget.class); | |
279 | if (target != null) { | |
280 | target.beginCompoundChange(); | |
281 | } | |
282 | ||
283 | IProgressService progressService= PlatformUI.getWorkbench().getProgressService(); | |
284 | IRunnableContext context= getSite().getWorkbenchWindow(); | |
285 | if (context == null) { | |
286 | context= progressService; | |
287 | } | |
288 | IEditingSupport helper= createViewerHelper(); | |
289 | try { | |
290 | registerHelper(helper, editor); | |
291 | progressService.runInUI(context, new WorkbenchRunnableAdapter(op, op.getScheduleRule()), op.getScheduleRule()); | |
292 | IProblem parseError= op.getParseError(); | |
293 | if (parseError != null) { | |
294 | String message= Messages.format(ActionMessages.OrganizeImportsAction_single_error_parse, parseError.getMessage()); | |
295 | MessageDialog.openInformation(getShell(), ActionMessages.OrganizeImportsAction_error_title, message); | |
296 | if (parseError.getSourceStart() != -1) { | |
297 | editor.selectAndReveal(parseError.getSourceStart(), parseError.getSourceEnd() - parseError.getSourceStart() + 1); | |
298 | } | |
299 | } else { | |
300 | setStatusBarMessage(getOrganizeInfo(op), editor); | |
301 | } | |
302 | } catch (InvocationTargetException e) { | |
303 | ExceptionHandler.handle(e, getShell(), ActionMessages.OrganizeImportsAction_error_title, ActionMessages.OrganizeImportsAction_error_message); | |
304 | } catch (InterruptedException e) { | |
305 | } finally { | |
306 | deregisterHelper(helper, editor); | |
307 | if (target != null) { | |
308 | target.endCompoundChange(); | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | private String getOrganizeInfo(OrganizeImportsOperation op) { | |
314 | int nImportsAdded= op.getNumberOfImportsAdded(); | |
315 | if (nImportsAdded >= 0) { | |
316 | if (nImportsAdded == 1) { | |
317 | return ActionMessages.OrganizeImportsAction_summary_added_singular; | |
318 | } else { | |
319 | return Messages.format(ActionMessages.OrganizeImportsAction_summary_added_plural, String.valueOf(nImportsAdded)); | |
320 | } | |
321 | } else { | |
322 | if (nImportsAdded == -1) { | |
323 | return ActionMessages.OrganizeImportsAction_summary_removed_singular; | |
324 | } else { | |
325 | return Messages.format(ActionMessages.OrganizeImportsAction_summary_removed_plural, String.valueOf(-nImportsAdded)); | |
326 | } | |
327 | } | |
328 | } | |
329 | ||
330 | private IChooseImportQuery createChooseImportQuery(final JavaEditor editor) { | |
331 | return new IChooseImportQuery() { | |
332 | public TypeNameMatch[] chooseImports(TypeNameMatch[][] openChoices, ISourceRange[] ranges) { | |
333 | return doChooseImports(openChoices, ranges, editor); | |
334 | } | |
335 | }; | |
336 | } | |
337 | ||
338 | private TypeNameMatch[] doChooseImports(TypeNameMatch[][] openChoices, final ISourceRange[] ranges, final JavaEditor editor) { | |
339 | // remember selection | |
340 | ISelection sel= editor.getSelectionProvider().getSelection(); | |
341 | TypeNameMatch[] result= null; | |
342 | ILabelProvider labelProvider= new TypeNameMatchLabelProvider(TypeNameMatchLabelProvider.SHOW_FULLYQUALIFIED); | |
343 | ||
344 | MultiElementListSelectionDialog dialog= new MultiElementListSelectionDialog(getShell(), labelProvider) { | |
345 | @Override | |
346 | protected void handleSelectionChanged() { | |
347 | super.handleSelectionChanged(); | |
348 | // show choices in editor | |
349 | doListSelectionChanged(getCurrentPage(), ranges, editor); | |
350 | } | |
351 | }; | |
352 | fIsQueryShowing= true; | |
353 | dialog.setTitle(ActionMessages.OrganizeImportsAction_selectiondialog_title); | |
354 | dialog.setMessage(ActionMessages.OrganizeImportsAction_selectiondialog_message); | |
355 | dialog.setElements(openChoices); | |
356 | dialog.setComparator(ORGANIZE_IMPORT_COMPARATOR); | |
357 | if (dialog.open() == Window.OK) { | |
358 | Object[] res= dialog.getResult(); | |
359 | result= new TypeNameMatch[res.length]; | |
360 | for (int i= 0; i < res.length; i++) { | |
361 | Object[] array= (Object[]) res[i]; | |
362 | if (array.length > 0) { | |
363 | result[i]= (TypeNameMatch) array[0]; | |
364 | QualifiedTypeNameHistory.remember(result[i].getFullyQualifiedName()); | |
365 | } | |
366 | } | |
367 | } | |
368 | // restore selection | |
369 | if (sel instanceof ITextSelection) { | |
370 | ITextSelection textSelection= (ITextSelection) sel; | |
371 | editor.selectAndReveal(textSelection.getOffset(), textSelection.getLength()); | |
372 | } | |
373 | fIsQueryShowing= false; | |
374 | return result; | |
375 | } | |
376 | ||
377 | private void doListSelectionChanged(int page, ISourceRange[] ranges, JavaEditor editor) { | |
378 | if (ranges != null && page >= 0 && page < ranges.length) { | |
379 | ISourceRange range= ranges[page]; | |
380 | editor.selectAndReveal(range.getOffset(), range.getLength()); | |
381 | } | |
382 | } | |
383 | ||
384 | private void setStatusBarMessage(String message, JavaEditor editor) { | |
385 | IStatusLineManager manager= editor.getEditorSite().getActionBars().getStatusLineManager(); | |
386 | manager.setMessage(message); | |
387 | } | |
388 | ||
389 | private IEditingSupport createViewerHelper() { | |
390 | return new IEditingSupport() { | |
391 | public boolean isOriginator(DocumentEvent event, IRegion subjectRegion) { | |
392 | return true; // assume true, since we only register while we are active | |
393 | } | |
394 | public boolean ownsFocusShell() { | |
395 | return fIsQueryShowing; | |
396 | } | |
397 | ||
398 | }; | |
399 | } | |
400 | ||
401 | private void registerHelper(IEditingSupport helper, JavaEditor editor) { | |
402 | ISourceViewer viewer= editor.getViewer(); | |
403 | if (viewer instanceof IEditingSupportRegistry) { | |
404 | IEditingSupportRegistry registry= (IEditingSupportRegistry) viewer; | |
405 | registry.register(helper); | |
406 | } | |
407 | } | |
408 | ||
409 | private void deregisterHelper(IEditingSupport helper, JavaEditor editor) { | |
410 | ISourceViewer viewer= editor.getViewer(); | |
411 | if (viewer instanceof IEditingSupportRegistry) { | |
412 | IEditingSupportRegistry registry= (IEditingSupportRegistry) viewer; | |
413 | registry.unregister(helper); | |
414 | } | |
415 | } | |
416 | } |