package edu.ucsd.arcum.interpreter.query;
import static edu.ucsd.arcum.ArcumPlugin.DEBUG;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.core.dom.*;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.interpreter.ast.ASTUtil;
import edu.ucsd.arcum.interpreter.ast.ASTUtil.ASTPath;
import edu.ucsd.arcum.interpreter.fragments.ModifierElement;
import edu.ucsd.arcum.interpreter.fragments.SignatureEntity;
import edu.ucsd.arcum.interpreter.fragments.Union;
import edu.ucsd.arcum.interpreter.transformation.CodeRewriter;
import edu.ucsd.arcum.util.StringUtil;
import edu.ucsd.arcum.util.SystemUtil;
public abstract class Entity
{
public static boolean isModifiersEdge(StructuralPropertyDescriptor edge) {
return edge == TypeDeclaration.MODIFIERS2_PROPERTY
|| edge == EnumDeclaration.MODIFIERS2_PROPERTY
|| edge == AnnotationTypeDeclaration.MODIFIERS2_PROPERTY
|| edge == MethodDeclaration.MODIFIERS2_PROPERTY;
}
public static String getDisplayString(@Union("Entity") Object entity) {
if (entity == null) {
return "-";
}
else if (entity instanceof ITypeBinding) {
return ((ITypeBinding)entity).getQualifiedName();
}
else {
String result;
if (entity instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration decl = (AbstractTypeDeclaration)entity;
SimpleName name = decl.getName();
result = String.format("%s", name.toString());
}
else {
result = entity.toString();
}
return StringUtil.minimizeWhitespace(result);
}
}
public static String getQualifiedName(Object entity) {
if (entity instanceof ITypeBinding) {
return ((ITypeBinding)entity).getQualifiedName();
}
return entity.toString().trim();
}
// Two AST nodes might be equal in tree structure, but for this method location
// matters. If the nodes have no parent, then they don't have a location yet,
// and are considered equal. If they do have a parent, then we use identity
// semantics
public static int compareToWithLocations(Object thiz, Object that) {
int result;
if (thiz instanceof ASTNode && that instanceof ASTNode) {
if (((ASTNode)thiz).getParent() == null
// URGENT (!!!): There's a question if this should be && or ||. My
// feeling is that one of them being null is sufficient, not both.
|| ((ASTNode)that).getParent() == null)
{
result = Entity.compareTo(thiz, that);
}
else if (thiz == that) {
result = 0;
}
else {
result = SystemUtil.compareIdentityCodesConsistently(thiz, that);
}
}
else {
// At least one entity is not an ASTNode; e.g. one/both might be a
// type binding instead
result = Entity.compareTo(thiz, that);
}
return result;
}
// MONDAY: Consider ITypeBinding and IMethodBindings to be canonicalized and
// represented as the keys (as Strings)
public static int compareTo(Object thiz, Object that) {
if (thiz == that || thiz.equals(that)) {
return 0;
}
else {
boolean result;
if (thiz instanceof Type && that instanceof Type) {
result = compareTypes((Type)thiz, (Type)that);
}
else if (thiz instanceof Type) {
result = compareTypeToX((Type)thiz, that);
}
else if (that instanceof Type) {
result = compareTypeToX((Type)that, thiz);
}
else if (thiz instanceof BindingKeyValue && that instanceof BindingKeyValue) {
result = thiz.equals(that);
}
else if (thiz instanceof BindingKeyValue && that instanceof ASTNode) {
result = compareBindingToASTNode((BindingKeyValue)thiz, (ASTNode)that);
}
else if (thiz instanceof ASTNode && that instanceof BindingKeyValue) {
result = compareBindingToASTNode((BindingKeyValue)that, (ASTNode)thiz);
}
else if (thiz instanceof ITypeBinding && that instanceof ITypeBinding) {
result = compareTypeBindings((ITypeBinding)thiz, (ITypeBinding)that);
}
else if (thiz instanceof TypeDeclaration && that instanceof ITypeBinding) {
result = compareTypeDeclarationToTypeBinding((TypeDeclaration)thiz,
(ITypeBinding)that);
}
else if (thiz instanceof ITypeBinding && that instanceof TypeDeclaration) {
result = compareTypeDeclarationToTypeBinding((TypeDeclaration)that,
(ITypeBinding)thiz);
}
else if (thiz instanceof SimpleName && that instanceof String) {
result = compareSimpleNameAndString(thiz, that);
}
else if (thiz instanceof String && that instanceof SimpleName) {
result = compareSimpleNameAndString(that, thiz);
}
else if (thiz instanceof Name && that instanceof ITypeBinding) {
result = compareNameAndTypeBinding((Name)thiz, (ITypeBinding)that);
}
else if (thiz instanceof ITypeBinding && that instanceof Name) {
result = compareNameAndTypeBinding((Name)that, (ITypeBinding)thiz);
}
else if (thiz instanceof Name && that instanceof Type) {
result = compareNameAndType(thiz, that);
}
else if (thiz instanceof Name && that instanceof Type) {
result = compareNameAndType(thiz, that);
}
else if (thiz instanceof ModifierElement && that instanceof Modifier) {
result = compareModifierElementAndModifier((ModifierElement)thiz,
(Modifier)that);
}
else if (thiz instanceof Modifier && that instanceof ModifierElement) {
result = compareModifierElementAndModifier((ModifierElement)that,
(Modifier)thiz);
}
else if (thiz instanceof SignatureEntity && that instanceof MethodDeclaration)
{
result = compareSignatureEntityAndASTNode((SignatureEntity)thiz,
(MethodDeclaration)that);
}
else if (thiz instanceof MethodDeclaration && that instanceof SignatureEntity)
{
result = compareSignatureEntityAndASTNode((SignatureEntity)that,
(MethodDeclaration)thiz);
}
else if (thiz instanceof ASTNode) {
if (thiz instanceof Name && that instanceof Name) {
Name n1 = (Name)thiz;
ITypeBinding tb1 = n1.resolveTypeBinding();
if (tb1 != null) {
String qualifiedName = tb1.getQualifiedName();
if (qualifiedName.equals(((Name)that).getFullyQualifiedName()))
return 0;
}
}
result = ((ASTNode)thiz).subtreeMatch(
new ModuloQualificationASTMatcher(), that);
}
else {
result = false;
}
// TASK !!!!!
// if (result == false) {
// System.out.printf("%s != %s%n", getDisplayString(thiz), getDisplayString(that));
// System.out.printf("%s != %s%n", StringUtil.debugDisplay(thiz), StringUtil.debugDisplay(that));
// }
return booleanToTroolian(result, thiz, that);
}
}
private static boolean compareBindingToASTNode(BindingKeyValue thiz, ASTNode that) {
IBinding binding = thiz.getOriginalBinding();
ASTNode foundAST = EntityDataBase.findASTNode(binding);
return compareToWithLocations(foundAST, that) == 0;
}
private static boolean compareTypeToX(Type type, Object something) {
boolean result;
ITypeBinding nodeBinding = (ITypeBinding)EntityDataBase
.resolveBindingNullOK(type);
if (nodeBinding != null) {
if (something instanceof ITypeBinding) {
ITypeBinding valueBinding = (ITypeBinding)something;
result = compareTypeBindings(nodeBinding, valueBinding);
}
else {
result = nodeBinding.equals(something);
}
}
else {
result = compareTypeToValue(type, something);
}
return result;
}
private static boolean compareTypeDeclarationToTypeBinding(
TypeDeclaration typeDeclaration, ITypeBinding tb2)
{
ITypeBinding tb1 = (ITypeBinding)EntityDataBase.resolveBinding(typeDeclaration);
return compareTypeBindings(tb1, tb2);
}
private static boolean compareTypes(Type t1, Type t2) {
boolean result;
ITypeBinding tb1 = (ITypeBinding)EntityDataBase.resolveBindingNullOK(t1);
ITypeBinding tb2 = (ITypeBinding)EntityDataBase.resolveBindingNullOK(t2);
if (tb1 != null && tb2 != null) {
result = tb1.isEqualTo(tb2);
}
else {
// in some cases, we are comparing against our own generated
// code, so it's possible for these to match
result = t1.subtreeMatch(new ModuloQualificationASTMatcher(), t2);
}
return result;
}
private static boolean compareNameAndType(Object thiz, Object that) {
boolean result;
Name thizName = (Name)thiz;
IBinding tb1 = EntityDataBase.resolveBinding(thizName);
Type thatType = (Type)that;
ITypeBinding tb2 = (ITypeBinding)EntityDataBase.resolveBinding(thatType);
result = tb1.isEqualTo(tb2);
return result;
}
private static boolean compareNameAndTypeBinding(Name name, ITypeBinding tb1) {
ITypeBinding tb2 = name.resolveTypeBinding();
if (tb2 != null) {
if (tb1.isEqualTo(tb2)) {
if (DEBUG) {
System.out.printf("%s is indeed equal to %s%n", name, tb1);
}
return true;
}
else {
return false;
}
}
else {
System.err
.printf("Type information is unavailable: possible internal error%n");
String nameRep = name.toString();
String tb1Rep = tb1.getQualifiedName();
boolean result = nameRep.equals(tb1Rep);
return result;
}
}
private static boolean compareSimpleNameAndString(Object thiz, Object that) {
boolean result;
SimpleName thisName = (SimpleName)thiz;
result = thisName.getIdentifier().equals(that);
return result;
}
private static boolean compareTypeBindings(ITypeBinding tb1, ITypeBinding tb2) {
if (tb1.isEqualTo(tb2)) {
return true;
}
else {
String node = tb1.getQualifiedName();
String value = tb2.getQualifiedName();
if (DEBUG && node.equals(value)) {
System.out.printf("Found: %s equals %s.%n", node, value);
}
return node.equals(value);
}
}
private static boolean compareModifierElementAndModifier(
ModifierElement modifierElement, Modifier modifier)
{
return modifierElement.isSameModifier(modifier);
}
private static boolean compareSignatureEntityAndASTNode(SignatureEntity signature,
MethodDeclaration methodDecl)
{
IMethodBinding binding = methodDecl.resolveBinding();
boolean result = signature.hasSameSignatureAs(binding);
return result;
}
private static boolean compareTypeToValue(Type type, Object value) {
String name = type.toString();
if (value instanceof ITypeBinding) {
ITypeBinding valueBinding = (ITypeBinding)value;
String qualifiedName = valueBinding.getQualifiedName();
if (name.equals(qualifiedName)) {
return true;
}
else {
String lastSegment = qualifiedName.substring(qualifiedName
.lastIndexOf(".") + 1);
if (name.equals(lastSegment)) {
// there's a chance they do match... FIXME: don't be so optimistic,
// however, this nitty-gritty case might only occur when we are
// comparing against AST nodes we've created ourselves (because the
// better type information would otherwise be available) and thus
// matching won't be too affected. We could try to find the imports
// and compare it against the qualifiedName.
return true;
}
return false;
}
}
else {
return value.equals(type);
}
}
// If value is true, then 0 is returned. Otherwise, some consistent ordering
// between a and b is returned based on their identity hash codes.
private static int booleanToTroolian(boolean value, Object a, Object b) {
if (value) {
return 0;
}
else {
String aStr = getDisplayString(a);
String bStr = getDisplayString(b);
return aStr.compareTo(bStr);
// The problem with using the identity codes is that type bindings
// aren't consistent, because they are not the same object always. Two
// "equivalent" type bindings, however, will have the same display
// string, so those are compared for a consistent ordering instead
// return SystemUtil.compareIdentityCodesConsistently(a, b);
}
}
public static ASTNode getASTNodeValue(Object entity) {
if (entity instanceof ASTNode) {
ASTNode result = (ASTNode)entity;
result = ASTUtil.queryUpdatedNode(result);
return (ASTNode)result;
}
else {
return null;
}
}
public static String valueAsString(Object entity) {
if (entity instanceof ITypeBinding) {
return ((ITypeBinding)entity).getQualifiedName();
}
else if (entity == null) {
return "null";
}
return entity.toString().trim();
}
public static <T extends ASTNode> T copySubtree(AST target, T node) {
T copy = (T)ASTNode.copySubtree(target, node);
ASTUtil.recordUpdatedNode(node, copy);
List<ASTNode> trackedNodes = CodeRewriter.findAllTrackedNodes(node);
for (ASTNode trackedNode : trackedNodes) {
Object id = CodeRewriter.getTrackingID(trackedNode);
ASTPath pathToRoot = ASTUtil.getPathToRoot(trackedNode, node);
ASTNode toTrack = pathToRoot.getASTNodeFrom(copy);
CodeRewriter.setTrackingID(toTrack, id);
}
return copy;
}
public static ITypeBinding getTypeOf(Object entity) {
if (entity instanceof Type) {
Type type = (Type)entity;
ITypeBinding binding = (ITypeBinding)EntityDataBase.resolveBinding(type);
return binding;
}
else if (entity instanceof ITypeBinding) {
return (ITypeBinding)entity;
}
else if (entity instanceof BindingKeyValue) {
BindingKeyValue keyValue = (BindingKeyValue)entity;
EntityType type = keyValue.getType();
if (type == EntityType.TYPE) {
ITypeBinding binding = (ITypeBinding)keyValue.getOriginalBinding();
return binding;
}
else if (type == EntityType.METHOD) {
IMethodBinding binding = (IMethodBinding)keyValue.getOriginalBinding();
// NOTE: The typeOf a constructor could be the type of the class,
// instead of void as returned by this method:
return binding.getReturnType();
}
else {
ArcumError.fatalError("Cannot handle %s case in getTypeOf", type);
return null;
}
}
else {
ASTNode node = Entity.getASTNodeValue(entity);
ITypeBinding typeBinding = null;
if (node instanceof Expression) {
Expression expression = (Expression)node;
typeBinding = expression.resolveTypeBinding();
}
else if (node instanceof FieldDeclaration) {
FieldDeclaration fieldDecl = (FieldDeclaration)node;
List fragments = fieldDecl.fragments();
VariableDeclarationFragment varDecl;
varDecl = (VariableDeclarationFragment)fragments.get(0);
SimpleName name = varDecl.getName();
typeBinding = typeOfName(name);
}
else if (node instanceof MethodDeclaration) {
ArcumError.fatalError("Not implemented yet in getTypeOf");
return null;
}
return typeBinding;
}
}
private static ITypeBinding typeOfName(Name name) {
ITypeBinding typeBinding;
IBinding binding = EntityDataBase.resolveBinding(name);
switch (binding.getKind()) {
case IBinding.VARIABLE:
IVariableBinding var = (IVariableBinding)binding;
typeBinding = var.getType();
break;
case IBinding.TYPE:
typeBinding = (ITypeBinding)binding;
break;
default:
ArcumError.fatalError("Unhandled typeOfName case: %s", binding);
return null/*unreachable*/;
}
return typeBinding;
}
public static Object canonicalizeRepresentation(Object entity) {
if (entity instanceof MethodDeclaration) {
MethodDeclaration methodDeclaration = (MethodDeclaration)entity;
IMethodBinding binding = methodDeclaration.resolveBinding();
if (binding != null) {
return BindingKeyValue.newInstance(EntityType.METHOD, binding);
}
// otherwise it is synthetic
}
return entity;
}
// Returns true when all "elements" of the given set are found in the "nodes" set
public static <T> boolean subsetOf(Iterable<T> elements, Collection<ASTNode> nodes)
{
nextElement: for (T element : elements) {
for (ASTNode node : nodes) {
if (compareTo(element, node) == 0) {
continue nextElement;
}
}
return false;
}
return true;
}
public static boolean isReturnType(Type type) {
StructuralPropertyDescriptor spd = type.getLocationInParent();
return spd == MethodDeclaration.RETURN_TYPE2_PROPERTY;
}
}