]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/text/correction/GetterSetterCorrectionSubProcessor.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / ui / org / eclipse / jdt / internal / ui / text / correction / GetterSetterCorrectionSubProcessor.java
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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.ui.text.correction;
12
13 import java.lang.reflect.InvocationTargetException;
14 import java.util.ArrayList;
15 import java.util.Collection;
16
17 import org.eclipse.swt.graphics.Image;
18 import org.eclipse.swt.widgets.Display;
19
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.NullProgressMonitor;
23
24 import org.eclipse.core.resources.IFile;
25
26 import org.eclipse.jface.text.IDocument;
27
28 import org.eclipse.ui.IWorkbenchWindow;
29 import org.eclipse.ui.PlatformUI;
30
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;
35
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;
58
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;
66
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;
73
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;
81
82
83 public class GetterSetterCorrectionSubProcessor {
84
85         public static final String SELF_ENCAPSULATE_FIELD_ID= "org.eclipse.jdt.ui.correction.encapsulateField.assist"; //$NON-NLS-1$
86
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;
94
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;
102                 }
103         }
104
105         public static class SelfEncapsulateFieldProposal extends ChangeCorrectionProposal { // public for tests
106
107                 private IField fField;
108                 private boolean fNoDialog;
109
110                 public SelfEncapsulateFieldProposal(int relevance, IField field) {
111                         super(getDescription(field), null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE));
112                         fField= field;
113                         fNoDialog= false;
114                         setCommandId(SELF_ENCAPSULATE_FIELD_ID);
115                 }
116
117                 public IField getField() {
118                         return fField;
119                 }
120
121                 public void setNoDialog(boolean noDialog) {
122                         fNoDialog= noDialog;
123                 }
124
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;
138                                         }
139                                 }
140                         }
141                         return null;
142                 }
143
144
145                 private static String getDescription(IField field) {
146                         return Messages.format(CorrectionMessages.GetterSetterCorrectionSubProcessor_creategetterunsingencapsulatefield_description, BasicElementLabels.getJavaElementName(field.getElementName()));
147                 }
148
149                 /*
150                  * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension5#getAdditionalProposalInfo(org.eclipse.core.runtime.IProgressMonitor)
151                  * @since 3.5
152                  */
153                 @Override
154                 public Object getAdditionalProposalInfo(IProgressMonitor monitor) {
155                         return CorrectionMessages.GetterSetterCorrectionSubProcessor_additional_info;
156                 }
157
158                 @Override
159                 public void apply(IDocument document) {
160                         try {
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
164                                 if (fNoDialog) {
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) {
168                                                 try {
169                                                         helper.perform(false, false);
170                                                 } catch (InterruptedException e) {
171                                                         JavaPlugin.log(e);
172                                                 } catch (InvocationTargetException e) {
173                                                         JavaPlugin.log(e);
174                                                 }
175                                         } else {
176                                                 Display.getDefault().syncExec(new Runnable() {
177                                                         public void run() {
178                                                                 try {
179                                                                         helper.perform(false, false);
180                                                                 } catch (InterruptedException e) {
181                                                                         JavaPlugin.log(e);
182                                                                 } catch (InvocationTargetException e) {
183                                                                         JavaPlugin.log(e);
184                                                                 }
185                                                         }
186                                                 });
187                                         }
188                                 } else {
189                                         new RefactoringStarter().activate(new SelfEncapsulateFieldWizard(refactoring), JavaPlugin.getActiveWorkbenchShell(), "", RefactoringSaveHelper.SAVE_REFACTORING); //$NON-NLS-1$
190                                 }
191                         } catch (JavaModelException e) {
192                                 ExceptionHandler.handle(e, CorrectionMessages.GetterSetterCorrectionSubProcessor_encapsulate_field_error_title, CorrectionMessages.GetterSetterCorrectionSubProcessor_encapsulate_field_error_message);
193                         }
194                 }
195         }
196
197         /**
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
204          */
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)
210                                         return false;
211                                 if (problemId == IProblem.UnqualifiedFieldAccess)
212                                         return false;
213                         }
214                 }
215                 return addGetterSetterProposal(context, coveringNode, resultingCollections, 7);
216         }
217
218         public static void addGetterSetterProposal(IInvocationContext context, IProblemLocation location, Collection<ICommandAccess> proposals, int relevance) {
219                 addGetterSetterProposal(context, location.getCoveringNode(context.getASTRoot()), proposals, relevance);
220         }
221
222         private static boolean addGetterSetterProposal(IInvocationContext context, ASTNode coveringNode, Collection<ICommandAccess> proposals, int relevance) {
223                 if (!(coveringNode instanceof SimpleName)) {
224                         return false;
225                 }
226                 SimpleName sn= (SimpleName) coveringNode;
227
228                 IBinding binding= sn.resolveBinding();
229                 if (!(binding instanceof IVariableBinding))
230                         return false;
231                 IVariableBinding variableBinding= (IVariableBinding) binding;
232                 if (!variableBinding.isField())
233                         return false;
234
235                 if (proposals == null)
236                         return true;
237
238                 ChangeCorrectionProposal proposal= getProposal(context.getCompilationUnit(), sn, variableBinding, relevance);
239                 if (proposal != null)
240                         proposals.add(proposal);
241                 return true;
242         }
243
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;
248
249                 ASTNode parent= sn.getParent();
250                 switch (parent.getNodeType()) {
251                         case ASTNode.QUALIFIED_NAME:
252                                 accessNode= (Expression) parent;
253                                 qualifier= ((QualifiedName) parent).getQualifier();
254                                 break;
255                         case ASTNode.SUPER_FIELD_ACCESS:
256                                 accessNode= (Expression) parent;
257                                 qualifier= ((SuperFieldAccess) parent).getQualifier();
258                                 useSuper= true;
259                                 break;
260                 }
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);
265                 else
266                         return addGetterProposal(gspc, relevance);
267         }
268
269         /**
270          * Proposes a getter for this field.
271          * 
272          * @param context the proposal parameter
273          * @param relevance relevance of this proposal
274          * @return the proposal if available or null
275          */
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);
281
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);
285                         return proposal;
286                 } else {
287                         IJavaElement element= context.variableBinding.getJavaElement();
288                         if (element instanceof IField) {
289                                 IField field= (IField) element;
290                                 try {
291                                         if (RefactoringAvailabilityTester.isSelfEncapsulateAvailable(field))
292                                                 return new SelfEncapsulateFieldProposal(relevance, field);
293                                 } catch (JavaModelException e) {
294                                         JavaPlugin.log(e);
295                                 }
296                         }
297                 }
298                 return null;
299         }
300
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)
306                         return 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()))
309                         return getter;
310                 return null;
311         }
312
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);
323                         return invocation;
324                 } else {
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);
331                         return invocation;
332                 }
333         }
334
335         /**
336          * Proposes a setter for this field.
337          * 
338          * @param context the proposal parameter
339          * @param relevance relevance of this proposal
340          * @return the proposal if available or null
341          */
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)
347                         return null;
348
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);
356
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);
360                         return proposal;
361                 } else {
362                         IJavaElement element= context.variableBinding.getJavaElement();
363                         if (element instanceof IField) {
364                                 IField field= (IField) element;
365                                 try {
366                                         if (RefactoringAvailabilityTester.isSelfEncapsulateAvailable(field))
367                                                 return new SelfEncapsulateFieldProposal(relevance, field);
368                                 } catch (JavaModelException e) {
369                                         JavaPlugin.log(e);
370                                 }
371                         }
372                 }
373                 return null;
374         }
375
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$
379                 if (!isBoolean)
380                         isBoolean= ast.resolveWellKnownType("java.lang.Boolean") == context.variableBinding.getType(); //$NON-NLS-1$
381                 return isBoolean;
382         }
383
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$
392                 }
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));
398                 }
399                 return result;
400         }
401
402 }