diff --git a/src/main/java/spoon/reflect/factory/TypeFactory.java b/src/main/java/spoon/reflect/factory/TypeFactory.java index 738d161a1df..79b506bccca 100644 --- a/src/main/java/spoon/reflect/factory/TypeFactory.java +++ b/src/main/java/spoon/reflect/factory/TypeFactory.java @@ -356,7 +356,7 @@ public CtTypeParameterReference createReference(CtTypeParameter type) { ref.addAnnotation(ctAnnotation.clone()); } ref.setSimpleName(type.getSimpleName()); - + ref.setParent(type); return ref; } diff --git a/src/main/java/spoon/reflect/reference/CtTypeParameterReference.java b/src/main/java/spoon/reflect/reference/CtTypeParameterReference.java index a362abead54..d3f7ea178f1 100644 --- a/src/main/java/spoon/reflect/reference/CtTypeParameterReference.java +++ b/src/main/java/spoon/reflect/reference/CtTypeParameterReference.java @@ -72,11 +72,27 @@ public interface CtTypeParameterReference extends CtTypeReference { */ T setBoundingType(CtTypeReference superType); - // overriding the return type + /** + * Returns the {@link CtTypeParameter}, a {@link CtTypeParameter}, that declares the type parameter + * referenced or null if the reference is not in a context where such type parameter is declared. + * See also {@link #getTypeParameterDeclaration()} which has a different semantic. + */ @Override @DerivedProperty CtTypeParameter getDeclaration(); + /** + * Returns the declaration in the target type. In the following example, getTypeParameterDeclaration of T in Dog<T> returns the type parameter definition "X" (while {@link #getDeclaration()} returns the "T" of Cat). + *
+	 * class Dog<X>{}
+	 * class Cat<T> {
+	 * Dog<T> dog;
+	 * }
+	 * 
+ */ + @DerivedProperty + CtTypeParameter getTypeParameterDeclaration(); + // overriding the return type @Override CtTypeParameterReference clone(); diff --git a/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java b/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java index b210f15886a..8bdbcf2737e 100644 --- a/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java +++ b/src/main/java/spoon/support/reflect/reference/CtTypeParameterReferenceImpl.java @@ -186,25 +186,32 @@ public CtTypeParameter getDeclaration() { return result; } } + return null; + } + + @Override + public CtTypeParameter getTypeParameterDeclaration() { CtElement parent = this.getParent(); - // case 2: this is an actual type argument of a type reference eg List + // case 1: this is an actual type argument of a type reference eg List if (parent instanceof CtTypeReference) { CtType t = ((CtTypeReference) parent).getTypeDeclaration(); - return findTypeParamDeclaration(t, this.getSimpleName()); + return findTypeParamDeclarationByPosition(t, ((CtTypeReference) parent).getActualTypeArguments().indexOf(this)); } - // case 3: this is an actual type argument of a method/constructor reference + // case 2: this is an actual type argument of a method/constructor reference if (parent instanceof CtExecutableReference) { CtExecutable exec = ((CtExecutableReference) parent).getExecutableDeclaration(); if (exec instanceof CtMethod || exec instanceof CtConstructor) { - return findTypeParamDeclaration((CtFormalTypeDeclarer) exec, this.getSimpleName()); + return findTypeParamDeclarationByPosition((CtFormalTypeDeclarer) exec, ((CtTypeReference) parent).getActualTypeArguments().indexOf(this)); } } + return null; } + private CtTypeParameter findTypeParamDeclaration(CtFormalTypeDeclarer type, String refName) { for (CtTypeParameter typeParam : type.getFormalCtTypeParameters()) { if (typeParam.getSimpleName().equals(refName)) { @@ -214,6 +221,10 @@ private CtTypeParameter findTypeParamDeclaration(CtFormalTypeDeclarer type, Stri return null; } + private CtTypeParameter findTypeParamDeclarationByPosition(CtFormalTypeDeclarer type, int position) { + return type.getFormalCtTypeParameters().get(position); + } + @Override public CtType getTypeDeclaration() { return getDeclaration(); diff --git a/src/test/java/spoon/test/generics/GenericsTest.java b/src/test/java/spoon/test/generics/GenericsTest.java index 10a6a9d9d30..fa64a887e19 100644 --- a/src/test/java/spoon/test/generics/GenericsTest.java +++ b/src/test/java/spoon/test/generics/GenericsTest.java @@ -172,6 +172,7 @@ public void testTypeParameterDeclarer() throws Exception { assertEquals("T", classThatDefinesANewTypeArgument.getFormalCtTypeParameters().get(0).getSimpleName()); assertSame(classThatDefinesANewTypeArgument, typeParam.getTypeParameterDeclarer()); CtTypeParameterReference typeParamReference = typeParam.getReference(); + assertSame(typeParam, typeParamReference.getDeclaration()); // creating an appropriate context CtMethod m = classThatDefinesANewTypeArgument.getFactory().createMethod(); @@ -182,6 +183,7 @@ public void testTypeParameterDeclarer() throws Exception { // the final assertions assertSame(typeParam, typeParamReference.getDeclaration()); + assertSame(classThatDefinesANewTypeArgument, typeParamReference.getDeclaration().getParent()); // now testing that the getDeclaration of a type parameter is actually a dynamic lookup @@ -589,12 +591,7 @@ public void testTypeParameterReferenceAsActualTypeArgument() throws Exception { CtTypeParameter typeParam = aTacos.getFormalCtTypeParameters().get(0); CtTypeParameterReference typeParamRef = typeParam.getReference(); - - - // a tyoe parameter ref with no context cannot be resolved - assertSame(null, typeParamRef.getDeclaration()); - // by default a typeParamRef is not in a tree - assertEquals(false, typeParamRef.isParentInitialized()); + assertSame(typeParam, typeParamRef.getDeclaration()); assertEquals("spoon.test.generics.ClassThatDefinesANewTypeArgument", typeRef.toString()); @@ -611,7 +608,11 @@ public void testTypeParameterReferenceAsActualTypeArgument() throws Exception { //typeParamRef has got new parent assertSame(typeRef, typeParamRef.getParent()); - assertSame(typeParam, typeParamRef.getDeclaration()); + // null because without context + assertEquals(null, typeParamRef.getDeclaration()); + assertEquals(typeParam, typeParamRef.getTypeParameterDeclaration()); + typeParamRef.setSimpleName("Y"); + assertEquals(typeParam, typeParamRef.getTypeParameterDeclaration()); } @Test public void testGenericTypeReference() throws Exception { @@ -626,7 +627,11 @@ public void testGenericTypeReference() throws Exception { assertTrue(genericTypeRef.getActualTypeArguments().size()>0); assertEquals(aTacos.getFormalCtTypeParameters().size(), genericTypeRef.getActualTypeArguments().size()); for(int i=0; i