package org.eclipse.jdt.internal.core.manipulation.dom;
import java.util.Iterator;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.WhileStatement;
public class NecessaryParenthesesChecker {
private static Expression getExpression(ParenthesizedExpression node) {
Expression expression= node.getExpression();
while (expression instanceof ParenthesizedExpression) {
expression= ((ParenthesizedExpression)expression).getExpression();
}
return expression;
}
private static boolean expressionTypeNeedsParentheses(Expression expression) {
int type= expression.getNodeType();
return type == ASTNode.INFIX_EXPRESSION
|| type == ASTNode.CONDITIONAL_EXPRESSION
|| type == ASTNode.PREFIX_EXPRESSION
|| type == ASTNode.POSTFIX_EXPRESSION
|| type == ASTNode.CAST_EXPRESSION
|| type == ASTNode.INSTANCEOF_EXPRESSION
|| type == ASTNode.ARRAY_CREATION
|| type == ASTNode.ASSIGNMENT;
}
private static boolean locationNeedsParentheses(StructuralPropertyDescriptor locationInParent) {
if (locationInParent instanceof ChildListPropertyDescriptor && locationInParent != InfixExpression.EXTENDED_OPERANDS_PROPERTY) {
return false;
}
if (locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY
|| locationInParent == SingleVariableDeclaration.INITIALIZER_PROPERTY
|| locationInParent == ReturnStatement.EXPRESSION_PROPERTY
|| locationInParent == EnhancedForStatement.EXPRESSION_PROPERTY
|| locationInParent == ForStatement.EXPRESSION_PROPERTY
|| locationInParent == WhileStatement.EXPRESSION_PROPERTY
|| locationInParent == DoStatement.EXPRESSION_PROPERTY
|| locationInParent == AssertStatement.EXPRESSION_PROPERTY
|| locationInParent == AssertStatement.MESSAGE_PROPERTY
|| locationInParent == IfStatement.EXPRESSION_PROPERTY
|| locationInParent == SwitchStatement.EXPRESSION_PROPERTY
|| locationInParent == SwitchCase.EXPRESSION_PROPERTY
|| locationInParent == SwitchCase.EXPRESSIONS2_PROPERTY
|| locationInParent == ArrayAccess.INDEX_PROPERTY
|| locationInParent == ThrowStatement.EXPRESSION_PROPERTY
|| locationInParent == SynchronizedStatement.EXPRESSION_PROPERTY
|| locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY) {
return false;
}
return true;
}
private static boolean isAllOperandsHaveSameType(InfixExpression expression, ITypeBinding leftOperandType, ITypeBinding rightOperandType) {
ITypeBinding binding= leftOperandType;
if (binding == null)
return false;
ITypeBinding current= rightOperandType;
if (binding != current)
return false;
for (Iterator<Expression> iterator= expression.extendedOperands().iterator(); iterator.hasNext();) {
Expression operand= iterator.next();
current= operand.resolveTypeBinding();
if (binding != current)
return false;
}
return true;
}
private static boolean isIntegerType(ITypeBinding binding) {
if (binding == null)
return false;
if (!binding.isPrimitive())
return false;
String name= binding.getName();
if (isIntegerNumber(name))
return true;
return false;
}
private static boolean isStringType(ITypeBinding binding) {
if (binding == null)
return false;
return "java.lang.String".equals(binding.getQualifiedName());
}
private static boolean isAssociative(InfixExpression.Operator operator, ITypeBinding infixExprType, boolean isAllOperandsHaveSameType) {
if (operator == InfixExpression.Operator.PLUS)
return isStringType(infixExprType) || isIntegerType(infixExprType) && isAllOperandsHaveSameType;
if (operator == InfixExpression.Operator.TIMES)
return isIntegerType(infixExprType) && isAllOperandsHaveSameType;
if (operator == InfixExpression.Operator.CONDITIONAL_AND
|| operator == InfixExpression.Operator.CONDITIONAL_OR
|| operator == InfixExpression.Operator.AND
|| operator == InfixExpression.Operator.OR
|| operator == InfixExpression.Operator.XOR)
return true;
return false;
}
private static boolean isIntegerNumber(String name) {
return "int".equals(name) || "long".equals(name) || "byte".equals(name) || "char".equals(name) || "short".equals(name);
}
private static boolean needsParenthesesInInfixExpression(Expression expression, InfixExpression parentInfix, StructuralPropertyDescriptor locationInParent,
ITypeBinding leftOperandType) {
InfixExpression.Operator parentInfixOperator= parentInfix.getOperator();
ITypeBinding rightOperandType;
ITypeBinding parentInfixExprType;
if (leftOperandType == null) {
leftOperandType= parentInfix.getLeftOperand().resolveTypeBinding();
rightOperandType= parentInfix.getRightOperand().resolveTypeBinding();
parentInfixExprType= parentInfix.resolveTypeBinding();
} else {
rightOperandType= expression.resolveTypeBinding();
parentInfixExprType= getInfixExpressionType(parentInfixOperator, leftOperandType, rightOperandType);
}
boolean isAllOperandsHaveSameType= isAllOperandsHaveSameType(parentInfix, leftOperandType, rightOperandType);
if (locationInParent == InfixExpression.LEFT_OPERAND_PROPERTY) {
return false;
} else if (isAssociative(parentInfixOperator, parentInfixExprType, isAllOperandsHaveSameType)) {
if (expression instanceof InfixExpression) {
InfixExpression infixExpression= (InfixExpression)expression;
Operator operator= infixExpression.getOperator();
if (isStringType(parentInfixExprType)) {
if (parentInfixOperator == InfixExpression.Operator.PLUS && operator == InfixExpression.Operator.PLUS && isStringType(infixExpression.resolveTypeBinding())) {
return !isStringType(infixExpression.getLeftOperand().resolveTypeBinding()) && !isStringType(leftOperandType);
}
return true;
}
if (parentInfixOperator != InfixExpression.Operator.TIMES)
return false;
if (operator == InfixExpression.Operator.TIMES)
return false;
if (operator == InfixExpression.Operator.REMAINDER || operator == InfixExpression.Operator.DIVIDE)
return true;
return false;
}
return false;
} else {
return true;
}
}
private static ITypeBinding getInfixExpressionType(InfixExpression.Operator operator, ITypeBinding leftOperandType, ITypeBinding rightOperandType) {
if (leftOperandType == rightOperandType) {
return leftOperandType;
}
if (operator == InfixExpression.Operator.PLUS) {
if (isStringType(leftOperandType)) {
return leftOperandType;
} else if (isStringType(rightOperandType)) {
return rightOperandType;
}
}
return null;
}
public static boolean canRemoveParentheses(Expression expression) {
return canRemoveParentheses(expression, expression.getParent(), expression.getLocationInParent());
}
public static boolean canRemoveParentheses(Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent) {
if (!(expression instanceof ParenthesizedExpression)) {
return false;
}
return !needsParentheses(getExpression((ParenthesizedExpression)expression), parent, locationInParent);
}
public static boolean needsParenthesesForRightOperand(Expression rightOperand, InfixExpression infixExpression, ITypeBinding leftOperandType) {
return needsParentheses(rightOperand, infixExpression, InfixExpression.RIGHT_OPERAND_PROPERTY, leftOperandType);
}
public static boolean needsParentheses(Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent) {
return needsParentheses(expression, parent, locationInParent, null);
}
private static boolean needsParentheses(Expression expression, ASTNode parent, StructuralPropertyDescriptor locationInParent, ITypeBinding leftOperandType) {
if (!expressionTypeNeedsParentheses(expression))
return false;
if (!locationNeedsParentheses(locationInParent)) {
return false;
}
if (parent instanceof Expression) {
Expression parentExpression= (Expression)parent;
if (expression instanceof PrefixExpression) {
return needsParenthesesForPrefixExpression(parentExpression, ((PrefixExpression) expression).getOperator());
}
if (expression instanceof ArrayCreation) {
return parentExpression instanceof ArrayAccess && ((ArrayCreation) expression).getInitializer() == null;
}
int expressionPrecedence= OperatorPrecedence.getExpressionPrecedence(expression);
int parentPrecedence= OperatorPrecedence.getExpressionPrecedence(parentExpression);
if (expressionPrecedence > parentPrecedence)
return false;
if (expressionPrecedence < parentPrecedence)
return true;
if (parentExpression instanceof InfixExpression) {
return needsParenthesesInInfixExpression(expression, (InfixExpression) parentExpression, locationInParent, leftOperandType);
}
if (parentExpression instanceof ConditionalExpression && locationInParent == ConditionalExpression.EXPRESSION_PROPERTY) {
return true;
}
return false;
}
return true;
}
private static boolean needsParenthesesForPrefixExpression(Expression parentExpression, PrefixExpression.Operator expressionOperator) {
if (parentExpression instanceof PrefixExpression) {
PrefixExpression.Operator parentOperator= ((PrefixExpression) parentExpression).getOperator();
if (parentOperator == PrefixExpression.Operator.PLUS &&
(expressionOperator == PrefixExpression.Operator.PLUS || expressionOperator == PrefixExpression.Operator.INCREMENT)) {
return true;
}
if (parentOperator == PrefixExpression.Operator.MINUS &&
(expressionOperator == PrefixExpression.Operator.MINUS || expressionOperator == PrefixExpression.Operator.DECREMENT)) {
return true;
}
} else if (parentExpression instanceof InfixExpression) {
InfixExpression.Operator parentOperator= ((InfixExpression) parentExpression).getOperator();
if (parentOperator == InfixExpression.Operator.PLUS &&
(expressionOperator == PrefixExpression.Operator.PLUS || expressionOperator == PrefixExpression.Operator.INCREMENT)) {
return true;
}
if (parentOperator == InfixExpression.Operator.MINUS &&
(expressionOperator == PrefixExpression.Operator.MINUS || expressionOperator == PrefixExpression.Operator.DECREMENT)) {
return true;
}
}
return false;
}
}