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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.ui.actions;
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;
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.core.runtime.NullProgressMonitor;
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;
27 import org.eclipse.jface.text.IRewriteTarget;
28 import org.eclipse.jface.text.ITextSelection;
30 import org.eclipse.ui.IEditorPart;
31 import org.eclipse.ui.IWorkbenchSite;
32 import org.eclipse.ui.PlatformUI;
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;
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;
56 import org.eclipse.jdt.ui.JavaUI;
57 import org.eclipse.jdt.ui.SharedASTProvider;
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;
76 * Creates constructors for a type based on existing fields.
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.
83 * The action is applicable to structured selections containing elements of type
87 * This class may be instantiated; it is not intended to be subclassed.
92 * @noextend This class is not intended to be subclassed by clients.
94 public class GenerateNewConstructorUsingFieldsAction extends SelectionDispatchAction {
96 private CompilationUnitEditor fEditor;
99 * Note: This constructor is for internal use only. Clients should not call this
102 * @param editor the compilation unit editor
104 * @noreference This constructor is not intended to be referenced by clients.
106 public GenerateNewConstructorUsingFieldsAction(CompilationUnitEditor editor) {
107 this(editor.getEditorSite());
109 setEnabled(checkEnabledEditor());
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>.
117 * @param site the site providing context information for this action
119 public GenerateNewConstructorUsingFieldsAction(IWorkbenchSite site) {
121 setText(ActionMessages.GenerateConstructorUsingFieldsAction_label);
122 setDescription(ActionMessages.GenerateConstructorUsingFieldsAction_description);
123 setToolTipText(ActionMessages.GenerateConstructorUsingFieldsAction_tooltip);
125 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.CREATE_NEW_CONSTRUCTOR_ACTION);
128 private boolean canEnable(IStructuredSelection selection) throws JavaModelException {
129 if (getSelectedFields(selection) != null)
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();
137 if ((selection.size() == 1) && (selection.getFirstElement() instanceof ICompilationUnit))
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);
156 private boolean checkEnabledEditor() {
157 return fEditor != null && SelectionConverter.canOperateOn(fEditor);
161 * Returns fields in the selection or <code>null</code> if the selection is empty or
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);
173 // remember the CU of the first element
174 unit= field.getCompilationUnit();
178 } else if (!unit.equals(field.getCompilationUnit())) {
179 // all fields must be in the same CU
183 final IType declaringType= field.getDeclaringType();
184 if (declaringType.isInterface() || declaringType.isAnnotation() || declaringType.isAnonymous())
186 } catch (JavaModelException exception) {
187 JavaPlugin.log(exception);
190 fields[index]= field;
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()) {
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())
212 } else if (elements[0] instanceof IField) {
213 return ((IField) elements[0]).getCompilationUnit().findPrimaryType();
219 * (non-Javadoc) Method declared on SelectionDispatchAction
222 public void run(IStructuredSelection selection) {
224 IType selectionType= getSelectedType(selection);
225 if (selectionType == null) {
226 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_not_applicable);
231 IField[] selectedFields= getSelectedFields(selection);
233 if (canRunOn(selectedFields)) {
234 run(selectedFields[0].getDeclaringType(), selectedFields, false);
237 Object firstElement= selection.getFirstElement();
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);
247 } else if (type.isInterface()) {
248 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_interface_not_applicable);
252 run(((ICompilationUnit) firstElement).findPrimaryType(), new IField[0], false);
254 } catch (CoreException exception) {
255 ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
260 * (non-Javadoc) Method declared on SelectionDispatchAction
263 public void run(ITextSelection selection) {
264 if (!ActionUtil.isProcessable(fEditor)) {
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);
275 IJavaElement element= SelectionConverter.getElementAtOffset(fEditor);
276 if (element != null) {
277 IType type= (IType) element.getAncestor(IJavaElement.TYPE);
279 if (type.getFields().length > 0) {
280 run(type, new IField[0], true);
282 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
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) {
297 // ---- Helpers -------------------------------------------------------------------
299 void run(IType type, IField[] selectedFields, boolean activated) throws CoreException {
300 if (!ElementValidator.check(type, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, activated)) {
304 if (!ActionUtil.isEditable(fEditor, getShell(), type)) {
309 ICompilationUnit cu= type.getCompilationUnit();
312 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateNewConstructorUsingFieldsAction_error_not_a_source_file);
317 List<IField> allSelected= Arrays.asList(selectedFields);
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);
327 HashMap<IJavaElement, IVariableBinding> fieldsToBindings= new HashMap<IJavaElement, IVariableBinding>();
328 ArrayList<IVariableBinding> selected= new ArrayList<IVariableBinding>();
330 IVariableBinding[] candidates= typeBinding.getDeclaredFields();
331 for (int i= 0; i < candidates.length; i++) {
332 IVariableBinding curr= candidates[i];
333 if (curr.isSynthetic()) {
336 if (Modifier.isStatic(curr.getModifiers())) {
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>
345 IJavaElement javaElement= curr.getJavaElement();
346 fieldsToBindings.put(javaElement, curr);
347 if (allSelected.contains(javaElement)) {
351 if (fieldsToBindings.isEmpty()) {
352 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
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);
365 fields.addAll(fieldsToBindings.values()); // paranoia code, should not happen
367 final GenerateConstructorUsingFieldsContentProvider provider= new GenerateConstructorUsingFieldsContentProvider(fields, selected);
368 IMethodBinding[] bindings= null;
370 if (typeBinding.isAnonymous()) {
371 MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_anonymous_class);
375 if (typeBinding.isEnum()) {
376 bindings= new IMethodBinding[] {getObjectConstructor(astRoot.getAST())};
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);
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()));
396 final int dialogResult= dialog.open();
397 if (dialogResult == Window.OK) {
398 Object[] elements= dialog.getResult();
399 if (elements == null) {
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]);
408 IVariableBinding[] variables= result.toArray(new IVariableBinding[result.size()]);
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;
416 target.beginCompoundChange();
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();
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.
431 if (target != null) {
432 target.endCompoundChange();
436 notifyResult(dialogResult == Window.OK);
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$
445 * (non-Javadoc) Method declared on SelectionDispatchAction
448 public void selectionChanged(IStructuredSelection selection) {
450 setEnabled(canEnable(selection));
451 } catch (JavaModelException e) {
452 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
453 if (JavaModelUtil.isExceptionToBeLogged(e))
459 // ---- Java Editor --------------------------------------------------------------
462 * (non-Javadoc) Method declared on SelectionDispatchAction
465 public void selectionChanged(ITextSelection selection) {