]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/dom/NecessaryParenthesesChecker.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / dom / NecessaryParenthesesChecker.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 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 *******************************************************************************/
11
12package org.eclipse.jdt.internal.corext.dom;
13
14import java.util.Iterator;
15
16import org.eclipse.jdt.core.dom.ASTNode;
17import org.eclipse.jdt.core.dom.ArrayAccess;
18import org.eclipse.jdt.core.dom.AssertStatement;
19import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
20import org.eclipse.jdt.core.dom.ConditionalExpression;
21import org.eclipse.jdt.core.dom.DoStatement;
22import org.eclipse.jdt.core.dom.EnhancedForStatement;
23import org.eclipse.jdt.core.dom.Expression;
24import org.eclipse.jdt.core.dom.ForStatement;
25import org.eclipse.jdt.core.dom.ITypeBinding;
26import org.eclipse.jdt.core.dom.IfStatement;
27import org.eclipse.jdt.core.dom.InfixExpression;
28import org.eclipse.jdt.core.dom.InfixExpression.Operator;
29import org.eclipse.jdt.core.dom.ParenthesizedExpression;
30import org.eclipse.jdt.core.dom.ReturnStatement;
31import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
32import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
33import org.eclipse.jdt.core.dom.SwitchCase;
34import org.eclipse.jdt.core.dom.SwitchStatement;
35import org.eclipse.jdt.core.dom.SynchronizedStatement;
36import org.eclipse.jdt.core.dom.ThrowStatement;
37import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
38import org.eclipse.jdt.core.dom.WhileStatement;
39
40import org.eclipse.jdt.internal.corext.refactoring.code.OperatorPrecedence;
41
42/**
43 * Helper class to check if an expression requires parentheses.
44 *
45 * @since 3.7
46 */
47public class NecessaryParenthesesChecker {
48
49 /*
50 * Get the expression wrapped by the parentheses
51 * i.e. ((((expression)))) -> expression
52 */
53 private static Expression getExpression(ParenthesizedExpression node) {
54 Expression expression= node.getExpression();
55 while (expression instanceof ParenthesizedExpression) {
56 expression= ((ParenthesizedExpression)expression).getExpression();
57 }
58 return expression;
59 }
60
61 private static boolean expressionTypeNeedsParentheses(Expression expression) {
62 int type= expression.getNodeType();
63 return type == ASTNode.INFIX_EXPRESSION
64 || type == ASTNode.CONDITIONAL_EXPRESSION
65 || type == ASTNode.PREFIX_EXPRESSION
66 || type == ASTNode.POSTFIX_EXPRESSION
67 || type == ASTNode.CAST_EXPRESSION
68 || type == ASTNode.INSTANCEOF_EXPRESSION
69 || type == ASTNode.ARRAY_CREATION
70 || type == ASTNode.ASSIGNMENT;
71 }
72
73 private static boolean locationNeedsParentheses(StructuralPropertyDescriptor locationInParent) {
74 if (locationInParent instanceof ChildListPropertyDescriptor && locationInParent != InfixExpression.EXTENDED_OPERANDS_PROPERTY) {
75 // e.g. argument lists of MethodInvocation, ClassInstanceCreation, dimensions of ArrayCreation ...
76 return false;
77 }
78 if (locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY
79 || locationInParent == SingleVariableDeclaration.INITIALIZER_PROPERTY
80 || locationInParent == ReturnStatement.EXPRESSION_PROPERTY
81 || locationInParent == EnhancedForStatement.EXPRESSION_PROPERTY
82 || locationInParent == ForStatement.EXPRESSION_PROPERTY
83 || locationInParent == WhileStatement.EXPRESSION_PROPERTY
84 || locationInParent == DoStatement.EXPRESSION_PROPERTY
85 || locationInParent == AssertStatement.EXPRESSION_PROPERTY
86 || locationInParent == AssertStatement.MESSAGE_PROPERTY
87 || locationInParent == IfStatement.EXPRESSION_PROPERTY
88 || locationInParent == SwitchStatement.EXPRESSION_PROPERTY
89 || locationInParent == SwitchCase.EXPRESSION_PROPERTY
90 || locationInParent == ArrayAccess.INDEX_PROPERTY
91 || locationInParent == ThrowStatement.EXPRESSION_PROPERTY
92 || locationInParent == SynchronizedStatement.EXPRESSION_PROPERTY
93 || locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY) {
94 return false;
95 }
96 return true;
97 }
98
99 /*
100 * Do all operands in expression have same type
101 */
102 private static boolean isAllOperandsHaveSameType(InfixExpression expression) {
103 ITypeBinding binding= expression.getLeftOperand().resolveTypeBinding();
104 if (binding == null)
105 return false;
106
107 ITypeBinding current= expression.getRightOperand().resolveTypeBinding();
108 if (binding != current)
109 return false;
110
111 for (Iterator<Expression> iterator= expression.extendedOperands().iterator(); iterator.hasNext();) {
112 Expression operand= iterator.next();
113 current= operand.resolveTypeBinding();
114 if (binding != current)
115 return false;
116 }
117
118 return true;
119 }
120
121 /*
122 * Is the expression of integer type
123 */
124 private static boolean isExpressionIntegerType(Expression expression) {
125 ITypeBinding binding= expression.resolveTypeBinding();
126 if (binding == null)
127 return false;
128
129 if (!binding.isPrimitive())
130 return false;
131
132 String name= binding.getName();
133 if (isIntegerNumber(name))
134 return true;
135
136 return false;
137 }
138
139 private static boolean isExpressionStringType(Expression expression) {
140 ITypeBinding binding= expression.resolveTypeBinding();
141 if (binding == null)
142 return false;
143
144 return "java.lang.String".equals(binding.getQualifiedName()); //$NON-NLS-1$
145 }
146
147 /*
148 * Is the given expression associative?
149 *
150 * This is true if and only if:<br>
151 * <code>left operator (right) == (right) operator left == right operator left</code>
152 */
153 private static boolean isAssociative(InfixExpression expression) {
154 Operator operator= expression.getOperator();
155
156 if (operator == InfixExpression.Operator.PLUS)
157 return isExpressionStringType(expression) || isExpressionIntegerType(expression) && isAllOperandsHaveSameType(expression);
158
159 if (operator == InfixExpression.Operator.TIMES)
160 return isExpressionIntegerType(expression) && isAllOperandsHaveSameType(expression);
161
162 if (operator == InfixExpression.Operator.CONDITIONAL_AND
163 || operator == InfixExpression.Operator.CONDITIONAL_OR
164 || operator == InfixExpression.Operator.AND
165 || operator == InfixExpression.Operator.OR
166 || operator == InfixExpression.Operator.XOR)
167 return true;
168
169 return false;
170 }
171
172 private static boolean isIntegerNumber(String name) {
173 return "int".equals(name) || "long".equals(name) || "byte".equals(name) || "char".equals(name) || "short".equals(name); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
174 }
175
176 private static boolean needsParenthesesInInfixExpression(Expression expression, InfixExpression parentInfix, StructuralPropertyDescriptor locationInParent) {
177 if (locationInParent == InfixExpression.LEFT_OPERAND_PROPERTY) {
178 //we have (expr op expr) op expr
179 //infix expressions are evaluated from left to right -> parentheses not needed
180 return false;
181 } else if (isAssociative(parentInfix)) {
182 //we have parent op (expr op expr) and op is associative
183 //left op (right) == (right) op left == right op left
184 if (expression instanceof InfixExpression) {
185 InfixExpression infixExpression= (InfixExpression)expression;
186 Operator operator= infixExpression.getOperator();
187
188 if (isExpressionStringType(parentInfix)) {
189 if (parentInfix.getOperator() == InfixExpression.Operator.PLUS && operator == InfixExpression.Operator.PLUS && isExpressionStringType(infixExpression)) {
190 // 1 + ("" + 2) == 1 + "" + 2
191 // 1 + (2 + "") != 1 + 2 + ""
192 // "" + (2 + "") == "" + 2 + ""
193 return !isExpressionStringType(infixExpression.getLeftOperand()) && !isExpressionStringType(parentInfix.getLeftOperand());
194 }
195 //"" + (1 + 2), "" + (1 - 2) etc
196 return true;
197 }
198
199 if (parentInfix.getOperator() != InfixExpression.Operator.TIMES)
200 return false;
201
202 if (operator == InfixExpression.Operator.TIMES)
203 // x * (y * z) == x * y * z
204 return false;
205
206 if (operator == InfixExpression.Operator.REMAINDER || operator == InfixExpression.Operator.DIVIDE)
207 // x * (y % z) != x * y % z , x * (y / z) == x * y / z rounding involved
208 return true;
209
210 return false;
211 }
212 return false;
213 } else {
214 return true;
215 }
216 }
217
218 /**
219 * Can the parentheses be removed from the parenthesized expression ?
220 *
221 * <p>
222 * <b>Note:</b> The parenthesized expression must not be an unparented node.
223 * </p>
224 *
225 * @param expression the parenthesized expression
226 * @return <code>true</code> if parentheses can be removed, <code>false</code> otherwise.
227 */
228 public static boolean canRemoveParentheses(Expression expression) {
229 return canRemoveParentheses(expression, expression.getParent(), expression.getLocationInParent());
230 }
231
232 /**
233 * Can the parentheses be removed from the parenthesized expression when inserted into
234 * <code>parent</code> at <code>locationInParent</code> ?
235 *
236 * <p>
237 * <b>Note:</b> The parenthesized expression can be an unparented node.
238 * </p>
239 *
240 * @param expression the parenthesized expression
241 * @param parent the parent node
242 * @param locationInParent location of expression in the parent
243 * @return <code>true</code> if parentheses can be removed, <code>false</code> otherwise.
244 */
245 public static boolean canRemoveParentheses(Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent) {
246 if (!(expression instanceof ParenthesizedExpression)) {
247 return false;
248 }
249 return !needsParentheses(getExpression((ParenthesizedExpression)expression), parent, locationInParent);
250 }
251
252 /**
253 * Does the <code>expression</code> need parentheses when inserted into <code>parent</code> at
254 * <code>locationInParent</code> ?
255 *
256 * <p>
257 * <b>Note:</b> The expression can be an unparented node.
258 * </p>
259 *
260 * @param expression the expression
261 * @param parent the parent node
262 * @param locationInParent location of expression in the parent
263 * @return <code>true</code> if the expression needs parentheses, <code>false</code> otherwise.
264 */
265 public static boolean needsParentheses(Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent) {
266 if (!expressionTypeNeedsParentheses(expression))
267 return false;
268
269 if (!locationNeedsParentheses(locationInParent)) {
270 return false;
271 }
272
273 if (parent instanceof Expression) {
274 Expression parentExpression= (Expression)parent;
275
276 int expressionPrecedence= OperatorPrecedence.getExpressionPrecedence(expression);
277 int parentPrecedence= OperatorPrecedence.getExpressionPrecedence(parentExpression);
278
279 if (expressionPrecedence > parentPrecedence)
280 //(opEx) opParent and opEx binds more -> parentheses not needed
281 return false;
282
283 if (expressionPrecedence < parentPrecedence)
284 //(opEx) opParent and opEx binds less -> parentheses needed
285 return true;
286
287 //(opEx) opParent binds equal
288
289 if (parentExpression instanceof InfixExpression) {
290 return needsParenthesesInInfixExpression(expression, (InfixExpression)parentExpression, locationInParent);
291 }
292
293 if (parentExpression instanceof ConditionalExpression && locationInParent == ConditionalExpression.EXPRESSION_PROPERTY) {
294 return true;
295 }
296
297 return false;
298 }
299
300 return true;
301 }
302}