blob: 85bb5c46f1ab794bafcddba02e302352393ff769 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Fabrice TIERCELIN - initial API and implementation from AutoRefactor
*******************************************************************************/
package org.eclipse.jdt.internal.corext.dom;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.dom.ASTMatcher;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
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.TryStatement;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
/**
* Matches two pieces of code on semantic (not on syntax).
* ASTSemanticMatcher.java matches more cases than ASTMatcher.java:
* - Inverted commutative operations:
* k + 1
* 1 + k
*
* - Mirrored boolean expressions:
* k > 1
* 1 < k
*
* - Neutral operands:
* i + j
* i + j + 0
*
* - Pushed down negations:
* !(k > 0)
* (k <= 0)
*
* - Integer identical operations:
* i >= 1
* i > 0
*
* - Identical statements (not expressions):
* i++;
* ++i;
* i += 1;
* i = i + 1;
*
* - Blocked vs lone statements:
* if (isValid) {i++;}
* if (isValid) i++;
*
* - Parentherized vs unparentherized expression:
* if ((isValid)) {i++;}
* if (isValid) {i++;}
*
* - Inverted if statements or ternary expressions:
* if (isValid) k++; else m--;
* if (!isValid) m--; else k++;
*
* You can also match opposite boolean expressions:
* ASTSemanticMatcher.matchOpposite()
*/
public class ASTSemanticMatcher extends ASTMatcher {
/**
* Singleton.
*/
public static final ASTSemanticMatcher INSTANCE= new ASTSemanticMatcher();
private static final Map<PrefixExpression.Operator, InfixExpression.Operator> PREFIX_TO_INFIX_OPERATOR= new HashMap<PrefixExpression.Operator, InfixExpression.Operator>() {
private static final long serialVersionUID= -8949107654517355855L;
{
put(PrefixExpression.Operator.INCREMENT, InfixExpression.Operator.PLUS);
put(PrefixExpression.Operator.DECREMENT, InfixExpression.Operator.MINUS);
}
};
private static final Map<PrefixExpression.Operator, Assignment.Operator> PREFIX_TO_ASSIGN_OPERATOR= new HashMap<PrefixExpression.Operator, Assignment.Operator>() {
private static final long serialVersionUID= -8949107654517355856L;
{
put(PrefixExpression.Operator.INCREMENT, Assignment.Operator.PLUS_ASSIGN);
put(PrefixExpression.Operator.DECREMENT, Assignment.Operator.MINUS_ASSIGN);
}
};
private static final Map<PostfixExpression.Operator, InfixExpression.Operator> POSTFIX_TO_INFIX_OPERATOR= new HashMap<PostfixExpression.Operator, InfixExpression.Operator>() {
private static final long serialVersionUID= -8949107654517355857L;
{
put(PostfixExpression.Operator.INCREMENT, InfixExpression.Operator.PLUS);
put(PostfixExpression.Operator.DECREMENT, InfixExpression.Operator.MINUS);
}
};
private static final Map<PostfixExpression.Operator, Assignment.Operator> POSTFIX_TO_ASSIGN_OPERATOR= new HashMap<PostfixExpression.Operator, Assignment.Operator>() {
private static final long serialVersionUID= -8949107654517355858L;
{
put(PostfixExpression.Operator.INCREMENT, Assignment.Operator.PLUS_ASSIGN);
put(PostfixExpression.Operator.DECREMENT, Assignment.Operator.MINUS_ASSIGN);
}
};
private static final Map<PrefixExpression.Operator, PostfixExpression.Operator> PREFIX_TO_POSTFIX_OPERATOR= new HashMap<PrefixExpression.Operator, PostfixExpression.Operator>() {
private static final long serialVersionUID= -8949107654517355859L;
{
put(PrefixExpression.Operator.INCREMENT, PostfixExpression.Operator.INCREMENT);
put(PrefixExpression.Operator.DECREMENT, PostfixExpression.Operator.DECREMENT);
}
};
private static final Map<Assignment.Operator, InfixExpression.Operator> ASSIGN_TO_INFIX_OPERATOR= new HashMap<Assignment.Operator, InfixExpression.Operator>() {
private static final long serialVersionUID= -8949107654517355859L;
{
put(Assignment.Operator.PLUS_ASSIGN, InfixExpression.Operator.PLUS);
put(Assignment.Operator.MINUS_ASSIGN, InfixExpression.Operator.MINUS);
put(Assignment.Operator.TIMES_ASSIGN, InfixExpression.Operator.TIMES);
put(Assignment.Operator.DIVIDE_ASSIGN, InfixExpression.Operator.DIVIDE);
put(Assignment.Operator.BIT_AND_ASSIGN, InfixExpression.Operator.AND);
put(Assignment.Operator.BIT_OR_ASSIGN, InfixExpression.Operator.OR);
put(Assignment.Operator.BIT_XOR_ASSIGN, InfixExpression.Operator.XOR);
put(Assignment.Operator.REMAINDER_ASSIGN, InfixExpression.Operator.REMAINDER);
put(Assignment.Operator.LEFT_SHIFT_ASSIGN, InfixExpression.Operator.LEFT_SHIFT);
put(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN, InfixExpression.Operator.RIGHT_SHIFT_SIGNED);
put(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN, InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED);
}
};
private static final Map<InfixExpression.Operator, InfixExpression.Operator> INFIX_TO_MIRROR_OPERATOR= new HashMap<InfixExpression.Operator, InfixExpression.Operator>() {
private static final long serialVersionUID= -8949107654517355857L;
{
put(InfixExpression.Operator.EQUALS, InfixExpression.Operator.EQUALS);
put(InfixExpression.Operator.NOT_EQUALS, InfixExpression.Operator.NOT_EQUALS);
put(InfixExpression.Operator.CONDITIONAL_AND, InfixExpression.Operator.CONDITIONAL_AND);
put(InfixExpression.Operator.CONDITIONAL_OR, InfixExpression.Operator.CONDITIONAL_OR);
put(InfixExpression.Operator.AND, InfixExpression.Operator.AND);
put(InfixExpression.Operator.OR, InfixExpression.Operator.OR);
put(InfixExpression.Operator.XOR, InfixExpression.Operator.XOR);
put(InfixExpression.Operator.PLUS, InfixExpression.Operator.PLUS);
put(InfixExpression.Operator.TIMES, InfixExpression.Operator.TIMES);
put(InfixExpression.Operator.GREATER, InfixExpression.Operator.LESS);
put(InfixExpression.Operator.LESS, InfixExpression.Operator.GREATER);
put(InfixExpression.Operator.LESS_EQUALS, InfixExpression.Operator.GREATER_EQUALS);
put(InfixExpression.Operator.GREATER_EQUALS, InfixExpression.Operator.LESS_EQUALS);
}
};
@Override
public boolean match(final InfixExpression node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof PrefixExpression) {
PrefixExpression pe= (PrefixExpression) other;
if (ASTNodes.hasOperator(pe, PrefixExpression.Operator.NOT)) {
return matchNegative(node, pe.getOperand());
}
}
if (other instanceof InfixExpression) {
InfixExpression infixExpression= (InfixExpression) other;
if (!ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.PLUS)
|| ASTNodes.hasType(node.getLeftOperand(), short.class.getSimpleName(), int.class.getSimpleName(), long.class.getSimpleName(), float.class.getSimpleName(), double.class.getSimpleName(), Short.class.getCanonicalName(),
Integer.class.getCanonicalName(), Long.class.getCanonicalName(), Float.class.getCanonicalName(), Double.class.getCanonicalName())
&& ASTNodes.hasType(node.getRightOperand(), short.class.getSimpleName(), int.class.getSimpleName(), long.class.getSimpleName(), float.class.getSimpleName(), double.class.getSimpleName(), Short.class.getCanonicalName(),
Integer.class.getCanonicalName(), Long.class.getCanonicalName(), Float.class.getCanonicalName(), Double.class.getCanonicalName())) {
if (!node.hasExtendedOperands() && !infixExpression.hasExtendedOperands()
&& node.getOperator().equals(INFIX_TO_MIRROR_OPERATOR.get(infixExpression.getOperator()))
&& ASTNodes.isPassiveWithoutFallingThrough(node.getLeftOperand())
&& ASTNodes.isPassiveWithoutFallingThrough(node.getRightOperand())
&& safeSubtreeMatch(node.getLeftOperand(), infixExpression.getRightOperand())
&& safeSubtreeMatch(node.getRightOperand(), infixExpression.getLeftOperand())) {
return true;
}
if (node.getOperator().equals(infixExpression.getOperator()) && ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.PLUS, InfixExpression.Operator.TIMES, InfixExpression.Operator.AND,
InfixExpression.Operator.CONDITIONAL_AND, InfixExpression.Operator.OR,
InfixExpression.Operator.CONDITIONAL_OR, InfixExpression.Operator.XOR)
&& isOperandsMatching(node, infixExpression, true)) {
return true;
}
}
}
return super.match(node, other);
}
@Override
public boolean match(final ParenthesizedExpression node, final Object otherObject) {
Object other= unbracket(otherObject);
return safeSubtreeMatch(node.getExpression(), other);
}
private Object unbracket(final Object otherObject) {
if (otherObject instanceof ParenthesizedExpression) {
return ((ParenthesizedExpression) otherObject).getExpression();
}
return otherObject;
}
@Override
public boolean match(final PrefixExpression node, final Object otherObject) {
Object other= unbracket(otherObject);
if (!(other instanceof PrefixExpression) && ASTNodes.hasOperator(node, PrefixExpression.Operator.NOT)) {
return matchNegative(node.getOperand(), other);
}
if (node.getParent() instanceof Statement) {
if (other instanceof Assignment) {
return match0(node, (Assignment) other);
}
if (other instanceof PostfixExpression) {
return match0(node, (PostfixExpression) other);
}
}
return super.match(node, other);
}
@Override
public boolean match(final PostfixExpression node, final Object otherObject) {
Object other= unbracket(otherObject);
if (node.getParent() instanceof Statement) {
if (other instanceof Assignment) {
return match0(node, (Assignment) other);
}
if (other instanceof PrefixExpression) {
return match0((PrefixExpression) other, node);
}
}
return super.match(node, other);
}
@Override
public boolean match(final Assignment node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof PrefixExpression && ((PrefixExpression) other).getParent() instanceof Statement) {
return match0((PrefixExpression) other, node);
}
if (other instanceof PostfixExpression && ((PostfixExpression) other).getParent() instanceof Statement) {
return match0((PostfixExpression) other, node);
}
if (other instanceof Assignment) {
return matchAssignmentWithAndWithoutEqual(node, (Assignment) other)
|| matchAssignmentWithAndWithoutEqual((Assignment) other, node) || super.match(node, other);
}
return super.match(node, other);
}
private boolean matchAssignmentWithAndWithoutEqual(final Assignment node, final Assignment assignment) {
if (ASTNodes.hasOperator(node, Assignment.Operator.ASSIGN)
&& node.getRightHandSide() instanceof InfixExpression) {
InfixExpression infixExpression= (InfixExpression) node.getRightHandSide();
if (!infixExpression.hasExtendedOperands()
&& ASTNodes.hasOperator(assignment, Assignment.Operator.PLUS_ASSIGN, Assignment.Operator.MINUS_ASSIGN,
Assignment.Operator.TIMES_ASSIGN, Assignment.Operator.DIVIDE_ASSIGN,
Assignment.Operator.BIT_AND_ASSIGN, Assignment.Operator.BIT_OR_ASSIGN,
Assignment.Operator.BIT_XOR_ASSIGN, Assignment.Operator.REMAINDER_ASSIGN,
Assignment.Operator.LEFT_SHIFT_ASSIGN, Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN,
Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN)
&& ASSIGN_TO_INFIX_OPERATOR.get(assignment.getOperator()).equals(infixExpression.getOperator())) {
return safeSubtreeMatch(node.getLeftHandSide(), assignment.getLeftHandSide())
&& safeSubtreeMatch(infixExpression.getLeftOperand(), assignment.getLeftHandSide())
&& safeSubtreeMatch(infixExpression.getRightOperand(), assignment.getRightHandSide());
}
}
return false;
}
private boolean match0(final PrefixExpression prefixExpression, final PostfixExpression postfixExpression) {
return postfixExpression.getOperator().equals(PREFIX_TO_POSTFIX_OPERATOR.get(prefixExpression.getOperator()))
&& safeSubtreeMatch(prefixExpression.getOperand(), postfixExpression.getOperand());
}
private boolean match0(final PrefixExpression prefixExpression, final Assignment assignment) {
return match0(assignment, prefixExpression.getOperand(), PREFIX_TO_INFIX_OPERATOR.get(prefixExpression.getOperator()),
PREFIX_TO_ASSIGN_OPERATOR.get(prefixExpression.getOperator()));
}
private boolean match0(final PostfixExpression postfixExpression, final Assignment assignment) {
return match0(assignment, postfixExpression.getOperand(), POSTFIX_TO_INFIX_OPERATOR.get(postfixExpression.getOperator()),
POSTFIX_TO_ASSIGN_OPERATOR.get(postfixExpression.getOperator()));
}
private boolean match0(final Assignment assignment, final Expression prefixOrPostfixOperand,
final InfixExpression.Operator infixAssociatedOperator, final Assignment.Operator assignmentAssociatedOperator) {
if (ASTNodes.hasOperator(assignment, Assignment.Operator.ASSIGN)
&& assignment.getRightHandSide() instanceof InfixExpression) {
InfixExpression infixExpression= (InfixExpression) assignment.getRightHandSide();
if (!infixExpression.hasExtendedOperands() && infixAssociatedOperator.equals(infixExpression.getOperator())) {
if (isOneLiteral(infixExpression.getRightOperand())) {
return safeSubtreeMatch(prefixOrPostfixOperand, assignment.getLeftHandSide())
&& safeSubtreeMatch(prefixOrPostfixOperand, infixExpression.getLeftOperand());
}
if (ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.PLUS) && isOneLiteral(infixExpression.getLeftOperand())) {
return safeSubtreeMatch(prefixOrPostfixOperand, assignment.getLeftHandSide())
&& safeSubtreeMatch(prefixOrPostfixOperand, infixExpression.getRightOperand());
}
}
} else if (ASTNodes.hasOperator(assignment, Assignment.Operator.PLUS_ASSIGN, Assignment.Operator.MINUS_ASSIGN) && assignmentAssociatedOperator.equals(assignment.getOperator())
&& isOneLiteral(assignment)) {
return safeSubtreeMatch(prefixOrPostfixOperand, assignment.getLeftHandSide());
}
return false;
}
private boolean isOneLiteral(final Expression operand) {
return Long.valueOf(1).equals(ASTNodes.getIntegerLiteral(operand));
}
@Override
public boolean match(final Block node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof AssertStatement || other instanceof BreakStatement
|| other instanceof ConstructorInvocation || other instanceof ContinueStatement
|| other instanceof DoStatement || other instanceof EmptyStatement
|| other instanceof EnhancedForStatement || other instanceof ExpressionStatement
|| other instanceof ForStatement || other instanceof IfStatement || other instanceof LabeledStatement
|| other instanceof ReturnStatement || other instanceof SuperConstructorInvocation
|| other instanceof SwitchStatement || other instanceof SynchronizedStatement
|| other instanceof ThrowStatement || other instanceof TryStatement
|| other instanceof TypeDeclarationStatement || other instanceof VariableDeclarationStatement
|| other instanceof WhileStatement) {
return match0(node, (Statement) other);
}
return super.match(node, other);
}
@Override
public boolean match(final AssertStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final BreakStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final ConstructorInvocation node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final ContinueStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final DoStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final EmptyStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final EnhancedForStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final ExpressionStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final ConditionalExpression node, final Object otherObject) {
Object other= unbracket(otherObject);
if (super.match(node, other)) {
return true;
}
if (other instanceof ConditionalExpression) {
ConditionalExpression ce= (ConditionalExpression) other;
if (node.getElseExpression() != null && ce.getElseExpression() != null) {
return matchNegative(node.getExpression(), ce.getExpression())
&& safeSubtreeMatch(node.getThenExpression(), ce.getElseExpression())
&& safeSubtreeMatch(node.getElseExpression(), ce.getThenExpression());
}
}
return false;
}
@Override
public boolean match(final ForStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final IfStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
if (super.match(node, other)) {
return true;
}
if (other instanceof IfStatement) {
IfStatement is= (IfStatement) other;
if (node.getElseStatement() != null && is.getElseStatement() != null) {
return matchNegative(node.getExpression(), is.getExpression())
&& safeSubtreeMatch(node.getThenStatement(), is.getElseStatement())
&& safeSubtreeMatch(node.getElseStatement(), is.getThenStatement());
}
}
return false;
}
@Override
public boolean match(final LabeledStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final ReturnStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final SuperConstructorInvocation node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final SwitchStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final SynchronizedStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final ThrowStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final TryStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final TypeDeclarationStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final VariableDeclarationStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
@Override
public boolean match(final WhileStatement node, final Object otherObject) {
Object other= unbracket(otherObject);
if (other instanceof Block) {
return match0((Block) other, (Statement) node);
}
return super.match(node, other);
}
private boolean match0(final Block node, final Statement other) {
if ((node.getParent() instanceof IfStatement || node.getParent() instanceof ForStatement
|| node.getParent() instanceof EnhancedForStatement || node.getParent() instanceof WhileStatement
|| node.getParent() instanceof DoStatement) && node.statements().size() == 1) {
return safeSubtreeMatch(node.statements().get(0), other) || super.match(node, other);
}
return super.match(node, other);
}
/**
* Match the negative boolean.
*
* @param node Node to check
* @param otherObject Node to compare
* @return True if it is the negative boolean.
*/
public boolean matchNegative(final ASTNode node, final Object otherObject) {
Object other= unbracket(otherObject);
if (node instanceof ParenthesizedExpression) {
return matchNegative(((ParenthesizedExpression) node).getExpression(), other);
}
if (node instanceof PrefixExpression) {
PrefixExpression pe= (PrefixExpression) node;
if (ASTNodes.hasOperator(pe, PrefixExpression.Operator.NOT)) {
if (other instanceof PrefixExpression
&& ASTNodes.hasOperator((PrefixExpression) other, PrefixExpression.Operator.NOT)) {
return matchNegative(pe.getOperand(), ((PrefixExpression) other).getOperand());
}
return safeSubtreeMatch(pe.getOperand(), other);
}
} else if (other instanceof PrefixExpression
&& ASTNodes.hasOperator((PrefixExpression) other, PrefixExpression.Operator.NOT)) {
return safeSubtreeMatch(node, ((PrefixExpression) other).getOperand());
}
if (other instanceof ASTNode) {
Boolean value= ASTNodes.getBooleanLiteral(node);
Boolean otherValue= ASTNodes.getBooleanLiteral((ASTNode) other);
if (value != null && otherValue != null) {
return value ^ otherValue;
}
}
if (!(node instanceof InfixExpression) || !(other instanceof InfixExpression)) {
return false;
}
InfixExpression infixExpression1= (InfixExpression) node;
InfixExpression infixExpression2= (InfixExpression) other;
Expression leftOperand1= infixExpression1.getLeftOperand();
Expression rightOperand1= infixExpression1.getRightOperand();
Expression leftOperand2= infixExpression2.getLeftOperand();
Expression rightOperand2= infixExpression2.getRightOperand();
if (infixExpression1.getOperator().equals(infixExpression2.getOperator())
&& !infixExpression1.hasExtendedOperands() && !infixExpression2.hasExtendedOperands()
&& ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.EQUALS, InfixExpression.Operator.NOT_EQUALS,
InfixExpression.Operator.XOR)) {
return matchOneNegativeOther(leftOperand1, leftOperand2, rightOperand2, rightOperand1)
|| matchOneNegativeOther(rightOperand2, rightOperand1, leftOperand1, leftOperand2) || ASTNodes.isPassiveWithoutFallingThrough(leftOperand1) && ASTNodes.isPassiveWithoutFallingThrough(rightOperand1) && ASTNodes.isPassiveWithoutFallingThrough(leftOperand2)
&& ASTNodes.isPassiveWithoutFallingThrough(rightOperand2)
&& (matchOneNegativeOther(leftOperand1, leftOperand2, rightOperand2, rightOperand1)
|| matchOneNegativeOther(rightOperand2, rightOperand1, leftOperand1,
leftOperand2));
}
InfixExpression.Operator negatedOperator= ASTNodes.negatedInfixOperator(infixExpression1.getOperator());
if (infixExpression2.getOperator().equals(negatedOperator)) {
if (ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.CONDITIONAL_AND, InfixExpression.Operator.CONDITIONAL_OR,
InfixExpression.Operator.AND, InfixExpression.Operator.OR)) {
return isOperandsMatching(infixExpression1, infixExpression2, false);
}
if (ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.EQUALS, InfixExpression.Operator.NOT_EQUALS)) {
return isOperandsMatching(infixExpression1, infixExpression2, true);
}
if (ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.GREATER, InfixExpression.Operator.GREATER_EQUALS,
InfixExpression.Operator.LESS, InfixExpression.Operator.LESS_EQUALS)
&& ASTNodes.isPassiveWithoutFallingThrough(leftOperand1)
&& ASTNodes.isPassiveWithoutFallingThrough(rightOperand1)
&& ASTNodes.isPassiveWithoutFallingThrough(leftOperand2)
&& ASTNodes.isPassiveWithoutFallingThrough(rightOperand2)) {
return safeSubtreeMatch(leftOperand1, leftOperand2) && safeSubtreeMatch(rightOperand1, rightOperand2);
}
return false;
}
return (ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.GREATER) && ASTNodes.hasOperator(infixExpression2, InfixExpression.Operator.GREATER_EQUALS) || ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.GREATER_EQUALS) && ASTNodes.hasOperator(infixExpression2, InfixExpression.Operator.GREATER) || ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.LESS) && ASTNodes.hasOperator(infixExpression2, InfixExpression.Operator.LESS_EQUALS) || ASTNodes.hasOperator(infixExpression1, InfixExpression.Operator.LESS_EQUALS) && ASTNodes.hasOperator(infixExpression2, InfixExpression.Operator.LESS))
&& !infixExpression1.hasExtendedOperands()
&& !infixExpression2.hasExtendedOperands()
&& ASTNodes.isPassiveWithoutFallingThrough(leftOperand1)
&& ASTNodes.isPassiveWithoutFallingThrough(rightOperand1)
&& ASTNodes.isPassiveWithoutFallingThrough(leftOperand2)
&& ASTNodes.isPassiveWithoutFallingThrough(rightOperand2)
&& safeSubtreeMatch(leftOperand1, rightOperand2)
&& safeSubtreeMatch(rightOperand1, leftOperand2);
}
private boolean matchOneNegativeOther(final Expression equalOperand1, final Expression equalOperand2,
final Expression negativeOperand1, final Expression negativeOperand2) {
return safeSubtreeMatch(equalOperand1, equalOperand2) && matchNegative(negativeOperand1, negativeOperand2);
}
private boolean isOperandsMatching(final InfixExpression infixExpression1, final InfixExpression infixExpression2, final boolean equal) {
List<Expression> operands1 = getConsistentOperands(infixExpression1);
List<Expression> operands2 = getConsistentOperands(infixExpression2);
if (operands1.size() != operands2.size()) {
return false;
}
boolean isMatching= true;
Iterator<Expression> iterator1= operands1.iterator();
Iterator<Expression> iterator2= operands2.iterator();
while (iterator1.hasNext() && iterator2.hasNext()) {
Expression expression= iterator1.next();
Expression otherExpression= iterator2.next();
if (equal ? !safeSubtreeMatch(expression, otherExpression) : !matchNegative(expression, otherExpression)) {
isMatching= false;
break;
}
}
if (isMatching) {
return true;
}
for (Expression expression : operands1) {
if (!ASTNodes.isPassiveWithoutFallingThrough(expression)) {
return false;
}
}
for (Expression expression : operands2) {
if (!ASTNodes.isPassiveWithoutFallingThrough(expression)) {
return false;
}
}
for (Iterator<Expression> iterator3= operands1.iterator(); iterator3.hasNext();) {
Expression expression= iterator3.next();
for (Iterator<Expression> iterator4= operands2.iterator(); iterator4.hasNext();) {
Expression otherExpression= iterator4.next();
if (equal ? safeSubtreeMatch(expression, otherExpression) : matchNegative(expression, otherExpression)) {
iterator3.remove();
iterator4.remove();
break;
}
}
}
return operands1.isEmpty() && operands2.isEmpty();
}
private List<Expression> getConsistentOperands(final InfixExpression infixExpression) {
List<Expression> operands= ASTNodes.allOperands(infixExpression);
for (Iterator<Expression> iterator= operands.iterator(); iterator.hasNext() && operands.size() > 1;) {
Expression operand= iterator.next();
Long numberLiteral= ASTNodes.getIntegerLiteral(operand);
Boolean booleanValue= ASTNodes.getBooleanLiteral(operand);
if (ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.CONDITIONAL_AND)) {
if (Boolean.TRUE.equals(booleanValue)) {
iterator.remove();
}
} else if (ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.CONDITIONAL_OR)) {
if (Boolean.FALSE.equals(booleanValue)) {
iterator.remove();
}
} else if (ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.PLUS)) {
if (Long.valueOf(0).equals(numberLiteral)) {
iterator.remove();
}
} else if (ASTNodes.hasOperator(infixExpression, InfixExpression.Operator.TIMES) && Long.valueOf(1).equals(numberLiteral)) {
iterator.remove();
}
}
return operands;
}
}