KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > util > MethodOverrideTester


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.internal.corext.util;
13
14 import java.util.HashMap JavaDoc;
15 import java.util.Map JavaDoc;
16
17 import org.eclipse.core.runtime.Assert;
18
19 import org.eclipse.jdt.core.Flags;
20 import org.eclipse.jdt.core.IMember;
21 import org.eclipse.jdt.core.IMethod;
22 import org.eclipse.jdt.core.IType;
23 import org.eclipse.jdt.core.ITypeHierarchy;
24 import org.eclipse.jdt.core.ITypeParameter;
25 import org.eclipse.jdt.core.JavaModelException;
26 import org.eclipse.jdt.core.Signature;
27
28
29 public class MethodOverrideTester {
30     private static class Substitutions {
31         
32         public static final Substitutions EMPTY_SUBST= new Substitutions();
33         
34         private HashMap JavaDoc fMap;
35         
36         public Substitutions() {
37             fMap= null;
38         }
39         
40         public void addSubstitution(String JavaDoc typeVariable, String JavaDoc substitution, String JavaDoc erasure) {
41             if (fMap == null) {
42                 fMap= new HashMap JavaDoc(3);
43             }
44             fMap.put(typeVariable, new String JavaDoc[] { substitution, erasure });
45         }
46         
47         private String JavaDoc[] getSubstArray(String JavaDoc typeVariable) {
48             if (fMap != null) {
49                 return (String JavaDoc[]) fMap.get(typeVariable);
50             }
51             return null;
52         }
53                         
54         public String JavaDoc getSubstitution(String JavaDoc typeVariable) {
55             String JavaDoc[] subst= getSubstArray(typeVariable);
56             if (subst != null) {
57                 return subst[0];
58             }
59             return null;
60         }
61         
62         public String JavaDoc getErasure(String JavaDoc typeVariable) {
63             String JavaDoc[] subst= getSubstArray(typeVariable);
64             if (subst != null) {
65                 return subst[1];
66             }
67             return null;
68         }
69     }
70     
71     private final IType fFocusType;
72     private final ITypeHierarchy fHierarchy;
73     
74     private Map JavaDoc /* <IMethod, Substitutions> */ fMethodSubstitutions;
75     private Map JavaDoc /* <IType, Substitutions> */ fTypeVariableSubstitutions;
76             
77     public MethodOverrideTester(IType focusType, ITypeHierarchy hierarchy) {
78         if (focusType == null || hierarchy == null) {
79             throw new IllegalArgumentException JavaDoc();
80         }
81         fFocusType= focusType;
82         fHierarchy= hierarchy;
83         fTypeVariableSubstitutions= null;
84         fMethodSubstitutions= null;
85     }
86     
87     public IType getFocusType() {
88         return fFocusType;
89     }
90     
91     public ITypeHierarchy getTypeHierarchy() {
92         return fHierarchy;
93     }
94     
95     /**
96      * Finds the method that declares the given method. A declaring method is the 'original' method declaration that does
97      * not override nor implement a method. <code>null</code> is returned it the given method does not override
98      * a method. When searching, super class are examined before implemented interfaces.
99      * @param testVisibility If true the result is tested on visibility. Null is returned if the method is not visible.
100      * @throws JavaModelException
101      */

102     public IMethod findDeclaringMethod(IMethod overriding, boolean testVisibility) throws JavaModelException {
103         IMethod result= null;
104         IMethod overridden= findOverriddenMethod(overriding, testVisibility);
105         while (overridden != null) {
106             result= overridden;
107             overridden= findOverriddenMethod(result, testVisibility);
108         }
109         return result;
110     }
111     
112     /**
113      * Finds the method that is overridden by the given method.
114      * First the super class is examined and then the implemented interfaces.
115      * @param testVisibility If true the result is tested on visibility. Null is returned if the method is not visible.
116      * @throws JavaModelException
117      */

118     public IMethod findOverriddenMethod(IMethod overriding, boolean testVisibility) throws JavaModelException {
119         int flags= overriding.getFlags();
120         if (Flags.isPrivate(flags) || Flags.isStatic(flags) || overriding.isConstructor()) {
121             return null;
122         }
123         
124         IType type= overriding.getDeclaringType();
125         IType superClass= fHierarchy.getSuperclass(type);
126         if (superClass != null) {
127             IMethod res= findOverriddenMethodInHierarchy(superClass, overriding);
128             if (res != null && !Flags.isPrivate(res.getFlags())) {
129                 if (!testVisibility || JavaModelUtil.isVisibleInHierarchy(res, type.getPackageFragment())) {
130                     return res;
131                 }
132             }
133         }
134         if (!overriding.isConstructor()) {
135             IType[] interfaces= fHierarchy.getSuperInterfaces(type);
136             for (int i= 0; i < interfaces.length; i++) {
137                 IMethod res= findOverriddenMethodInHierarchy(interfaces[i], overriding);
138                 if (res != null) {
139                     return res; // methods from interfaces are always public and therefore visible
140
}
141             }
142         }
143         return null;
144     }
145     
146     /**
147      * Finds the directly overridden method in a type and its super types. First the super class is examined and then the implemented interfaces.
148      * With generics it is possible that 2 methods in the same type are overidden at the same time. In that case, the first overridden method found is returned.
149      * @param type The type to find methods in
150      * @param overriding The overriding method
151      * @return The first overridden method or <code>null</code> if no method is overridden
152      * @throws JavaModelException
153      */

154     public IMethod findOverriddenMethodInHierarchy(IType type, IMethod overriding) throws JavaModelException {
155         IMethod method= findOverriddenMethodInType(type, overriding);
156         if (method != null) {
157             return method;
158         }
159         IType superClass= fHierarchy.getSuperclass(type);
160         if (superClass != null) {
161             IMethod res= findOverriddenMethodInHierarchy(superClass, overriding);
162             if (res != null) {
163                 return res;
164             }
165         }
166         if (!overriding.isConstructor()) {
167             IType[] superInterfaces= fHierarchy.getSuperInterfaces(type);
168             for (int i= 0; i < superInterfaces.length; i++) {
169                 IMethod res= findOverriddenMethodInHierarchy(superInterfaces[i], overriding);
170                 if (res != null) {
171                     return res;
172                 }
173             }
174         }
175         return method;
176     }
177     
178     /**
179      * Finds an overridden method in a type. WWith generics it is possible that 2 methods in the same type are overidden at the same time.
180      * In that case the first overridden method found is returned.
181      * @param overriddenType The type to find methods in
182      * @param overriding The overriding method
183      * @return The first overridden method or <code>null</code> if no method is overridden
184      * @throws JavaModelException
185      */

186     public IMethod findOverriddenMethodInType(IType overriddenType, IMethod overriding) throws JavaModelException {
187         IMethod[] overriddenMethods= overriddenType.getMethods();
188         for (int i= 0; i < overriddenMethods.length; i++) {
189             if (isSubsignature(overriding, overriddenMethods[i])) {
190                 return overriddenMethods[i];
191             }
192         }
193         return null;
194     }
195     
196     /**
197      * Finds an overriding method in a type.
198      * @param overridingType The type to find methods in
199      * @param overridden The overridden method
200      * @return The overriding method or <code>null</code> if no method is overriding.
201      * @throws JavaModelException
202      */

203     public IMethod findOverridingMethodInType(IType overridingType, IMethod overridden) throws JavaModelException {
204         IMethod[] overridingMethods= overridingType.getMethods();
205         for (int i= 0; i < overridingMethods.length; i++) {
206             if (isSubsignature(overridingMethods[i], overridden)) {
207                 return overridingMethods[i];
208             }
209         }
210         return null;
211     }
212     
213     /**
214      * Tests if a method is a subsignature of another method.
215      * @param overriding overriding method (m1)
216      * @param overridden overridden method (m2)
217      * @return <code>true</code> iff the method <code>m1</code> is a subsignature of the method <code>m2</code>.
218      * This is one of the requirements for m1 to override m2.
219      * Accessibility and return types are not taken into account.
220      * Note that subsignature is <em>not</em> symmetric!
221      * @throws JavaModelException
222      */

223     public boolean isSubsignature(IMethod overriding, IMethod overridden) throws JavaModelException {
224         if (!overridden.getElementName().equals(overriding.getElementName())) {
225             return false;
226         }
227         int nParameters= overridden.getNumberOfParameters();
228         if (nParameters != overriding.getNumberOfParameters()) {
229             return false;
230         }
231         
232         if (!hasCompatibleTypeParameters(overriding, overridden)) {
233             return false;
234         }
235         
236         return nParameters == 0 || hasCompatibleParameterTypes(overriding, overridden);
237     }
238
239     private boolean hasCompatibleTypeParameters(IMethod overriding, IMethod overridden) throws JavaModelException {
240         ITypeParameter[] overriddenTypeParameters= overridden.getTypeParameters();
241         ITypeParameter[] overridingTypeParameters= overriding.getTypeParameters();
242         int nOverridingTypeParameters= overridingTypeParameters.length;
243         if (overriddenTypeParameters.length != nOverridingTypeParameters) {
244             return nOverridingTypeParameters == 0;
245         }
246         Substitutions overriddenSubst= getMethodSubstitions(overridden);
247         Substitutions overridingSubst= getMethodSubstitions(overriding);
248         for (int i= 0; i < nOverridingTypeParameters; i++) {
249             String JavaDoc erasure1= overriddenSubst.getErasure(overriddenTypeParameters[i].getElementName());
250             String JavaDoc erasure2= overridingSubst.getErasure(overridingTypeParameters[i].getElementName());
251             if (erasure1 == null || !erasure1.equals(erasure2)) {
252                 return false;
253             }
254             // comparing only the erasure is not really correct: Need to compare all bounds, that can be in different order
255
int nBounds= overriddenTypeParameters[i].getBounds().length;
256             if (nBounds > 1 && nBounds != overridingTypeParameters[i].getBounds().length) {
257                 return false;
258             }
259         }
260         return true;
261     }
262
263     private boolean hasCompatibleParameterTypes(IMethod overriding, IMethod overridden) throws JavaModelException {
264         String JavaDoc[] overriddenParamTypes= overridden.getParameterTypes();
265         String JavaDoc[] overridingParamTypes= overriding.getParameterTypes();
266         
267         String JavaDoc[] substitutedOverriding= new String JavaDoc[overridingParamTypes.length];
268         boolean testErasure= false;
269         
270         for (int i= 0; i < overridingParamTypes.length; i++) {
271             String JavaDoc overriddenParamSig= overriddenParamTypes[i];
272             String JavaDoc overriddenParamName= getSubstitutedTypeName(overriddenParamSig, overridden);
273             String JavaDoc overridingParamName= getSubstitutedTypeName(overridingParamTypes[i], overriding);
274             substitutedOverriding[i]= overridingParamName;
275             if (!overriddenParamName.equals(overridingParamName)) {
276                 testErasure= true;
277                 break;
278             }
279         }
280         if (testErasure) {
281             for (int i= 0; i < overridingParamTypes.length; i++) {
282                 String JavaDoc overriddenParamSig= overriddenParamTypes[i];
283                 String JavaDoc overriddenParamName= getErasedTypeName(overriddenParamSig, overridden);
284                 String JavaDoc overridingParamName= substitutedOverriding[i];
285                 if (overridingParamName == null)
286                     overridingParamName= getSubstitutedTypeName(overridingParamTypes[i], overriding);
287                 if (!overriddenParamName.equals(overridingParamName)) {
288                     return false;
289                 }
290             }
291         }
292         return true;
293     }
294     
295     private String JavaDoc getVariableSubstitution(IMember context, String JavaDoc variableName) throws JavaModelException {
296         IType type;
297         if (context instanceof IMethod) {
298             String JavaDoc subst= getMethodSubstitions((IMethod) context).getSubstitution(variableName);
299             if (subst != null) {
300                 return subst;
301             }
302             type= context.getDeclaringType();
303         } else {
304             type= (IType) context;
305         }
306         String JavaDoc subst= getTypeSubstitions(type).getSubstitution(variableName);
307         if (subst != null) {
308             return subst;
309         }
310         return variableName; // not a type variable
311
}
312     
313     private String JavaDoc getVariableErasure(IMember context, String JavaDoc variableName) throws JavaModelException {
314         IType type;
315         if (context instanceof IMethod) {
316             String JavaDoc subst= getMethodSubstitions((IMethod) context).getErasure(variableName);
317             if (subst != null) {
318                 return subst;
319             }
320             type= context.getDeclaringType();
321         } else {
322             type= (IType) context;
323         }
324         String JavaDoc subst= getTypeSubstitions(type).getErasure(variableName);
325         if (subst != null) {
326             return subst;
327         }
328         return variableName; // not a type variable
329
}
330     
331     /*
332      * Returns the substitutions for a method's type parameters
333      */

334     private Substitutions getMethodSubstitions(IMethod method) throws JavaModelException {
335         if (fMethodSubstitutions == null) {
336             fMethodSubstitutions= new LRUMap(3);
337         }
338         
339         Substitutions s= (Substitutions) fMethodSubstitutions.get(method);
340         if (s == null) {
341             ITypeParameter[] typeParameters= method.getTypeParameters();
342             if (typeParameters.length == 0) {
343                 s= Substitutions.EMPTY_SUBST;
344             } else {
345                 IType instantiatedType= method.getDeclaringType();
346                 s= new Substitutions();
347                 for (int i= 0; i < typeParameters.length; i++) {
348                     ITypeParameter curr= typeParameters[i];
349                     s.addSubstitution(curr.getElementName(), '+' + String.valueOf(i), getTypeParameterErasure(curr, instantiatedType));
350                 }
351             }
352             fMethodSubstitutions.put(method, s);
353         }
354         return s;
355     }
356     
357     /*
358      * Returns the substitutions for a type's type parameters
359      */

360     private Substitutions getTypeSubstitions(IType type) throws JavaModelException {
361         if (fTypeVariableSubstitutions == null) {
362             fTypeVariableSubstitutions= new HashMap JavaDoc();
363             computeSubstitutions(fFocusType, null, null);
364         }
365         Substitutions subst= (Substitutions) fTypeVariableSubstitutions.get(type);
366         if (subst == null) {
367             return Substitutions.EMPTY_SUBST;
368         }
369         return subst;
370     }
371     
372     private void computeSubstitutions(IType instantiatedType, IType instantiatingType, String JavaDoc[] typeArguments) throws JavaModelException {
373         Substitutions s= new Substitutions();
374         fTypeVariableSubstitutions.put(instantiatedType, s);
375         
376         ITypeParameter[] typeParameters= instantiatedType.getTypeParameters();
377         
378         if (instantiatingType == null) { // the focus type
379
for (int i= 0; i < typeParameters.length; i++) {
380                 ITypeParameter curr= typeParameters[i];
381                 // use star to make type variables different from type refs
382
s.addSubstitution(curr.getElementName(), '*' + curr.getElementName(), getTypeParameterErasure(curr, instantiatedType));
383             }
384         } else {
385             if (typeParameters.length == typeArguments.length) {
386                 for (int i= 0; i < typeParameters.length; i++) {
387                     ITypeParameter curr= typeParameters[i];
388                     String JavaDoc substString= getSubstitutedTypeName(typeArguments[i], instantiatingType); // substitute in the context of the instantiatingType
389
String JavaDoc erasure= getErasedTypeName(typeArguments[i], instantiatingType); // get the erasure from the type argument
390
s.addSubstitution(curr.getElementName(), substString, erasure);
391                 }
392             } else if (typeArguments.length == 0) { // raw type reference
393
for (int i= 0; i < typeParameters.length; i++) {
394                     ITypeParameter curr= typeParameters[i];
395                     String JavaDoc erasure= getTypeParameterErasure(curr, instantiatedType);
396                     s.addSubstitution(curr.getElementName(), erasure, erasure);
397                 }
398             } else {
399                 // code with errors
400
}
401         }
402         String JavaDoc superclassTypeSignature= instantiatedType.getSuperclassTypeSignature();
403         if (superclassTypeSignature != null) {
404             String JavaDoc[] superTypeArguments= Signature.getTypeArguments(superclassTypeSignature);
405             IType superclass= fHierarchy.getSuperclass(instantiatedType);
406             if (superclass != null && !fTypeVariableSubstitutions.containsKey(superclass)) {
407                 computeSubstitutions(superclass, instantiatedType, superTypeArguments);
408             }
409         }
410         String JavaDoc[] superInterfacesTypeSignature= instantiatedType.getSuperInterfaceTypeSignatures();
411         int nInterfaces= superInterfacesTypeSignature.length;
412         if (nInterfaces > 0) {
413             IType[] superInterfaces= fHierarchy.getSuperInterfaces(instantiatedType);
414             if (superInterfaces.length == nInterfaces) {
415                 for (int i= 0; i < nInterfaces; i++) {
416                     String JavaDoc[] superTypeArguments= Signature.getTypeArguments(superInterfacesTypeSignature[i]);
417                     IType superInterface= superInterfaces[i];
418                     if (!fTypeVariableSubstitutions.containsKey(superInterface)) {
419                         computeSubstitutions(superInterface, instantiatedType, superTypeArguments);
420                     }
421                 }
422             }
423         }
424     }
425     
426     private String JavaDoc getTypeParameterErasure(ITypeParameter typeParameter, IType context) throws JavaModelException {
427         String JavaDoc[] bounds= typeParameter.getBounds();
428         if (bounds.length > 0) {
429             return getSubstitutedTypeName(Signature.createTypeSignature(bounds[0], false), context);
430         }
431         return "Object"; //$NON-NLS-1$
432
}
433     
434
435     /**
436      * Translates the type signature to a 'normalized' type name where all variables are substituted for the given type or method context.
437      * The returned name contains only simple names and can be used to compare against other substituted type names
438      * @param typeSig The type signature to translate
439      * @param context The context for the substitution
440      * @return a type name
441      * @throws JavaModelException
442      */

443     private String JavaDoc getSubstitutedTypeName(String JavaDoc typeSig, IMember context) throws JavaModelException {
444         return internalGetSubstitutedTypeName(typeSig, context, false, new StringBuffer JavaDoc()).toString();
445     }
446     
447     private String JavaDoc getErasedTypeName(String JavaDoc typeSig, IMember context) throws JavaModelException {
448         return internalGetSubstitutedTypeName(typeSig, context, true, new StringBuffer JavaDoc()).toString();
449     }
450         
451     private StringBuffer JavaDoc internalGetSubstitutedTypeName(String JavaDoc typeSig, IMember context, boolean erasure, StringBuffer JavaDoc buf) throws JavaModelException {
452         int sigKind= Signature.getTypeSignatureKind(typeSig);
453         switch (sigKind) {
454             case Signature.BASE_TYPE_SIGNATURE:
455                 return buf.append(Signature.toString(typeSig));
456             case Signature.ARRAY_TYPE_SIGNATURE:
457                 internalGetSubstitutedTypeName(Signature.getElementType(typeSig), context, erasure, buf);
458                 for (int i= Signature.getArrayCount(typeSig); i > 0; i--) {
459                     buf.append('[').append(']');
460                 }
461                 return buf;
462             case Signature.CLASS_TYPE_SIGNATURE: {
463                 String JavaDoc erasureSig= Signature.getTypeErasure(typeSig);
464                 String JavaDoc erasureName= Signature.getSimpleName(Signature.toString(erasureSig));
465                 
466                 char ch= erasureSig.charAt(0);
467                 if (ch == Signature.C_RESOLVED) {
468                     buf.append(erasureName);
469                 } else if (ch == Signature.C_UNRESOLVED) { // could be a type variable
470
if (erasure) {
471                         buf.append(getVariableErasure(context, erasureName));
472                     } else {
473                         buf.append(getVariableSubstitution(context, erasureName));
474                     }
475                 } else {
476                     Assert.isTrue(false, "Unknown class type signature"); //$NON-NLS-1$
477
}
478                 if (!erasure) {
479                     String JavaDoc[] typeArguments= Signature.getTypeArguments(typeSig);
480                     if (typeArguments.length > 0) {
481                         buf.append('<');
482                         for (int i= 0; i < typeArguments.length; i++) {
483                             if (i > 0) {
484                                 buf.append(',');
485                             }
486                             internalGetSubstitutedTypeName(typeArguments[i], context, erasure, buf);
487                         }
488                         buf.append('>');
489                     }
490                 }
491                 return buf;
492             }
493             case Signature.TYPE_VARIABLE_SIGNATURE:
494                 String JavaDoc varName= Signature.toString(typeSig);
495                 if (erasure) {
496                     return buf.append(getVariableErasure(context, varName));
497                 } else {
498                     return buf.append(getVariableSubstitution(context, varName));
499                 }
500             case Signature.WILDCARD_TYPE_SIGNATURE: {
501                 buf.append('?');
502                 char ch= typeSig.charAt(0);
503                 if (ch == Signature.C_STAR) {
504                     return buf;
505                 } else if (ch == Signature.C_EXTENDS) {
506                     buf.append(" extends "); //$NON-NLS-1$
507
} else {
508                     buf.append(" super "); //$NON-NLS-1$
509
}
510                 return internalGetSubstitutedTypeName(typeSig.substring(1), context, erasure, buf);
511             }
512             case Signature.CAPTURE_TYPE_SIGNATURE:
513                 return internalGetSubstitutedTypeName(typeSig.substring(1), context, erasure, buf);
514             default:
515                 Assert.isTrue(false, "Unhandled type signature kind"); //$NON-NLS-1$
516
return buf;
517         }
518     }
519             
520 }
521
Popular Tags