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 * Martin Moebius (m.moebius@gmx.de) - initial API and implementation
11 * IBM Corporation - updates
12 *******************************************************************************/
14 package org.eclipse.jdt.ui.actions;
16 import java.lang.reflect.InvocationTargetException;
17 import java.util.ArrayList;
18 import java.util.HashSet;
19 import java.util.List;
21 import org.eclipse.swt.SWT;
22 import org.eclipse.swt.events.SelectionAdapter;
23 import org.eclipse.swt.events.SelectionEvent;
24 import org.eclipse.swt.graphics.Image;
25 import org.eclipse.swt.layout.GridData;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Control;
28 import org.eclipse.swt.widgets.Link;
29 import org.eclipse.swt.widgets.Shell;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IStatus;
33 import org.eclipse.core.runtime.NullProgressMonitor;
35 import org.eclipse.jface.dialogs.MessageDialog;
36 import org.eclipse.jface.operation.IRunnableContext;
37 import org.eclipse.jface.viewers.ILabelProvider;
38 import org.eclipse.jface.viewers.IStructuredSelection;
39 import org.eclipse.jface.viewers.ITreeContentProvider;
40 import org.eclipse.jface.viewers.Viewer;
41 import org.eclipse.jface.viewers.ViewerComparator;
42 import org.eclipse.jface.window.Window;
44 import org.eclipse.jface.text.IRewriteTarget;
45 import org.eclipse.jface.text.ITextSelection;
47 import org.eclipse.ui.IEditorPart;
48 import org.eclipse.ui.IWorkbenchSite;
49 import org.eclipse.ui.PlatformUI;
50 import org.eclipse.ui.dialogs.ISelectionStatusValidator;
52 import org.eclipse.jdt.core.ICompilationUnit;
53 import org.eclipse.jdt.core.IField;
54 import org.eclipse.jdt.core.IJavaElement;
55 import org.eclipse.jdt.core.IType;
56 import org.eclipse.jdt.core.JavaModelException;
57 import org.eclipse.jdt.core.Signature;
58 import org.eclipse.jdt.core.dom.CompilationUnit;
59 import org.eclipse.jdt.core.dom.IMethodBinding;
60 import org.eclipse.jdt.core.dom.ITypeBinding;
61 import org.eclipse.jdt.core.dom.IVariableBinding;
62 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
64 import org.eclipse.jdt.internal.corext.codemanipulation.AddDelegateMethodsOperation;
65 import org.eclipse.jdt.internal.corext.codemanipulation.AddDelegateMethodsOperation.DelegateEntry;
66 import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
67 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
68 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
69 import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
70 import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
71 import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContextType;
72 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
73 import org.eclipse.jdt.internal.corext.util.JdtFlags;
74 import org.eclipse.jdt.internal.corext.util.Messages;
76 import org.eclipse.jdt.ui.JavaUI;
77 import org.eclipse.jdt.ui.SharedASTProvider;
79 import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
80 import org.eclipse.jdt.internal.ui.JavaPlugin;
81 import org.eclipse.jdt.internal.ui.actions.ActionMessages;
82 import org.eclipse.jdt.internal.ui.actions.ActionUtil;
83 import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
84 import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
85 import org.eclipse.jdt.internal.ui.dialogs.SourceActionDialog;
86 import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
87 import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
88 import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
89 import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext;
90 import org.eclipse.jdt.internal.ui.util.ElementValidator;
91 import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
92 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
95 * Creates delegate methods for a type's fields. Opens a dialog with a list of fields for
96 * which delegate methods can be generated. User is able to check or uncheck items before
97 * methods are generated.
99 * Will open the parent compilation unit in a Java editor. The result is unsaved, so the
100 * user can decide if the changes are acceptable.
102 * The action is applicable to structured selections containing elements of type
103 * <code>IField</code> or <code>IType</code>.
106 * This class may be instantiated; it is not intended to be subclassed.
111 * @noextend This class is not intended to be subclassed by clients.
113 public class AddDelegateMethodsAction extends SelectionDispatchAction {
115 // ---- Helpers -------------------------------------------------------------------
117 private static class AddDelegateMethodsActionStatusValidator implements ISelectionStatusValidator {
119 private static int fEntries;
121 AddDelegateMethodsActionStatusValidator(int entries) {
125 public IStatus validate(Object[] selection) {
126 int selectedCount= 0;
127 int duplicateCount= 0;
128 if (selection != null && selection.length > 0) {
130 HashSet<String> signatures= new HashSet<String>(selection.length);
131 for (int index= 0; index < selection.length; index++) {
132 if (selection[index] instanceof DelegateEntry) {
133 DelegateEntry delegateEntry= (DelegateEntry) selection[index];
134 if (!signatures.add(getSignature(delegateEntry.delegateMethod)))
140 if (duplicateCount > 0) {
141 return new StatusInfo(IStatus.ERROR, duplicateCount == 1
142 ? ActionMessages.AddDelegateMethodsAction_duplicate_methods_singular
143 : Messages.format(ActionMessages.AddDelegateMethodsAction_duplicate_methods_plural, String.valueOf(duplicateCount)));
145 return new StatusInfo(IStatus.INFO, Messages.format(ActionMessages.AddDelegateMethodsAction_selectioninfo_more, new Object[] { String.valueOf(selectedCount), String.valueOf(fEntries) }));
148 private String getSignature(IMethodBinding binding) {
149 StringBuffer buf= new StringBuffer(binding.getName()).append('(');
150 ITypeBinding[] parameterTypes= binding.getParameterTypes();
151 for (int i= 0; i < parameterTypes.length; i++) {
152 buf.append(parameterTypes[i].getTypeDeclaration().getName());
155 return buf.toString();
160 public static class AddDelegateMethodsContentProvider implements ITreeContentProvider {
162 private DelegateEntry[] fDelegateEntries;
163 private IVariableBinding[] fExpanded= new IVariableBinding[0];
165 AddDelegateMethodsContentProvider(CompilationUnit astRoot, IType type, IField[] fields) throws JavaModelException {
167 final ITypeBinding binding= ASTNodes.getTypeBinding(astRoot, type);
168 if (binding != null) {
169 fDelegateEntries= StubUtility2.getDelegatableMethods(binding);
171 List<IVariableBinding> expanded= new ArrayList<IVariableBinding>();
172 for (int index= 0; index < fields.length; index++) {
173 VariableDeclarationFragment fragment= ASTNodeSearchUtil.getFieldDeclarationFragmentNode(fields[index], astRoot);
174 if (fragment != null) {
175 IVariableBinding variableBinding= fragment.resolveBinding();
176 if (variableBinding != null)
177 expanded.add(variableBinding);
180 fExpanded= expanded.toArray(new IVariableBinding[expanded.size()]);
184 public void dispose() {
187 public Object[] getChildren(Object element) {
188 if (element instanceof IVariableBinding) {
189 List<DelegateEntry> result= new ArrayList<DelegateEntry>();
190 for (int i= 0; i < fDelegateEntries.length; i++) {
191 if (element == fDelegateEntries[i].field) {
192 result.add(fDelegateEntries[i]);
195 return result.toArray();
200 public int getCount() {
201 return fDelegateEntries.length;
204 public Object[] getElements(Object inputElement) {
205 HashSet<IVariableBinding> result= new HashSet<IVariableBinding>();
206 for (int i= 0; i < fDelegateEntries.length; i++) {
207 DelegateEntry curr= fDelegateEntries[i];
208 curr.generated_2166174217760458360(result, AddDelegateMethodsContentProvider.this);
210 return result.toArray();
213 public IVariableBinding[] getExpandedElements() {
217 public IVariableBinding[] getInitiallySelectedElements() {
221 public Object getParent(Object element) {
222 if (element instanceof DelegateEntry)
223 return ((DelegateEntry) element).field;
227 public boolean hasChildren(Object element) {
228 return element instanceof IVariableBinding;
231 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
235 private static class AddDelegateMethodsDialog extends SourceActionDialog {
237 public AddDelegateMethodsDialog(Shell parent, ILabelProvider labelProvider, ITreeContentProvider contentProvider, CompilationUnitEditor editor, IType type, boolean isConstructor) throws JavaModelException {
238 super(parent, labelProvider, contentProvider, editor, type, isConstructor);
242 protected void configureShell(Shell shell) {
243 super.configureShell(shell);
244 PlatformUI.getWorkbench().getHelpSystem().setHelp(shell, IJavaHelpContextIds.ADD_DELEGATE_METHODS_SELECTION_DIALOG);
248 * @see org.eclipse.jdt.internal.ui.dialogs.SourceActionDialog#createLinkControl(org.eclipse.swt.widgets.Composite)
251 protected Control createLinkControl(Composite composite) {
252 Link link= new Link(composite, SWT.WRAP);
253 link.setText(ActionMessages.AddDelegateMethodsAction_template_link_message);
254 link.addSelectionListener(new SelectionAdapter() {
256 public void widgetSelected(SelectionEvent e) {
257 openCodeTempatePage(CodeTemplateContextType.OVERRIDECOMMENT_ID);
260 link.setToolTipText(ActionMessages.AddDelegateMethodsAction_template_link_tooltip);
262 GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
263 gridData.widthHint= convertWidthInCharsToPixels(40); // only expand further if anyone else requires it
264 link.setLayoutData(gridData);
269 private static class AddDelegateMethodsLabelProvider extends BindingLabelProvider {
272 public Image getImage(Object element) {
273 if (element instanceof DelegateEntry) {
274 DelegateEntry delegateEntry= (DelegateEntry) element;
275 return super.getImage(delegateEntry.delegateMethod);
276 } else if (element instanceof IVariableBinding) {
277 return super.getImage(element);
283 public String getText(Object element) {
284 if (element instanceof DelegateEntry) {
285 DelegateEntry delegateEntry= (DelegateEntry) element;
286 return super.getText(delegateEntry.delegateMethod);
287 } else if (element instanceof IVariableBinding) {
288 return super.getText(element);
294 public static class AddDelegateMethodsViewerComparator extends ViewerComparator {
297 public int category(Object element) {
298 if (element instanceof DelegateEntry)
304 public int compare(Viewer viewer, Object o1, Object o2) {
305 if (o1 instanceof DelegateEntry && o2 instanceof DelegateEntry) {
306 String bindingLabel1= BindingLabelProvider.getBindingLabel(((DelegateEntry) o1).delegateMethod, BindingLabelProvider.DEFAULT_TEXTFLAGS);
307 String bindingLabel2= BindingLabelProvider.getBindingLabel(((DelegateEntry) o2).delegateMethod, BindingLabelProvider.DEFAULT_TEXTFLAGS);
308 return getComparator().compare(bindingLabel1, bindingLabel2);
309 } else if (o1 instanceof IVariableBinding && o2 instanceof IVariableBinding) {
310 return getComparator().compare(((IVariableBinding) o1).getName(), ((IVariableBinding) o2).getName());
316 private static final String DIALOG_TITLE= ActionMessages.AddDelegateMethodsAction_error_title;
318 private static boolean hasPrimitiveType(IField field) throws JavaModelException {
319 String signature= field.getTypeSignature();
320 char first= Signature.getElementType(signature).charAt(0);
321 return (first != Signature.C_RESOLVED && first != Signature.C_UNRESOLVED);
324 private static boolean isArray(IField field) throws JavaModelException {
325 return Signature.getArrayCount(field.getTypeSignature()) > 0;
328 private CompilationUnitEditor fEditor;
331 * Note: This constructor is for internal use only. Clients should not call this
334 * @param editor the compilation unit editor
336 * @noreference This constructor is not intended to be referenced by clients.
338 public AddDelegateMethodsAction(CompilationUnitEditor editor) {
339 this(editor.getEditorSite());
341 setEnabled(SelectionConverter.getInputAsCompilationUnit(editor) != null);
345 * Creates a new <code>AddDelegateMethodsAction</code>. The action requires that
346 * the selection provided by the site's selection provider is of type <code>
347 * org.eclipse.jface.viewers.IStructuredSelection</code>.
349 * @param site the site providing context information for this action
351 public AddDelegateMethodsAction(IWorkbenchSite site) {
353 setText(ActionMessages.AddDelegateMethodsAction_label);
354 setDescription(ActionMessages.AddDelegateMethodsAction_description);
355 setToolTipText(ActionMessages.AddDelegateMethodsAction_tooltip);
357 PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.ADD_DELEGATE_METHODS_ACTION);
360 private boolean canEnable(IStructuredSelection selection) throws JavaModelException {
361 if (getSelectedFields(selection) != null)
364 if ((selection.size() == 1) && (selection.getFirstElement() instanceof IType)) {
365 IType type= (IType) selection.getFirstElement();
366 return type.getCompilationUnit() != null && !type.isInterface() && !type.isAnonymous();
369 if ((selection.size() == 1) && (selection.getFirstElement() instanceof ICompilationUnit))
375 private boolean canRunOn(IField[] fields) throws JavaModelException {
376 if (fields == null || fields.length == 0)
379 for (int index= 0; index < fields.length; index++) {
380 if (!JdtFlags.isEnum(fields[index]) && !hasPrimitiveType(fields[index]) || isArray(fields[index]))
384 MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_not_applicable);
388 private boolean canRunOn(IType type) throws JavaModelException {
389 if (type == null || type.getCompilationUnit() == null) {
390 MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_not_in_source_file);
392 } else if (type.isAnnotation()) {
393 MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_annotation_not_applicable);
395 } else if (type.isInterface()) {
396 MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_interface_not_applicable);
399 return canRunOn(type.getFields());
402 private IField[] getSelectedFields(IStructuredSelection selection) {
403 List<?> elements= selection.toList();
404 if (elements.size() > 0) {
405 IField[] result= new IField[elements.size()];
406 ICompilationUnit unit= null;
407 for (int index= 0; index < elements.size(); index++) {
408 if (elements.get(index) instanceof IField) {
409 IField field= (IField) elements.get(index);
412 // remember the CU of the first element
413 unit= field.getCompilationUnit();
417 } else if (!unit.equals(field.getCompilationUnit())) {
418 // all fields must be in the same CU
422 final IType type= field.getDeclaringType();
423 if (type.isInterface() || type.isAnonymous()) {
426 } catch (JavaModelException exception) {
427 JavaPlugin.log(exception);
431 result[index]= field;
442 * (non-Javadoc) Method declared on SelectionDispatchAction
445 public void run(IStructuredSelection selection) {
447 IField[] selectedFields= getSelectedFields(selection);
448 if (canRunOn(selectedFields)) {
449 run(selectedFields[0].getDeclaringType(), selectedFields, false);
452 Object firstElement= selection.getFirstElement();
453 if (firstElement instanceof IType)
454 run((IType) firstElement, new IField[0], false);
455 else if (firstElement instanceof ICompilationUnit)
456 run(JavaElementUtil.getMainType((ICompilationUnit) firstElement), new IField[0], false);
457 else if (!(firstElement instanceof IField))
458 MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_not_applicable);
459 } catch (CoreException e) {
460 ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_error_actionfailed);
466 * (non-Javadoc) Method declared on SelectionDispatchAction
469 public void run(ITextSelection selection) {
471 if (!ActionUtil.isProcessable(fEditor))
474 IJavaElement[] elements= SelectionConverter.codeResolveForked(fEditor, true);
475 if (elements.length == 1 && (elements[0] instanceof IField)) {
476 IField field= (IField) elements[0];
477 run(field.getDeclaringType(), new IField[] { field}, true);
480 IJavaElement element= SelectionConverter.getElementAtOffset(fEditor);
481 if (element != null) {
482 IType type= (IType) element.getAncestor(IJavaElement.TYPE);
484 if (type.getFields().length > 0) {
485 run(type, new IField[0], true);
490 MessageDialog.openInformation(getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_not_applicable);
491 } catch (CoreException e) {
492 ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_error_actionfailed);
493 } catch (InvocationTargetException e) {
494 ExceptionHandler.handle(e, getShell(), DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_error_actionfailed);
495 } catch (InterruptedException e) {
500 private void run(IType type, IField[] preselected, boolean editor) throws CoreException {
501 if (!ElementValidator.check(type, getShell(), DIALOG_TITLE, editor))
503 if (!ActionUtil.isEditable(fEditor, getShell(), type))
507 showUI(type, preselected);
510 // ---- Structured Viewer -----------------------------------------------------------
513 * (non-Javadoc) Method declared on SelectionDispatchAction
516 public void selectionChanged(IStructuredSelection selection) {
518 setEnabled(canEnable(selection));
519 } catch (JavaModelException e) {
520 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
521 if (JavaModelUtil.isExceptionToBeLogged(e))
527 // ---- Java Editor --------------------------------------------------------------
530 * (non-Javadoc) Method declared on SelectionDispatchAction
533 public void selectionChanged(ITextSelection selection) {
536 private void showUI(IType type, IField[] fields) {
538 CompilationUnit astRoot= SharedASTProvider.getAST(type.getCompilationUnit(), SharedASTProvider.WAIT_YES, new NullProgressMonitor());
540 AddDelegateMethodsContentProvider provider= new AddDelegateMethodsContentProvider(astRoot, type, fields);
541 SourceActionDialog dialog= new AddDelegateMethodsDialog(getShell(), new AddDelegateMethodsLabelProvider(), provider, fEditor, type, false);
542 dialog.setValidator(new AddDelegateMethodsActionStatusValidator(provider.getCount()));
543 AddDelegateMethodsViewerComparator comparator= new AddDelegateMethodsViewerComparator();
544 dialog.generated_6831724063556090705(comparator);
545 dialog.setMessage(ActionMessages.AddDelegateMethodsAction_message);
546 dialog.setTitle(ActionMessages.AddDelegateMethodsAction_title);
547 IVariableBinding[] expanded= provider.getExpandedElements();
548 if (expanded.length > 0) {
549 dialog.setExpandedElements(expanded);
551 Object[] elements= provider.getElements(null);
552 if (elements.length > 0) {
553 comparator.sort(null, elements);
554 Object[] expand= { elements[0]};
555 dialog.setExpandedElements(expand);
558 dialog.setInitialSelections(provider.getInitiallySelectedElements());
559 dialog.setSize(60, 18);
560 int result= dialog.open();
561 if (result == Window.OK) {
562 Object[] object= dialog.getResult();
563 if (object == null) {
567 List<DelegateEntry> tuples= new ArrayList<DelegateEntry>(object.length);
568 for (int index= 0; index < object.length; index++) {
569 if (object[index] instanceof DelegateEntry)
570 tuples.add((DelegateEntry) object[index]);
572 IEditorPart part= JavaUI.openInEditor(type);
573 IRewriteTarget target= (IRewriteTarget) part.getAdapter(IRewriteTarget.class);
576 target.beginCompoundChange();
577 CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject());
578 settings.createComments= dialog.getGenerateComment();
580 DelegateEntry[] methodToDelegate= tuples.toArray(new DelegateEntry[tuples.size()]);
582 AddDelegateMethodsOperation operation= new AddDelegateMethodsOperation(astRoot, methodToDelegate, dialog.getElementPosition(), settings, true, false);
583 IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
585 context= new BusyIndicatorRunnableContext();
587 PlatformUI.getWorkbench().getProgressService().runInUI(context, new WorkbenchRunnableAdapter(operation, operation.getSchedulingRule()), operation.getSchedulingRule());
588 } catch (InterruptedException exception) {
593 target.endCompoundChange();
596 notifyResult(result == Window.OK);
597 } catch (CoreException exception) {
598 ExceptionHandler.handle(exception, DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_error_actionfailed);
599 } catch (InvocationTargetException e) {
600 ExceptionHandler.handle(e, DIALOG_TITLE, ActionMessages.AddDelegateMethodsAction_error_actionfailed);