KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > refactoring > code > CallInliner


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for:
11  * o bug "inline method - doesn't handle implicit cast" (see
12  * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941).
13  * o bug inline method: compile error (array related)
14  * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38471)
15  * o inline call that is used in a field initializer
16  * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
17  * o inline call a field initializer: could detect self reference
18  * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=44417)
19  * o Allow 'this' constructor to be inlined
20  * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093)
21  *******************************************************************************/

22 package org.eclipse.jdt.internal.corext.refactoring.code;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Set JavaDoc;
29
30 import org.eclipse.text.edits.TextEdit;
31 import org.eclipse.text.edits.TextEditGroup;
32
33 import org.eclipse.core.runtime.Assert;
34 import org.eclipse.core.runtime.CoreException;
35
36 import org.eclipse.core.filebuffers.ITextFileBuffer;
37
38 import org.eclipse.jface.text.BadLocationException;
39
40 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
41 import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
42
43 import org.eclipse.jdt.core.ICompilationUnit;
44 import org.eclipse.jdt.core.dom.AST;
45 import org.eclipse.jdt.core.dom.ASTNode;
46 import org.eclipse.jdt.core.dom.ASTVisitor;
47 import org.eclipse.jdt.core.dom.ArrayCreation;
48 import org.eclipse.jdt.core.dom.ArrayInitializer;
49 import org.eclipse.jdt.core.dom.Assignment;
50 import org.eclipse.jdt.core.dom.Block;
51 import org.eclipse.jdt.core.dom.BodyDeclaration;
52 import org.eclipse.jdt.core.dom.CastExpression;
53 import org.eclipse.jdt.core.dom.CompilationUnit;
54 import org.eclipse.jdt.core.dom.DoStatement;
55 import org.eclipse.jdt.core.dom.EnhancedForStatement;
56 import org.eclipse.jdt.core.dom.Expression;
57 import org.eclipse.jdt.core.dom.FieldAccess;
58 import org.eclipse.jdt.core.dom.FieldDeclaration;
59 import org.eclipse.jdt.core.dom.ForStatement;
60 import org.eclipse.jdt.core.dom.IBinding;
61 import org.eclipse.jdt.core.dom.IMethodBinding;
62 import org.eclipse.jdt.core.dom.ITypeBinding;
63 import org.eclipse.jdt.core.dom.IVariableBinding;
64 import org.eclipse.jdt.core.dom.IfStatement;
65 import org.eclipse.jdt.core.dom.LabeledStatement;
66 import org.eclipse.jdt.core.dom.MethodDeclaration;
67 import org.eclipse.jdt.core.dom.MethodInvocation;
68 import org.eclipse.jdt.core.dom.Modifier;
69 import org.eclipse.jdt.core.dom.Name;
70 import org.eclipse.jdt.core.dom.ParenthesizedExpression;
71 import org.eclipse.jdt.core.dom.ReturnStatement;
72 import org.eclipse.jdt.core.dom.SimpleName;
73 import org.eclipse.jdt.core.dom.Statement;
74 import org.eclipse.jdt.core.dom.SuperFieldAccess;
75 import org.eclipse.jdt.core.dom.SwitchStatement;
76 import org.eclipse.jdt.core.dom.ThisExpression;
77 import org.eclipse.jdt.core.dom.Type;
78 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
79 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
80 import org.eclipse.jdt.core.dom.WhileStatement;
81 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
82 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
83 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
84
85 import org.eclipse.jdt.internal.corext.Corext;
86 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
87 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
88 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
89 import org.eclipse.jdt.internal.corext.dom.Bindings;
90 import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
91 import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
92 import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
93 import org.eclipse.jdt.internal.corext.dom.Selection;
94 import org.eclipse.jdt.internal.corext.dom.TypeBindingVisitor;
95 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
96 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
97 import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatusCodes;
98 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
99 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
100 import org.eclipse.jdt.internal.corext.refactoring.code.flow.InputFlowAnalyzer;
101 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType;
102 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TypeEnvironment;
103 import org.eclipse.jdt.internal.corext.refactoring.util.NoCommentSourceRangeComputer;
104 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers;
105
106 import org.eclipse.jdt.internal.ui.JavaPlugin;
107
108 public class CallInliner {
109
110     private ICompilationUnit fCUnit;
111     private ASTRewrite fRewrite;
112     private ImportRewrite fImportRewrite;
113     private ITextFileBuffer fBuffer;
114     private SourceProvider fSourceProvider;
115     private TypeEnvironment fTypeEnvironment;
116     
117     private BodyDeclaration fBodyDeclaration;
118     private CodeScopeBuilder.Scope fRootScope;
119     private int fNumberOfLocals;
120     
121     private ASTNode fInvocation;
122     
123     private int fInsertionIndex;
124     private ListRewrite fListRewrite;
125     
126     private boolean fNeedsStatement;
127     private ASTNode fTargetNode;
128     private FlowContext fFlowContext;
129     private FlowInfo fFlowInfo;
130     private CodeScopeBuilder.Scope fInvocationScope;
131     private boolean fFieldInitializer;
132     private List JavaDoc fLocals;
133     private CallContext fContext;
134     
135     private class InlineEvaluator extends HierarchicalASTVisitor {
136         private ParameterData fFormalArgument;
137         private boolean fResult;
138         public InlineEvaluator(ParameterData argument) {
139             fFormalArgument= argument;
140         }
141         public boolean getResult() {
142             return fResult;
143         }
144         private boolean setResult(boolean result) {
145             fResult= result;
146             return false;
147         }
148         public boolean visit(Expression node) {
149             int accessMode= fFormalArgument.getSimplifiedAccessMode();
150             if (accessMode == FlowInfo.WRITE)
151                 return setResult(false);
152             if (accessMode == FlowInfo.UNUSED)
153                 return setResult(true);
154             if (ASTNodes.isLiteral(node))
155                 return setResult(true);
156             return setResult(fFormalArgument.getNumberOfAccesses() <= 1);
157         }
158         public boolean visit(SimpleName node) {
159             IBinding binding= node.resolveBinding();
160             if (binding instanceof IVariableBinding) {
161                 int accessMode = fFormalArgument.getSimplifiedAccessMode();
162                 if (accessMode == FlowInfo.READ || accessMode == FlowInfo.UNUSED)
163                     return setResult(true);
164                 // from now on we only have write accesses.
165
IVariableBinding vb= (IVariableBinding)binding;
166                 if (vb.isField())
167                     return setResult(false);
168                 return setResult(fFlowInfo.hasAccessMode(fFlowContext, vb, FlowInfo.UNUSED | FlowInfo.WRITE));
169             }
170             return setResult(false);
171         }
172         public boolean visit(FieldAccess node) {
173             return visit(node.getName());
174         }
175         public boolean visit(SuperFieldAccess node) {
176             return visit(node.getName());
177         }
178         public boolean visit(ThisExpression node) {
179             int accessMode= fFormalArgument.getSimplifiedAccessMode();
180             if (accessMode == FlowInfo.READ || accessMode == FlowInfo.UNUSED)
181                 return setResult(true);
182             return setResult(false);
183         }
184     }
185
186     private static class AmbiguousMethodAnalyzer implements TypeBindingVisitor {
187         private TypeEnvironment fTypeEnvironment;
188         private TType[] fTypes;
189         private IMethodBinding fOriginal;
190
191         public AmbiguousMethodAnalyzer(TypeEnvironment typeEnvironment, IMethodBinding original, TType[] types) {
192             fTypeEnvironment= typeEnvironment;
193             fOriginal= original;
194             fTypes= types;
195         }
196         public boolean visit(ITypeBinding node) {
197             IMethodBinding[] methods= node.getDeclaredMethods();
198             for (int i= 0; i < methods.length; i++) {
199                 IMethodBinding candidate= methods[i];
200                 if (candidate == fOriginal) {
201                     continue;
202                 }
203                 if (fOriginal.getName().equals(candidate.getName())) {
204                     if (canImplicitlyCall(candidate)) {
205                         return false;
206                     }
207                 }
208             }
209             return true;
210         }
211         /**
212          * Returns <code>true</code> if the method can be called without explicit casts;
213          * otherwise <code>false</code>.
214          */

215         private boolean canImplicitlyCall(IMethodBinding candidate) {
216             ITypeBinding[] parameters= candidate.getParameterTypes();
217             if (parameters.length != fTypes.length) {
218                 return false;
219             }
220             for (int i= 0; i < parameters.length; i++) {
221                 if (!fTypes[i].canAssignTo(fTypeEnvironment.create(parameters[i]))) {
222                     return false;
223                 }
224             }
225             return true;
226         }
227     }
228
229     public CallInliner(ICompilationUnit unit, CompilationUnit targetAstRoot, SourceProvider provider) throws CoreException {
230         super();
231         fCUnit= unit;
232         fBuffer= RefactoringFileBuffers.acquire(fCUnit);
233         fSourceProvider= provider;
234         fImportRewrite= StubUtility.createImportRewrite(targetAstRoot, true);
235         fLocals= new ArrayList JavaDoc(3);
236         fRewrite= ASTRewrite.create(targetAstRoot.getAST());
237         fRewrite.setTargetSourceRangeComputer(new NoCommentSourceRangeComputer());
238         fTypeEnvironment= new TypeEnvironment();
239     }
240
241     public void dispose() {
242         try {
243             RefactoringFileBuffers.release(fCUnit);
244         } catch (CoreException exception) {
245             JavaPlugin.log(exception);
246         }
247     }
248     
249     
250     public ImportRewrite getImportEdit() {
251         return fImportRewrite;
252     }
253     
254     public ASTNode getTargetNode() {
255         return fTargetNode;
256     }
257     
258     public void initialize(BodyDeclaration declaration) {
259         fBodyDeclaration= declaration;
260         fRootScope= CodeScopeBuilder.perform(declaration, fSourceProvider.getDeclaration().resolveBinding());
261         fNumberOfLocals= 0;
262         switch (declaration.getNodeType()) {
263             case ASTNode.METHOD_DECLARATION:
264             case ASTNode.INITIALIZER:
265                 fNumberOfLocals= LocalVariableIndex.perform(declaration);
266                 break;
267         }
268     }
269
270     public RefactoringStatus initialize(ASTNode invocation, int severity) {
271         RefactoringStatus result= new RefactoringStatus();
272         fInvocation= invocation;
273         fLocals= new ArrayList JavaDoc(3);
274         
275         checkMethodDeclaration(result, severity);
276         if (result.getSeverity() >= severity)
277             return result;
278         
279         initializeRewriteState();
280         initializeTargetNode();
281         flowAnalysis();
282         
283         fContext= new CallContext(fInvocation, fInvocationScope, fTargetNode.getNodeType(), fImportRewrite);
284         
285         try {
286             computeRealArguments();
287             computeReceiver();
288         } catch (BadLocationException exception) {
289             JavaPlugin.log(exception);
290         }
291         checkInvocationContext(result, severity);
292         
293         return result;
294     }
295
296     private void initializeRewriteState() {
297         // field initializer can be inside of a block if used in a local class
298
// but block can't be a child of field initializer
299
if(ASTNodes.getParent(fInvocation, ASTNode.FIELD_DECLARATION) != null) {
300             fFieldInitializer= true;
301         }
302     }
303
304     private void initializeTargetNode() {
305         ASTNode parent= fInvocation.getParent();
306         int nodeType= parent.getNodeType();
307         if (nodeType == ASTNode.EXPRESSION_STATEMENT || nodeType == ASTNode.RETURN_STATEMENT) {
308             fTargetNode= parent;
309         } else {
310             fTargetNode= fInvocation;
311         }
312     }
313
314     // the checks depend on invocation context and therefore can't be done in SourceAnalyzer
315
private void checkMethodDeclaration(RefactoringStatus result, int severity) {
316         MethodDeclaration methodDeclaration= fSourceProvider.getDeclaration();
317         // it is not allowed to inline constructor invocation only if it is used for class instance creation
318
// if constructor is invoked from another constructor then we can inline such invocation
319
if (fInvocation.getNodeType() != ASTNode.CONSTRUCTOR_INVOCATION && methodDeclaration.isConstructor()) {
320             result.addEntry(new RefactoringStatusEntry(
321                 severity,
322                 RefactoringCoreMessages.CallInliner_constructors,
323                 JavaStatusContext.create(fCUnit, fInvocation)));
324         }
325         if (fSourceProvider.hasSuperMethodInvocation() && fInvocation.getNodeType() == ASTNode.METHOD_INVOCATION) {
326             Expression receiver= ((MethodInvocation)fInvocation).getExpression();
327             if (receiver instanceof ThisExpression) {
328                 result.addEntry(new RefactoringStatusEntry(
329                     severity,
330                     RefactoringCoreMessages.CallInliner_super_into_this_expression,
331                     JavaStatusContext.create(fCUnit, fInvocation)));
332             }
333         }
334     }
335
336     private void checkInvocationContext(RefactoringStatus result, int severity) {
337         if (fInvocation.getNodeType() == ASTNode.METHOD_INVOCATION) {
338             Expression exp= ((MethodInvocation)fInvocation).getExpression();
339             if (exp != null && exp.resolveTypeBinding() == null) {
340                 addEntry(result, RefactoringCoreMessages.CallInliner_receiver_type,
341                     RefactoringStatusCodes.INLINE_METHOD_NULL_BINDING, severity);
342                 return;
343             }
344         }
345         int nodeType= fTargetNode.getNodeType();
346         if (nodeType == ASTNode.EXPRESSION_STATEMENT) {
347             if (fSourceProvider.isExecutionFlowInterrupted()) {
348                 addEntry(result, RefactoringCoreMessages.CallInliner_execution_flow,
349                     RefactoringStatusCodes.INLINE_METHOD_EXECUTION_FLOW, severity);
350                 return;
351             }
352         } else if (nodeType == ASTNode.METHOD_INVOCATION) {
353             ASTNode parent= fTargetNode.getParent();
354             if (isReturnStatement(parent)) {
355                 //support inlining even if the execution flow is interrupted
356
return;
357             }
358             if (fSourceProvider.isExecutionFlowInterrupted()) {
359                 addEntry(result, RefactoringCoreMessages.CallInliner_execution_flow,
360                     RefactoringStatusCodes.INLINE_METHOD_EXECUTION_FLOW, severity);
361                 return;
362             }
363             if (isAssignment(parent) || isSingleDeclaration(parent)) {
364                 // we support inlining expression in assigment and initializers as
365
// long as the execution flow isn't interrupted.
366
return;
367             } else {
368                 boolean isFieldDeclaration= ASTNodes.getParent(fInvocation, FieldDeclaration.class) != null;
369                 if (!fSourceProvider.isSimpleFunction()) {
370                     if (isMultiDeclarationFragment(parent)) {
371                         addEntry(result, RefactoringCoreMessages.CallInliner_multiDeclaration,
372                             RefactoringStatusCodes.INLINE_METHOD_INITIALIZER_IN_FRAGEMENT, severity);
373                     } else if (isFieldDeclaration) {
374                         addEntry(result,
375                             RefactoringCoreMessages.CallInliner_field_initializer_simple,
376                             RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
377                     } else {
378                         addEntry(result, RefactoringCoreMessages.CallInliner_simple_functions,
379                             RefactoringStatusCodes.INLINE_METHOD_ONLY_SIMPLE_FUNCTIONS, severity);
380                     }
381                     return;
382                 }
383                 if (isFieldDeclaration) {
384                     int argumentsCount= fContext.arguments.length;
385                     for (int i= 0; i < argumentsCount; i++) {
386                         ParameterData parameter= fSourceProvider.getParameterData(i);
387                         if(parameter.isWrite()) {
388                             addEntry(result,
389                                 RefactoringCoreMessages.CallInliner_field_initialize_write_parameter,
390                                 RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
391                             return;
392                         }
393                     }
394                     if(fLocals.size() > 0) {
395                         addEntry(result,
396                             RefactoringCoreMessages.CallInliner_field_initialize_new_local,
397                             RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
398                         return;
399                     }
400                     // verify that the field is not referenced by the initializer method
401
VariableDeclarationFragment variable= (VariableDeclarationFragment)ASTNodes.getParent(fInvocation, ASTNode.VARIABLE_DECLARATION_FRAGMENT);
402                     if(fSourceProvider.isVariableReferenced(variable.resolveBinding())) {
403                         addEntry(result,
404                             RefactoringCoreMessages.CallInliner_field_initialize_self_reference,
405                             RefactoringStatusCodes.INLINE_METHOD_FIELD_INITIALIZER, severity);
406                         return;
407                     }
408                 }
409             }
410         }
411     }
412
413     private static boolean isAssignment(ASTNode node) {
414         return node instanceof Assignment;
415     }
416     
417     private static boolean isReturnStatement(ASTNode node) {
418         return node instanceof ReturnStatement;
419     }
420     
421     private static boolean isSingleDeclaration(ASTNode node) {
422         int type= node.getNodeType();
423         if (type == ASTNode.SINGLE_VARIABLE_DECLARATION)
424             return true;
425         if (type == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
426             node= node.getParent();
427             if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
428                 VariableDeclarationStatement vs= (VariableDeclarationStatement)node;
429                 return vs.fragments().size() == 1;
430             }
431         }
432         return false;
433     }
434     
435     private static boolean isMultiDeclarationFragment(ASTNode node) {
436         int nodeType= node.getNodeType();
437         if (nodeType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
438             node= node.getParent();
439             if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
440                 VariableDeclarationStatement vs= (VariableDeclarationStatement)node;
441                 return vs.fragments().size() > 1;
442             }
443         }
444         return false;
445     }
446     
447     private void addEntry(RefactoringStatus result, String JavaDoc message, int code, int severity) {
448         result.addEntry(new RefactoringStatusEntry(
449             severity, message,
450             JavaStatusContext.create(fCUnit, fInvocation),
451             Corext.getPluginId(),
452             code, null));
453     }
454
455     private void flowAnalysis() {
456         fInvocationScope= fRootScope.findScope(fTargetNode.getStartPosition(), fTargetNode.getLength());
457         fInvocationScope.setCursor(fTargetNode.getStartPosition());
458         fFlowContext= new FlowContext(0, fNumberOfLocals + 1);
459         fFlowContext.setConsiderAccessMode(true);
460         fFlowContext.setComputeMode(FlowContext.ARGUMENTS);
461         Selection selection= Selection.createFromStartLength(fInvocation.getStartPosition(), fInvocation.getLength());
462         switch (fBodyDeclaration.getNodeType()) {
463             case ASTNode.INITIALIZER:
464             case ASTNode.FIELD_DECLARATION:
465             case ASTNode.METHOD_DECLARATION:
466                 fFlowInfo= new InputFlowAnalyzer(fFlowContext, selection, true).perform(fBodyDeclaration);
467                 break;
468             default:
469                 Assert.isTrue(false, "Should not happen"); //$NON-NLS-1$
470
}
471     }
472     
473     public RefactoringStatus perform(TextEditGroup textEditGroup) throws CoreException {
474         RefactoringStatus result= new RefactoringStatus();
475         String JavaDoc[] blocks= fSourceProvider.getCodeBlocks(fContext);
476         if(!fFieldInitializer) {
477             initializeInsertionPoint(fSourceProvider.getNumberOfStatements() + fLocals.size());
478         }
479         
480         addNewLocals(textEditGroup);
481         replaceCall(result, blocks, textEditGroup);
482         return result;
483     }
484     
485     public TextEdit getModifications() {
486         return fRewrite.rewriteAST(fBuffer.getDocument(), fCUnit.getJavaProject().getOptions(true));
487     }
488
489     private void computeRealArguments() throws BadLocationException {
490         List JavaDoc arguments= Invocations.getArguments(fInvocation);
491         Set JavaDoc canNotInline= crossCheckArguments(arguments);
492         boolean needsVarargBoxing= needsVarargBoxing(arguments);
493         int varargIndex= fSourceProvider.getVarargIndex();
494         String JavaDoc[] realArguments= new String JavaDoc[needsVarargBoxing ? varargIndex + 1 : arguments.size()];
495         for (int i= 0; i < (needsVarargBoxing ? varargIndex : arguments.size()); i++) {
496             Expression expression= (Expression)arguments.get(i);
497             ParameterData parameter= fSourceProvider.getParameterData(i);
498             if (canInline(expression, parameter) && !canNotInline.contains(expression)) {
499                 realArguments[i] = getContent(expression);
500                 // fixes bugs #35905, #38471
501
if (argumentNeedsParenthesis(expression, parameter)) {
502                     realArguments[i] = "(" + realArguments[i] + ")"; //$NON-NLS-1$ //$NON-NLS-2$
503
}
504             } else {
505                 String JavaDoc name= fInvocationScope.createName(parameter.getName(), true);
506                 realArguments[i]= name;
507                 fLocals.add(createLocalDeclaration(
508                     parameter.getTypeBinding(), name,
509                     (Expression)fRewrite.createCopyTarget(expression)));
510             }
511         }
512         if (needsVarargBoxing) {
513             ParameterData parameter= fSourceProvider.getParameterData(varargIndex);
514             String JavaDoc name= fInvocationScope.createName(parameter.getName(), true);
515             realArguments[varargIndex]= name;
516             AST ast= fInvocation.getAST();
517             Type type= fImportRewrite.addImport(parameter.getTypeBinding(), ast);
518             VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
519             fragment.setName(ast.newSimpleName(name));
520             ArrayInitializer initializer= ast.newArrayInitializer();
521             for (int i= varargIndex; i < arguments.size(); i++) {
522                 initializer.expressions().add(fRewrite.createCopyTarget((ASTNode)arguments.get(i)));
523             }
524             fragment.setInitializer(initializer);
525             VariableDeclarationStatement decl= ast.newVariableDeclarationStatement(fragment);
526             decl.setType(type);
527             fLocals.add(decl);
528         }
529         fContext.arguments= realArguments;
530     }
531     
532     private boolean needsVarargBoxing(List JavaDoc arguments) {
533         if (!fSourceProvider.isVarargs())
534             return false;
535         /*
536         if (!fSourceProvider.hasArrayAccess())
537             return false;
538         */

539         int index= fSourceProvider.getVarargIndex();
540         // we have varags but the call doesn't pass any arguments
541
if (index >= arguments.size())
542             return true;
543         // parameter is array type
544
// one arg
545
if (index == arguments.size() - 1) {
546             ITypeBinding argument= ((Expression)arguments.get(index)).resolveTypeBinding();
547             if (argument == null)
548                 return false;
549             ITypeBinding parameter= fSourceProvider.getParameterData(index).getTypeBinding();
550             return !fTypeEnvironment.create(argument).canAssignTo(fTypeEnvironment.create(parameter));
551         }
552         return true;
553     }
554     
555     private boolean argumentNeedsParenthesis(Expression expression, ParameterData param) {
556         if (expression instanceof CastExpression || expression instanceof ArrayCreation)
557             return true;
558         int argPrecedence= OperatorPrecedence.getValue(expression);
559         int paramPrecedence= param.getOperatorPrecedence();
560         if (argPrecedence != -1 && paramPrecedence != -1)
561             return argPrecedence < paramPrecedence;
562         
563         return false;
564     }
565     
566     private void computeReceiver() throws BadLocationException {
567         Expression receiver= Invocations.getExpression(fInvocation);
568         if (receiver == null)
569             return;
570         final boolean isName= receiver instanceof Name;
571         if (isName)
572             fContext.receiverIsStatic= ((Name)receiver).resolveBinding() instanceof ITypeBinding;
573         if (ASTNodes.isLiteral(receiver) || isName || receiver instanceof ThisExpression) {
574             fContext.receiver= fBuffer.getDocument().get(receiver.getStartPosition(), receiver.getLength());
575             return;
576         }
577         switch(fSourceProvider.getReceiversToBeUpdated()) {
578             case 0:
579                 // Make sure we evaluate the current receiver. Best is to assign to
580
// local.
581
fLocals.add(createLocalDeclaration(
582                     receiver.resolveTypeBinding(),
583                     fInvocationScope.createName("r", true), //$NON-NLS-1$
584
(Expression)fRewrite.createCopyTarget(receiver)));
585                 return;
586             case 1:
587                 fContext.receiver= fBuffer.getDocument().get(receiver.getStartPosition(), receiver.getLength());
588                 return;
589             default:
590                 String JavaDoc local= fInvocationScope.createName("r", true); //$NON-NLS-1$
591
fLocals.add(createLocalDeclaration(
592                     receiver.resolveTypeBinding(),
593                     local,
594                     (Expression)fRewrite.createCopyTarget(receiver)));
595                 fContext.receiver= local;
596                 return;
597         }
598     }
599
600     private void addNewLocals(TextEditGroup textEditGroup) {
601         if (fLocals.isEmpty())
602             return;
603         for (Iterator JavaDoc iter= fLocals.iterator(); iter.hasNext();) {
604             ASTNode element= (ASTNode)iter.next();
605             fListRewrite.insertAt(element, fInsertionIndex++, textEditGroup);
606         }
607     }
608
609     private void replaceCall(RefactoringStatus status, String JavaDoc[] blocks, TextEditGroup textEditGroup) {
610         // Inline empty body
611
if (blocks.length == 0) {
612             if (fNeedsStatement) {
613                 fRewrite.replace(fTargetNode, fTargetNode.getAST().newEmptyStatement(), textEditGroup);
614             } else {
615                 fRewrite.remove(fTargetNode, textEditGroup);
616             }
617         } else {
618             ASTNode node= null;
619             for (int i= 0; i < blocks.length - 1; i++) {
620                 node= fRewrite.createStringPlaceholder(blocks[i], ASTNode.RETURN_STATEMENT);
621                 fListRewrite.insertAt(node, fInsertionIndex++, textEditGroup);
622             }
623             String JavaDoc block= blocks[blocks.length - 1];
624             // We can inline a call where the declaration is a function and the call itself
625
// is a statement. In this case we have to create a temporary variable if the
626
// returned expression must be evaluated.
627
if (fContext.callMode == ASTNode.EXPRESSION_STATEMENT && fSourceProvider.hasReturnValue()) {
628                 if (fSourceProvider.mustEvaluateReturnedExpression()) {
629                     if (fSourceProvider.returnValueNeedsLocalVariable()) {
630                         IMethodBinding invocation= Invocations.resolveBinding(fInvocation);
631                         node= createLocalDeclaration(
632                             invocation.getReturnType(),
633                             fInvocationScope.createName(fSourceProvider.getMethodName(), true),
634                             (Expression)fRewrite.createStringPlaceholder(block, ASTNode.METHOD_INVOCATION));
635                     } else {
636                         node= fTargetNode.getAST().newExpressionStatement(
637                             (Expression)fRewrite.createStringPlaceholder(block, ASTNode.METHOD_INVOCATION));
638                     }
639                 } else {
640                     node= null;
641                 }
642             } else if (fTargetNode instanceof Expression) {
643                 node= fRewrite.createStringPlaceholder(block, ASTNode.METHOD_INVOCATION);
644                 
645                 // fixes bug #24941
646
if(needsExplicitCast(status)) {
647                     AST ast= node.getAST();
648                     CastExpression castExpression= ast.newCastExpression();
649                     Type returnType= fImportRewrite.addImport(fSourceProvider.getReturnType(), ast);
650                     castExpression.setType(returnType);
651                     castExpression.setExpression((Expression)node);
652                     node= castExpression;
653                 }
654                 
655                 if (needsParenthesis()) {
656                     ParenthesizedExpression pExp= fTargetNode.getAST().newParenthesizedExpression();
657                     pExp.setExpression((Expression)node);
658                     node= pExp;
659                 }
660             } else {
661                 node= fRewrite.createStringPlaceholder(block, ASTNode.RETURN_STATEMENT);
662             }
663             
664             // Now replace the target node with the source node
665
if (node != null) {
666                 if (fTargetNode == null) {
667                     fListRewrite.insertAt(node, fInsertionIndex++, textEditGroup);
668                 } else {
669                     fRewrite.replace(fTargetNode, node, textEditGroup);
670                 }
671             } else {
672                 if (fTargetNode != null) {
673                     fRewrite.remove(fTargetNode, textEditGroup);
674                 }
675             }
676         }
677     }
678
679     /**
680      * @return <code>true</code> if explicit cast is needed otherwise <code>false</code>
681      */

682     private boolean needsExplicitCast(RefactoringStatus status) {
683         // if the return type of the method is the same as the type of the
684
// returned expression then we don't need an explicit cast.
685
if (fSourceProvider.returnTypeMatchesReturnExpressions())
686                 return false;
687         ASTNode parent= fTargetNode.getParent();
688         int nodeType= parent.getNodeType();
689         if (nodeType == ASTNode.METHOD_INVOCATION) {
690             MethodInvocation methodInvocation= (MethodInvocation)parent;
691             if(methodInvocation.getExpression() == fTargetNode)
692                 return false;
693             IMethodBinding method= methodInvocation.resolveMethodBinding();
694             if (method == null) {
695                 status.addError(RefactoringCoreMessages.CallInliner_cast_analysis_error,
696                     JavaStatusContext.create(fCUnit, methodInvocation));
697                 return false;
698             }
699             ITypeBinding[] parameters= method.getParameterTypes();
700             int argumentIndex= methodInvocation.arguments().indexOf(fInvocation);
701             List JavaDoc returnExprs= fSourceProvider.getReturnExpressions();
702             // it is inferred that only methods consisting of a single
703
// return statement can be inlined as parameters in other
704
// method invocations
705
if (returnExprs.size() != 1)
706                 return false;
707             parameters[argumentIndex]= ((Expression)returnExprs.get(0)).resolveTypeBinding();
708
709             ITypeBinding type= ASTNodes.getReceiverTypeBinding(methodInvocation);
710             TypeBindingVisitor visitor= new AmbiguousMethodAnalyzer(
711                 fTypeEnvironment, method, fTypeEnvironment.create(parameters));
712             if(!visitor.visit(type)) {
713                 return true;
714             }
715             else if(type.isInterface()) {
716                 return !Bindings.visitInterfaces(type, visitor);
717             }
718             else if(Modifier.isAbstract(type.getModifiers())) {
719                 return !Bindings.visitHierarchy(type, visitor);
720             }
721             else {
722                 // it is not needed to visit interfaces if receiver is a concrete class
723
return !Bindings.visitSuperclasses(type, visitor);
724             }
725         }
726         return false;
727     }
728
729     private boolean needsParenthesis() {
730         if (!fSourceProvider.needsReturnedExpressionParenthesis())
731             return false;
732         ASTNode parent= fTargetNode.getParent();
733         int type= parent.getNodeType();
734         return
735             type == ASTNode.METHOD_INVOCATION ||
736             (parent instanceof Expression && type != ASTNode.ASSIGNMENT) ||
737             (fSourceProvider.returnsConditionalExpression() &&
738                 type == ASTNode.VARIABLE_DECLARATION_FRAGMENT &&
739                 ((VariableDeclarationFragment)parent).getInitializer() == fTargetNode);
740     }
741     
742     private VariableDeclarationStatement createLocalDeclaration(ITypeBinding type, String JavaDoc name, Expression initializer) {
743         String JavaDoc typeName= fImportRewrite.addImport(type);
744         VariableDeclarationStatement decl= (VariableDeclarationStatement)ASTNodeFactory.newStatement(
745             fInvocation.getAST(), typeName + " " + name + ";"); //$NON-NLS-1$ //$NON-NLS-2$
746
((VariableDeclarationFragment)decl.fragments().get(0)).setInitializer(initializer);
747         return decl;
748     }
749
750     /**
751      * Checks whether arguments are passed to the method which do some assignments
752      * inside the expression. If so these arguments can't be inlined into the
753      * calling method since the assignments might be reorder. An example is:
754      * <code>
755      * add((field=args).length,field.hashCode());
756      * </code>
757      * Field might not be initialized when the arguments are reorder in the called
758      * method.
759      */

760     private Set JavaDoc crossCheckArguments(List JavaDoc arguments) {
761         final Set JavaDoc assigned= new HashSet JavaDoc();
762         final Set JavaDoc result= new HashSet JavaDoc();
763         for (Iterator JavaDoc iter= arguments.iterator(); iter.hasNext();) {
764             final Expression expression= (Expression) iter.next();
765             expression.accept(new ASTVisitor() {
766                 public boolean visit(Assignment node) {
767                     Expression lhs= node.getLeftHandSide();
768                     if (lhs instanceof Name) {
769                         IBinding binding= ((Name)lhs).resolveBinding();
770                         if (binding instanceof IVariableBinding) {
771                             assigned.add(binding);
772                             result.add(expression);
773                         }
774                     }
775                     return true;
776                 }
777             });
778         }
779         for (Iterator JavaDoc iter= arguments.iterator(); iter.hasNext();) {
780             final Expression expression= (Expression) iter.next();
781             if (!result.contains(expression)) {
782                 expression.accept(new HierarchicalASTVisitor() {
783                     public boolean visit(Name node) {
784                         IBinding binding= node.resolveBinding();
785                         if (binding != null && assigned.contains(binding))
786                             result.add(expression);
787                         return false;
788                     }
789                 });
790             }
791         }
792         return result;
793     }
794     
795     private boolean canInline(Expression actualParameter, ParameterData formalParameter) {
796         InlineEvaluator evaluator= new InlineEvaluator(formalParameter);
797         actualParameter.accept(evaluator);
798         return evaluator.getResult();
799     }
800     
801     private void initializeInsertionPoint(int nos) {
802         fInsertionIndex= -1;
803         fNeedsStatement= false;
804         // if we have a constructor invocation the invocation itself is already a statement
805
ASTNode parentStatement= fInvocation instanceof Statement
806             ? fInvocation
807             : ASTNodes.getParent(fInvocation, Statement.class);
808         ASTNode container= parentStatement.getParent();
809         int type= container.getNodeType();
810         if (type == ASTNode.BLOCK) {
811             Block block= (Block)container;
812             fListRewrite= fRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
813             fInsertionIndex= fListRewrite.getRewrittenList().indexOf(parentStatement);
814         } else if (type == ASTNode.SWITCH_STATEMENT) {
815             SwitchStatement switchStatement= (SwitchStatement)container;
816             fListRewrite= fRewrite.getListRewrite(switchStatement, SwitchStatement.STATEMENTS_PROPERTY);
817             fInsertionIndex= fListRewrite.getRewrittenList().indexOf(parentStatement);
818         } else if (isControlStatement(container) || type == ASTNode.LABELED_STATEMENT) {
819             fNeedsStatement= true;
820             if (nos > 1 || needsBlockAroundDanglingIf()) {
821                 Block block= fInvocation.getAST().newBlock();
822                 fInsertionIndex= 0;
823                 Statement currentStatement= null;
824                 switch(type) {
825                     case ASTNode.LABELED_STATEMENT:
826                         currentStatement= ((LabeledStatement)container).getBody();
827                         break;
828                     case ASTNode.FOR_STATEMENT:
829                         currentStatement= ((ForStatement)container).getBody();
830                         break;
831                     case ASTNode.ENHANCED_FOR_STATEMENT:
832                         currentStatement= ((EnhancedForStatement)container).getBody();
833                         break;
834                     case ASTNode.WHILE_STATEMENT:
835                         currentStatement= ((WhileStatement)container).getBody();
836                         break;
837                     case ASTNode.DO_STATEMENT:
838                         currentStatement= ((DoStatement)container).getBody();
839                         break;
840                     case ASTNode.IF_STATEMENT:
841                         IfStatement node= (IfStatement)container;
842                         Statement thenPart= node.getThenStatement();
843                         if (fTargetNode == thenPart || ASTNodes.isParent(fTargetNode, thenPart)) {
844                             currentStatement= thenPart;
845                         } else {
846                             currentStatement= node.getElseStatement();
847                         }
848                         break;
849                 }
850                 Assert.isNotNull(currentStatement);
851                 fRewrite.replace(currentStatement, block, null);
852                 fListRewrite= fRewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY);
853                 // The method to be inlined is not the body of the control statement.
854
if (currentStatement != fTargetNode) {
855                     fListRewrite.insertLast(fRewrite.createCopyTarget(currentStatement), null);
856                 } else {
857                     // We can't replace a copy with something else. So we
858
// have to insert all statements to be inlined.
859
fTargetNode= null;
860                 }
861             }
862         }
863         // We only insert one new statement or we delete the existing call.
864
// So there is no need to have an insertion index.
865
}
866
867     private boolean needsBlockAroundDanglingIf() {
868         /* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=169331
869          *
870          * Situation:
871          * boolean a, b;
872          * void toInline() {
873          * if (a)
874          * hashCode();
875          * }
876          * void m() {
877          * if (b)
878          * toInline();
879          * else
880          * toString();
881          * }
882          * => needs block around inlined "if (a)..." to avoid attaching else to wrong if.
883          */

884         return fTargetNode.getLocationInParent() == IfStatement.THEN_STATEMENT_PROPERTY
885                 && fTargetNode.getParent().getStructuralProperty(IfStatement.ELSE_STATEMENT_PROPERTY) != null
886                 && fSourceProvider.isDangligIf();
887     }
888
889     private String JavaDoc getContent(ASTNode node) throws BadLocationException {
890         return fBuffer.getDocument().get(node.getStartPosition(), node.getLength());
891     }
892
893     private boolean isControlStatement(ASTNode node) {
894         int type= node.getNodeType();
895         return type == ASTNode.IF_STATEMENT || type == ASTNode.FOR_STATEMENT || type == ASTNode.ENHANCED_FOR_STATEMENT ||
896                 type == ASTNode.WHILE_STATEMENT || type == ASTNode.DO_STATEMENT;
897     }
898 }
899
Popular Tags