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