blob: 8d4388ea278e1b735789440dcb9a5d777cdb667b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ui.actions;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.compiler.IScanner;
import org.eclipse.jdt.core.compiler.ITerminalSymbols;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.internal.corext.codemanipulation.AddCustomConstructorOperation;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.TokenScanner;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.ActionMessages;
import org.eclipse.jdt.internal.ui.actions.ActionUtil;
import org.eclipse.jdt.internal.ui.actions.GenerateConstructorUsingFieldsContentProvider;
import org.eclipse.jdt.internal.ui.actions.GenerateConstructorUsingFieldsSelectionDialog;
import org.eclipse.jdt.internal.ui.actions.GenerateConstructorUsingFieldsValidator;
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext;
import org.eclipse.jdt.internal.ui.util.ElementValidator;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
/**
* Creates constructors for a type based on existing fields.
* <p>
* Will open the parent compilation unit in a Java editor. Opens a dialog with a list
* fields from which a constructor will be generated. User is able to check or uncheck
* items before constructors are generated. The result is unsaved, so the user can decide
* if the changes are acceptable.
* <p>
* The action is applicable to structured selections containing elements of type
* <code>IType</code>.
*
* <p>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
*
* @since 3.0
*/
public class GenerateNewConstructorUsingFieldsAction extends SelectionDispatchAction {
private CompilationUnitEditor fEditor;
/**
* Note: This constructor is for internal use only. Clients should not call this
* constructor.
*
* @param editor the compilation unit editor
*/
public GenerateNewConstructorUsingFieldsAction(CompilationUnitEditor editor) {
this(editor.getEditorSite());
fEditor= editor;
setEnabled(checkEnabledEditor());
}
/**
* Creates a new <code>GenerateConstructorUsingFieldsAction</code>. The action requires
* that the selection provided by the site's selection provider is of type <code>
* org.eclipse.jface.viewers.IStructuredSelection</code>.
*
* @param site the site providing context information for this action
*/
public GenerateNewConstructorUsingFieldsAction(IWorkbenchSite site) {
super(site);
setText(ActionMessages.GenerateConstructorUsingFieldsAction_label);
setDescription(ActionMessages.GenerateConstructorUsingFieldsAction_description);
setToolTipText(ActionMessages.GenerateConstructorUsingFieldsAction_tooltip);
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.CREATE_NEW_CONSTRUCTOR_ACTION);
}
private boolean canEnable(IStructuredSelection selection) throws JavaModelException {
if (getSelectedFields(selection) != null)
return true;
if ((selection.size() == 1) && (selection.getFirstElement() instanceof IType)) {
IType type= (IType) selection.getFirstElement();
return type.getCompilationUnit() != null && !type.isInterface() && !type.isAnnotation();
}
if ((selection.size() == 1) && (selection.getFirstElement() instanceof ICompilationUnit))
return true;
return false;
}
private boolean canRunOn(IField[] fields) throws JavaModelException {
if (fields != null && fields.length > 0) {
for (int index= 0; index < fields.length; index++) {
if (JdtFlags.isEnum(fields[index])) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_enum_not_applicable);
return false;
}
}
return true;
}
return false;
}
private boolean checkEnabledEditor() {
return fEditor != null && SelectionConverter.canOperateOn(fEditor);
}
/*
* Returns fields in the selection or <code>null</code> if the selection is empty or
* not valid.
*/
private IField[] getSelectedFields(IStructuredSelection selection) {
List elements= selection.toList();
if (elements.size() > 0) {
IField[] fields= new IField[elements.size()];
ICompilationUnit unit= null;
for (int index= 0; index < elements.size(); index++) {
if (elements.get(index) instanceof IField) {
IField field= (IField) elements.get(index);
if (index == 0) {
// remember the CU of the first element
unit= field.getCompilationUnit();
if (unit == null) {
return null;
}
} else if (!unit.equals(field.getCompilationUnit())) {
// all fields must be in the same CU
return null;
}
try {
final IType declaringType= field.getDeclaringType();
if (declaringType.isInterface() || declaringType.isAnnotation())
return null;
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
return null;
}
fields[index]= field;
} else {
return null;
}
}
return fields;
}
return null;
}
private IType getSelectedType(IStructuredSelection selection) throws JavaModelException {
Object[] elements= selection.toArray();
if (elements.length == 1 && (elements[0] instanceof IType)) {
IType type= (IType) elements[0];
if (type.getCompilationUnit() != null && !type.isInterface() && !type.isAnnotation()) {
return type;
}
} else if (elements[0] instanceof ICompilationUnit) {
ICompilationUnit unit= (ICompilationUnit) elements[0];
IType type= unit.findPrimaryType();
if (type != null && !type.isInterface() && !type.isAnnotation())
return type;
} else if (elements[0] instanceof IField) {
return ((IField) elements[0]).getCompilationUnit().findPrimaryType();
}
return null;
}
/*
* (non-Javadoc) Method declared on SelectionDispatchAction
*/
public void run(IStructuredSelection selection) {
try {
IType selectionType= getSelectedType(selection);
if (selectionType == null) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_not_applicable);
notifyResult(false);
return;
}
IField[] selectedFields= getSelectedFields(selection);
if (canRunOn(selectedFields)) {
run(selectedFields[0].getDeclaringType(), selectedFields, false);
return;
}
Object firstElement= selection.getFirstElement();
if (firstElement instanceof IType) {
run((IType) firstElement, new IField[0], false);
} else if (firstElement instanceof ICompilationUnit) {
IType type= ((ICompilationUnit) firstElement).findPrimaryType();
if (type.isAnnotation()) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_annotation_not_applicable);
notifyResult(false);
return;
} else if (type.isInterface()) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_interface_not_applicable);
notifyResult(false);
return;
} else
run(((ICompilationUnit) firstElement).findPrimaryType(), new IField[0], false);
}
} catch (CoreException exception) {
ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
}
}
/*
* (non-Javadoc) Method declared on SelectionDispatchAction
*/
public void run(ITextSelection selection) {
if (!ActionUtil.isProcessable(getShell(), fEditor)) {
notifyResult(false);
return;
}
try {
IJavaElement[] elements= SelectionConverter.codeResolveForked(fEditor, true);
if (elements.length == 1 && (elements[0] instanceof IField)) {
IField field= (IField) elements[0];
run(field.getDeclaringType(), new IField[] { field}, false);
return;
}
IJavaElement element= SelectionConverter.getElementAtOffset(fEditor);
if (element != null) {
IType type= (IType) element.getAncestor(IJavaElement.TYPE);
if (type != null) {
if (type.getFields().length > 0) {
run(type, new IField[0], true);
} else {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
}
return;
}
}
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_not_applicable);
} catch (CoreException exception) {
ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
} catch (InvocationTargetException exception) {
ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
} catch (InterruptedException e) {
// cancelled
}
}
// ---- Helpers -------------------------------------------------------------------
void run(IType type, IField[] selected, boolean activated) throws CoreException {
if (!ElementValidator.check(type, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, activated)) {
notifyResult(false);
return;
}
if (!ActionUtil.isProcessable(getShell(), type)) {
notifyResult(false);
return;
}
IField[] candidates= type.getFields();
ArrayList fields= new ArrayList();
for (int index= 0; index < candidates.length; index++) {
boolean isStatic= Flags.isStatic(candidates[index].getFlags());
boolean isFinal= Flags.isFinal(candidates[index].getFlags());
if (!isStatic) {
if (isFinal) {
try {
// Do not add final fields which have been set in the <clinit>
IScanner scanner= ToolFactory.createScanner(true, false, false, false);
scanner.setSource(candidates[index].getSource().toCharArray());
TokenScanner tokenScanner= new TokenScanner(scanner);
tokenScanner.getTokenStartOffset(ITerminalSymbols.TokenNameEQUAL, 0);
} catch (JavaModelException e) {
} catch (CoreException e) {
fields.add(candidates[index]);
}
} else
fields.add(candidates[index]);
}
}
if (fields.isEmpty()) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_typeContainsNoFields_message);
notifyResult(false);
return;
}
final GenerateConstructorUsingFieldsContentProvider provider= new GenerateConstructorUsingFieldsContentProvider(type, fields, Arrays.asList(selected));
IMethodBinding[] bindings= null;
final ITypeBinding provided= provider.getType();
if (provided.isAnonymous()) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_anonymous_class);
notifyResult(false);
return;
}
if (provided.isEnum()) {
bindings= new IMethodBinding[] {getObjectConstructor(provider.getCompilationUnit())};
} else {
bindings= StubUtility2.getVisibleConstructors(provided, false, true);
if (bindings.length == 0) {
MessageDialog.openInformation(getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_nothing_found);
notifyResult(false);
return;
}
}
GenerateConstructorUsingFieldsSelectionDialog dialog= new GenerateConstructorUsingFieldsSelectionDialog(getShell(), new BindingLabelProvider(), provider, fEditor, type, bindings);
dialog.setCommentString(ActionMessages.SourceActionDialog_createConstructorComment);
dialog.setTitle(ActionMessages.GenerateConstructorUsingFieldsAction_dialog_title);
dialog.setInitialSelections(provider.getInitiallySelectedElements());
dialog.setContainerMode(true);
dialog.setSize(60, 18);
dialog.setInput(new Object());
dialog.setMessage(ActionMessages.GenerateConstructorUsingFieldsAction_dialog_label);
dialog.setValidator(new GenerateConstructorUsingFieldsValidator(dialog, provided, fields.size()));
final int dialogResult= dialog.open();
if (dialogResult == Window.OK) {
Object[] elements= dialog.getResult();
if (elements == null) {
notifyResult(false);
return;
}
ArrayList result= new ArrayList(elements.length);
for (int index= 0; index < elements.length; index++) {
if (elements[index] instanceof IVariableBinding)
result.add(elements[index]);
}
IVariableBinding[] variables= new IVariableBinding[result.size()];
result.toArray(variables);
IEditorPart editor= EditorUtility.openInEditor(type.getCompilationUnit());
CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject());
settings.createComments= dialog.getGenerateComment();
IMethodBinding constructor= dialog.getSuperConstructorChoice();
IRewriteTarget target= editor != null ? (IRewriteTarget) editor.getAdapter(IRewriteTarget.class) : null;
if (target != null)
target.beginCompoundChange();
try {
AddCustomConstructorOperation operation= new AddCustomConstructorOperation(type, dialog.getElementPosition(), provider.getCompilationUnit(), variables, constructor, settings, true, false);
operation.setVisibility(dialog.getVisibilityModifier());
if (constructor.getParameterTypes().length == 0)
operation.setOmitSuper(dialog.isOmitSuper());
IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
if (context == null)
context= new BusyIndicatorRunnableContext();
PlatformUI.getWorkbench().getProgressService().runInUI(context, new WorkbenchRunnableAdapter(operation, operation.getSchedulingRule()), operation.getSchedulingRule());
} catch (InvocationTargetException exception) {
ExceptionHandler.handle(exception, getShell(), ActionMessages.GenerateConstructorUsingFieldsAction_error_title, ActionMessages.GenerateConstructorUsingFieldsAction_error_actionfailed);
} catch (InterruptedException exception) {
// Do nothing. Operation has been canceled by user.
} finally {
if (target != null) {
target.endCompoundChange();
}
}
}
notifyResult(dialogResult == Window.OK);
}
private IMethodBinding getObjectConstructor(CompilationUnit compilationUnit) {
final ITypeBinding binding= compilationUnit.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
return Bindings.findMethodInType(binding, "Object", new ITypeBinding[0]); //$NON-NLS-1$
}
/*
* (non-Javadoc) Method declared on SelectionDispatchAction
*/
public void selectionChanged(IStructuredSelection selection) {
try {
setEnabled(canEnable(selection));
} catch (JavaModelException e) {
// http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
if (JavaModelUtil.isExceptionToBeLogged(e))
JavaPlugin.log(e);
setEnabled(false);
}
}
// ---- Java Editor --------------------------------------------------------------
/*
* (non-Javadoc) Method declared on SelectionDispatchAction
*/
public void selectionChanged(ITextSelection selection) {
}
}