package org.objectstyle.wolips.bindings.wod; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; public class BindingValueKey implements Comparable<BindingValueKey> { private String _bindingName; private IMember _bindingMember; private IType _bindingDeclaringType; private IType _nextType; private TypeCache _cache; private BindingValueKey _parent; protected IJavaProject _javaProject; public BindingValueKey(String bindingName, IType bindingDeclaringType, IMember bindingMember, IJavaProject javaProject, TypeCache cache) { _bindingName = bindingName; _bindingMember = bindingMember; _bindingDeclaringType = bindingDeclaringType; _javaProject = javaProject; _cache = cache; } @Override public boolean equals(Object o) { return o instanceof BindingValueKey && compareTo((BindingValueKey) o) == 0; } @Override public int hashCode() { return (_bindingName == null) ? 0 : _bindingName.hashCode(); } public int compareTo(BindingValueKey o) { return (o == null) ? -1 : (_bindingName == null) ? ((o._bindingName == null) ? 0 : 1) : _bindingName.compareTo(o._bindingName); } public IType getDeclaringType() { return (_bindingMember == null) ? null : _bindingMember.getDeclaringType(); } public String getBindingName() { return _bindingName; } public IMember getBindingMember() { return _bindingMember; } public String getMemberTypeName(IMember member) throws JavaModelException { String result = null; if (member != null) { if (member instanceof IMethod) { result = ((IMethod) member).getReturnType(); } else { result = ((IField) member).getTypeSignature(); } } return result; } public String getNextTypeName() { try { String nextTypeName; if (_nextType != null) { nextTypeName = Signature.createTypeSignature(_nextType.getFullyQualifiedName(),true); } else { nextTypeName = getMemberTypeName(_bindingMember); } return nextTypeName; } catch (JavaModelException e) { throw new RuntimeException("Failed to get the next type name for " + _bindingMember + ".", e); } } public IType getNextType() throws JavaModelException { if (_nextType == null) { _nextType = resolveNextType(null); } return _nextType; } public IType getNextType(BindingValueKey parentBinding) throws JavaModelException { if (_nextType == null) { _nextType = resolveNextType(parentBinding); } return _nextType; } public boolean isLeaf() throws JavaModelException { boolean isLeaf = false; IType nextType = getNextType(); if (nextType != null) { String name = nextType.getFullyQualifiedName(); if ("java.lang.String".equals(name) || "java.lang.Object".equals(name)) { isLeaf = true; } } else { isLeaf = true; } return isLeaf; } protected IType resolveNextType(BindingValueKey parentBinding) throws JavaModelException { String nextTypeName = getNextTypeName(); if (nextTypeName == null || nextTypeName.length() == 0) { return null; } String typeSignatureName = Signature.getSignatureSimpleName(Signature.getElementType(nextTypeName)); //long a = System.currentTimeMillis(); if (parentBinding != null) { _parent = parentBinding; } else { _parent = this; } // Q: This is probably one of the most convoluted pieces of code I have written in wolips // Nothing to see here.. it appears to work, move along. // Resolve type name for binding BindingValueKey binding = this; IType declaringType = getDeclaringType(); String lastTypeName = null; while(isGenericType(nextTypeName, declaringType) && !nextTypeName.equals(lastTypeName)) { //System.out.println("BindingValueKey.resolveNextType.while_isGenericType: " + nextTypeName + " / " + lastTypeName + " " + toString()); lastTypeName = nextTypeName; typeSignatureName = Signature.getSignatureSimpleName(Signature.getElementType(nextTypeName)); String[] declaringTypeParameters = declaringType.getTypeParameterSignatures(); String[] declaringTypeArgs = binding._parent._bindingDeclaringType.getTypeParameterSignatures(); String[] memberTypeArgs = Signature.getTypeArguments(getMemberTypeName(binding._parent._bindingMember)); String[] superTypeArgs = Signature.getTypeArguments(binding._parent._bindingDeclaringType.getSuperclassTypeSignature()); /* Resolve next type using generic type arguments * * This iterates over the declaring type's parameter list to find the index of the type value, * it then checks the declaring type for a matching argument, if none is found it defers to the * superclass type signature for a match, lastly it will check the the binding member * (corresponding method or field) for type args. * If no match is found it will fall through and fail to resolve the type. */ for (int i = 0; i < declaringTypeParameters.length; i++) { String param = declaringTypeParameters[i]; String currentParameterType = Signature.getTypeVariable(param); // If the typeSignature name is he same as the declaring type parameter we know what parameter index to look for if (typeSignatureName.equals(currentParameterType)) { // Try declared type arguments first if (i < declaringTypeArgs.length) { nextTypeName = Signature.createTypeSignature(Signature.getTypeVariable(declaringTypeArgs[i]), false); binding = binding._parent; declaringType = binding._bindingDeclaringType; break; } // Next check the type parameters of the superclass for a match if (superTypeArgs.length > 0) { String superTypeName = Signature.getTypeErasure(binding._parent._bindingDeclaringType.getSuperclassName()); IType superType = _cache.getTypeForNameInType(Signature.createTypeSignature(superTypeName, false), binding._parent._bindingDeclaringType); String[] superTypeParameters = superType.getTypeParameterSignatures(); boolean found = false; for (int j = 0; j < superTypeParameters.length; j++) { if (typeSignatureName.equals(Signature.getTypeVariable(superTypeParameters[j]))) { nextTypeName = superTypeArgs[j]; binding = binding._parent; declaringType = binding._bindingDeclaringType; found = true; break; } } if (found) break; } // Last chance binding type parameter on method or field next if (i < memberTypeArgs.length) { nextTypeName = memberTypeArgs[i]; declaringType = binding._parent.getDeclaringType(); break; } } } } String nextTypeNameErasure = Signature.getTypeErasure(nextTypeName); //System.out.println("BindingValueKey.resolveNextType: " + nextTypeNameErasure + " / " + _bindingDeclaringType); IType nextType = _cache.getTypeForNameInType(nextTypeNameErasure, declaringType); //if (System.currentTimeMillis() - a > 0) { // System.out.println("BindingValueKey.resolveNextType: " + nextTypeNameErasure + ", " + (System.currentTimeMillis() - a) + ", " + declaringType.getElementName()); //} return nextType; } private static boolean isGenericType(String typeName, IType declaringType) throws JavaModelException { String[] typeParameters = declaringType.getTypeParameterSignatures(); String typeVariable = Signature.getSignatureSimpleName(typeName); for (int i = 0; i < typeParameters.length; i++) { String currentParameterType = Signature.getTypeVariable(typeParameters[i]); if (typeVariable.equals(currentParameterType)) return true; } return false; } @Override public String toString() { return "[BindingKey: bindingName = " + _bindingName + "; bindingMember = " + _bindingMember + "; parent = " + ((_parent != null && _parent != this) ? _parent.toString() : "null") + "]"; } }