1 /******************************************************************************* 2 * Copyright (c) 2000, 2018 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.corext.fix; 15 16 import java.util.ArrayList; 17 import java.util.HashSet; 18 19 import org.eclipse.core.runtime.CoreException; 20 21 import org.eclipse.text.edits.TextEditGroup; 22 23 import org.eclipse.jdt.core.dom.AST; 24 import org.eclipse.jdt.core.dom.ASTNode; 25 import org.eclipse.jdt.core.dom.ASTVisitor; 26 import org.eclipse.jdt.core.dom.CompilationUnit; 27 import org.eclipse.jdt.core.dom.Expression; 28 import org.eclipse.jdt.core.dom.InfixExpression; 29 import org.eclipse.jdt.core.dom.InstanceofExpression; 30 import org.eclipse.jdt.core.dom.ParenthesizedExpression; 31 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 32 33 import org.eclipse.jdt.internal.core.manipulation.dom.NecessaryParenthesesChecker; 34 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; 35 import org.eclipse.jdt.internal.corext.refactoring.util.NoCommentSourceRangeComputer; 36 37 import org.eclipse.jdt.ui.cleanup.ICleanUpFix; 38 39 public class ExpressionsFix extends CompilationUnitRewriteOperationsFix { 40 41 private static final class MissingParenthesisVisitor extends ASTVisitor { 42 43 private final ArrayList<ASTNode> fNodes; 44 MissingParenthesisVisitor(ArrayList<ASTNode> nodes)45 private MissingParenthesisVisitor(ArrayList<ASTNode> nodes) { 46 fNodes= nodes; 47 } 48 49 @Override postVisit(ASTNode node)50 public void postVisit(ASTNode node) { 51 if (needsParentesis(node)) { 52 fNodes.add(node); 53 } 54 } 55 needsParentesis(ASTNode node)56 private boolean needsParentesis(ASTNode node) { 57 if (!(node.getParent() instanceof InfixExpression)) 58 return false; 59 60 if (node instanceof InstanceofExpression) 61 return true; 62 63 if (node instanceof InfixExpression) { 64 InfixExpression expression = (InfixExpression) node; 65 InfixExpression.Operator operator = expression.getOperator(); 66 67 InfixExpression parentExpression = (InfixExpression) node.getParent(); 68 InfixExpression.Operator parentOperator = parentExpression.getOperator(); 69 70 if (parentOperator == operator) 71 return false; 72 73 return true; 74 } 75 76 return false; 77 } 78 } 79 80 private static final class UnnecessaryParenthesisVisitor extends ASTVisitor { 81 82 private final ArrayList<ParenthesizedExpression> fNodes; 83 UnnecessaryParenthesisVisitor(ArrayList<ParenthesizedExpression> nodes)84 private UnnecessaryParenthesisVisitor(ArrayList<ParenthesizedExpression> nodes) { 85 fNodes= nodes; 86 } 87 88 @Override visit(ParenthesizedExpression node)89 public boolean visit(ParenthesizedExpression node) { 90 if (NecessaryParenthesesChecker.canRemoveParentheses(node)) { 91 fNodes.add(node); 92 } 93 94 return true; 95 } 96 } 97 98 private static class AddParenthesisOperation extends CompilationUnitRewriteOperation { 99 100 private final Expression[] fExpressions; 101 AddParenthesisOperation(Expression[] expressions)102 public AddParenthesisOperation(Expression[] expressions) { 103 fExpressions= expressions; 104 } 105 106 @Override rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model)107 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException { 108 TextEditGroup group= createTextEditGroup(FixMessages.ExpressionsFix_addParanoiacParentheses_description, cuRewrite); 109 110 ASTRewrite rewrite= cuRewrite.getASTRewrite(); 111 AST ast= cuRewrite.getRoot().getAST(); 112 113 for (Expression expression : fExpressions) { 114 // add parenthesis around expression 115 ParenthesizedExpression parenthesizedExpression= ast.newParenthesizedExpression(); 116 parenthesizedExpression.setExpression((Expression) rewrite.createCopyTarget(expression)); 117 rewrite.replace(expression, parenthesizedExpression, group); 118 } 119 } 120 } 121 122 private static class RemoveParenthesisOperation extends CompilationUnitRewriteOperation { 123 124 private final HashSet<ParenthesizedExpression> fExpressions; 125 RemoveParenthesisOperation(HashSet<ParenthesizedExpression> expressions)126 public RemoveParenthesisOperation(HashSet<ParenthesizedExpression> expressions) { 127 fExpressions= expressions; 128 } 129 130 @Override rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model)131 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException { 132 TextEditGroup group= createTextEditGroup(FixMessages.ExpressionsFix_removeUnnecessaryParentheses_description, cuRewrite); 133 134 ASTRewrite rewrite= cuRewrite.getASTRewrite(); 135 rewrite.setTargetSourceRangeComputer(new NoCommentSourceRangeComputer()); 136 137 while (fExpressions.size() > 0) { 138 ParenthesizedExpression parenthesizedExpression= fExpressions.iterator().next(); 139 fExpressions.remove(parenthesizedExpression); 140 ParenthesizedExpression down= parenthesizedExpression; 141 while (fExpressions.contains(down.getExpression())) { 142 down= (ParenthesizedExpression)down.getExpression(); 143 fExpressions.remove(down); 144 } 145 146 ASTNode move= rewrite.createMoveTarget(down.getExpression()); 147 148 ParenthesizedExpression top= parenthesizedExpression; 149 while (fExpressions.contains(top.getParent())) { 150 top= (ParenthesizedExpression)top.getParent(); 151 fExpressions.remove(top); 152 } 153 154 rewrite.replace(top, move, group); 155 } 156 } 157 } 158 createAddParanoidalParenthesisFix(CompilationUnit compilationUnit, ASTNode[] coveredNodes)159 public static ExpressionsFix createAddParanoidalParenthesisFix(CompilationUnit compilationUnit, ASTNode[] coveredNodes) { 160 if (coveredNodes == null) 161 return null; 162 163 if (coveredNodes.length == 0) 164 return null; 165 // check sub-expressions in fully covered nodes 166 final ArrayList<ASTNode> changedNodes = new ArrayList<>(); 167 for (ASTNode covered : coveredNodes) { 168 if (covered instanceof InfixExpression) 169 covered.accept(new MissingParenthesisVisitor(changedNodes)); 170 } 171 if (changedNodes.isEmpty() || (changedNodes.size() == 1 && changedNodes.get(0).equals(coveredNodes[0]))) 172 return null; 173 174 175 CompilationUnitRewriteOperation op= new AddParenthesisOperation(changedNodes.toArray(new Expression[changedNodes.size()])); 176 return new ExpressionsFix(FixMessages.ExpressionsFix_addParanoiacParentheses_description, compilationUnit, new CompilationUnitRewriteOperation[] {op}); 177 } 178 createRemoveUnnecessaryParenthesisFix(CompilationUnit compilationUnit, ASTNode[] nodes)179 public static ExpressionsFix createRemoveUnnecessaryParenthesisFix(CompilationUnit compilationUnit, ASTNode[] nodes) { 180 // check sub-expressions in fully covered nodes 181 final ArrayList<ParenthesizedExpression> changedNodes= new ArrayList<>(); 182 for (ASTNode covered : nodes) { 183 if (covered instanceof ParenthesizedExpression || covered instanceof InfixExpression) 184 covered.accept(new UnnecessaryParenthesisVisitor(changedNodes)); 185 } 186 if (changedNodes.isEmpty()) 187 return null; 188 189 HashSet<ParenthesizedExpression> expressions= new HashSet<>(changedNodes); 190 RemoveParenthesisOperation op= new RemoveParenthesisOperation(expressions); 191 return new ExpressionsFix(FixMessages.ExpressionsFix_removeUnnecessaryParentheses_description, compilationUnit, new CompilationUnitRewriteOperation[] {op}); 192 } 193 createCleanUp(CompilationUnit compilationUnit, boolean addParanoicParentesis, boolean removeUnnecessaryParenthesis)194 public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, 195 boolean addParanoicParentesis, 196 boolean removeUnnecessaryParenthesis) { 197 198 if (addParanoicParentesis) { 199 final ArrayList<ASTNode> changedNodes = new ArrayList<>(); 200 compilationUnit.accept(new MissingParenthesisVisitor(changedNodes)); 201 202 if (changedNodes.isEmpty()) 203 return null; 204 205 CompilationUnitRewriteOperation op= new AddParenthesisOperation(changedNodes.toArray(new Expression[changedNodes.size()])); 206 return new ExpressionsFix(FixMessages.ExpressionsFix_add_parentheses_change_name, compilationUnit, new CompilationUnitRewriteOperation[] {op}); 207 } else if (removeUnnecessaryParenthesis) { 208 final ArrayList<ParenthesizedExpression> changedNodes = new ArrayList<>(); 209 compilationUnit.accept(new UnnecessaryParenthesisVisitor(changedNodes)); 210 211 if (changedNodes.isEmpty()) 212 return null; 213 214 HashSet<ParenthesizedExpression> expressions= new HashSet<>(changedNodes); 215 CompilationUnitRewriteOperation op= new RemoveParenthesisOperation(expressions); 216 return new ExpressionsFix(FixMessages.ExpressionsFix_remove_parentheses_change_name, compilationUnit, new CompilationUnitRewriteOperation[] {op}); 217 } 218 return null; 219 } 220 ExpressionsFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations)221 protected ExpressionsFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) { 222 super(name, compilationUnit, fixRewriteOperations); 223 } 224 } 225