]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/ui/org/eclipse/jdt/ui/actions/GenerateNewConstructorUsingFieldsAction.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / ui / org / eclipse / jdt / ui / actions / GenerateNewConstructorUsingFieldsAction.java
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.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.List;
18
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.core.runtime.NullProgressMonitor;
21
22 import org.eclipse.jface.dialogs.MessageDialog;
23 import org.eclipse.jface.operation.IRunnableContext;
24 import org.eclipse.jface.viewers.IStructuredSelection;
25 import org.eclipse.jface.window.Window;
26
27 import org.eclipse.jface.text.IRewriteTarget;
28 import org.eclipse.jface.text.ITextSelection;
29
30 import org.eclipse.ui.IEditorPart;
31 import org.eclipse.ui.IWorkbenchSite;
32 import org.eclipse.ui.PlatformUI;
33
34 import org.eclipse.jdt.core.ICompilationUnit;
35 import org.eclipse.jdt.core.IField;
36 import org.eclipse.jdt.core.IJavaElement;
37 import org.eclipse.jdt.core.IType;
38 import org.eclipse.jdt.core.JavaModelException;
39 import org.eclipse.jdt.core.dom.AST;
40 import org.eclipse.jdt.core.dom.ASTNode;
41 import org.eclipse.jdt.core.dom.CompilationUnit;
42 import org.eclipse.jdt.core.dom.IMethodBinding;
43 import org.eclipse.jdt.core.dom.ITypeBinding;
44 import org.eclipse.jdt.core.dom.IVariableBinding;
45 import org.eclipse.jdt.core.dom.Modifier;
46 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
47
48 import org.eclipse.jdt.internal.corext.codemanipulation.AddCustomConstructorOperation;
49 import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
50 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
51 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
52 import org.eclipse.jdt.internal.corext.dom.Bindings;
53 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
54 import org.eclipse.jdt.internal.corext.util.JdtFlags;
55
56 import org.eclipse.jdt.ui.JavaUI;
57 import org.eclipse.jdt.ui.SharedASTProvider;
58
59 import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
60 import org.eclipse.jdt.internal.ui.JavaPlugin;
61 import org.eclipse.jdt.internal.ui.actions.ActionMessages;
62 import org.eclipse.jdt.internal.ui.actions.ActionUtil;
63 import org.eclipse.jdt.internal.ui.actions.GenerateConstructorUsingFieldsContentProvider;
64 import org.eclipse.jdt.internal.ui.actions.GenerateConstructorUsingFieldsSelectionDialog;
65 import org.eclipse.jdt.internal.ui.actions.GenerateConstructorUsingFieldsValidator;
66 import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
67 import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
68 import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
69 import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
70 import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext;
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.viewsupport.BindingLabelProvider;
74
75 /**
76  * Creates constructors for a type based on existing fields.
77  * <p>
78  * Will open the parent compilation unit in a Java editor. Opens a dialog with a list
79  * fields from which a constructor will be generated. User is able to check or uncheck
80  * items before constructors are generated. The result is unsaved, so the user can decide
81  * if the changes are acceptable.
82  * <p>
83  * The action is applicable to structured selections containing elements of type
84  * <code>IType</code>.
85  *
86  * <p>
87  * This class may be instantiated; it is not intended to be subclassed.
88  * </p>
89  *
90  * @since 3.0
91  *
92  * @noextend This class is not intended to be subclassed by clients.
93  */
94 public class GenerateNewConstructorUsingFieldsAction extends SelectionDispatchAction {
95
96         private CompilationUnitEditor fEditor;
97
98         /**
99          * Note: This constructor is for internal use only. Clients should not call this
100          * constructor.
101          *
102          * @param editor the compilation unit editor
103          *
104          * @noreference This constructor is not intended to be referenced by clients.
105          */
106         public GenerateNewConstructorUsingFieldsAction(CompilationUnitEditor editor) {
107                 this(editor.getEditorSite());
108                 fEditor= editor;
109                 setEnabled(checkEnabledEditor());
110         }
111
112         /**
113          * Creates a new <code>GenerateConstructorUsingFieldsAction</code>. The action requires
114          * that the selection provided by the site's selection provider is of type <code>
115          * org.eclipse.jface.viewers.IStructuredSelection</code>.
116          *
117          * @param site the site providing context information for this action
118          */
119         public GenerateNewConstructorUsingFieldsAction(IWorkbenchSite site) {
120                 super(site);
121                 setText(ActionMessages.GenerateConstructorUsingFieldsAction_label);
122                 setDescription(ActionMessages.GenerateConstructorUsingFieldsAction_description);
123                 setToolTipText(ActionMessages.GenerateConstructorUsingFieldsAction_tooltip);
124
125                 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.CREATE_NEW_CONSTRUCTOR_ACTION);
126         }
127
128         private boolean canEnable(IStructuredSelection selection) throws JavaModelException {
129                 if (getSelectedFields(selection) != null)
130                         return true;
131
132                 if ((selection.size() == 1) && (selection.getFirstElement() instanceof IType)) {
133                         IType type= (IType) selection.getFirstElement();
134                         return type.getCompilationUnit() != null && !type.isInterface() && !type.isAnnotation() && !type.isAnonymous();
135                 }
136
137                 if ((selection.size() == 1) && (selection.getFirstElement() instanceof ICompilationUnit))
138                         return true;
139
140                 return false;
141         }
142
143         private boolean canRunOn(IField[] fields) throws JavaModelException {
144                 if (fields != null && fields.length > 0) {
145                         for (int index= 0; index < fields.length; index++) {
146                                 if (JdtFlags.isEnum(fields[index])) {
147                                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_enum_not_applicable);
148                                         return false;
149                                 }
150                         }
151                         return true;
152                 }
153                 return false;
154         }
155
156         private boolean checkEnabledEditor() {
157                 return fEditor != null && SelectionConverter.canOperateOn(fEditor);
158         }
159
160         /*
161          * Returns fields in the selection or <code>null</code> if the selection is empty or
162          * not valid.
163          */
164         private IField[] getSelectedFields(IStructuredSelection selection) {
165                 List<?> elements= selection.toList();
166                 if (elements.size() > 0) {
167                         IField[] fields= new IField[elements.size()];
168                         ICompilationUnit unit= null;
169                         for (int index= 0; index < elements.size(); index++) {
170                                 if (elements.get(index) instanceof IField) {
171                                         IField field= (IField) elements.get(index);
172                                         if (index == 0) {
173                                                 // remember the CU of the first element
174                                                 unit= field.getCompilationUnit();
175                                                 if (unit == null) {
176                                                         return null;
177                                                 }
178                                         } else if (!unit.equals(field.getCompilationUnit())) {
179                                                 // all fields must be in the same CU
180                                                 return null;
181                                         }
182                                         try {
183                                                 final IType declaringType= field.getDeclaringType();
184                                                 if (declaringType.isInterface() || declaringType.isAnnotation() || declaringType.isAnonymous())
185                                                         return null;
186                                         } catch (JavaModelException exception) {
187                                                 JavaPlugin.log(exception);
188                                                 return null;
189                                         }
190                                         fields[index]= field;
191                                 } else {
192                                         return null;
193                                 }
194                         }
195                         return fields;
196                 }
197                 return null;
198         }
199
200         private IType getSelectedType(IStructuredSelection selection) throws JavaModelException {
201                 Object[] elements= selection.toArray();
202                 if (elements.length == 1 && (elements[0] instanceof IType)) {
203                         IType type= (IType) elements[0];
204                         if (type.getCompilationUnit() != null && !type.isInterface() && !type.isAnnotation()) {
205                                 return type;
206                         }
207                 } else if (elements[0] instanceof ICompilationUnit) {
208                         ICompilationUnit unit= (ICompilationUnit) elements[0];
209                         IType type= unit.findPrimaryType();
210                         if (type != null && !type.isInterface() && !type.isAnnotation())
211                                 return type;
212                 } else if (elements[0] instanceof IField) {
213                         return ((IField) elements[0]).getCompilationUnit().findPrimaryType();
214                 }
215                 return null;
216         }
217
218         /*
219          * (non-Javadoc) Method declared on SelectionDispatchAction
220          */
221         @Override
222         public void run(IStructuredSelection selection) {
223                 try {
224                         IType selectionType= getSelectedType(selection);
225                         if (selectionType == null) {
226                                 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_not_applicable);
227                                 notifyResult(false);
228                                 return;
229                         }
230
231                         IField[] selectedFields= getSelectedFields(selection);
232
233                         if (canRunOn(selectedFields)) {
234                                 run(selectedFields[0].getDeclaringType(), selectedFields, false);
235                                 return;
236                         }
237                         Object firstElement= selection.getFirstElement();
238
239                         if (firstElement instanceof IType) {
240                                 run((IType) firstElement, new IField[0], false);
241                         } else if (firstElement instanceof ICompilationUnit) {
242                                 IType type= ((ICompilationUnit) firstElement).findPrimaryType();
243                                 if (type.isAnnotation()) {
244                                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_annotation_not_applicable);
245                                         notifyResult(false);
246                                         return;
247                                 } else if (type.isInterface()) {
248                                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_interface_not_applicable);
249                                         notifyResult(false);
250                                         return;
251                                 } else
252                                         run(((ICompilationUnit) firstElement).findPrimaryType(), new IField[0], false);
253                         }
254                 } catch (CoreException exception) {
255                         ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
256                 }
257         }
258
259         /*
260          * (non-Javadoc) Method declared on SelectionDispatchAction
261          */
262         @Override
263         public void run(ITextSelection selection) {
264                 if (!ActionUtil.isProcessable(fEditor)) {
265                         notifyResult(false);
266                         return;
267                 }
268                 try {
269                         IJavaElement[] elements= SelectionConverter.codeResolveForked(fEditor, true);
270                         if (elements.length == 1 && (elements[0] instanceof IField)) {
271                                 IField field= (IField) elements[0];
272                                 run(field.getDeclaringType(), new IField[] { field}, false);
273                                 return;
274                         }
275                         IJavaElement element= SelectionConverter.getElementAtOffset(fEditor);
276                         if (element != null) {
277                                 IType type= (IType) element.getAncestor(IJavaElement.TYPE);
278                                 if (type != null) {
279                                         if (type.getFields().length > 0) {
280                                                 run(type, new IField[0], true);
281                                         } else {
282                                                 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
283                                         }
284                                         return;
285                                 }
286                         }
287                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_not_applicable);
288                 } catch (CoreException exception) {
289                         ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
290                 } catch (InvocationTargetException exception) {
291                         ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
292                 } catch (InterruptedException e) {
293                         // cancelled
294                 }
295         }
296
297         // ---- Helpers -------------------------------------------------------------------
298
299         void run(IType type, IField[] selectedFields, boolean activated) throws CoreException {
300                 if (!ElementValidator.check(type, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, activated)) {
301                         notifyResult(false);
302                         return;
303                 }
304                 if (!ActionUtil.isEditable(fEditor, getShell(), type)) {
305                         notifyResult(false);
306                         return;
307                 }
308
309                 ICompilationUnit cu= type.getCompilationUnit();
310
311                 if (cu == null) {
312                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateNewConstructorUsingFieldsAction_error_not_a_source_file);
313                         notifyResult(false);
314                         return;
315                 }
316
317                 List<IField> allSelected= Arrays.asList(selectedFields);
318
319                 CompilationUnit astRoot= SharedASTProvider.getAST(cu, SharedASTProvider.WAIT_YES, new NullProgressMonitor());
320                 ITypeBinding typeBinding= ASTNodes.getTypeBinding(astRoot, type);
321                 if (typeBinding == null) {
322                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateNewConstructorUsingFieldsAction_error_not_a_source_file);
323                         notifyResult(false);
324                         return;
325                 }
326
327                 HashMap<IJavaElement, IVariableBinding> fieldsToBindings= new HashMap<IJavaElement, IVariableBinding>();
328                 ArrayList<IVariableBinding> selected= new ArrayList<IVariableBinding>();
329
330                 IVariableBinding[] candidates= typeBinding.getDeclaredFields();
331                 for (int i= 0; i < candidates.length; i++) {
332                         IVariableBinding curr= candidates[i];
333                         if (curr.isSynthetic()) {
334                                 continue;
335                         }
336                         if (Modifier.isStatic(curr.getModifiers())) {
337                                 continue;
338                         }
339                         if (Modifier.isFinal(curr.getModifiers())) {
340                                 ASTNode declaringNode= astRoot.findDeclaringNode(curr);
341                                 if (declaringNode instanceof VariableDeclarationFragment && ((VariableDeclarationFragment) declaringNode).getInitializer() != null) {
342                                         continue; // Do not add final fields which have been set in the <clinit>
343                                 }
344                         }
345                         IJavaElement javaElement= curr.getJavaElement();
346                         fieldsToBindings.put(javaElement, curr);
347                         if (allSelected.contains(javaElement)) {
348                                 selected.add(curr);
349                         }
350                 }
351                 if (fieldsToBindings.isEmpty()) {
352                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
353                         notifyResult(false);
354                         return;
355                 }
356
357                 ArrayList<IVariableBinding> fields= new ArrayList<IVariableBinding>();
358                 IField[] allFields= type.getFields();
359                 for (int i= 0; i < allFields.length; i++) {
360                         IVariableBinding fieldBinding= fieldsToBindings.remove(allFields[i]);
361                         if (fieldBinding != null) {
362                                 fields.add(fieldBinding);
363                         }
364                 }
365                 fields.addAll(fieldsToBindings.values()); // paranoia code, should not happen
366
367                 final GenerateConstructorUsingFieldsContentProvider provider= new GenerateConstructorUsingFieldsContentProvider(fields, selected);
368                 IMethodBinding[] bindings= null;
369
370                 if (typeBinding.isAnonymous()) {
371                         MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_anonymous_class);
372                         notifyResult(false);
373                         return;
374                 }
375                 if (typeBinding.isEnum()) {
376                         bindings= new IMethodBinding[] {getObjectConstructor(astRoot.getAST())};
377                 } else {
378                         bindings= StubUtility2.getVisibleConstructors(typeBinding, false, true);
379                         if (bindings.length == 0) {
380                                 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_nothing_found);
381                                 notifyResult(false);
382                                 return;
383                         }
384                 }
385
386                 GenerateConstructorUsingFieldsSelectionDialog dialog= new GenerateConstructorUsingFieldsSelectionDialog(getShell(), new BindingLabelProvider(), provider, fEditor, type, bindings);
387                 dialog.setCommentString(ActionMessages.SourceActionDialog_createConstructorComment);
388                 dialog.setTitle(ActionMessages.GenerateConstructorUsingFieldsAction_dialog_title);
389                 dialog.setInitialSelections(provider.getInitiallySelectedElements());
390                 dialog.setContainerMode(true);
391                 dialog.setSize(60, 18);
392                 dialog.setInput(new Object());
393                 dialog.setMessage(ActionMessages.GenerateConstructorUsingFieldsAction_dialog_label);
394                 dialog.setValidator(new GenerateConstructorUsingFieldsValidator(dialog, typeBinding, fields.size()));
395
396                 final int dialogResult= dialog.open();
397                 if (dialogResult == Window.OK) {
398                         Object[] elements= dialog.getResult();
399                         if (elements == null) {
400                                 notifyResult(false);
401                                 return;
402                         }
403                         ArrayList<IVariableBinding> result= new ArrayList<IVariableBinding>(elements.length);
404                         for (int index= 0; index < elements.length; index++) {
405                                 if (elements[index] instanceof IVariableBinding)
406                                         result.add((IVariableBinding) elements[index]);
407                         }
408                         IVariableBinding[] variables= result.toArray(new IVariableBinding[result.size()]);
409
410                         IEditorPart editor= JavaUI.openInEditor(cu);
411                         CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject());
412                         settings.createComments= dialog.getGenerateComment();
413                         IMethodBinding constructor= dialog.getSuperConstructorChoice();
414                         IRewriteTarget target= editor != null ? (IRewriteTarget) editor.getAdapter(IRewriteTarget.class) : null;
415                         if (target != null)
416                                 target.beginCompoundChange();
417                         try {
418                                 AddCustomConstructorOperation operation= new AddCustomConstructorOperation(astRoot, typeBinding, variables, constructor, dialog.getElementPosition(), settings, true, false);
419                                 operation.setVisibility(dialog.getVisibilityModifier());
420                                 if (constructor.getParameterTypes().length == 0)
421                                         operation.setOmitSuper(dialog.isOmitSuper());
422                                 IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
423                                 if (context == null)
424                                         context= new BusyIndicatorRunnableContext();
425                                 PlatformUI.getWorkbench().getProgressService().runInUI(context, new WorkbenchRunnableAdapter(operation, operation.getSchedulingRule()), operation.getSchedulingRule());
426                         } catch (InvocationTargetException exception) {
427                                 ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
428                         } catch (InterruptedException exception) {
429                                 // Do nothing. Operation has been canceled by user.
430                         } finally {
431                                 if (target != null) {
432                                         target.endCompoundChange();
433                                 }
434                         }
435                 }
436                 notifyResult(dialogResult == Window.OK);
437         }
438
439         private IMethodBinding getObjectConstructor(AST ast) {
440                 final ITypeBinding binding= ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
441                 return Bindings.findMethodInType(binding, "Object", new ITypeBinding[0]); //$NON-NLS-1$
442         }
443
444         /*
445          * (non-Javadoc) Method declared on SelectionDispatchAction
446          */
447         @Override
448         public void selectionChanged(IStructuredSelection selection) {
449                 try {
450                         setEnabled(canEnable(selection));
451                 } catch (JavaModelException e) {
452                         // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
453                         if (JavaModelUtil.isExceptionToBeLogged(e))
454                                 JavaPlugin.log(e);
455                         setEnabled(false);
456                 }
457         }
458
459         // ---- Java Editor --------------------------------------------------------------
460
461         /*
462          * (non-Javadoc) Method declared on SelectionDispatchAction
463          */
464         @Override
465         public void selectionChanged(ITextSelection selection) {
466         }
467 }