/******************************************************************************* * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for * bug "inline method - doesn't handle implicit cast" (see * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941). *******************************************************************************/ package org.incha.core.jswingripples.parser; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IImportDeclaration; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.ILocalVariable; //import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.IPackageBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.jdt.core.dom.SuperFieldAccess; @SuppressWarnings({"rawtypes", "unchecked"}) public class BindingSupport { public final String ARRAY_LENGTH_FIELD_BINDING_STRING= "(array type):length";//$NON-NLS-1$ private final ICompilationUnit[] units; /** * @param units compilation units. */ public BindingSupport(final ICompilationUnit[] units) { super(); this.units = units; } /** * Checks if the two bindings are equals. Also works across binding environments. * @param b1 first binding treated as <code>this. So it must * not be <code>null * @param b2 the second binding. * @return boolean */ public boolean equals(final IBinding b1, final IBinding b2) { return b1.isEqualTo(b2); } /** * Checks if the declarations of two bindings are equals. * Also works across binding environments. * @param b1 first binding, must not be <code>null * @param b2 second binding, must not be <code>null * @return boolean */ public boolean equalDeclarations(final IBinding b1, final IBinding b2) { if (b1.getKind() != b2.getKind()) return false; return getDeclaration(b1).isEqualTo(getDeclaration(b2)); } /** * Checks if the two arrays of bindings have the same length and * their elements are equal. Uses * <code>Bindings.equals(IBinding, IBinding) to compare. * @param b1 the first array of bindings. Must not be <code>null. * @param b2 the second array of bindings. * @return boolean */ public boolean equals(final IBinding[] b1, final IBinding[] b2) { Assert.isNotNull(b1); if (b1 == b2) return true; if (b2 == null) return false; if (b1.length != b2.length) return false; for (int i= 0; i < b1.length; i++) { if (! equals(b1[i], b2[i])) return false; } return true; } public int hashCode(final IBinding binding){ Assert.isNotNull(binding); final String key= binding.getKey(); if (key == null) return binding.hashCode(); return key.hashCode(); } /** * Note: this method is for debugging and testing purposes only. * There are tests whose pre-computed test results rely on the returned String's format. * @param binding the binding * @return a string representation of given binding * @see org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider */ public String asString(final IBinding binding) { if (binding instanceof IMethodBinding) return asString((IMethodBinding)binding); else if (binding instanceof ITypeBinding) return ((ITypeBinding)binding).getQualifiedName(); else if (binding instanceof IVariableBinding) return asString((IVariableBinding)binding); return binding.toString(); } private String asString(final IVariableBinding variableBinding) { if (! variableBinding.isField()) return variableBinding.toString(); if (variableBinding.getDeclaringClass() == null) { Assert.isTrue(variableBinding.getName().equals("length"));//$NON-NLS-1$ return ARRAY_LENGTH_FIELD_BINDING_STRING; } final StringBuffer result= new StringBuffer(); result.append(variableBinding.getDeclaringClass().getName()); result.append(':'); result.append(variableBinding.getName()); return result.toString(); } private String asString(final IMethodBinding method) { final StringBuffer result= new StringBuffer(); result.append(method.getDeclaringClass().getName()); result.append(':'); result.append(method.getName()); result.append('('); final ITypeBinding[] parameters= method.getParameterTypes(); final int lastComma= parameters.length - 1; for (int i= 0; i < parameters.length; i++) { final ITypeBinding parameter= parameters[i]; result.append(parameter.getName()); if (i < lastComma) result.append(", "); //$NON-NLS-1$ } result.append(')'); return result.toString(); } public String getTypeQualifiedName(final ITypeBinding type) { final List result= new ArrayList(5); createName(type, false, result); final StringBuffer buffer= new StringBuffer(); for (int i= 0; i < result.size(); i++) { if (i > 0) { buffer.append('.'); } buffer.append(((String) result.get(i))); } return buffer.toString(); } /** * Returns the fully qualified name of the specified type binding. * <p> * If the binding resolves to a generic type, the fully qualified name of the raw type is returned. * * @param type the type binding to get its fully qualified name * @return the fully qualified name */ public String getFullyQualifiedName(final ITypeBinding type) { String name= type.getQualifiedName(); final int index= name.indexOf('<'); if (index > 0) name= name.substring(0, index); return name; } // // public String getImportName(final IBinding binding) { // ITypeBinding declaring= null; // switch (binding.getKind()) { // case IBinding.TYPE: // return getRawQualifiedName((ITypeBinding) binding); // case IBinding.PACKAGE: // return binding.getName() + ".*"; //$NON-NLS-1$ // case IBinding.METHOD: // declaring= ((IMethodBinding) binding).getDeclaringClass(); // break; // case IBinding.VARIABLE: // declaring= ((IVariableBinding) binding).getDeclaringClass(); // if (declaring == null) { // return binding.getName(); // array.length // } // // break; // default: // return binding.getName(); // } // return JavaModelUtil.concatenateName(getRawQualifiedName(declaring), binding.getName()); // } // private void createName(final ITypeBinding type, final boolean includePackage, final List list) { ITypeBinding baseType= type; if (type.isArray()) { baseType= type.getElementType(); } if (!baseType.isPrimitive() && !baseType.isNullType()) { final ITypeBinding declaringType= baseType.getDeclaringClass(); if (declaringType != null) { createName(declaringType, includePackage, list); } else if (includePackage && !baseType.getPackage().isUnnamed()) { final String[] components= baseType.getPackage().getNameComponents(); for (int i= 0; i < components.length; i++) { list.add(components[i]); } } } if (!baseType.isAnonymous()) { list.add(type.getName()); } else { list.add("$local$"); //$NON-NLS-1$ } } public String[] getNameComponents(final ITypeBinding type) { final List result= new ArrayList(5); createName(type, false, result); return (String[]) result.toArray(new String[result.size()]); } public String[] getAllNameComponents(final ITypeBinding type) { final List result= new ArrayList(5); createName(type, true, result); return (String[]) result.toArray(new String[result.size()]); } public ITypeBinding getTopLevelType(ITypeBinding type) { ITypeBinding parent= type.getDeclaringClass(); while (parent != null) { type= parent; parent= type.getDeclaringClass(); } return type; } /** * Checks whether the passed type binding is a runtime exception. * * @param thrownException the type binding * * @return <code>true if the passed type binding is a runtime exception; * otherwise <code>false is returned */ public boolean isRuntimeException(final ITypeBinding thrownException) { if (thrownException == null || thrownException.isPrimitive() || thrownException.isArray()) return false; return findTypeInHierarchy(thrownException, "java.lang.RuntimeException") != null; //$NON-NLS-1$ } /** * Finds the field specified by <code>fieldName in * the given <code>type. Returns null if no such field exits. * @param type the type to search the field in * @param fieldName the field name * @return the binding representing the field or <code>null */ public IVariableBinding findFieldInType(final ITypeBinding type, final String fieldName) { if (type.isPrimitive()) return null; final IVariableBinding[] fields= type.getDeclaredFields(); for (int i= 0; i < fields.length; i++) { final IVariableBinding field= fields[i]; if (field.getName().equals(fieldName)) return field; } return null; } /** * Finds the field specified by <code>fieldName in * the type hierarchy denoted by the given type. Returns <code>null if no such field * exists. If the field is defined in more than one super type only the first match is * returned. First the super class is examined and than the implemented interfaces. * @param type The type to search the field in * @param fieldName The name of the field to find * @return the variable binding representing the field */ public IVariableBinding findFieldInHierarchy(final ITypeBinding type, final String fieldName) { IVariableBinding field= findFieldInType(type, fieldName); if (field != null) return field; final ITypeBinding superClass= type.getSuperclass(); if (superClass != null) { field= findFieldInHierarchy(superClass, fieldName); if (field != null) return field; } final ITypeBinding[] interfaces= type.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { field= findFieldInHierarchy(interfaces[i], fieldName); if (field != null) // no private fields in interfaces return field; } return null; } /** * Finds the method specified by <code>methodName and parameters in * the given <code>type. Returns null if no such method exits. * @param type The type to search the method in * @param methodName The name of the method to find * @param parameters The parameter types of the method to find. If <code>null is passed, only * the name is matched and parameters are ignored. * @return the method binding representing the method */ public IMethodBinding findMethodInType(final ITypeBinding type, final String methodName, final ITypeBinding[] parameters) { if (type.isPrimitive()) return null; final IMethodBinding[] methods= type.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { if (parameters == null) { if (methodName.equals(methods[i].getName())) return methods[i]; } else { if (isEqualMethod(methods[i], methodName, parameters)) return methods[i]; } } return null; } /** * Finds the method specified by <code>methodName and parameters in * the type hierarchy denoted by the given type. Returns <code>null if no such method * exists. If the method is defined in more than one super type only the first match is * returned. First the super class is examined and than the implemented interfaces. * @param type The type to search the method in * @param methodName The name of the method to find * @param parameters The parameter types of the method to find. If <code>null is passed, only the name is matched and parameters are ignored. * @return the method binding representing the method */ public IMethodBinding findMethodInHierarchy(final ITypeBinding type, final String methodName, final ITypeBinding[] parameters) { IMethodBinding method= findMethodInType(type, methodName, parameters); if (method != null) return method; final ITypeBinding superClass= type.getSuperclass(); if (superClass != null) { method= findMethodInHierarchy(superClass, methodName, parameters); if (method != null) return method; } final ITypeBinding[] interfaces= type.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { method= findMethodInHierarchy(interfaces[i], methodName, parameters); if (method != null) return method; } return null; } /** * Finds the method specified by <code>methodName and parameters in * the given <code>type. Returns null if no such method exits. * @param type The type to search the method in * @param methodName The name of the method to find * @param parameters The parameter types of the method to find. If <code>null is passed, only the name is matched and parameters are ignored. * @return the method binding representing the method */ public IMethodBinding findMethodInType(final ITypeBinding type, final String methodName, final String[] parameters) { if (type.isPrimitive()) return null; final IMethodBinding[] methods= type.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { if (parameters == null) { if (methodName.equals(methods[i].getName())) return methods[i]; } else { if (isEqualMethod(methods[i], methodName, parameters)) return methods[i]; } } return null; } /** * Finds the method specified by <code>methodName and parameters in * the type hierarchy denoted by the given type. Returns <code>null if no such method * exists. If the method is defined in more than one super type only the first match is * returned. First the super class is examined and than the implemented interfaces. * @param type the type to search the method in * @param methodName The name of the method to find * @param parameters The parameter types of the method to find. If <code>null is passed, only the name is matched and parameters are ignored. * @return the method binding representing the method */ public IMethodBinding findMethodInHierarchy(final ITypeBinding type, final String methodName, final String[] parameters) { IMethodBinding method= findMethodInType(type, methodName, parameters); if (method != null) return method; final ITypeBinding superClass= type.getSuperclass(); if (superClass != null) { method= findMethodInHierarchy(superClass, methodName, parameters); if (method != null) return method; } final ITypeBinding[] interfaces= type.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { method= findMethodInHierarchy(interfaces[i], methodName, parameters); if (method != null) return method; } return null; } /** * Finds the method in the given <code>type that is overridden by the specified method. * Returns <code>null if no such method exits. * @param type The type to search the method in * @param method The specified method that would override the result * @return the method binding of the method that is overridden by the specified <code>method, or null */ public IMethodBinding findOverriddenMethodInType(final ITypeBinding type, final IMethodBinding method) { final IMethodBinding[] methods= type.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { if (isSubsignature(method, methods[i])) return methods[i]; } return null; } /** * Finds a method in the hierarchy of <code>type that is overridden by binding. * Returns <code>null if no such method exists. If the method is defined in more than one super type only the first match is * returned. First the super class is examined and than the implemented interfaces. * @param type The type to search the method in * @param binding The method that overrides * @return the method binding overridden the method */ public IMethodBinding findOverriddenMethodInHierarchy(final ITypeBinding type, final IMethodBinding binding) { IMethodBinding method= findOverriddenMethodInType(type, binding); if (method != null) return method; final ITypeBinding superClass= type.getSuperclass(); if (superClass != null) { method= findOverriddenMethodInHierarchy(superClass, binding); if (method != null) return method; } final ITypeBinding[] interfaces= type.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { method= findOverriddenMethodInHierarchy(interfaces[i], binding); if (method != null) return method; } return null; } /** * Finds the method that is overridden by the given method. The search is bottom-up, so this * returns the nearest defining/declaring method. * @param overriding overriding method * @param testVisibility If true the result is tested on visibility. Null is returned if the method is not visible. * @return the method binding representing the method */ public IMethodBinding findOverriddenMethod(final IMethodBinding overriding, final boolean testVisibility) { final int modifiers= overriding.getModifiers(); if (Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || overriding.isConstructor()) { return null; } final ITypeBinding type= overriding.getDeclaringClass(); if (type.getSuperclass() != null) { final IMethodBinding res= findOverriddenMethodInHierarchy(type.getSuperclass(), overriding); if (res != null && !Modifier.isPrivate(res.getModifiers())) { if (!testVisibility || isVisibleInHierarchy(res, overriding.getDeclaringClass().getPackage())) { return res; } } } final ITypeBinding[] interfaces= type.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { final IMethodBinding res= findOverriddenMethodInHierarchy(interfaces[i], overriding); if (res != null) { return res; // methods from interfaces are always public and therefore visible } } return null; } public boolean isVisibleInHierarchy(final IMethodBinding member, final IPackageBinding pack) { final int otherflags= member.getModifiers(); final ITypeBinding declaringType= member.getDeclaringClass(); if (Modifier.isPublic(otherflags) || Modifier.isProtected(otherflags) || (declaringType != null && declaringType.isInterface())) { return true; } else if (Modifier.isPrivate(otherflags)) { return false; } return declaringType != null && pack == declaringType.getPackage(); } /** * Returns all super types (classes and interfaces) for the given type. * @param type The type to get the supertypes of. * @return all super types (excluding <code>type) */ public ITypeBinding[] getAllSuperTypes(final ITypeBinding type) { final Set result= new HashSet(); collectSuperTypes(type, result); result.remove(type); return (ITypeBinding[]) result.toArray(new ITypeBinding[result.size()]); } private void collectSuperTypes(final ITypeBinding curr, final Set collection) { if (collection.add(curr)) { final ITypeBinding[] interfaces= curr.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { collectSuperTypes(interfaces[i], collection); } final ITypeBinding superClass= curr.getSuperclass(); if (superClass != null) { collectSuperTypes(superClass, collection); } } } // // /** // * Method to visit a type hierarchy defined by a given type. // * // * @param type the type which hierarchy is to be visited // * @param visitor the visitor // * @return <code>false if the visiting got interrupted // */ // public boolean visitHierarchy(final ITypeBinding type, final TypeBindingVisitor visitor) { // boolean result= visitSuperclasses(type, visitor); // if(result) { // result= visitInterfaces(type, visitor); // } // return result; // } // /** // * Method to visit a interface hierarchy defined by a given type. // * // * @param type the type which interface hierarchy is to be visited // * @param visitor the visitor // * @return <code>false if the visiting got interrupted // */ // public boolean visitInterfaces(final ITypeBinding type, final TypeBindingVisitor visitor) { // final ITypeBinding[] interfaces= type.getInterfaces(); // for (int i= 0; i < interfaces.length; i++) { // if (!visitor.visit(interfaces[i])) { // return false; // } // } // return true; // } // // /** // * Method to visit a super class hierarchy defined by a given type. // * // * @param type the type which super class hierarchy is to be visited // * @param visitor the visitor // * @return <code>false if the visiting got interrupted // */ // public boolean visitSuperclasses(ITypeBinding type, final TypeBindingVisitor visitor) { // while ((type= type.getSuperclass()) != null) { // if (!visitor.visit(type)) { // return false; // } // } // return true; // } // /** * Tests whether the two methods are erasure-equivalent. * @param method the first method * @param methodName the name of the second method * @param parameters the parameters of the second parameters * @return return <code>true if the two bindings are equal * @deprecated use {@link #isSubsignature(IMethodBinding, IMethodBinding)} */ //TODO: rename to isErasureEquivalentMethod and change to two IMethodBinding parameters @Deprecated public boolean isEqualMethod(final IMethodBinding method, final String methodName, final ITypeBinding[] parameters) { if (!method.getName().equals(methodName)) return false; final ITypeBinding[] methodParameters= method.getParameterTypes(); if (methodParameters.length != parameters.length) return false; for (int i= 0; i < parameters.length; i++) { if (!equals(methodParameters[i].getErasure(), parameters[i].getErasure())) return false; } //Can't use this fix, since some clients assume that this method tests erasure equivalence: // if (method.getTypeParameters().length == 0) { // //a method without type parameters cannot be overridden by one that declares type parameters -> can be exact here // for (int i= 0; i < parameters.length; i++) { // if ( ! (equals(methodParameters[i], parameters[i]) // || equals(methodParameters[i].getErasure(), parameters[i]))) // subsignature // return false; // } // } else { // //this will find all overridden methods, but may generate false positives in some cases: // for (int i= 0; i < parameters.length; i++) { // if (!equals(methodParameters[i].getErasure(), parameters[i].getErasure())) // return false; // } // } return true; } /** * @param overriding overriding method (m1) * @param overridden overridden method (m2) * @return <code>true iff the method m1 is a subsignature of the method m2. * This is one of the requirements for m1 to override m2. * Accessibility and return types are not taken into account. * Note that subsignature is <em>not symmetric! */ public boolean isSubsignature(final IMethodBinding overriding, final IMethodBinding overridden) { //TODO: use IMethodBinding#isSubsignature(..) once it is tested and fixed (only erasure of m1's parameter types, considering type variable counts, doing type variable substitution if (!overriding.getName().equals(overridden.getName())) return false; final ITypeBinding[] m1Params= overriding.getParameterTypes(); final ITypeBinding[] m2Params= overridden.getParameterTypes(); if (m1Params.length != m2Params.length) return false; final ITypeBinding[] m1TypeParams= overriding.getTypeParameters(); final ITypeBinding[] m2TypeParams= overridden.getTypeParameters(); if (m1TypeParams.length != m2TypeParams.length && m1TypeParams.length != 0) //non-generic m1 can override a generic m2 return false; //m1TypeParameters.length == (m2TypeParameters.length || 0) if (m2TypeParams.length != 0) { //Note: this branch does not 100% adhere to the spec and may report some false positives. // Full compliance would require major duplication of compiler code. //Compare type parameter bounds: for (int i= 0; i < m1TypeParams.length; i++) { // loop over m1TypeParams, which is either empty, or equally long as m2TypeParams final Set m1Bounds= getTypeBoundsForSubsignature(m1TypeParams[i]); final Set m2Bounds= getTypeBoundsForSubsignature(m2TypeParams[i]); if (! m1Bounds.equals(m2Bounds)) return false; } //Compare parameter types: if (equals(m2Params, m1Params)) return true; for (int i= 0; i < m1Params.length; i++) { ITypeBinding m1Param= m1Params[i]; if (containsTypeVariables(m1Param)) m1Param= m1Param.getErasure(); // try to achieve effect of "rename type variables" else if (m1Param.isRawType()) m1Param= m1Param.getTypeDeclaration(); if (! (equals(m1Param, m2Params[i].getErasure()))) // can erase m2 return false; } return true; } else { // m1TypeParams.length == m2TypeParams.length == 0 if (equals(m1Params, m2Params)) return true; for (int i= 0; i < m1Params.length; i++) { ITypeBinding m1Param= m1Params[i]; if (m1Param.isRawType()) m1Param= m1Param.getTypeDeclaration(); if (! (equals(m1Param, m2Params[i].getErasure()))) // can erase m2 return false; } return true; } } private boolean containsTypeVariables(final ITypeBinding type) { if (type.isTypeVariable()) return true; if (type.isArray()) return containsTypeVariables(type.getElementType()); if (type.isCapture()) return containsTypeVariables(type.getWildcard()); if (type.isParameterizedType()) return containsTypeVariables(type.getTypeArguments()); if (type.isTypeVariable()) return containsTypeVariables(type.getTypeBounds()); if (type.isWildcardType() && type.getBound() != null) return containsTypeVariables(type.getBound()); return false; } private boolean containsTypeVariables(final ITypeBinding[] types) { for (int i= 0; i < types.length; i++) if (containsTypeVariables(types[i])) return true; return false; } private Set getTypeBoundsForSubsignature(final ITypeBinding typeParameter) { final ITypeBinding[] typeBounds= typeParameter.getTypeBounds(); final int count= typeBounds.length; if (count == 0) return Collections.EMPTY_SET; final Set result= new HashSet(typeBounds.length); for (int i= 0; i < typeBounds.length; i++) { final ITypeBinding bound= typeBounds[i]; if ("java.lang.Object".equals(typeBounds[0].getQualifiedName())) //$NON-NLS-1$ continue; else if (containsTypeVariables(bound)) result.add(bound.getErasure()); // try to achieve effect of "rename type variables" else if (bound.isRawType()) result.add(bound.getTypeDeclaration()); else result.add(bound); } return result; } /** * @param method * @param methodName * @param parameters * @return <code>true iff the method * m1 (with name <code>methodName and method parameters parameters) * is a subsignature of the method <code>m2. Accessibility and return types are not taken into account. */ public boolean isEqualMethod(final IMethodBinding method, final String methodName, final String[] parameters) { if (!method.getName().equals(methodName)) return false; final ITypeBinding[] methodParameters= method.getParameterTypes(); if (methodParameters.length != parameters.length) return false; String first, second; int index; for (int i= 0; i < parameters.length; i++) { first= parameters[i]; index= first.indexOf('<'); if (index > 0) first= first.substring(0, index); second= methodParameters[i].getErasure().getQualifiedName(); index= second.indexOf('<'); if (index > 0) second= second.substring(0, index); if (!first.equals(second)) return false; } return true; } /** * Finds a type binding for a given fully qualified type in the hierarchy of a type. * Returns <code>null if no type binding is found. * @param hierarchyType the binding representing the hierarchy * @param fullyQualifiedTypeName the fully qualified name to search for * @return the type binding */ public ITypeBinding findTypeInHierarchy(final ITypeBinding hierarchyType, final String fullyQualifiedTypeName) { if (hierarchyType.isArray() || hierarchyType.isPrimitive()) { return null; } if (fullyQualifiedTypeName.equals(hierarchyType.getQualifiedName())) { return hierarchyType; } final ITypeBinding superClass= hierarchyType.getSuperclass(); if (superClass != null) { final ITypeBinding res= findTypeInHierarchy(superClass, fullyQualifiedTypeName); if (res != null) { return res; } } final ITypeBinding[] superInterfaces= hierarchyType.getInterfaces(); for (int i= 0; i < superInterfaces.length; i++) { final ITypeBinding res= findTypeInHierarchy(superInterfaces[i], fullyQualifiedTypeName); if (res != null) { return res; } } return null; } /** * Returns the binding of the variable written in an Assignment. * @param assignment The assignment * @return The binding or <code>null if no bindings are available. */ public IVariableBinding getAssignedVariable(final Assignment assignment) { final Expression leftHand = assignment.getLeftHandSide(); switch (leftHand.getNodeType()) { case ASTNode.SIMPLE_NAME: return (IVariableBinding) ((SimpleName) leftHand).resolveBinding(); case ASTNode.QUALIFIED_NAME: return (IVariableBinding) ((QualifiedName) leftHand).getName().resolveBinding(); case ASTNode.FIELD_ACCESS: return ((FieldAccess) leftHand).resolveFieldBinding(); case ASTNode.SUPER_FIELD_ACCESS: return ((SuperFieldAccess) leftHand).resolveFieldBinding(); default: return null; } } /** * Returns <code>true if the given type is a super type of a candidate. * <code>true is returned if the two type bindings are identical (TODO) * @param possibleSuperType the type to inspect * @param type the type whose super types are looked at * @return <code>true iff possibleSuperType is * a super type of <code>type or is equal to it */ public boolean isSuperType(final ITypeBinding possibleSuperType, final ITypeBinding type) { if (type.isArray() || type.isPrimitive()) { return false; } if (equals(type, possibleSuperType)) { return true; } final ITypeBinding superClass= type.getSuperclass(); if (superClass != null) { if (isSuperType(possibleSuperType, superClass)) { return true; } } if (possibleSuperType.isInterface()) { final ITypeBinding[] superInterfaces= type.getInterfaces(); for (int i= 0; i < superInterfaces.length; i++) { if (isSuperType(possibleSuperType, superInterfaces[i])) { return true; } } } return false; } /** * Finds the compilation unit where the type of the given <code>ITypeBinding is defined, * using the class path defined by the given Java project. Returns <code>null * if no compilation unit is found (e.g. type binding is from a binary type) * @param typeBinding the type binding to search for * @param project the project used as a scope * @return the compilation unit containing the type * @throws JavaModelException if an errors occurs in the Java model */ public ICompilationUnit findCompilationUnit(final ITypeBinding typeBinding) throws JavaModelException { final IJavaElement type= typeBinding.getJavaElement(); if (type instanceof IType) return ((IType) type).getCompilationUnit(); else return null; } /** * Finds a method for the given <code>IMethodBinding. Returns * <code>null if the type doesn't contain a corresponding method. * @param method the method to find * @param type the type to look in * @return the corresponding IMethod or <code>null * @throws JavaModelException if an error occurs in the Java model */ public IMethod findMethod(IMethodBinding method, final IType type) throws JavaModelException { method= method.getMethodDeclaration(); final IMethod[] candidates= type.getMethods(); for (int i= 0; i < candidates.length; i++) { final IMethod candidate= candidates[i]; if (candidate.getElementName().equals(method.getName()) && sameParameters(method, candidate)) { return candidate; } } return null; } //---- Helper methods to convert a method --------------------------------------------- private boolean sameParameters(final IMethodBinding method, final IMethod candidate) throws JavaModelException { final ITypeBinding[] methodParamters= method.getParameterTypes(); final String[] candidateParameters= candidate.getParameterTypes(); if (methodParamters.length != candidateParameters.length) return false; final IType scope= candidate.getDeclaringType(); for (int i= 0; i < methodParamters.length; i++) { final ITypeBinding methodParameter= methodParamters[i]; final String candidateParameter= candidateParameters[i]; if (!sameParameter(methodParameter, candidateParameter, scope)) return false; } return true; } private boolean sameParameter(ITypeBinding type, String candidate, final IType scope) throws JavaModelException { if (type.getDimensions() != Signature.getArrayCount(candidate)) return false; // Normalizes types if (type.isArray()) type= type.getElementType(); candidate= Signature.getElementType(candidate); if ((Signature.getTypeSignatureKind(candidate) == Signature.BASE_TYPE_SIGNATURE) != type.isPrimitive()) { return false; } if (type.isPrimitive() || type.isTypeVariable()) { return type.getName().equals(Signature.toString(candidate)); } else { // normalize (quick hack until binding.getJavaElement works) candidate= Signature.getTypeErasure(candidate); type= type.getErasure(); final String shortName = Signature.toString(candidate); if (candidate.charAt(Signature.getArrayCount(candidate)) == Signature.C_RESOLVED) { return shortName.equals(getFullyQualifiedName(type)); } else { final String[][] qualifiedCandidates= resolveType(shortName); if (qualifiedCandidates == null || qualifiedCandidates.length == 0) return false; final String packageName= type.getPackage().isUnnamed() ? "" : type.getPackage().getName(); //$NON-NLS-1$ final String typeName= getTypeQualifiedName(type); for (int i= 0; i < qualifiedCandidates.length; i++) { final String[] qualifiedCandidate= qualifiedCandidates[i]; if ( qualifiedCandidate[0].equals(packageName) && qualifiedCandidate[1].equals(typeName)) return true; } } } return false; } /** * @param shortName * @return */ private String[][] resolveType(final String shortName) { final List<String> types = new LinkedList<String>(); try { for (final ICompilationUnit unit : units) { for (final IType type : unit.getTypes()) { if (type.getFullyQualifiedName().endsWith("." + shortName)) { types.add(type.getFullyQualifiedName()); } } //add some package final IPackageDeclaration[] packages = unit.getPackageDeclarations(); for (final IPackageDeclaration p : packages) { types.add(p.getElementName() + "." + shortName); } //add also types from import declaration final IImportDeclaration[] imports = unit.getImports(); for (final IImportDeclaration decl : imports) { String className = null; final String name = decl.getElementName(); if (decl.isOnDemand()) { className = name.substring(0, name.length() - 1) + shortName ; } else if (name.endsWith(shortName)){ className = name; } if (className != null) { types.add(className); } } } } catch (final JavaModelException e) { e.printStackTrace(); } final String[][] candidates = new String[types.size()][]; for (int i = 0; i < types.size(); i++) { final String className = types.get(i); final int index = className.lastIndexOf('.'); candidates[i] = new String[2]; if (index > -1) { candidates[i][0] = className.substring(0, index); candidates[i][1] = className.substring(index + 1); } else { candidates[i][0] = ""; candidates[i][1] = className; } } return candidates; } /** * Normalizes a type binding received from an expression to a type binding that can be used in a declaration signature. * Anonymous types are normalized, to the super class or interface. For null or void bindings * <code>null is returned. * @param binding the binding to normalize * @return the normalized binding */ public ITypeBinding normalizeTypeBinding(final ITypeBinding binding) { if (binding != null && !binding.isNullType() && !isVoidType(binding)) { if (binding.isAnonymous()) { final ITypeBinding[] baseBindings= binding.getInterfaces(); if (baseBindings.length > 0) { return baseBindings[0]; } return binding.getSuperclass(); } if (binding.isCapture()) { return binding.getWildcard(); } return binding; } return null; } public boolean isVoidType(final ITypeBinding binding) { return "void".equals(binding.getName()); //$NON-NLS-1$ } /** * Normalizes the binding so that it can be used as a type inside a declaration * (e.g. variable declaration, method return type, parameter type, ...). For * null bindings Object is returned. * @param binding binding to normalize * @param ast current ast * * @return the normalized type to be used in declarations */ public ITypeBinding normalizeForDeclarationUse(ITypeBinding binding, final AST ast) { if (binding.isNullType()) return ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ if (binding.isPrimitive()) return binding; binding= normalizeTypeBinding(binding); if (binding == null || !binding.isWildcardType()) return binding; if (binding.isUpperbound()) { return binding.getBound(); } else { return ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ } } /** * Returns the type binding of the node's parent type declaration. * @param node * @return the type binding of the node's parent type declaration */ public ITypeBinding getBindingOfParentType(ASTNode node) { while (node != null) { if (node instanceof AbstractTypeDeclaration) { return ((AbstractTypeDeclaration) node).resolveBinding(); } else if (node instanceof AnonymousClassDeclaration) { return ((AnonymousClassDeclaration) node).resolveBinding(); } node= node.getParent(); } return null; } /** * Returns the type binding of the node's type context or null if the node is an annotation, type parameter or super type declaration of a tope level type. * The result of this method is equal to the result of {@link #getBindingOfParentType(ASTNode)} for nodes in the type's body. * @param node * @return the type binding of the node's parent type context */ public ITypeBinding getBindingOfParentTypeContext(ASTNode node) { StructuralPropertyDescriptor lastLocation= null; while (node != null) { if (node instanceof AbstractTypeDeclaration) { final AbstractTypeDeclaration decl= (AbstractTypeDeclaration) node; if (lastLocation == decl.getBodyDeclarationsProperty()) { return decl.resolveBinding(); } else if (decl instanceof EnumDeclaration && lastLocation == EnumDeclaration.ENUM_CONSTANTS_PROPERTY) { return decl.resolveBinding(); } } else if (node instanceof AnonymousClassDeclaration) { return ((AnonymousClassDeclaration) node).resolveBinding(); } lastLocation= node.getLocationInParent(); node= node.getParent(); } return null; } public String getRawName(final ITypeBinding binding) { final String name= binding.getName(); if (binding.isParameterizedType() || binding.isGenericType()) { final int idx= name.indexOf('<'); if (idx != -1) { return name.substring(0, idx); } } return name; } public String getRawQualifiedName(final ITypeBinding binding) { final String EMPTY= ""; //$NON-NLS-1$ if (binding.isAnonymous() || binding.isLocal()) { return EMPTY; } if (binding.isPrimitive() || binding.isNullType() || binding.isTypeVariable()) { return binding.getName(); } if (binding.isArray()) { final String elementTypeQualifiedName = getRawQualifiedName(binding.getElementType()); if (elementTypeQualifiedName.length() != 0) { final StringBuffer stringBuffer= new StringBuffer(elementTypeQualifiedName); stringBuffer.append('[').append(']'); return stringBuffer.toString(); } else { return EMPTY; } } if (binding.isMember()) { final String outerName= getRawQualifiedName(binding.getDeclaringClass()); if (outerName.length() > 0) { final StringBuffer buffer= new StringBuffer(); buffer.append(outerName); buffer.append('.'); buffer.append(getRawName(binding)); return buffer.toString(); } else { return EMPTY; } } else if (binding.isTopLevel()) { final IPackageBinding packageBinding= binding.getPackage(); final StringBuffer buffer= new StringBuffer(); if (packageBinding != null && packageBinding.getName().length() > 0) { buffer.append(packageBinding.getName()).append('.'); } buffer.append(getRawName(binding)); return buffer.toString(); } return EMPTY; } /** * Tests if the given node is a declaration, not a instance of a generic type, method or field. * Declarations can be found in AST with CompilationUnit.findDeclaringNode * @param binding binding to test * @return returns <code>true if the binding is a declaration binding */ public boolean isDeclarationBinding(final IBinding binding) { switch (binding.getKind()) { case IBinding.TYPE: return ((ITypeBinding) binding).getTypeDeclaration() == binding; case IBinding.VARIABLE: return ((IVariableBinding) binding).getVariableDeclaration() == binding; case IBinding.METHOD: return ((IMethodBinding) binding).getMethodDeclaration() == binding; } return true; } public IBinding getDeclaration(final IBinding binding) { switch (binding.getKind()) { case IBinding.TYPE: return ((ITypeBinding) binding).getTypeDeclaration(); case IBinding.VARIABLE: return ((IVariableBinding) binding).getVariableDeclaration(); case IBinding.METHOD: return ((IMethodBinding) binding).getMethodDeclaration(); } return binding; } /** * @param candidates the candidates * @param overridable the overriding method * @return returns <code>true> if the overriding method overrides a candidate * @deprecated Need to review: Use {@link #isSubsignature(IMethodBinding, IMethodBinding)} if the two bindings * are in the same hierarchy (directly overrides each other), or {@link #findMethodInHierarchy(ITypeBinding, String, ITypeBinding[])} * else. */ @Deprecated public boolean containsSignatureEquivalentConstructor(final IMethodBinding[] candidates, final IMethodBinding overridable) { for (int index= 0; index < candidates.length; index++) { if (isSignatureEquivalentConstructor(candidates[index], overridable)) return true; } return false; } private boolean isSignatureEquivalentConstructor(final IMethodBinding overridden, final IMethodBinding overridable) { if (!overridden.isConstructor() || !overridable.isConstructor()) return false; if (overridden.isDefaultConstructor()) return false; return areSubTypeCompatible(overridden, overridable); } /** * @param overridden the overridden method * @param overridable the overriding method * @return returns <code>true if the overriding method overrrides the overridden * @deprecated Need to review: Use {@link #isSubsignature(IMethodBinding, IMethodBinding)} if the two bindings * are in the same hierarchy (directly overrides each other), or {@link #findMethodInHierarchy(ITypeBinding, String, ITypeBinding[])} * else. */ @Deprecated public boolean areOverriddenMethods(final IMethodBinding overridden, final IMethodBinding overridable) { if (!overridden.getName().equals(overridable.getName())) return false; return areSubTypeCompatible(overridden, overridable); } private boolean areSubTypeCompatible(final IMethodBinding overridden, final IMethodBinding overridable) { if (overridden.getParameterTypes().length != overridable.getParameterTypes().length) return false; final ITypeBinding overriddenReturn= overridden.getReturnType(); final ITypeBinding overridableReturn= overridable.getReturnType(); if (overriddenReturn == null || overridableReturn == null) return false; if (!overriddenReturn.getErasure().isSubTypeCompatible(overridableReturn.getErasure())) return false; final ITypeBinding[] overriddenTypes= overridden.getParameterTypes(); final ITypeBinding[] overridableTypes= overridable.getParameterTypes(); Assert.isTrue(overriddenTypes.length == overridableTypes.length); for (int index= 0; index < overriddenTypes.length; index++) { final ITypeBinding overridableErasure= overridableTypes[index].getErasure(); final ITypeBinding overriddenErasure= overriddenTypes[index].getErasure(); if (!overridableErasure.isSubTypeCompatible(overriddenErasure) || !overridableErasure.getKey().equals(overriddenErasure.getKey())) return false; } final ITypeBinding[] overriddenExceptions= overridden.getExceptionTypes(); final ITypeBinding[] overridableExceptions= overridable.getExceptionTypes(); boolean checked= false; for (int index= 0; index < overriddenExceptions.length; index++) { checked= false; for (int offset= 0; offset < overridableExceptions.length; offset++) { if (overriddenExceptions[index].isSubTypeCompatible(overridableExceptions[offset])) checked= true; } if (!checked) return false; } return true; } /** * @param type * @return */ public IType findType(final ITypeBinding origin) { ITypeBinding type = origin; if (origin != null && type.isArray()) { type = type.getComponentType(); } if (type == null) { return null; } //attempt to resolve by full qualified name. final String name = type.getQualifiedName(); final IType result = getTypeByFullQuelifiedName(name); if (result != null) { return result; } //attempt to resolve by short name final String[][] qualifiedCandidates= resolveType(name); if (qualifiedCandidates == null || qualifiedCandidates.length == 0) return null; for (int i= 0; i < qualifiedCandidates.length; i++) { final String[] qualifiedCandidate= qualifiedCandidates[i]; String fullName = qualifiedCandidate[1]; if (qualifiedCandidate[0].length() > 0) { fullName = qualifiedCandidate[0] + "." + fullName; } final IType t = getTypeByFullQuelifiedName(fullName); if (t != null) { return t; } } return null; } /** * @param name * @return */ private IType getTypeByFullQuelifiedName(final String name) { try { for (final ICompilationUnit unit : units) { final IType[] types = unit.getAllTypes(); for (final IType t : types) { if (t.getFullyQualifiedName().equals(name)) { return t; } } } } catch (final JavaModelException e) { e.printStackTrace(); } return null; } /** * @param binding * @return * @throws JavaModelException */ public IPackageDeclaration findPackageDeclaration(final IPackageBinding binding) throws JavaModelException { final IPackageBinding pckg = binding; for (final ICompilationUnit unit : units) { final IPackageDeclaration[] packages = unit.getPackageDeclarations(); for (final IPackageDeclaration p : packages) { if (p.getElementName().equals(pckg.getName())) { return p; } } } return null; } /** * @param binding * @return */ public IJavaElement findJavaElement(final IBinding binding) { if (binding == null) { return null; } try { if (binding instanceof ITypeBinding) { return findType((ITypeBinding) binding); } else if (binding instanceof IVariableBinding) { final IVariableBinding var = (IVariableBinding) binding; if (var.isField()) { return findField(var); } else { final IMethod m = findMethod(var.getDeclaringMethod()); if (m != null){ //check parameters for (final ILocalVariable el : m.getParameters()) { if (el.getElementName().equals(var.getName())) { return el; } } //check children for (final IJavaElement el : m.getChildren()) { if (el.getElementName().equals(var.getName())) { return el; } } //if not found check all members of given compilation unit // ICompilationUnit unit = getType(var.getType()).getCompilationUnit(); // JavaDomUtils.getAllNodes(unit); } } } else if (binding instanceof IMethodBinding) { return findMethod((IMethodBinding) binding); } else if (binding instanceof IPackageBinding) { return findPackageDeclaration((IPackageBinding) binding); } } catch (final JavaModelException e) { e.printStackTrace(); } return null; } /** * @param binding * @return * @throws JavaModelException */ private IMethod findMethod(final IMethodBinding binding) { try { final IType type = findType(binding.getDeclaringClass()); if (type != null) { final IMethod m = findMethod(binding, type); return m; } } catch (final JavaModelException e) { e.printStackTrace(); return null; } return null; } /** * @param binding * @return */ private IField findField(final IVariableBinding binding) { try { if (binding.isField()) { final IType type = findType(binding.getDeclaringClass()); if (type != null) { for (final IJavaElement e : type.getChildren()) { if (e instanceof IField && ((IField) e).getElementName().equals(binding.getName())) { return ((IField) e); } } } } } catch (final JavaModelException e) { e.printStackTrace(); } return null; } }