]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2008, 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 | * Mateusz Matela <mateusz.matela@gmail.com> - [code manipulation] [dcr] toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=26070 | |
11 | * Mateusz Matela <mateusz.matela@gmail.com> - [toString] toString() generator: Fields in declaration order - https://bugs.eclipse.org/bugs/show_bug.cgi?id=279924 | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.jdt.ui.actions; | |
14 | ||
15 | ||
16 | import java.lang.reflect.InvocationTargetException; | |
17 | ||
18 | import org.eclipse.swt.widgets.Shell; | |
19 | ||
20 | import org.eclipse.core.runtime.CoreException; | |
21 | import org.eclipse.core.runtime.jobs.ISchedulingRule; | |
22 | ||
23 | import org.eclipse.core.resources.IWorkspaceRunnable; | |
24 | import org.eclipse.core.resources.ResourcesPlugin; | |
25 | ||
26 | import org.eclipse.jface.dialogs.Dialog; | |
27 | import org.eclipse.jface.dialogs.MessageDialog; | |
28 | import org.eclipse.jface.operation.IRunnableContext; | |
29 | import org.eclipse.jface.viewers.IStructuredSelection; | |
30 | import org.eclipse.jface.window.Window; | |
31 | ||
32 | import org.eclipse.jface.text.IRewriteTarget; | |
33 | import org.eclipse.jface.text.ITextSelection; | |
34 | ||
35 | import org.eclipse.ui.IEditorPart; | |
36 | import org.eclipse.ui.IWorkbenchSite; | |
37 | import org.eclipse.ui.PlatformUI; | |
38 | ||
39 | import org.eclipse.ltk.core.refactoring.RefactoringStatus; | |
40 | import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; | |
41 | import org.eclipse.ltk.ui.refactoring.RefactoringUI; | |
42 | ||
43 | import org.eclipse.jdt.core.IClassFile; | |
44 | import org.eclipse.jdt.core.ICompilationUnit; | |
45 | import org.eclipse.jdt.core.IJavaElement; | |
46 | import org.eclipse.jdt.core.IJavaProject; | |
47 | import org.eclipse.jdt.core.IMember; | |
48 | import org.eclipse.jdt.core.IOpenable; | |
49 | import org.eclipse.jdt.core.ISourceReference; | |
50 | import org.eclipse.jdt.core.IType; | |
51 | import org.eclipse.jdt.core.JavaModelException; | |
52 | import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; | |
53 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
54 | import org.eclipse.jdt.core.dom.ITypeBinding; | |
55 | import org.eclipse.jdt.core.dom.NodeFinder; | |
56 | ||
57 | import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; | |
58 | import org.eclipse.jdt.internal.corext.dom.ASTNodes; | |
59 | import org.eclipse.jdt.internal.corext.fix.CleanUpConstants; | |
60 | import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; | |
61 | import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; | |
62 | import org.eclipse.jdt.internal.corext.util.JavaModelUtil; | |
63 | import org.eclipse.jdt.internal.corext.util.Messages; | |
64 | ||
65 | import org.eclipse.jdt.ui.JavaUI; | |
66 | import org.eclipse.jdt.ui.PreferenceConstants; | |
67 | import org.eclipse.jdt.ui.cleanup.CleanUpOptions; | |
68 | ||
69 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
70 | import org.eclipse.jdt.internal.ui.actions.ActionMessages; | |
71 | import org.eclipse.jdt.internal.ui.actions.ActionUtil; | |
72 | import org.eclipse.jdt.internal.ui.actions.SelectionConverter; | |
73 | import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter; | |
74 | import org.eclipse.jdt.internal.ui.dialogs.SourceActionDialog; | |
75 | import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; | |
76 | import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor; | |
77 | import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings; | |
78 | import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext; | |
79 | import org.eclipse.jdt.internal.ui.util.ElementValidator; | |
80 | import org.eclipse.jdt.internal.ui.util.ExceptionHandler; | |
81 | import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; | |
82 | ||
83 | /** | |
84 | * An abstract class containing elements common for <code>GenerateHashCodeEqualsAction</code> and | |
85 | * <code>GenerateToStringAction</code> | |
86 | * | |
87 | * since 3.5 | |
88 | */ | |
89 | public abstract class GenerateMethodAbstractAction extends SelectionDispatchAction { | |
90 | ||
91 | ||
92 | CompilationUnitEditor fEditor; | |
93 | CompilationUnit fUnit; | |
94 | ITypeBinding fTypeBinding; | |
95 | ||
96 | protected GenerateMethodAbstractAction(IWorkbenchSite site) { | |
97 | super(site); | |
98 | } | |
99 | ||
100 | static RefactoringStatusContext createRefactoringStatusContext(IJavaElement element) { | |
101 | if (element instanceof IMember) { | |
102 | return JavaStatusContext.create((IMember) element); | |
103 | } | |
104 | if (element instanceof ISourceReference) { | |
105 | IOpenable openable= element.getOpenable(); | |
106 | try { | |
107 | if (openable instanceof ICompilationUnit) { | |
108 | return JavaStatusContext.create((ICompilationUnit) openable, ((ISourceReference) element).getSourceRange()); | |
109 | } else if (openable instanceof IClassFile) { | |
110 | return JavaStatusContext.create((IClassFile) openable, ((ISourceReference) element).getSourceRange()); | |
111 | } | |
112 | } catch (JavaModelException e) { | |
113 | // ignore | |
114 | } | |
115 | } | |
116 | return null; | |
117 | } | |
118 | ||
119 | /** | |
120 | * Can this action be enabled on the specified selection? | |
121 | * | |
122 | * @param selection the selection to test | |
123 | * @return <code>true</code> if it can be enabled, <code>false</code> | |
124 | * otherwise | |
125 | * @throws JavaModelException if the kind of the selection cannot be | |
126 | * determined | |
127 | */ | |
128 | boolean canEnable(final IStructuredSelection selection) throws JavaModelException { | |
129 | if (selection.size() == 1) { | |
130 | final Object element= selection.getFirstElement(); | |
131 | if (element instanceof IType) { | |
132 | final IType type= (IType) element; | |
133 | return type.getCompilationUnit() != null && type.isClass(); | |
134 | } | |
135 | if (element instanceof ICompilationUnit) | |
136 | return true; | |
137 | } | |
138 | return false; | |
139 | } | |
140 | ||
141 | /** | |
142 | * Returns the single selected type from the specified selection. | |
143 | * | |
144 | * @param selection the selection | |
145 | * @return a single selected type, or <code>null</code> | |
146 | * @throws JavaModelException if the kind of the selection cannot be | |
147 | * determined | |
148 | */ | |
149 | IType getSelectedType(final IStructuredSelection selection) throws JavaModelException { | |
150 | if (selection.size() == 1 && selection.getFirstElement() instanceof IType) { | |
151 | final IType type= (IType) selection.getFirstElement(); | |
152 | if (type.getCompilationUnit() != null && type.isClass()) | |
153 | return type; | |
154 | } else if (selection.getFirstElement() instanceof ICompilationUnit) { | |
155 | final ICompilationUnit unit= (ICompilationUnit) selection.getFirstElement(); | |
156 | final IType type= unit.findPrimaryType(); | |
157 | if (type != null && type.isClass()) | |
158 | return type; | |
159 | } | |
160 | return null; | |
161 | } | |
162 | ||
163 | @Override | |
164 | public void run(IStructuredSelection selection) { | |
165 | try { | |
166 | checkAndRun(getSelectedType(selection)); | |
167 | } catch (CoreException exception) { | |
168 | ExceptionHandler.handle(exception, getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_error_cannot_create); | |
169 | } | |
170 | } | |
171 | ||
172 | ||
173 | @Override | |
174 | public void run(ITextSelection selection) { | |
175 | try { | |
176 | checkAndRun(SelectionConverter.getTypeAtOffset(fEditor)); | |
177 | } catch (CoreException e) { | |
178 | ExceptionHandler.handle(e, getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_error_cannot_create); | |
179 | } | |
180 | } | |
181 | ||
182 | void checkAndRun(IType type) throws CoreException { | |
183 | if (type == null) { | |
184 | MessageDialog.openInformation(getShell(), getErrorCaption(), | |
185 | ActionMessages.GenerateMethodAbstractAction_error_not_applicable); | |
186 | notifyResult(false); | |
187 | } | |
188 | if (!ElementValidator.check(type, getShell(), getErrorCaption(), false) | |
189 | || ! ActionUtil.isEditable(fEditor, getShell(), type)) { | |
190 | notifyResult(false); | |
191 | return; | |
192 | } | |
193 | if (type == null) { | |
194 | MessageDialog.openError(getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_error_removed_type); | |
195 | notifyResult(false); | |
196 | return; | |
197 | } | |
198 | if (type.isAnnotation()) { | |
199 | MessageDialog.openInformation(getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_annotation_not_applicable); | |
200 | notifyResult(false); | |
201 | return; | |
202 | } | |
203 | if (type.isInterface()) { | |
204 | MessageDialog.openInformation(getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_interface_not_applicable); | |
205 | notifyResult(false); | |
206 | return; | |
207 | } | |
208 | if (type.isEnum()) { | |
209 | MessageDialog.openInformation(getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_enum_not_applicable); | |
210 | notifyResult(false); | |
211 | return; | |
212 | } | |
213 | if (type.isAnonymous()) { | |
214 | MessageDialog.openError(getShell(), getErrorCaption(), ActionMessages.GenerateMethodAbstractAction_anonymous_type_not_applicable); | |
215 | notifyResult(false); | |
216 | return; | |
217 | } | |
218 | run(getShell(), type); | |
219 | } | |
220 | ||
221 | /** | |
222 | * Runs the action. | |
223 | * | |
224 | * @param shell the shell to use | |
225 | * @param type the type to generate stubs for | |
226 | * @throws CoreException if an error occurs | |
227 | */ | |
228 | void run(Shell shell, IType type) throws CoreException { | |
229 | ||
230 | initialize(type); | |
231 | ||
232 | boolean regenerate= false; | |
233 | if (isMethodAlreadyImplemented(fTypeBinding)) { | |
234 | regenerate= MessageDialog.openQuestion(getShell(), getErrorCaption(), Messages.format(ActionMessages.GenerateMethodAbstractAction_already_has_this_method_error, new String[] { | |
235 | BasicElementLabels.getJavaElementName(fTypeBinding.getQualifiedName()), getAlreadyImplementedErrorMethodName() })); | |
236 | if (!regenerate) { | |
237 | notifyResult(false); | |
238 | return; | |
239 | } | |
240 | } | |
241 | ||
242 | if (!generateCandidates()) { | |
243 | MessageDialog.openInformation(getShell(), getErrorCaption(), | |
244 | getNoMembersError()); | |
245 | notifyResult(false); | |
246 | return; | |
247 | } | |
248 | ||
249 | final SourceActionDialog dialog= createDialog(shell, type); | |
250 | final int dialogResult= dialog.open(); | |
251 | ||
252 | if (dialogResult == Window.OK) { | |
253 | ||
254 | final Object[] selected= dialog.getResult(); | |
255 | if (selected == null) { | |
256 | notifyResult(false); | |
257 | return; | |
258 | } | |
259 | final CodeGenerationSettings settings= createSettings(type, dialog); | |
260 | final IWorkspaceRunnable operation= createOperation(selected, settings, regenerate, type, dialog.getElementPosition()); | |
261 | ||
262 | ITypeBinding superclass= fTypeBinding.getSuperclass(); | |
263 | RefactoringStatus status= new RefactoringStatus(); | |
264 | ||
265 | status.merge(checkGeneralConditions(type, settings, selected)); | |
266 | ||
267 | if (!"java.lang.Object".equals(superclass.getQualifiedName())) { //$NON-NLS-1$ | |
268 | status.merge(checkSuperClass(superclass)); | |
269 | } | |
270 | ||
271 | for (int i= 0; i < selected.length; i++) { | |
272 | status.merge(checkMember(selected[i])); | |
273 | } | |
274 | ||
275 | if (status.hasEntries()) { | |
276 | Dialog d= RefactoringUI.createLightWeightStatusDialog(status, getShell(), getErrorCaption()); | |
277 | if (d.open() != Window.OK) { | |
278 | notifyResult(false); | |
279 | return; | |
280 | } | |
281 | } | |
282 | ||
283 | final IEditorPart editor= JavaUI.openInEditor(type.getCompilationUnit()); | |
284 | final IRewriteTarget target= editor != null ? (IRewriteTarget) editor.getAdapter(IRewriteTarget.class) : null; | |
285 | ||
286 | if (target != null) | |
287 | target.beginCompoundChange(); | |
288 | try { | |
289 | IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow(); | |
290 | if (context == null) | |
291 | context= new BusyIndicatorRunnableContext(); | |
292 | ISchedulingRule schedulingRule= ResourcesPlugin.getWorkspace().getRoot(); | |
293 | PlatformUI.getWorkbench().getProgressService().runInUI(context, new WorkbenchRunnableAdapter(operation, schedulingRule), schedulingRule); | |
294 | } catch (InvocationTargetException exception) { | |
295 | ExceptionHandler.handle(exception, shell, getErrorCaption(), null); | |
296 | } catch (InterruptedException exception) { | |
297 | // Do nothing. Operation has been canceled by user. | |
298 | } finally { | |
299 | if (target != null) | |
300 | target.endCompoundChange(); | |
301 | } | |
302 | } | |
303 | notifyResult(dialogResult == Window.OK); | |
304 | } | |
305 | ||
306 | ||
307 | /** | |
308 | * | |
309 | * @param type the type for which a method is created | |
310 | * @param dialog the dialog box where the user has defined his preferences | |
311 | * @return settings applicable for this action | |
312 | */ | |
313 | CodeGenerationSettings createSettings(IType type, SourceActionDialog dialog) { | |
314 | CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject()); | |
315 | return settings.generated_9083001889753781598(dialog, this); | |
316 | } | |
317 | ||
318 | /** | |
319 | * @return Message to be shown when a method cannot be generated due to lack of appropriate members | |
320 | */ | |
321 | abstract String getNoMembersError(); | |
322 | ||
323 | /** | |
324 | * @return Caption for a dialog with error message | |
325 | */ | |
326 | abstract String getErrorCaption(); | |
327 | ||
328 | abstract IWorkspaceRunnable createOperation(Object[] selectedBindings, CodeGenerationSettings settings, boolean regenerate, IJavaElement type, IJavaElement elementPosition); | |
329 | ||
330 | /** | |
331 | * Checks whether general requirements are fulfilled | |
332 | * | |
333 | * @param type the type for which a method is created | |
334 | * @param settings preferences define by the user | |
335 | * @param selected the type's members selected to be used in generated method | |
336 | * @return RefactoringStatus containing information about eventual problems | |
337 | */ | |
338 | abstract RefactoringStatus checkGeneralConditions(IType type, CodeGenerationSettings settings, Object[] selected); | |
339 | ||
340 | /** | |
341 | * Checks whether a member fulfills requirements expected by method generator | |
342 | * | |
343 | * @param object member binding to be checked | |
344 | * @return RefactoringStatus containing information about eventual problems | |
345 | */ | |
346 | abstract RefactoringStatus checkMember(Object object); | |
347 | ||
348 | /** | |
349 | * Checks whether the superclass fulfills requirements expected by method generator | |
350 | * @param superclass superclass type binding to be checked | |
351 | * @return RefactoringStatus containing information about eventual problems | |
352 | */ | |
353 | abstract RefactoringStatus checkSuperClass(ITypeBinding superclass); | |
354 | ||
355 | /** | |
356 | * Creates the action's main dialog. | |
357 | * | |
358 | * @param shell the shell to use | |
359 | * @param type the type to generate stubs for | |
360 | * @return a dialog with generator-specific options | |
361 | * @throws JavaModelException if creation of the dialog fails | |
362 | */ | |
363 | abstract SourceActionDialog createDialog(Shell shell, IType type) throws JavaModelException; | |
364 | ||
365 | /** | |
366 | * Chooses type members that can be used in generated method. | |
367 | * Returns false, if there are no such members and the method cannot | |
368 | * be generated | |
369 | * @return true, if the method can be generated (i.e. there are appropriate member fields) | |
370 | * @throws JavaModelException if an error in java model occurs | |
371 | */ | |
372 | abstract boolean generateCandidates() throws JavaModelException; | |
373 | ||
374 | /** | |
375 | * | |
376 | * @return The message displayed when the method is already implemented in the type. | |
377 | */ | |
378 | abstract String getAlreadyImplementedErrorMethodName(); | |
379 | ||
380 | /** | |
381 | * | |
382 | * @param typeBinding Type to be checked | |
383 | * @return true if given type already contains the method to be generated | |
384 | */ | |
385 | abstract boolean isMethodAlreadyImplemented(ITypeBinding typeBinding); | |
386 | ||
387 | boolean useBlocks(IJavaProject project) { | |
388 | if (CleanUpOptions.TRUE.equals(PreferenceConstants.getPreference(CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS, project))) { | |
389 | return CleanUpOptions.TRUE.equals(PreferenceConstants.getPreference(CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_ALWAYS, project)) | |
390 | || CleanUpOptions.TRUE.equals(PreferenceConstants.getPreference(CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_NO_FOR_RETURN_AND_THROW, project)); | |
391 | } | |
392 | return false; | |
393 | } | |
394 | ||
395 | void initialize(IType type) throws JavaModelException { | |
396 | RefactoringASTParser parser= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL); | |
397 | fUnit= parser.parse(type.getCompilationUnit(), true); | |
398 | fTypeBinding= null; | |
399 | // type cannot be anonymous | |
400 | final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(NodeFinder.perform(fUnit, type.getNameRange()), | |
401 | AbstractTypeDeclaration.class); | |
402 | if (declaration != null) | |
403 | fTypeBinding= declaration.resolveBinding(); | |
404 | } | |
405 | ||
406 | @Override | |
407 | public void selectionChanged(IStructuredSelection selection) { | |
408 | try { | |
409 | setEnabled(canEnable(selection)); | |
410 | } catch (JavaModelException exception) { | |
411 | if (JavaModelUtil.isExceptionToBeLogged(exception)) | |
412 | JavaPlugin.log(exception); | |
413 | setEnabled(false); | |
414 | } | |
415 | } | |
416 | ||
417 | @Override | |
418 | public void selectionChanged(ITextSelection selection) { | |
419 | // Do nothing | |
420 | } | |
421 | ||
422 | } |