]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/core refactoring/org/eclipse/jdt/internal/corext/refactoring/code/SourceProvider.java
114d29f5e4dc349450e520b1b7e71382a99a4a16
[ifi-stolz-refaktor.git] / case-study / jdt-before / core refactoring / org / eclipse / jdt / internal / corext / refactoring / code / SourceProvider.java
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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix 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 inline call that is used in a field initializer
14  *         (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
15  *       o inline call a field initializer: could detect self reference
16  *         (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=44417)
17  *******************************************************************************/
18 package org.eclipse.jdt.internal.corext.refactoring.code;
19
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
24
25 import org.eclipse.core.runtime.Assert;
26 import org.eclipse.core.runtime.CoreException;
27
28 import org.eclipse.core.filebuffers.ITextFileBuffer;
29
30 import org.eclipse.text.edits.MalformedTreeException;
31 import org.eclipse.text.edits.MultiTextEdit;
32 import org.eclipse.text.edits.RangeMarker;
33 import org.eclipse.text.edits.TextEdit;
34 import org.eclipse.text.edits.TextEditProcessor;
35 import org.eclipse.text.edits.UndoEdit;
36
37 import org.eclipse.jface.text.BadLocationException;
38 import org.eclipse.jface.text.Document;
39 import org.eclipse.jface.text.IDocument;
40 import org.eclipse.jface.text.IRegion;
41 import org.eclipse.jface.text.Region;
42 import org.eclipse.jface.text.TextUtilities;
43
44 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
45
46 import org.eclipse.jdt.core.ITypeRoot;
47 import org.eclipse.jdt.core.JavaModelException;
48 import org.eclipse.jdt.core.dom.AST;
49 import org.eclipse.jdt.core.dom.ASTNode;
50 import org.eclipse.jdt.core.dom.ASTVisitor;
51 import org.eclipse.jdt.core.dom.Block;
52 import org.eclipse.jdt.core.dom.CastExpression;
53 import org.eclipse.jdt.core.dom.ChildPropertyDescriptor;
54 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
55 import org.eclipse.jdt.core.dom.CompilationUnit;
56 import org.eclipse.jdt.core.dom.ConditionalExpression;
57 import org.eclipse.jdt.core.dom.DoStatement;
58 import org.eclipse.jdt.core.dom.EnhancedForStatement;
59 import org.eclipse.jdt.core.dom.Expression;
60 import org.eclipse.jdt.core.dom.FieldAccess;
61 import org.eclipse.jdt.core.dom.ForStatement;
62 import org.eclipse.jdt.core.dom.IBinding;
63 import org.eclipse.jdt.core.dom.IMethodBinding;
64 import org.eclipse.jdt.core.dom.ITypeBinding;
65 import org.eclipse.jdt.core.dom.IVariableBinding;
66 import org.eclipse.jdt.core.dom.IfStatement;
67 import org.eclipse.jdt.core.dom.LabeledStatement;
68 import org.eclipse.jdt.core.dom.MethodDeclaration;
69 import org.eclipse.jdt.core.dom.MethodInvocation;
70 import org.eclipse.jdt.core.dom.Modifier;
71 import org.eclipse.jdt.core.dom.Name;
72 import org.eclipse.jdt.core.dom.ParenthesizedExpression;
73 import org.eclipse.jdt.core.dom.ReturnStatement;
74 import org.eclipse.jdt.core.dom.SimpleName;
75 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
76 import org.eclipse.jdt.core.dom.Statement;
77 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
78 import org.eclipse.jdt.core.dom.ThisExpression;
79 import org.eclipse.jdt.core.dom.WhileStatement;
80 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
81 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
82 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
83
84 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
85 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
86 import org.eclipse.jdt.internal.corext.dom.Bindings;
87 import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
88 import org.eclipse.jdt.internal.corext.dom.NecessaryParenthesesChecker;
89 import org.eclipse.jdt.internal.corext.refactoring.code.SourceAnalyzer.NameData;
90 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers;
91 import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
92 import org.eclipse.jdt.internal.corext.util.Strings;
93
94 import org.eclipse.jdt.internal.ui.JavaPlugin;
95
96 /**
97  * A SourceProvider encapsulates a piece of code (source) and the logic
98  * to inline it into given CallContexts.
99  */
100 public class SourceProvider {
101
102         private ITypeRoot fTypeRoot;
103         private IDocument fDocument;
104         private MethodDeclaration fDeclaration;
105         private SourceAnalyzer fAnalyzer;
106         private boolean fMustEvalReturnedExpression;
107         private boolean fReturnValueNeedsLocalVariable;
108         private List<Expression> fReturnExpressions;
109         private IDocument fSource;
110
111         private static final int EXPRESSION_MODE= 1;
112         private static final int STATEMENT_MODE= 2;
113         private static final int RETURN_STATEMENT_MODE= 3;
114         private int fMarkerMode;
115
116
117         private class ReturnAnalyzer extends ASTVisitor {
118                 @Override
119                 public boolean visit(ReturnStatement node) {
120                         Expression expression= node.getExpression();
121                         if (!(ASTNodes.isLiteral(expression) || expression instanceof Name)) {
122                                 fMustEvalReturnedExpression= true;
123                         }
124                         if (Invocations.isInvocation(expression) || expression instanceof ClassInstanceCreation) {
125                                 fReturnValueNeedsLocalVariable= false;
126                         }
127                         fReturnExpressions.add(expression);
128                         return false;
129                 }
130         }
131
132         public SourceProvider(ITypeRoot typeRoot, MethodDeclaration declaration) {
133                 super();
134                 fTypeRoot= typeRoot;
135                 fDeclaration= declaration;
136                 List<SingleVariableDeclaration> parameters= fDeclaration.parameters();
137                 for (Iterator<SingleVariableDeclaration> iter= parameters.iterator(); iter.hasNext();) {
138                         SingleVariableDeclaration element= iter.next();
139                         ParameterData data= new ParameterData(element);
140                         element.setProperty(ParameterData.PROPERTY, data);
141                 }
142                 fAnalyzer= new SourceAnalyzer(fTypeRoot, fDeclaration);
143                 fReturnValueNeedsLocalVariable= true;
144                 fReturnExpressions= new ArrayList<Expression>();
145         }
146
147         /**
148          * TODO: unit's source does not match contents of source document and declaration node.
149          * @param typeRoot the type root
150          * @param source document containing the content of the type root
151          * @param declaration method declaration node
152          */
153         public SourceProvider(ITypeRoot typeRoot, IDocument source, MethodDeclaration declaration) {
154                 this(typeRoot, declaration);
155                 fSource= source;
156         }
157
158         public RefactoringStatus checkActivation() throws JavaModelException {
159                 return fAnalyzer.checkActivation();
160         }
161
162         public void initialize() throws JavaModelException {
163                 fDocument= fSource == null ? new Document(fTypeRoot.getBuffer().getContents()) : fSource;
164                 fAnalyzer.initialize();
165                 if (hasReturnValue()) {
166                         ASTNode last= getLastStatement();
167                         if (last != null) {
168                                 ReturnAnalyzer analyzer= new ReturnAnalyzer();
169                                 last.accept(analyzer);
170                         }
171                 }
172         }
173
174         public boolean isExecutionFlowInterrupted() {
175                 return fAnalyzer.isExecutionFlowInterrupted();
176         }
177
178         static class VariableReferenceFinder extends ASTVisitor {
179                 private boolean fResult;
180                 private IVariableBinding fBinding;
181                 public VariableReferenceFinder(IVariableBinding binding) {
182                         fBinding= binding;
183                 }
184                 public boolean getResult() {
185                         return fResult;
186                 }
187                 @Override
188                 public boolean visit(SimpleName node) {
189                         if(!fResult) {
190                                 fResult= Bindings.equals(fBinding, node.resolveBinding());
191                         }
192                         return false;
193                 }
194         }
195
196         /**
197          * Checks whether variable is referenced by the method declaration or not.
198          *
199          * @param binding binding of variable to check.
200          * @return <code>true</code> if variable is referenced by the method, otherwise <code>false</code>
201          */
202         public boolean isVariableReferenced(IVariableBinding binding) {
203                 VariableReferenceFinder finder= new VariableReferenceFinder(binding);
204                 fDeclaration.accept(finder);
205                 return finder.getResult();
206         }
207
208         public boolean hasReturnValue() {
209                 IMethodBinding binding= fDeclaration.resolveBinding();
210                 return binding.getReturnType() != fDeclaration.getAST().resolveWellKnownType("void"); //$NON-NLS-1$
211         }
212
213         // returns true if the declaration has a vararg and
214         // the body contains an array access to the vararg
215         public boolean hasArrayAccess() {
216                 return fAnalyzer.hasArrayAccess();
217         }
218
219         public boolean hasSuperMethodInvocation() {
220                 return fAnalyzer.hasSuperMethodInvocation();
221         }
222
223         public boolean mustEvaluateReturnedExpression() {
224                 return fMustEvalReturnedExpression;
225         }
226
227         public boolean returnValueNeedsLocalVariable() {
228                 return fReturnValueNeedsLocalVariable;
229         }
230
231         public int getNumberOfStatements() {
232                 return fDeclaration.getBody().statements().size();
233         }
234
235         public boolean isSimpleFunction() {
236                 List<Statement> statements= fDeclaration.getBody().statements();
237                 if (statements.size() != 1)
238                         return false;
239                 return statements.get(0) instanceof ReturnStatement;
240         }
241
242         public boolean isLastStatementReturn() {
243                 List<Statement> statements= fDeclaration.getBody().statements();
244                 if (statements.size() == 0)
245                         return false;
246                 return statements.get(statements.size() - 1) instanceof ReturnStatement;
247         }
248
249         public boolean isDangligIf() {
250                 List<Statement> statements= fDeclaration.getBody().statements();
251                 if (statements.size() != 1)
252                         return false;
253
254                 ASTNode p= statements.get(0);
255
256                 while (true) {
257                         if (p instanceof IfStatement) {
258                                 return ((IfStatement) p).getElseStatement() == null;
259                         } else {
260
261                                 ChildPropertyDescriptor childD;
262                                 if (p instanceof WhileStatement) {
263                                         childD= WhileStatement.BODY_PROPERTY;
264                                 } else if (p instanceof ForStatement) {
265                                         childD= ForStatement.BODY_PROPERTY;
266                                 } else if (p instanceof EnhancedForStatement) {
267                                         childD= EnhancedForStatement.BODY_PROPERTY;
268                                 } else if (p instanceof DoStatement) {
269                                         childD= DoStatement.BODY_PROPERTY;
270                                 } else if (p instanceof LabeledStatement) {
271                                         childD= LabeledStatement.BODY_PROPERTY;
272                                 } else {
273                                         return false;
274                                 }
275                                 Statement body= (Statement) p.getStructuralProperty(childD);
276                                 if (body instanceof Block) {
277                                         return false;
278                                 } else {
279                                         p= body;
280                                 }
281                         }
282                 }
283         }
284
285         public MethodDeclaration getDeclaration() {
286                 return fDeclaration;
287         }
288
289         public String getMethodName() {
290                 return fDeclaration.getName().getIdentifier();
291         }
292
293         public ITypeBinding getReturnType() {
294                 return fDeclaration.resolveBinding().getReturnType();
295         }
296
297         public List<Expression> getReturnExpressions() {
298                 return fReturnExpressions;
299         }
300
301         public boolean returnTypeMatchesReturnExpressions() {
302                 ITypeBinding returnType= getReturnType();
303                 for (Iterator<Expression> iter= fReturnExpressions.iterator(); iter.hasNext();) {
304                         Expression expression= iter.next();
305                         if (!Bindings.equals(returnType, expression.resolveTypeBinding()))
306                                 return false;
307                 }
308                 return true;
309         }
310
311         public ParameterData getParameterData(int index) {
312                 SingleVariableDeclaration decl= (SingleVariableDeclaration)fDeclaration.parameters().get(index);
313                 return (ParameterData)decl.getProperty(ParameterData.PROPERTY);
314         }
315
316         public ITypeRoot getTypeRoot() {
317                 return fTypeRoot;
318         }
319
320         public boolean needsReturnedExpressionParenthesis(ASTNode parent, StructuralPropertyDescriptor locationInParent) {
321                 ASTNode last= getLastStatement();
322                 if (last instanceof ReturnStatement) {
323                         return NecessaryParenthesesChecker.needsParentheses(((ReturnStatement)last).getExpression(), parent, locationInParent);
324                 }
325                 return false;
326         }
327
328         public boolean returnsConditionalExpression() {
329                 ASTNode last= getLastStatement();
330                 if (last instanceof ReturnStatement) {
331                         return ((ReturnStatement)last).getExpression() instanceof ConditionalExpression;
332                 }
333                 return false;
334         }
335
336         public int getReceiversToBeUpdated() {
337                 return fAnalyzer.getImplicitReceivers().size();
338         }
339
340         public boolean isVarargs() {
341                 return fDeclaration.isVarargs();
342         }
343
344         public int getVarargIndex() {
345                 return fDeclaration.parameters().size() - 1;
346         }
347
348         public TextEdit getDeleteEdit() {
349                 final ASTRewrite rewriter= ASTRewrite.create(fDeclaration.getAST());
350                 rewriter.remove(fDeclaration, null);
351                 return rewriter.rewriteAST(fDocument, fTypeRoot.getJavaProject().getOptions(true));
352         }
353
354         public String[] getCodeBlocks(CallContext context, ImportRewrite importRewrite) throws CoreException {
355                 final ASTRewrite rewriter= ASTRewrite.create(fDeclaration.getAST());
356                 replaceParameterWithExpression(rewriter, context, importRewrite);
357                 updateImplicitReceivers(rewriter, context);
358                 makeNamesUnique(rewriter, context.scope);
359                 updateTypeReferences(rewriter, context);
360                 updateStaticReferences(rewriter, context);
361                 updateTypeVariables(rewriter, context);
362                 updateMethodTypeVariable(rewriter, context);
363
364                 List<IRegion> ranges= null;
365                 if (hasReturnValue()) {
366                         if (context.callMode == ASTNode.RETURN_STATEMENT) {
367                                 ranges= getStatementRanges();
368                         } else {
369                                 ranges= getExpressionRanges();
370                         }
371                 } else {
372                         ASTNode last= getLastStatement();
373                         if (last != null && last.getNodeType() == ASTNode.RETURN_STATEMENT) {
374                                 ranges= getReturnStatementRanges();
375                         } else {
376                                 ranges= getStatementRanges();
377                         }
378                 }
379
380                 final TextEdit dummy= rewriter.rewriteAST(fDocument, fTypeRoot.getJavaProject().getOptions(true));
381                 int size= ranges.size();
382                 RangeMarker[] markers= new RangeMarker[size];
383                 for (int i= 0; i < markers.length; i++) {
384                         IRegion range= ranges.get(i);
385                         markers[i]= new RangeMarker(range.getOffset(), range.getLength());
386                 }
387                 int split;
388                 if (size <= 1) {
389                         split= Integer.MAX_VALUE;
390                 } else {
391                         IRegion region= ranges.get(0);
392                         split= region.getOffset() + region.getLength();
393                 }
394                 TextEdit[] edits= dummy.removeChildren();
395                 for (int i= 0; i < edits.length; i++) {
396                         TextEdit edit= edits[i];
397                         int pos= edit.getOffset() >= split ? 1 : 0;
398                         markers[pos].addChild(edit);
399                 }
400                 MultiTextEdit root= new MultiTextEdit(0, fDocument.getLength());
401                 root.addChildren(markers);
402
403                 try {
404                         TextEditProcessor processor= new TextEditProcessor(fDocument, root, TextEdit.CREATE_UNDO | TextEdit.UPDATE_REGIONS);
405                         UndoEdit undo= processor.performEdits();
406                         String[] result= getBlocks(markers);
407                         // It is faster to undo the changes than coping the buffer over and over again.
408                         processor= new TextEditProcessor(fDocument, undo, TextEdit.UPDATE_REGIONS);
409                         processor.performEdits();
410                         return result;
411                 } catch (MalformedTreeException exception) {
412                         JavaPlugin.log(exception);
413                 } catch (BadLocationException exception) {
414                         JavaPlugin.log(exception);
415                 }
416                 return new String[] {};
417         }
418
419         private Expression createParenthesizedExpression(Expression newExpression, AST ast) {
420                 ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
421                 parenthesized.setExpression(newExpression);
422                 return parenthesized;
423         }
424
425         private void replaceParameterWithExpression(ASTRewrite rewriter, CallContext context, ImportRewrite importRewrite) throws CoreException {
426                 Expression[] arguments= context.arguments;
427                 try {
428                         ITextFileBuffer buffer= RefactoringFileBuffers.acquire(context.compilationUnit);
429                         for (int i= 0; i < arguments.length; i++) {
430                                 Expression expression= arguments[i];
431                                 String expressionString= null;
432                                 if (expression instanceof SimpleName) {
433                                         expressionString= ((SimpleName)expression).getIdentifier();
434                                 } else {
435                                         try {
436                                                 expressionString= buffer.getDocument().get(expression.getStartPosition(), expression.getLength());
437                                         } catch (BadLocationException exception) {
438                                                 JavaPlugin.log(exception);
439                                                 continue;
440                                         }
441                                 }
442                                 ParameterData parameter= getParameterData(i);
443                                 List<SimpleName> references= parameter.references();
444                                 for (Iterator<SimpleName> iter= references.iterator(); iter.hasNext();) {
445                                         ASTNode element= iter.next();
446                                         Expression newExpression= (Expression)rewriter.createStringPlaceholder(expressionString, expression.getNodeType());
447                                         AST ast= rewriter.getAST();
448                                         ITypeBinding explicitCast= ASTNodes.getExplicitCast(expression, (Expression)element);
449                                         if (explicitCast != null) {
450                                                 CastExpression cast= ast.newCastExpression();
451                                                 if (NecessaryParenthesesChecker.needsParentheses(expression, cast, CastExpression.EXPRESSION_PROPERTY)) {
452                                                         newExpression= createParenthesizedExpression(newExpression, ast);
453                                                 }
454                                                 cast.setExpression(newExpression);
455                                                 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(expression, importRewrite);
456                                                 cast.setType(importRewrite.addImport(explicitCast, ast, importRewriteContext));
457                                                 expression= newExpression= cast;
458                                         }
459                                         if (NecessaryParenthesesChecker.needsParentheses(expression, element.getParent(), element.getLocationInParent())) {
460                                                 newExpression= createParenthesizedExpression(newExpression, ast);
461                                         }
462                                         rewriter.replace(element, newExpression, null);
463                                 }
464                         }
465                 } finally {
466                         RefactoringFileBuffers.release(context.compilationUnit);
467                 }
468         }
469
470         private void makeNamesUnique(ASTRewrite rewriter, CodeScopeBuilder.Scope scope) {
471                 Collection<NameData> usedCalleeNames= fAnalyzer.getUsedNames();
472                 for (Iterator<NameData> iter= usedCalleeNames.iterator(); iter.hasNext();) {
473                         SourceAnalyzer.NameData nd= iter.next();
474                         if (scope.isInUse(nd.getName())) {
475                                 String newName= scope.createName(nd.getName(), true);
476                                 List<SimpleName> references= nd.references();
477                                 for (Iterator<SimpleName> refs= references.iterator(); refs.hasNext();) {
478                                         SimpleName element= refs.next();
479                                         ASTNode newNode= rewriter.createStringPlaceholder(newName, ASTNode.METHOD_INVOCATION);
480                                         rewriter.replace(element, newNode, null);
481                                 }
482                         }
483                 }
484         }
485
486         private void updateImplicitReceivers(ASTRewrite rewriter, CallContext context) {
487                 if (context.receiver == null)
488                         return;
489                 List<Expression> implicitReceivers= fAnalyzer.getImplicitReceivers();
490                 for (Iterator<Expression> iter= implicitReceivers.iterator(); iter.hasNext();) {
491                         ASTNode node= iter.next();
492                         ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(node, context.importer);
493                         if (node instanceof MethodInvocation) {
494                                 final MethodInvocation inv= (MethodInvocation)node;
495                                 rewriter.set(inv, MethodInvocation.EXPRESSION_PROPERTY, createReceiver(rewriter, context, (IMethodBinding)inv.getName().resolveBinding(), importRewriteContext), null);
496                         } else if (node instanceof ClassInstanceCreation) {
497                                 final ClassInstanceCreation inst= (ClassInstanceCreation)node;
498                                 rewriter.set(inst, ClassInstanceCreation.EXPRESSION_PROPERTY, createReceiver(rewriter, context, inst.resolveConstructorBinding(), importRewriteContext), null);
499                         } else if (node instanceof ThisExpression) {
500                                 rewriter.replace(node, rewriter.createStringPlaceholder(context.receiver, ASTNode.METHOD_INVOCATION), null);
501                         } else if (node instanceof FieldAccess) {
502                                 final FieldAccess access= (FieldAccess)node;
503                                 rewriter.set(access, FieldAccess.EXPRESSION_PROPERTY, createReceiver(rewriter, context, access.resolveFieldBinding(), importRewriteContext), null);
504                         } else if (node instanceof SimpleName && ((SimpleName)node).resolveBinding() instanceof IVariableBinding) {
505                                 IVariableBinding vb= (IVariableBinding)((SimpleName)node).resolveBinding();
506                                 if (vb.isField()) {
507                                         Expression receiver= createReceiver(rewriter, context, vb, importRewriteContext);
508                                         if (receiver != null) {
509                                                 FieldAccess access= node.getAST().newFieldAccess();
510                                                 ASTNode target= rewriter.createMoveTarget(node);
511                                                 access.setName((SimpleName)target);
512                                                 access.setExpression(receiver);
513                                                 rewriter.replace(node, access, null);
514                                         }
515                                 }
516                         }
517                 }
518         }
519
520         private void updateTypeReferences(ASTRewrite rewriter, CallContext context) {
521                 ImportRewrite importer= context.importer;
522                 for (Iterator<SimpleName> iter= fAnalyzer.getTypesToImport().iterator(); iter.hasNext();) {
523                         Name element= iter.next();
524                         ITypeBinding binding= ASTNodes.getTypeBinding(element);
525                         if (binding != null && !binding.isLocal()) {
526                                 // We have collected names not types. So we have to import
527                                 // the declaration type if we reference a parameterized type
528                                 // since we have an entry for every name node (e.g. one for
529                                 // Vector and one for Integer in Vector<Integer>.
530                                 if (binding.isParameterizedType()) {
531                                         binding= binding.getTypeDeclaration();
532                                 }
533                                 String s= importer.addImport(binding);
534                                 if (!ASTNodes.asString(element).equals(s)) {
535                                         rewriter.replace(element, rewriter.createStringPlaceholder(s, ASTNode.SIMPLE_NAME), null);
536                                 }
537                         }
538                 }
539         }
540
541         private void updateStaticReferences(ASTRewrite rewriter, CallContext context) {
542                 ImportRewrite importer= context.importer;
543                 for (Iterator<SimpleName> iter= fAnalyzer.getStaticsToImport().iterator(); iter.hasNext();) {
544                         Name element= iter.next();
545                         IBinding binding= element.resolveBinding();
546                         if (binding != null) {
547                                 String s= importer.addStaticImport(binding);
548                                 if (!ASTNodes.asString(element).equals(s)) {
549                                         rewriter.replace(element, rewriter.createStringPlaceholder(s, ASTNode.SIMPLE_NAME), null);
550                                 }
551                         }
552                 }
553
554         }
555
556         private Expression createReceiver(ASTRewrite rewriter, CallContext context, IMethodBinding method, ImportRewriteContext importRewriteContext) {
557                 String receiver= getReceiver(context, method.getModifiers(), importRewriteContext);
558                 if (receiver == null)
559                         return null;
560                 return (Expression)rewriter.createStringPlaceholder(receiver, ASTNode.METHOD_INVOCATION);
561         }
562
563         private Expression createReceiver(ASTRewrite rewriter, CallContext context, IVariableBinding field, ImportRewriteContext importRewriteContext) {
564                 String receiver= getReceiver(context, field.getModifiers(), importRewriteContext);
565                 if (receiver == null)
566                         return null;
567                 return (Expression)rewriter.createStringPlaceholder(receiver, ASTNode.SIMPLE_NAME);
568         }
569
570         private String getReceiver(CallContext context, int modifiers, ImportRewriteContext importRewriteContext) {
571                 String receiver= context.receiver;
572                 ITypeBinding invocationType= ASTNodes.getEnclosingType(context.invocation);
573                 ITypeBinding sourceType= fDeclaration.resolveBinding().getDeclaringClass();
574                 if (!context.receiverIsStatic && Modifier.isStatic(modifiers)) {
575                         if ("this".equals(receiver) && invocationType != null && Bindings.equals(invocationType, sourceType)) { //$NON-NLS-1$
576                                 receiver= null;
577                         } else {
578                                 receiver= context.importer.addImport(sourceType, importRewriteContext);
579                         }
580                 }
581                 return receiver;
582         }
583
584         private void updateTypeVariables(ASTRewrite rewriter, CallContext context) {
585                 ITypeBinding type= context.getReceiverType();
586                 if (type == null)
587                         return;
588                 rewriteReferences(rewriter, type.getTypeArguments(), fAnalyzer.getTypeParameterReferences());
589         }
590
591         private void updateMethodTypeVariable(ASTRewrite rewriter, CallContext context) {
592                 IMethodBinding method= Invocations.resolveBinding(context.invocation);
593                 if (method == null)
594                         return;
595                 rewriteReferences(rewriter, method.getTypeArguments(), fAnalyzer.getMethodTypeParameterReferences());
596         }
597
598         private void rewriteReferences(ASTRewrite rewriter, ITypeBinding[] typeArguments, List<NameData> typeParameterReferences) {
599                 if (typeArguments.length == 0)
600                         return;
601                 Assert.isTrue(typeArguments.length == typeParameterReferences.size());
602                 for (int i= 0; i < typeArguments.length; i++) {
603                         SourceAnalyzer.NameData refData= typeParameterReferences.get(i);
604                         List<SimpleName> references= refData.references();
605                         String newName= typeArguments[i].getName();
606                         for (Iterator<SimpleName> iter= references.iterator(); iter.hasNext();) {
607                                 SimpleName name= iter.next();
608                                 rewriter.replace(name, rewriter.createStringPlaceholder(newName, ASTNode.SIMPLE_NAME), null);
609                         }
610                 }
611         }
612
613         private ASTNode getLastStatement() {
614                 List<Statement> statements= fDeclaration.getBody().statements();
615                 if (statements.isEmpty())
616                         return null;
617                 return statements.get(statements.size() - 1);
618         }
619
620         private List<IRegion> getReturnStatementRanges() {
621                 fMarkerMode= RETURN_STATEMENT_MODE;
622                 List<IRegion> result= new ArrayList<IRegion>(1);
623                 List<Statement> statements= fDeclaration.getBody().statements();
624                 int size= statements.size();
625                 if (size <= 1)
626                         return result;
627                 result.add(createRange(statements, size - 2));
628                 return result;
629         }
630
631         private List<IRegion> getStatementRanges() {
632                 fMarkerMode= STATEMENT_MODE;
633                 List<IRegion> result= new ArrayList<IRegion>(1);
634                 List<Statement> statements= fDeclaration.getBody().statements();
635                 int size= statements.size();
636                 if (size == 0)
637                         return result;
638                 result.add(createRange(statements, size - 1));
639                 return result;
640         }
641
642         private List<IRegion> getExpressionRanges() {
643                 fMarkerMode= EXPRESSION_MODE;
644                 List<IRegion> result= new ArrayList<IRegion>(2);
645                 List<Statement> statements= fDeclaration.getBody().statements();
646                 ReturnStatement rs= null;
647                 int size= statements.size();
648                 ASTNode node;
649                 switch (size) {
650                         case 0:
651                                 return result;
652                         case 1:
653                                 node= statements.get(0);
654                                 if (node.getNodeType() == ASTNode.RETURN_STATEMENT) {
655                                         rs= (ReturnStatement)node;
656                                 } else {
657                                         result.add(createRange(node, node));
658                                 }
659                                 break;
660                         default: {
661                                 node= statements.get(size - 1);
662                                 if (node.getNodeType() == ASTNode.RETURN_STATEMENT) {
663                                         result.add(createRange(statements, size - 2));
664                                         rs= (ReturnStatement)node;
665                                 } else {
666                                         result.add(createRange(statements, size - 1));
667                                 }
668                                 break;
669                         }
670                 }
671                 if (rs != null) {
672                         Expression exp= rs.getExpression();
673                         result.add(createRange(exp, exp));
674                 }
675                 return result;
676         }
677
678         private IRegion createRange(List<Statement> statements, int end) {
679                 ASTNode first= statements.get(0);
680                 ASTNode last= statements.get(end);
681                 return createRange(first, last);
682         }
683
684         private IRegion createRange(ASTNode first, ASTNode last) {
685                 ASTNode root= first.getRoot();
686                 if (root instanceof CompilationUnit) {
687                         CompilationUnit unit= (CompilationUnit)root;
688                         int start= unit.getExtendedStartPosition(first);
689                         int length = unit.getExtendedStartPosition(last) - start + unit.getExtendedLength(last);
690                         IRegion range= new Region(start, length);
691                         return range;
692                 } else {
693                         int start= first.getStartPosition();
694                         int length = last.getStartPosition() - start + last.getLength();
695                         IRegion range= new Region(start, length);
696                         return range;
697                 }
698         }
699
700         private String[] getBlocks(RangeMarker[] markers) throws BadLocationException {
701                 String[] result= new String[markers.length];
702                 for (int i= 0; i < markers.length; i++) {
703                         RangeMarker marker= markers[i];
704                         String content= fDocument.get(marker.getOffset(), marker.getLength());
705                         String lines[]= Strings.convertIntoLines(content);
706                         Strings.trimIndentation(lines, fTypeRoot.getJavaProject(), false);
707                         if (fMarkerMode == STATEMENT_MODE && lines.length == 2 && isSingleControlStatementWithoutBlock()) {
708                                 lines[1]= CodeFormatterUtil.createIndentString(1, fTypeRoot.getJavaProject()) + lines[1];
709                         }
710                         result[i]= Strings.concatenate(lines, TextUtilities.getDefaultLineDelimiter(fDocument));
711                 }
712                 return result;
713         }
714
715         private boolean isSingleControlStatementWithoutBlock() {
716                 List<Statement> statements= fDeclaration.getBody().statements();
717                 int size= statements.size();
718                 if (size != 1)
719                         return false;
720                 Statement statement= statements.get(size - 1);
721                 int nodeType= statement.getNodeType();
722                 if (nodeType == ASTNode.IF_STATEMENT) {
723                         IfStatement ifStatement= (IfStatement) statement;
724                         return !(ifStatement.getThenStatement() instanceof Block)
725                                 && !(ifStatement.getElseStatement() instanceof Block);
726                 } else if (nodeType == ASTNode.FOR_STATEMENT) {
727                         return !(((ForStatement)statement).getBody() instanceof Block);
728                 } else if (nodeType == ASTNode.WHILE_STATEMENT) {
729                         return !(((WhileStatement)statement).getBody() instanceof Block);
730                 }
731                 return false;
732         }
733 }