/*
* Copyright 2003-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.codebrowsing.selection;
import java.util.Iterator;
import java.util.List;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
import org.codehaus.groovy.ast.expr.BooleanExpression;
import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.EmptyExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.FieldExpression;
import org.codehaus.groovy.ast.expr.GStringExpression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.MapExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.MethodPointerExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.RangeExpression;
import org.codehaus.groovy.ast.expr.SpreadExpression;
import org.codehaus.groovy.ast.expr.SpreadMapExpression;
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.ast.expr.TernaryExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
/**
* Recursively checks to see if two expressions are the same.
*
* Note that we ignore annotations and generics. Also, see the
* caveat in {@link FindAllOccurrencesVisitor}.
*
* @author andrew
* @created May 12, 2010
*/
public class IsSameExpression {
/**
* true iff the two expressions match
*/
public boolean isSame(Expression left, Expression right) {
if (left == null && right == null) {
return true;
}
if (left == null || right == null) {
return false;
}
if (handleSpecialCases(left, right)) {
return true;
}
if (left.getClass() != right.getClass()) {
return false;
}
// Damn you Java for not having dynamic dispatch
if (left instanceof ArgumentListExpression) {
return visit((ArgumentListExpression) left, (ArgumentListExpression) right);
} else if (left instanceof ArrayExpression) {
return visit((ArrayExpression) left, (ArrayExpression) right);
} else if (left instanceof BinaryExpression) {
return visit((BinaryExpression) left, (BinaryExpression) right);
} else if (left instanceof BitwiseNegationExpression) {
return visit((BitwiseNegationExpression) left, (BitwiseNegationExpression) right);
} else if (left instanceof BooleanExpression) {
return visit((BooleanExpression) left, (BooleanExpression) right);
} else if (left instanceof CastExpression) {
return visit((CastExpression) left, (CastExpression) right);
} else if (left instanceof ClassExpression) {
return visit((ClassExpression) left, (ClassExpression) right);
} else if (left instanceof ClosureExpression) {
return visit((ClosureExpression) left, (ClosureExpression) right);
} else if (left instanceof ConstantExpression) {
return visit((ConstantExpression) left, (ConstantExpression) right);
} else if (left instanceof ConstructorCallExpression) {
return visit((ConstructorCallExpression) left, (ConstructorCallExpression) right);
} else if (left instanceof EmptyExpression) {
return visit((EmptyExpression) left, (EmptyExpression) right);
} else if (left instanceof FieldExpression) {
return visit((FieldExpression) left, (FieldExpression) right);
} else if (left instanceof GStringExpression) {
return visit((GStringExpression) left, (GStringExpression) right);
} else if (left instanceof ListExpression) {
// also ClosureListExpression
return visit((ListExpression) left, (ListExpression) right);
} else if (left instanceof MapExpression) {
// also NamedArgumentListExpression
return visit((MapExpression) left, (MapExpression) right);
} else if (left instanceof MethodCallExpression) {
return visit((MethodCallExpression) left, (MethodCallExpression) right);
} else if (left instanceof MethodPointerExpression) {
return visit((MethodPointerExpression) left, (MethodPointerExpression) right);
} else if (left instanceof PostfixExpression) {
return visit((PostfixExpression) left, (PostfixExpression) right);
} else if (left instanceof PrefixExpression) {
return visit((PrefixExpression) left, (PrefixExpression) right);
} else if (left instanceof PropertyExpression) {
return visit((PropertyExpression) left, (PropertyExpression) right);
} else if (left instanceof RangeExpression) {
return visit((RangeExpression) left, (RangeExpression) right);
} else if (left instanceof SpreadExpression) {
return visit((SpreadExpression) left, (SpreadExpression) right);
} else if (left instanceof SpreadMapExpression) {
return visit((SpreadMapExpression) left, (SpreadMapExpression) right);
} else if (left instanceof StaticMethodCallExpression) {
return visit((StaticMethodCallExpression) left, (StaticMethodCallExpression) right);
} else if (left instanceof TernaryExpression) {
// also ElvisOperatorExpression
return visit((TernaryExpression) left, (TernaryExpression) right);
} else if (left instanceof TupleExpression) {
// ArgumentListExpression
return visit((TupleExpression) left, (TupleExpression) right);
} else if (left instanceof UnaryMinusExpression) {
return visit((UnaryMinusExpression) left, (UnaryMinusExpression) right);
} else if (left instanceof UnaryPlusExpression) {
return visit((UnaryPlusExpression) left, (UnaryPlusExpression) right);
} else if (left instanceof VariableExpression) {
return visit((VariableExpression) left, (VariableExpression) right);
} else {
return false;
}
}
/**
* There are some special cases where the left and right expressions may not
* be of the same type, but they still match
*/
private boolean handleSpecialCases(Expression left, Expression right) {
if (left instanceof ConstantExpression && right instanceof ClassExpression) {
return right.getType().getName().equals(((ConstantExpression) left).getValue());
} else if (left instanceof ClassExpression && right instanceof ConstantExpression) {
return left.getType().getName().equals(((ConstantExpression) right).getValue());
}
return false;
}
private boolean visit(VariableExpression left, VariableExpression right) {
return left.getName().equals(right.getName());
}
private boolean visit(UnaryPlusExpression left, UnaryPlusExpression right) {
return isSame(left.getExpression(), right.getExpression());
}
private boolean visit(UnaryMinusExpression left, UnaryMinusExpression right) {
return isSame(left.getExpression(), right.getExpression());
}
private boolean visit(TupleExpression left, TupleExpression right) {
return checkExpressionList(left.getExpressions(), right.getExpressions());
}
private boolean visit(TernaryExpression left, TernaryExpression right) {
return isSame(left.getBooleanExpression(), right.getBooleanExpression()) &&
isSame(left.getTrueExpression(), right.getTrueExpression()) &&
isSame(left.getFalseExpression(), right.getFalseExpression());
}
private boolean visit(StaticMethodCallExpression left, StaticMethodCallExpression right) {
return visit(left.getType(), right.getType()) && left.getMethod().equals(right.getMethod()) &&
isSame(left.getArguments(), right.getArguments()) ;
}
private boolean visit(SpreadExpression left, SpreadExpression right) {
return isSame(left.getExpression(), right.getExpression());
}
private boolean visit(SpreadMapExpression left, SpreadMapExpression right) {
return isSame(left.getExpression(), right.getExpression());
}
private boolean visit(RangeExpression left, RangeExpression right) {
return isSame(left.getFrom(), right.getFrom()) && isSame(left.getTo(), right.getTo());
}
private boolean visit(PropertyExpression left, PropertyExpression right) {
return isSame(left.getObjectExpression(), right.getObjectExpression()) && isSame(left.getProperty(), right.getProperty());
}
private boolean visit(PrefixExpression left, PrefixExpression right) {
return nullEquals(left.getOperation(), (right.getOperation())) && isSame(left.getExpression(), right.getExpression());
}
private boolean visit(PostfixExpression left, PostfixExpression right) {
return nullEquals(left.getOperation(), (right.getOperation())) && isSame(left.getExpression(), right.getExpression());
}
private boolean visit(MethodPointerExpression left, MethodPointerExpression right) {
return isSame(left.getExpression(), right.getExpression()) &&
isSame(left.getMethodName(), right.getMethodName());
}
private boolean visit(MethodCallExpression left, MethodCallExpression right) {
return isSame(left.getObjectExpression(), right.getObjectExpression()) &&
isSame(left.getMethod(), right.getMethod()) &&
isSame(left.getArguments(), right.getArguments());
}
private boolean visit(MapExpression left, MapExpression right) {
return checkExpressionList(left.getMapEntryExpressions(), right.getMapEntryExpressions());
}
private boolean visit(ListExpression left, ListExpression right) {
return checkExpressionList(left.getExpressions(), right.getExpressions());
}
private boolean visit(GStringExpression left, GStringExpression right) {
return checkExpressionList(left.getStrings(), right.getStrings()) &&
checkExpressionList(left.getValues(), right.getValues());
}
private boolean visit(FieldExpression left, FieldExpression right) {
return visit(left.getField().getDeclaringClass(), right.getField().getDeclaringClass())
&& nullEquals(left.getFieldName(), right.getFieldName());
}
private boolean visit(EmptyExpression left, EmptyExpression right) {
return true;
}
private boolean visit(ConstructorCallExpression left, ConstructorCallExpression right) {
return visit(left.getType(), right.getType()) && isSame(left.getArguments(), right.getArguments());
}
private boolean visit(ConstantExpression left, ConstantExpression right) {
return nullEquals(left.getText(), right.getText());
}
private boolean visit(ClosureExpression left, ClosureExpression right) {
return false; // not implemented yet because we can't compare statements
}
private boolean visit(ClassExpression left, ClassExpression right) {
return visit(left.getType(), right.getType());
}
private boolean visit(CastExpression left, CastExpression right) {
return visit(left.getType(), right.getType()) && isSame(left.getExpression(), right.getExpression());
}
private boolean visit(BooleanExpression left, BooleanExpression right) {
return isSame(left.getExpression(), right.getExpression());
}
private boolean visit(BitwiseNegationExpression left, BitwiseNegationExpression right) {
return isSame(left.getExpression(), right.getExpression());
}
private boolean visit(BinaryExpression left, BinaryExpression right) {
return left.getOperation().getType() == right.getOperation().getType()
&& isSame(left.getLeftExpression(), right.getLeftExpression())
&& isSame(left.getRightExpression(), right.getRightExpression());
}
private boolean visit(ArrayExpression left, ArrayExpression right) {
return checkExpressionList(left.getExpressions(), right.getExpressions()) &&
checkExpressionList(left.getSizeExpression(), right.getSizeExpression());
}
private boolean visit(ClassNode left, ClassNode right) {
return nullEquals(left, right);
}
private boolean nullEquals(Object left, Object right) {
if (left == null && right == null) {
return true;
} else if (left == null || right == null) {
return false;
} else {
return left.equals(right);
}
}
private <T extends Expression> boolean checkExpressionList(List<T> left, List<T> right) {
if (right == null && left == null) {
return true;
} else if (right == null || left == null) {
return false;
} else if (left.size() != right.size()) {
return false;
}
Iterator<T> leftIter = left.iterator();
Iterator<T> rightIter = right.iterator();
boolean success = true;
while (leftIter.hasNext() && rightIter.hasNext()) {
success &= isSame(leftIter.next(), rightIter.next());
}
return success;
}
}