1 /*******************************************************************************
2 * Copyright (c) 2007, 2012 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.internal.ui.text.correction;
13 import java.lang.reflect.InvocationTargetException;
14 import java.util.ArrayList;
15 import java.util.Collection;
17 import org.eclipse.swt.graphics.Image;
18 import org.eclipse.swt.widgets.Display;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.NullProgressMonitor;
24 import org.eclipse.core.resources.IFile;
26 import org.eclipse.jface.text.IDocument;
28 import org.eclipse.ui.IWorkbenchWindow;
29 import org.eclipse.ui.PlatformUI;
31 import org.eclipse.ltk.core.refactoring.Change;
32 import org.eclipse.ltk.core.refactoring.CompositeChange;
33 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
34 import org.eclipse.ltk.core.refactoring.TextFileChange;
36 import org.eclipse.jdt.core.Flags;
37 import org.eclipse.jdt.core.ICompilationUnit;
38 import org.eclipse.jdt.core.IField;
39 import org.eclipse.jdt.core.IJavaElement;
40 import org.eclipse.jdt.core.IJavaProject;
41 import org.eclipse.jdt.core.JavaModelException;
42 import org.eclipse.jdt.core.compiler.IProblem;
43 import org.eclipse.jdt.core.dom.AST;
44 import org.eclipse.jdt.core.dom.ASTNode;
45 import org.eclipse.jdt.core.dom.Expression;
46 import org.eclipse.jdt.core.dom.IBinding;
47 import org.eclipse.jdt.core.dom.IMethodBinding;
48 import org.eclipse.jdt.core.dom.ITypeBinding;
49 import org.eclipse.jdt.core.dom.IVariableBinding;
50 import org.eclipse.jdt.core.dom.MethodInvocation;
51 import org.eclipse.jdt.core.dom.Modifier;
52 import org.eclipse.jdt.core.dom.Name;
53 import org.eclipse.jdt.core.dom.QualifiedName;
54 import org.eclipse.jdt.core.dom.SimpleName;
55 import org.eclipse.jdt.core.dom.SuperFieldAccess;
56 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
57 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
59 import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
60 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
61 import org.eclipse.jdt.internal.corext.dom.Bindings;
62 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
63 import org.eclipse.jdt.internal.corext.refactoring.sef.SelfEncapsulateFieldRefactoring;
64 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
65 import org.eclipse.jdt.internal.corext.util.Messages;
67 import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper;
68 import org.eclipse.jdt.ui.text.java.IInvocationContext;
69 import org.eclipse.jdt.ui.text.java.IProblemLocation;
70 import org.eclipse.jdt.ui.text.java.correction.ASTRewriteCorrectionProposal;
71 import org.eclipse.jdt.ui.text.java.correction.ChangeCorrectionProposal;
72 import org.eclipse.jdt.ui.text.java.correction.ICommandAccess;
74 import org.eclipse.jdt.internal.ui.JavaPlugin;
75 import org.eclipse.jdt.internal.ui.JavaPluginImages;
76 import org.eclipse.jdt.internal.ui.refactoring.RefactoringExecutionHelper;
77 import org.eclipse.jdt.internal.ui.refactoring.actions.RefactoringStarter;
78 import org.eclipse.jdt.internal.ui.refactoring.sef.SelfEncapsulateFieldWizard;
79 import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
80 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
83 public class GetterSetterCorrectionSubProcessor {
85 public static final String SELF_ENCAPSULATE_FIELD_ID= "org.eclipse.jdt.ui.correction.encapsulateField.assist"; //$NON-NLS-1$
87 private static class ProposalParameter {
88 public final boolean useSuper;
89 public final ICompilationUnit compilationUnit;
90 public final ASTRewrite astRewrite;
91 public final Expression accessNode;
92 public final Expression qualifier;
93 public final IVariableBinding variableBinding;
95 public ProposalParameter(boolean useSuper, ICompilationUnit compilationUnit, ASTRewrite rewrite, Expression accessNode, Expression qualifier, IVariableBinding variableBinding) {
96 this.useSuper= useSuper;
97 this.compilationUnit= compilationUnit;
98 this.astRewrite= rewrite;
99 this.accessNode= accessNode;
100 this.qualifier= qualifier;
101 this.variableBinding= variableBinding;
105 public static class SelfEncapsulateFieldProposal extends ChangeCorrectionProposal { // public for tests
107 private IField fField;
108 private boolean fNoDialog;
110 public SelfEncapsulateFieldProposal(int relevance, IField field) {
111 super(getDescription(field), null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE));
114 setCommandId(SELF_ENCAPSULATE_FIELD_ID);
117 public IField getField() {
121 public void setNoDialog(boolean noDialog) {
125 public TextFileChange getChange(IFile file) throws CoreException {
126 final SelfEncapsulateFieldRefactoring refactoring= new SelfEncapsulateFieldRefactoring(fField);
127 refactoring.setVisibility(Flags.AccPublic);
128 refactoring.setConsiderVisibility(false);//private field references are just searched in local file
129 refactoring.checkInitialConditions(new NullProgressMonitor());
130 refactoring.checkFinalConditions(new NullProgressMonitor());
131 Change createdChange= refactoring.createChange(new NullProgressMonitor());
132 if (createdChange instanceof CompositeChange) {
133 Change[] children= ((CompositeChange) createdChange).getChildren();
134 for (int i= 0; i < children.length; i++) {
135 Change curr= children[i];
136 if (curr instanceof TextFileChange && ((TextFileChange) curr).getFile().equals(file)) {
137 return (TextFileChange) curr;
145 private static String getDescription(IField field) {
146 return Messages.format(CorrectionMessages.GetterSetterCorrectionSubProcessor_creategetterunsingencapsulatefield_description, BasicElementLabels.getJavaElementName(field.getElementName()));
150 * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension5#getAdditionalProposalInfo(org.eclipse.core.runtime.IProgressMonitor)
154 public Object getAdditionalProposalInfo(IProgressMonitor monitor) {
155 return CorrectionMessages.GetterSetterCorrectionSubProcessor_additional_info;
159 public void apply(IDocument document) {
161 final SelfEncapsulateFieldRefactoring refactoring= new SelfEncapsulateFieldRefactoring(fField);
162 refactoring.setVisibility(Flags.AccPublic);
163 refactoring.setConsiderVisibility(false);//private field references are just searched in local file
165 IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
166 final RefactoringExecutionHelper helper= new RefactoringExecutionHelper(refactoring, RefactoringStatus.ERROR, RefactoringSaveHelper.SAVE_REFACTORING, JavaPlugin.getActiveWorkbenchShell(), window);
167 if (Display.getCurrent() != null) {
169 helper.perform(false, false);
170 } catch (InterruptedException e) {
172 } catch (InvocationTargetException e) {
176 Display.getDefault().syncExec(new Runnable() {
179 helper.perform(false, false);
180 } catch (InterruptedException e) {
182 } catch (InvocationTargetException e) {
189 new RefactoringStarter().activate(new SelfEncapsulateFieldWizard(refactoring), JavaPlugin.getActiveWorkbenchShell(), "", RefactoringSaveHelper.SAVE_REFACTORING); //$NON-NLS-1$
191 } catch (JavaModelException e) {
192 ExceptionHandler.handle(e, CorrectionMessages.GetterSetterCorrectionSubProcessor_encapsulate_field_error_title, CorrectionMessages.GetterSetterCorrectionSubProcessor_encapsulate_field_error_message);
198 * Used by quick assist
199 * @param context the invocation context
200 * @param coveringNode the covering node
201 * @param locations the problems at the corrent location
202 * @param resultingCollections the resulting proposals
203 * @return <code>true</code> if the quick assist is applicable at this offset
205 public static boolean addGetterSetterProposal(IInvocationContext context, ASTNode coveringNode, IProblemLocation[] locations, ArrayList<ICommandAccess> resultingCollections) {
206 if (locations != null) {
207 for (int i= 0; i < locations.length; i++) {
208 int problemId= locations[i].getProblemId();
209 if (problemId == IProblem.UnusedPrivateField)
211 if (problemId == IProblem.UnqualifiedFieldAccess)
215 return addGetterSetterProposal(context, coveringNode, resultingCollections, 7);
218 public static void addGetterSetterProposal(IInvocationContext context, IProblemLocation location, Collection<ICommandAccess> proposals, int relevance) {
219 addGetterSetterProposal(context, location.getCoveringNode(context.getASTRoot()), proposals, relevance);
222 private static boolean addGetterSetterProposal(IInvocationContext context, ASTNode coveringNode, Collection<ICommandAccess> proposals, int relevance) {
223 if (!(coveringNode instanceof SimpleName)) {
226 SimpleName sn= (SimpleName) coveringNode;
228 IBinding binding= sn.resolveBinding();
229 if (!(binding instanceof IVariableBinding))
231 IVariableBinding variableBinding= (IVariableBinding) binding;
232 if (!variableBinding.isField())
235 if (proposals == null)
238 ChangeCorrectionProposal proposal= getProposal(context.getCompilationUnit(), sn, variableBinding, relevance);
239 if (proposal != null)
240 proposals.add(proposal);
244 private static ChangeCorrectionProposal getProposal(ICompilationUnit cu, SimpleName sn, IVariableBinding variableBinding, int relevance) {
245 Expression accessNode= sn;
246 Expression qualifier= null;
247 boolean useSuper= false;
249 ASTNode parent= sn.getParent();
250 switch (parent.getNodeType()) {
251 case ASTNode.QUALIFIED_NAME:
252 accessNode= (Expression) parent;
253 qualifier= ((QualifiedName) parent).getQualifier();
255 case ASTNode.SUPER_FIELD_ACCESS:
256 accessNode= (Expression) parent;
257 qualifier= ((SuperFieldAccess) parent).getQualifier();
261 ASTRewrite rewrite= ASTRewrite.create(sn.getAST());
262 ProposalParameter gspc= new ProposalParameter(useSuper, cu, rewrite, accessNode, qualifier, variableBinding);
263 if (ASTResolving.isWriteAccess(sn))
264 return addSetterProposal(gspc, relevance);
266 return addGetterProposal(gspc, relevance);
270 * Proposes a getter for this field.
272 * @param context the proposal parameter
273 * @param relevance relevance of this proposal
274 * @return the proposal if available or null
276 private static ChangeCorrectionProposal addGetterProposal(ProposalParameter context, int relevance) {
277 IMethodBinding method= findGetter(context);
278 if (method != null) {
279 Expression mi= createMethodInvocation(context, method, null);
280 context.astRewrite.replace(context.accessNode, mi, null);
282 String label= Messages.format(CorrectionMessages.GetterSetterCorrectionSubProcessor_replacewithgetter_description, BasicElementLabels.getJavaCodeString(ASTNodes.asString(context.accessNode)));
283 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
284 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.compilationUnit, context.astRewrite, relevance, image);
287 IJavaElement element= context.variableBinding.getJavaElement();
288 if (element instanceof IField) {
289 IField field= (IField) element;
291 if (RefactoringAvailabilityTester.isSelfEncapsulateAvailable(field))
292 return new SelfEncapsulateFieldProposal(relevance, field);
293 } catch (JavaModelException e) {
301 private static IMethodBinding findGetter(ProposalParameter context) {
302 ITypeBinding returnType= context.variableBinding.getType();
303 String getterName= GetterSetterUtil.getGetterName(context.variableBinding, context.compilationUnit.getJavaProject(), null, isBoolean(context));
304 ITypeBinding declaringType= context.variableBinding.getDeclaringClass();
305 if (declaringType == null)
307 IMethodBinding getter= Bindings.findMethodInHierarchy(declaringType, getterName, new ITypeBinding[0]);
308 if (getter != null && getter.getReturnType().isAssignmentCompatible(returnType) && Modifier.isStatic(getter.getModifiers()) == Modifier.isStatic(context.variableBinding.getModifiers()))
313 private static Expression createMethodInvocation(ProposalParameter context, IMethodBinding method, Expression argument) {
314 AST ast= context.astRewrite.getAST();
315 Expression qualifier= context.qualifier;
316 if (context.useSuper) {
317 SuperMethodInvocation invocation= ast.newSuperMethodInvocation();
318 invocation.setName(ast.newSimpleName(method.getName()));
319 if (qualifier != null)
320 invocation.setQualifier((Name) context.astRewrite.createCopyTarget(qualifier));
321 if (argument != null)
322 invocation.arguments().add(argument);
325 MethodInvocation invocation= ast.newMethodInvocation();
326 invocation.setName(ast.newSimpleName(method.getName()));
327 if (qualifier != null)
328 invocation.setExpression((Expression) context.astRewrite.createCopyTarget(qualifier));
329 if (argument != null)
330 invocation.arguments().add(argument);
336 * Proposes a setter for this field.
338 * @param context the proposal parameter
339 * @param relevance relevance of this proposal
340 * @return the proposal if available or null
342 private static ChangeCorrectionProposal addSetterProposal(ProposalParameter context, int relevance) {
343 boolean isBoolean= isBoolean(context);
344 String setterName= GetterSetterUtil.getSetterName(context.variableBinding, context.compilationUnit.getJavaProject(), null, isBoolean);
345 ITypeBinding declaringType= context.variableBinding.getDeclaringClass();
346 if (declaringType == null)
349 IMethodBinding method= Bindings.findMethodInHierarchy(declaringType, setterName, new ITypeBinding[] { context.variableBinding.getType() });
350 if (method != null && Bindings.isVoidType(method.getReturnType()) && (Modifier.isStatic(method.getModifiers()) == Modifier.isStatic(context.variableBinding.getModifiers()))) {
351 Expression assignedValue= getAssignedValue(context);
352 if (assignedValue == null)
353 return null; //we don't know how to handle those cases.
354 Expression mi= createMethodInvocation(context, method, assignedValue);
355 context.astRewrite.replace(context.accessNode.getParent(), mi, null);
357 String label= Messages.format(CorrectionMessages.GetterSetterCorrectionSubProcessor_replacewithsetter_description, BasicElementLabels.getJavaCodeString(ASTNodes.asString(context.accessNode)));
358 Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
359 ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.compilationUnit, context.astRewrite, relevance, image);
362 IJavaElement element= context.variableBinding.getJavaElement();
363 if (element instanceof IField) {
364 IField field= (IField) element;
366 if (RefactoringAvailabilityTester.isSelfEncapsulateAvailable(field))
367 return new SelfEncapsulateFieldProposal(relevance, field);
368 } catch (JavaModelException e) {
376 private static boolean isBoolean(ProposalParameter context) {
377 AST ast= context.astRewrite.getAST();
378 boolean isBoolean= ast.resolveWellKnownType("boolean") == context.variableBinding.getType(); //$NON-NLS-1$
380 isBoolean= ast.resolveWellKnownType("java.lang.Boolean") == context.variableBinding.getType(); //$NON-NLS-1$
384 private static Expression getAssignedValue(ProposalParameter context) {
385 ASTNode parent= context.accessNode.getParent();
386 ASTRewrite astRewrite= context.astRewrite;
387 IJavaProject javaProject= context.compilationUnit.getJavaProject();
388 IMethodBinding getter= findGetter(context);
389 Expression getterExpression= null;
390 if (getter != null) {
391 getterExpression= astRewrite.getAST().newSimpleName("placeholder"); //$NON-NLS-1$
393 ITypeBinding type= context.variableBinding.getType();
394 boolean is50OrHigher= JavaModelUtil.is50OrHigher(javaProject);
395 Expression result= GetterSetterUtil.getAssignedValue(parent, astRewrite, getterExpression, type, is50OrHigher);
396 if (result != null && getterExpression != null && getterExpression.getParent() != null) {
397 getterExpression.getParent().setStructuralProperty(getterExpression.getLocationInParent(), createMethodInvocation(context, getter, null));