| /******************************************************************************* |
| * Copyright (c) 2000, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.internal.corext.fix; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.text.edits.TextEditGroup; |
| import org.eclipse.wst.jsdt.core.dom.ASTNode; |
| import org.eclipse.wst.jsdt.core.dom.Block; |
| import org.eclipse.wst.jsdt.core.dom.ChildPropertyDescriptor; |
| import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.dom.DoStatement; |
| import org.eclipse.wst.jsdt.core.dom.EnhancedForStatement; |
| import org.eclipse.wst.jsdt.core.dom.ForInStatement; |
| import org.eclipse.wst.jsdt.core.dom.ForStatement; |
| import org.eclipse.wst.jsdt.core.dom.IfStatement; |
| import org.eclipse.wst.jsdt.core.dom.ReturnStatement; |
| import org.eclipse.wst.jsdt.core.dom.Statement; |
| import org.eclipse.wst.jsdt.core.dom.ThrowStatement; |
| import org.eclipse.wst.jsdt.core.dom.WhileStatement; |
| import org.eclipse.wst.jsdt.core.dom.WithStatement; |
| import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite; |
| import org.eclipse.wst.jsdt.internal.ui.text.correction.ASTResolving; |
| |
| public class ControlStatementsFix extends AbstractFix { |
| |
| private final static class ControlStatementFinder extends GenericVisitor { |
| |
| private final List/*<IFixRewriteOperation>*/ fResult; |
| private final boolean fFindControlStatementsWithoutBlock; |
| private final boolean fRemoveUnnecessaryBlocks; |
| private final boolean fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow; |
| |
| public ControlStatementFinder(boolean findControlStatementsWithoutBlock, |
| boolean removeUnnecessaryBlocks, |
| boolean removeUnnecessaryBlocksOnlyWhenReturnOrThrow, |
| List resultingCollection) { |
| |
| fFindControlStatementsWithoutBlock= findControlStatementsWithoutBlock; |
| fRemoveUnnecessaryBlocks= removeUnnecessaryBlocks; |
| fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow= removeUnnecessaryBlocksOnlyWhenReturnOrThrow; |
| fResult= resultingCollection; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.wst.jsdt.core.dom.DoStatement) |
| */ |
| public boolean visit(DoStatement node) { |
| if (fFindControlStatementsWithoutBlock) { |
| Statement doBody= node.getBody(); |
| if (!(doBody instanceof Block)) { |
| fResult.add(new AddBlockOperation(DoStatement.BODY_PROPERTY, doBody, node)); |
| } |
| } else if (fRemoveUnnecessaryBlocks || fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(node, DoStatement.BODY_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) { |
| fResult.add(new RemoveBlockOperation(node, DoStatement.BODY_PROPERTY)); |
| } |
| } |
| return super.visit(node); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.wst.jsdt.core.dom.ForStatement) |
| */ |
| public boolean visit(ForStatement node) { |
| if (fFindControlStatementsWithoutBlock) { |
| Statement forBody= node.getBody(); |
| if (!(forBody instanceof Block)) { |
| fResult.add(new AddBlockOperation(ForStatement.BODY_PROPERTY, forBody, node)); |
| } |
| } else if (fRemoveUnnecessaryBlocks || fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(node, ForStatement.BODY_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) { |
| fResult.add(new RemoveBlockOperation(node, ForStatement.BODY_PROPERTY)); |
| } |
| } |
| return super.visit(node); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean visit(EnhancedForStatement node) { |
| if (fFindControlStatementsWithoutBlock) { |
| Statement forBody= node.getBody(); |
| if (!(forBody instanceof Block)) { |
| fResult.add(new AddBlockOperation(EnhancedForStatement.BODY_PROPERTY, forBody, node)); |
| } |
| } else if (fRemoveUnnecessaryBlocks || fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(node, EnhancedForStatement.BODY_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) { |
| fResult.add(new RemoveBlockOperation(node, EnhancedForStatement.BODY_PROPERTY)); |
| } |
| } |
| return super.visit(node); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.wst.jsdt.core.dom.IfStatement) |
| */ |
| public boolean visit(IfStatement statement) { |
| if (fFindControlStatementsWithoutBlock) { |
| Statement then= statement.getThenStatement(); |
| if (!(then instanceof Block)) { |
| fResult.add(new AddBlockOperation(IfStatement.THEN_STATEMENT_PROPERTY, then, statement)); |
| } |
| Statement elseStatement= statement.getElseStatement(); |
| if (elseStatement != null && !(elseStatement instanceof Block) && !(elseStatement instanceof IfStatement)) { |
| fResult.add(new AddBlockOperation(IfStatement.ELSE_STATEMENT_PROPERTY, elseStatement, statement)); |
| } |
| } else if (fRemoveUnnecessaryBlocks || fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(statement, IfStatement.THEN_STATEMENT_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) { |
| fResult.add(new RemoveBlockOperation(statement, IfStatement.THEN_STATEMENT_PROPERTY)); |
| } |
| if (!(statement.getElseStatement() instanceof IfStatement)) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(statement, IfStatement.ELSE_STATEMENT_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) { |
| fResult.add(new RemoveBlockOperation(statement, IfStatement.ELSE_STATEMENT_PROPERTY)); |
| } |
| } |
| } |
| return super.visit(statement); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor#visit(org.eclipse.wst.jsdt.core.dom.WhileStatement) |
| */ |
| public boolean visit(WhileStatement node) { |
| if (fFindControlStatementsWithoutBlock) { |
| Statement whileBody= node.getBody(); |
| if (!(whileBody instanceof Block)) { |
| fResult.add(new AddBlockOperation(WhileStatement.BODY_PROPERTY, whileBody, node)); |
| } |
| } else if (fRemoveUnnecessaryBlocks || fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(node, WhileStatement.BODY_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) |
| fResult.add(new RemoveBlockOperation(node, WhileStatement.BODY_PROPERTY)); |
| } |
| return super.visit(node); |
| } |
| |
| public boolean visit(WithStatement node) { |
| if (fFindControlStatementsWithoutBlock) { |
| Statement withBody= node.getBody(); |
| if (!(withBody instanceof Block)) { |
| fResult.add(new AddBlockOperation(WithStatement.BODY_PROPERTY, withBody, node)); |
| } |
| } else if (fRemoveUnnecessaryBlocks || fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow) { |
| if (RemoveBlockOperation.satisfiesCleanUpPrecondition(node, WithStatement.BODY_PROPERTY, fRemoveUnnecessaryBlocksOnlyWhenReturnOrThrow)) |
| fResult.add(new RemoveBlockOperation(node, WithStatement.BODY_PROPERTY)); |
| } |
| return super.visit(node); |
| } |
| |
| } |
| |
| private static class IfElseIterator { |
| |
| private IfStatement fCursor; |
| |
| public IfElseIterator(IfStatement item) { |
| fCursor= findStart(item); |
| } |
| |
| public IfStatement next() { |
| if (!hasNext()) |
| return null; |
| |
| IfStatement result= fCursor; |
| |
| if (fCursor.getElseStatement() instanceof IfStatement) { |
| fCursor= (IfStatement)fCursor.getElseStatement(); |
| } else { |
| fCursor= null; |
| } |
| |
| return result; |
| } |
| |
| public boolean hasNext() { |
| return fCursor != null; |
| } |
| |
| private IfStatement findStart(IfStatement item) { |
| while (item.getLocationInParent() == IfStatement.ELSE_STATEMENT_PROPERTY) { |
| item= (IfStatement)item.getParent(); |
| } |
| return item; |
| } |
| } |
| |
| private static final class AddBlockOperation extends AbstractFixRewriteOperation { |
| |
| private final ChildPropertyDescriptor fBodyProperty; |
| private final Statement fBody; |
| private final Statement fControlStatement; |
| |
| public AddBlockOperation(ChildPropertyDescriptor bodyProperty, Statement body, Statement controlStatement) { |
| fBodyProperty= bodyProperty; |
| fBody= body; |
| fControlStatement= controlStatement; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.internal.corext.fix.AbstractFix.IFixRewriteOperation#rewriteAST(org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite, java.util.List) |
| */ |
| public void rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups) throws CoreException { |
| ASTRewrite rewrite= cuRewrite.getASTRewrite(); |
| String label; |
| if (fBodyProperty == IfStatement.THEN_STATEMENT_PROPERTY) { |
| label = FixMessages.CodeStyleFix_ChangeIfToBlock_desription; |
| } else if (fBodyProperty == IfStatement.ELSE_STATEMENT_PROPERTY) { |
| label = FixMessages.CodeStyleFix_ChangeElseToBlock_description; |
| } else { |
| label = FixMessages.CodeStyleFix_ChangeControlToBlock_description; |
| } |
| |
| TextEditGroup group= createTextEditGroup(label); |
| textEditGroups.add(group); |
| |
| ASTNode moveTarget= rewrite.createMoveTarget(fBody); |
| Block replacingBody= cuRewrite.getRoot().getAST().newBlock(); |
| replacingBody.statements().add(moveTarget); |
| rewrite.set(fControlStatement, fBodyProperty, replacingBody, group); |
| } |
| |
| } |
| |
| static class RemoveBlockOperation extends AbstractFixRewriteOperation { |
| |
| private final Statement fStatement; |
| private final ChildPropertyDescriptor fChild; |
| |
| public RemoveBlockOperation(Statement controlStatement, ChildPropertyDescriptor child) { |
| fStatement= controlStatement; |
| fChild= child; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups) throws CoreException { |
| ASTRewrite rewrite= cuRewrite.getASTRewrite(); |
| |
| Block block= (Block)fStatement.getStructuralProperty(fChild); |
| Statement statement= (Statement)block.statements().get(0); |
| Statement moveTarget= (Statement)rewrite.createMoveTarget(statement); |
| |
| TextEditGroup group= createTextEditGroup(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription); |
| textEditGroups.add(group); |
| rewrite.set(fStatement, fChild, moveTarget, group); |
| } |
| |
| public static boolean satisfiesCleanUpPrecondition(Statement controlStatement, ChildPropertyDescriptor childDescriptor, boolean onlyReturnAndThrows) { |
| return satisfiesPrecondition(controlStatement, childDescriptor, onlyReturnAndThrows, true); |
| } |
| |
| public static boolean satisfiesQuickAssistPrecondition(Statement controlStatement, ChildPropertyDescriptor childDescriptor) { |
| return satisfiesPrecondition(controlStatement, childDescriptor, false, false); |
| } |
| |
| //Can the block around child with childDescriptor of controlStatement be removed? |
| private static boolean satisfiesPrecondition(Statement controlStatement, ChildPropertyDescriptor childDescriptor, boolean onlyReturnAndThrows, boolean cleanUpCheck) { |
| Object child= controlStatement.getStructuralProperty(childDescriptor); |
| |
| if (!(child instanceof Block)) |
| return false; |
| |
| Block block= (Block)child; |
| List list= block.statements(); |
| if (list.size() != 1) |
| return false; |
| |
| ASTNode singleStatement= (ASTNode)list.get(0); |
| |
| if (onlyReturnAndThrows) |
| if (!(singleStatement instanceof ReturnStatement) && !(singleStatement instanceof ThrowStatement)) |
| return false; |
| |
| if (controlStatement instanceof IfStatement) { |
| // if (true) { |
| // while (true) |
| // if (false) |
| // ; |
| // } else |
| // ; |
| |
| if (((IfStatement)controlStatement).getThenStatement() != child) |
| return true;//can always remove blocks in else part |
| |
| IfStatement ifStatement= (IfStatement)controlStatement; |
| if (ifStatement.getElseStatement() == null) |
| return true;//can always remove if no else part |
| |
| return !hasUnblockedIf((Statement)singleStatement, onlyReturnAndThrows, cleanUpCheck); |
| } else { |
| //if (true) |
| // while (true) { |
| // if (false) |
| // ; |
| // } |
| //else |
| // ; |
| if (!hasUnblockedIf((Statement)singleStatement, onlyReturnAndThrows, cleanUpCheck)) |
| return true; |
| |
| ASTNode currentChild= controlStatement; |
| ASTNode parent= currentChild.getParent(); |
| while (true) { |
| Statement body= null; |
| if (parent instanceof IfStatement) { |
| body= ((IfStatement)parent).getThenStatement(); |
| if (body == currentChild && ((IfStatement)parent).getElseStatement() != null)//->currentChild is an unblocked then part |
| return false; |
| } else if (parent instanceof WhileStatement) { |
| body= ((WhileStatement)parent).getBody(); |
| } else if (parent instanceof WithStatement) { |
| body= ((WithStatement)parent).getBody(); |
| } else if (parent instanceof DoStatement) { |
| body= ((DoStatement)parent).getBody(); |
| } else if (parent instanceof ForStatement) { |
| body= ((ForStatement)parent).getBody(); |
| } else if (parent instanceof EnhancedForStatement) { |
| body= ((EnhancedForStatement)parent).getBody(); |
| } else { |
| return true; |
| } |
| if (body != currentChild)//->parents child is a block |
| return true; |
| |
| currentChild= parent; |
| parent= currentChild.getParent(); |
| } |
| } |
| } |
| |
| private static boolean hasUnblockedIf(Statement p, boolean onlyReturnAndThrows, boolean cleanUpCheck) { |
| while (true) { |
| if (p instanceof IfStatement) { |
| return true; |
| } else { |
| |
| ChildPropertyDescriptor childD= null; |
| if (p instanceof WhileStatement) { |
| childD= WhileStatement.BODY_PROPERTY; |
| } else if (p instanceof WithStatement) { |
| childD= WithStatement.BODY_PROPERTY; |
| } else if (p instanceof ForStatement) { |
| childD= ForStatement.BODY_PROPERTY; |
| } else if (p instanceof EnhancedForStatement) { |
| childD= EnhancedForStatement.BODY_PROPERTY; |
| } else if (p instanceof DoStatement) { |
| childD= DoStatement.BODY_PROPERTY; |
| } else { |
| return false; |
| } |
| Statement body= (Statement)p.getStructuralProperty(childD); |
| if (body instanceof Block) { |
| if (!cleanUpCheck) { |
| return false; |
| } else { |
| if (!satisfiesPrecondition(p, childD, onlyReturnAndThrows, cleanUpCheck)) |
| return false; |
| |
| p= (Statement)((Block)body).statements().get(0); |
| } |
| } else { |
| p= body; |
| } |
| } |
| } |
| } |
| |
| } |
| |
| public static IFix[] createRemoveBlockFix(JavaScriptUnit compilationUnit, ASTNode node) { |
| Statement statement= ASTResolving.findParentStatement(node); |
| if (statement == null) { |
| return null; |
| } |
| |
| if (statement instanceof Block) { |
| Block block= (Block)statement; |
| if (block.statements().size() != 1) |
| return null; |
| |
| ASTNode parent= block.getParent(); |
| if (!(parent instanceof Statement)) |
| return null; |
| |
| statement= (Statement)parent; |
| } |
| |
| if (statement instanceof IfStatement) { |
| List result= new ArrayList(); |
| |
| List removeAllList= new ArrayList(); |
| |
| IfElseIterator iter= new IfElseIterator((IfStatement)statement); |
| IfStatement item= null; |
| while (iter.hasNext()) { |
| item= iter.next(); |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(item, IfStatement.THEN_STATEMENT_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(item, IfStatement.THEN_STATEMENT_PROPERTY); |
| removeAllList.add(op); |
| if (item == statement) |
| result.add(new ControlStatementsFix(FixMessages.ControlStatementsFix_removeIfBlock_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})); |
| } |
| } |
| |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(item, IfStatement.ELSE_STATEMENT_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(item, IfStatement.ELSE_STATEMENT_PROPERTY); |
| removeAllList.add(op); |
| if (item == statement) |
| result.add(new ControlStatementsFix(FixMessages.ControlStatementsFix_removeElseBlock_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})); |
| } |
| |
| if (removeAllList.size() > 1) { |
| IFixRewriteOperation[] allConvert= (IFixRewriteOperation[])removeAllList.toArray(new IFixRewriteOperation[removeAllList.size()]); |
| result.add(new ControlStatementsFix(FixMessages.ControlStatementsFix_removeIfElseBlock_proposalDescription, compilationUnit, allConvert)); |
| } |
| |
| return (IFix[])result.toArray(new IFix[result.size()]); |
| } else if (statement instanceof WhileStatement) { |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, WhileStatement.BODY_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(statement, WhileStatement.BODY_PROPERTY); |
| return new IFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})}; |
| } |
| } else if (statement instanceof ForStatement) { |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, ForStatement.BODY_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(statement, ForStatement.BODY_PROPERTY); |
| return new IFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})}; |
| } |
| } else if (statement instanceof ForInStatement) { |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, ForInStatement.BODY_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(statement, ForInStatement.BODY_PROPERTY); |
| return new IFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})}; |
| } |
| } else if (statement instanceof EnhancedForStatement) { |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, EnhancedForStatement.BODY_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(statement, EnhancedForStatement.BODY_PROPERTY); |
| return new IFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})}; |
| } |
| } else if (statement instanceof DoStatement) { |
| if (RemoveBlockOperation.satisfiesQuickAssistPrecondition(statement, DoStatement.BODY_PROPERTY)) { |
| RemoveBlockOperation op= new RemoveBlockOperation(statement, DoStatement.BODY_PROPERTY); |
| return new IFix[] {new ControlStatementsFix(FixMessages.ControlStatementsFix_removeBrackets_proposalDescription, compilationUnit, new IFixRewriteOperation[] {op})}; |
| } |
| } |
| |
| return null; |
| } |
| |
| public static IFix createCleanUp(JavaScriptUnit compilationUnit, |
| boolean convertSingleStatementToBlock, |
| boolean removeUnnecessaryBlock, |
| boolean removeUnnecessaryBlockContainingReturnOrThrow) throws CoreException { |
| |
| if (!convertSingleStatementToBlock && !removeUnnecessaryBlock && !removeUnnecessaryBlockContainingReturnOrThrow) |
| return null; |
| |
| List operations= new ArrayList(); |
| ControlStatementFinder finder= new ControlStatementFinder(convertSingleStatementToBlock, removeUnnecessaryBlock, removeUnnecessaryBlockContainingReturnOrThrow, operations); |
| compilationUnit.accept(finder); |
| |
| if (operations.isEmpty()) |
| return null; |
| |
| IFixRewriteOperation[] ops= (IFixRewriteOperation[])operations.toArray(new IFixRewriteOperation[operations.size()]); |
| return new ControlStatementsFix(FixMessages.ControlStatementsFix_change_name, compilationUnit, ops); |
| } |
| |
| protected ControlStatementsFix(String name, JavaScriptUnit compilationUnit, IFixRewriteOperation[] fixRewriteOperations) { |
| super(name, compilationUnit, fixRewriteOperations); |
| } |
| |
| } |