1 package com.stateofflow.eclipse.metrics.bindings;
2
3 import java.util.Collections;
4 import java.util.HashSet;
5 import java.util.Set;
6
7 import org.eclipse.core.runtime.Assert;
8 import org.eclipse.jdt.core.dom.IMethodBinding;
9 import org.eclipse.jdt.core.dom.ITypeBinding;
10 import org.eclipse.jdt.core.dom.Modifier;
11
12 public class Bindings {
13 private static IMethodBinding findDeclaredMethodInInterfaceHierarchy(final ITypeBinding type, final IMethodBinding overriding) {
14 final ITypeBinding[] interfaces = type.getInterfaces();
15 for (final ITypeBinding interface1 : interfaces) {
16 final IMethodBinding res = findOverriddenMethodInHierarchy(interface1, overriding);
17 if (res != null) {
18 return res;
19 }
20 }
21 return null;
22 }
23
24 public static IMethodBinding findOverriddenMethod(final IMethodBinding overriding) {
25 if (isStaticallyBindable(overriding)) {
26 return null;
27 }
28
29 final IMethodBinding overridden = findOverriddenMethodInSuperclassHeirarchy(overriding);
30 return overridden != null ? overridden : findDeclaredMethodInInterfaceHierarchy(overriding.getDeclaringClass(), overriding);
31 }
32
33 private static boolean isStaticallyBindable(final IMethodBinding overriding) {
34 final int modifiers = overriding.getModifiers();
35 return Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || overriding.isConstructor();
36 }
37
38 private static IMethodBinding findOverriddenMethodInHierarchy(final ITypeBinding type, final IMethodBinding binding) {
39 IMethodBinding method = findOverriddenMethodInType(type, binding);
40 if (method != null) {
41 return method;
42 }
43 final ITypeBinding superClass = type.getSuperclass();
44 if (superClass != null) {
45 method = findOverriddenMethodInHierarchy(superClass, binding);
46 if (method != null) {
47 return method;
48 }
49 }
50 return findDeclaredMethodInInterfaceHierarchy(type, binding);
51 }
52
53 private static IMethodBinding findOverriddenMethodInSuperclassHeirarchy(final IMethodBinding overriding) {
54 final ITypeBinding type = overriding.getDeclaringClass();
55 if (type.getSuperclass() != null) {
56 final IMethodBinding res = findOverriddenMethodInHierarchy(type.getSuperclass(), overriding);
57 if (res != null && !Modifier.isPrivate(res.getModifiers())) {
58 if (VisibilityAnalyser.isVisibleInHierarchy(res, overriding.getDeclaringClass().getPackage())) {
59 return res;
60 }
61 }
62 }
63
64 return null;
65 }
66
67 private static IMethodBinding findOverriddenMethodInType(final ITypeBinding type, final IMethodBinding method) {
68 final IMethodBinding[] methods = type.getDeclaredMethods();
69 for (final IMethodBinding method2 : methods) {
70 if (isSubsignature(method, method2)) {
71 return method2;
72 }
73 }
74 return null;
75 }
76
77 private static Set<ITypeBinding> getNonTrivialNormalisedTypeBounds(final ITypeBinding[] typeBounds) {
78 final Set<ITypeBinding> result = new HashSet<ITypeBinding>(typeBounds.length);
79 for (final ITypeBinding bound : typeBounds) {
80 result.add(normaliseTypeBound(bound));
81 }
82 return result;
83 }
84
85 private static Set<ITypeBinding> getNormalisedTypeBounds(final ITypeBinding[] typeBounds) {
86 return typeBounds.length == 0 || "java.lang.Object".equals(typeBounds[0].getQualifiedName()) ? Collections.<ITypeBinding> emptySet() : getNonTrivialNormalisedTypeBounds(typeBounds);
87 }
88
89 private static Set<ITypeBinding> getTypeBoundsForSubsignature(final ITypeBinding typeParameter) {
90 final ITypeBinding[] typeBounds = typeParameter.getTypeBounds();
91 final int count = typeBounds.length;
92 if (count == 0) {
93 return Collections.<ITypeBinding> emptySet();
94 }
95
96 return getNormalisedTypeBounds(typeBounds);
97 }
98
99 private static boolean isSubsignature(final IMethodBinding overriding, final IMethodBinding overridden) {
100 if (!overriding.getName().equals(overridden.getName())) {
101 return false;
102 }
103
104 final ITypeBinding[] m1Params = overriding.getParameterTypes();
105 final ITypeBinding[] m2Params = overridden.getParameterTypes();
106 if (m1Params.length != m2Params.length) {
107 return false;
108 }
109
110 final ITypeBinding[] m1TypeParams = overriding.getTypeParameters();
111 final ITypeBinding[] m2TypeParams = overridden.getTypeParameters();
112 return m1TypeParams.length == m2TypeParams.length && areAllTypeParametersBoundsEqual(m1TypeParams, m2TypeParams) && equalsErased(m1Params, m2Params);
113 }
114
115 private static boolean areAllTypeParametersBoundsEqual(final ITypeBinding[] m1TypeParams, final ITypeBinding[] m2TypeParams) {
116 Assert.isTrue(m1TypeParams.length == m2TypeParams.length);
117
118 for (int i = 0; i < m1TypeParams.length; i++) {
119 if (!getTypeBoundsForSubsignature(m1TypeParams[i]).equals(getTypeBoundsForSubsignature(m2TypeParams[i]))) {
120 return false;
121 }
122 }
123
124 return true;
125 }
126
127 private static boolean equalsErased(final ITypeBinding[] m1Params, final ITypeBinding[] m2Params) {
128 if (Equality.equals(m1Params, m2Params)) {
129 return true;
130 }
131 for (int i = 0; i < m1Params.length; i++) {
132 if (!(Equality.equals(normaliseTypeBound(m1Params[i]), m2Params[i].getErasure()))) {
133 return false;
134 }
135 }
136 return true;
137 }
138
139 private static ITypeBinding normaliseTypeBound(final ITypeBinding bound) {
140 if (TypeVariableAnalyser.containsTypeVariables(bound)) {
141 return bound.getErasure(); }
143 if (bound.isRawType()) {
144 return bound.getTypeDeclaration();
145 }
146 return bound;
147 }
148 }
149