1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * 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;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
25 import org.eclipse.core.runtime.Assert;
26 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.filebuffers.ITextFileBuffer;
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;
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;
44 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
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;
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;
94 import org.eclipse.jdt.internal.ui.JavaPlugin;
97 * A SourceProvider encapsulates a piece of code (source) and the logic
98 * to inline it into given CallContexts.
100 public class SourceProvider {
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;
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;
117 private class ReturnAnalyzer extends ASTVisitor {
119 public boolean visit(ReturnStatement node) {
120 Expression expression= node.getExpression();
121 if (!(ASTNodes.isLiteral(expression) || expression instanceof Name)) {
122 fMustEvalReturnedExpression= true;
124 if (Invocations.isInvocation(expression) || expression instanceof ClassInstanceCreation) {
125 fReturnValueNeedsLocalVariable= false;
127 fReturnExpressions.add(expression);
132 public SourceProvider(ITypeRoot typeRoot, MethodDeclaration declaration) {
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);
142 fAnalyzer= new SourceAnalyzer(fTypeRoot, fDeclaration);
143 fReturnValueNeedsLocalVariable= true;
144 fReturnExpressions= new ArrayList<Expression>();
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
153 public SourceProvider(ITypeRoot typeRoot, IDocument source, MethodDeclaration declaration) {
154 this(typeRoot, declaration);
158 public RefactoringStatus checkActivation() throws JavaModelException {
159 return fAnalyzer.checkActivation();
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();
168 ReturnAnalyzer analyzer= new ReturnAnalyzer();
169 last.accept(analyzer);
174 public boolean isExecutionFlowInterrupted() {
175 return fAnalyzer.isExecutionFlowInterrupted();
178 static class VariableReferenceFinder extends ASTVisitor {
179 private boolean fResult;
180 private IVariableBinding fBinding;
181 public VariableReferenceFinder(IVariableBinding binding) {
184 public boolean getResult() {
188 public boolean visit(SimpleName node) {
190 fResult= Bindings.equals(fBinding, node.resolveBinding());
197 * Checks whether variable is referenced by the method declaration or not.
199 * @param binding binding of variable to check.
200 * @return <code>true</code> if variable is referenced by the method, otherwise <code>false</code>
202 public boolean isVariableReferenced(IVariableBinding binding) {
203 VariableReferenceFinder finder= new VariableReferenceFinder(binding);
204 fDeclaration.accept(finder);
205 return finder.getResult();
208 public boolean hasReturnValue() {
209 IMethodBinding binding= fDeclaration.resolveBinding();
210 return binding.getReturnType() != fDeclaration.getAST().resolveWellKnownType("void"); //$NON-NLS-1$
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();
219 public boolean hasSuperMethodInvocation() {
220 return fAnalyzer.hasSuperMethodInvocation();
223 public boolean mustEvaluateReturnedExpression() {
224 return fMustEvalReturnedExpression;
227 public boolean returnValueNeedsLocalVariable() {
228 return fReturnValueNeedsLocalVariable;
231 public int getNumberOfStatements() {
232 return fDeclaration.getBody().statements().size();
235 public boolean isSimpleFunction() {
236 List<Statement> statements= fDeclaration.getBody().statements();
237 if (statements.size() != 1)
239 return statements.get(0) instanceof ReturnStatement;
242 public boolean isLastStatementReturn() {
243 List<Statement> statements= fDeclaration.getBody().statements();
244 if (statements.size() == 0)
246 return statements.get(statements.size() - 1) instanceof ReturnStatement;
249 public boolean isDangligIf() {
250 List<Statement> statements= fDeclaration.getBody().statements();
251 if (statements.size() != 1)
254 ASTNode p= statements.get(0);
257 if (p instanceof IfStatement) {
258 return ((IfStatement) p).getElseStatement() == null;
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;
275 Statement body= (Statement) p.getStructuralProperty(childD);
276 if (body instanceof Block) {
285 public MethodDeclaration getDeclaration() {
289 public String getMethodName() {
290 return fDeclaration.getName().getIdentifier();
293 public ITypeBinding getReturnType() {
294 return fDeclaration.resolveBinding().getReturnType();
297 public List<Expression> getReturnExpressions() {
298 return fReturnExpressions;
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()))
311 public ParameterData getParameterData(int index) {
312 SingleVariableDeclaration decl= (SingleVariableDeclaration)fDeclaration.parameters().get(index);
313 return (ParameterData)decl.getProperty(ParameterData.PROPERTY);
316 public ITypeRoot getTypeRoot() {
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);
328 public boolean returnsConditionalExpression() {
329 ASTNode last= getLastStatement();
330 if (last instanceof ReturnStatement) {
331 return ((ReturnStatement)last).getExpression() instanceof ConditionalExpression;
336 public int getReceiversToBeUpdated() {
337 return fAnalyzer.getImplicitReceivers().size();
340 public boolean isVarargs() {
341 return fDeclaration.isVarargs();
344 public int getVarargIndex() {
345 return fDeclaration.parameters().size() - 1;
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));
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);
364 List<IRegion> ranges= null;
365 if (hasReturnValue()) {
366 if (context.callMode == ASTNode.RETURN_STATEMENT) {
367 ranges= getStatementRanges();
369 ranges= getExpressionRanges();
372 ASTNode last= getLastStatement();
373 if (last != null && last.getNodeType() == ASTNode.RETURN_STATEMENT) {
374 ranges= getReturnStatementRanges();
376 ranges= getStatementRanges();
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());
389 split= Integer.MAX_VALUE;
391 IRegion region= ranges.get(0);
392 split= region.getOffset() + region.getLength();
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);
400 MultiTextEdit root= new MultiTextEdit(0, fDocument.getLength());
401 root.addChildren(markers);
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();
411 } catch (MalformedTreeException exception) {
412 JavaPlugin.log(exception);
413 } catch (BadLocationException exception) {
414 JavaPlugin.log(exception);
416 return new String[] {};
419 private Expression createParenthesizedExpression(Expression newExpression, AST ast) {
420 ParenthesizedExpression parenthesized= ast.newParenthesizedExpression();
421 parenthesized.setExpression(newExpression);
422 return parenthesized;
425 private void replaceParameterWithExpression(ASTRewrite rewriter, CallContext context, ImportRewrite importRewrite) throws CoreException {
426 Expression[] arguments= context.arguments;
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();
436 expressionString= buffer.getDocument().get(expression.getStartPosition(), expression.getLength());
437 } catch (BadLocationException exception) {
438 JavaPlugin.log(exception);
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);
454 cast.setExpression(newExpression);
455 ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(expression, importRewrite);
456 cast.setType(importRewrite.addImport(explicitCast, ast, importRewriteContext));
457 expression= newExpression= cast;
459 if (NecessaryParenthesesChecker.needsParentheses(expression, element.getParent(), element.getLocationInParent())) {
460 newExpression= createParenthesizedExpression(newExpression, ast);
462 rewriter.replace(element, newExpression, null);
466 RefactoringFileBuffers.release(context.compilationUnit);
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);
486 private void updateImplicitReceivers(ASTRewrite rewriter, CallContext context) {
487 if (context.receiver == null)
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();
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);
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();
533 String s= importer.addImport(binding);
534 if (!ASTNodes.asString(element).equals(s)) {
535 rewriter.replace(element, rewriter.createStringPlaceholder(s, ASTNode.SIMPLE_NAME), null);
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);
556 private Expression createReceiver(ASTRewrite rewriter, CallContext context, IMethodBinding method, ImportRewriteContext importRewriteContext) {
557 String receiver= getReceiver(context, method.getModifiers(), importRewriteContext);
558 if (receiver == null)
560 return (Expression)rewriter.createStringPlaceholder(receiver, ASTNode.METHOD_INVOCATION);
563 private Expression createReceiver(ASTRewrite rewriter, CallContext context, IVariableBinding field, ImportRewriteContext importRewriteContext) {
564 String receiver= getReceiver(context, field.getModifiers(), importRewriteContext);
565 if (receiver == null)
567 return (Expression)rewriter.createStringPlaceholder(receiver, ASTNode.SIMPLE_NAME);
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$
578 receiver= context.importer.addImport(sourceType, importRewriteContext);
584 private void updateTypeVariables(ASTRewrite rewriter, CallContext context) {
585 ITypeBinding type= context.getReceiverType();
588 rewriteReferences(rewriter, type.getTypeArguments(), fAnalyzer.getTypeParameterReferences());
591 private void updateMethodTypeVariable(ASTRewrite rewriter, CallContext context) {
592 IMethodBinding method= Invocations.resolveBinding(context.invocation);
595 rewriteReferences(rewriter, method.getTypeArguments(), fAnalyzer.getMethodTypeParameterReferences());
598 private void rewriteReferences(ASTRewrite rewriter, ITypeBinding[] typeArguments, List<NameData> typeParameterReferences) {
599 if (typeArguments.length == 0)
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);
613 private ASTNode getLastStatement() {
614 List<Statement> statements= fDeclaration.getBody().statements();
615 if (statements.isEmpty())
617 return statements.get(statements.size() - 1);
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();
627 result.add(createRange(statements, size - 2));
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();
638 result.add(createRange(statements, size - 1));
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();
653 node= statements.get(0);
654 if (node.getNodeType() == ASTNode.RETURN_STATEMENT) {
655 rs= (ReturnStatement)node;
657 result.add(createRange(node, node));
661 node= statements.get(size - 1);
662 if (node.getNodeType() == ASTNode.RETURN_STATEMENT) {
663 result.add(createRange(statements, size - 2));
664 rs= (ReturnStatement)node;
666 result.add(createRange(statements, size - 1));
672 Expression exp= rs.getExpression();
673 result.add(createRange(exp, exp));
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);
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);
693 int start= first.getStartPosition();
694 int length = last.getStartPosition() - start + last.getLength();
695 IRegion range= new Region(start, length);
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];
710 result[i]= Strings.concatenate(lines, TextUtilities.getDefaultLineDelimiter(fDocument));
715 private boolean isSingleControlStatementWithoutBlock() {
716 List<Statement> statements= fDeclaration.getBody().statements();
717 int size= statements.size();
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);