Test case for bug 206591
diff --git a/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/RegressionTests.java b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/RegressionTests.java
index 5e0cfae..0462a06 100644
--- a/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/RegressionTests.java
+++ b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/RegressionTests.java
@@ -168,4 +168,124 @@
);
}
+ /**
+ * Test the Types.isSubtype() API, in various inheritance scenarios
+ */
+ public void testBugzilla206591A() throws Exception {
+ final String projName = RegressionTests.class.getName() + "206591.Project"; //$NON-NLS-1$
+ IPath projectPath = env.addProject( projName, "1.5" ); //$NON-NLS-1$
+ env.addExternalJars( projectPath, Util.getJavaClassLibs() );
+
+ env.removePackageFragmentRoot( projectPath, "" ); //$NON-NLS-1$
+ env.addPackageFragmentRoot( projectPath, "src" ); //$NON-NLS-1$
+ env.setOutputFolder( projectPath, "bin" ); //$NON-NLS-1$
+
+ TestUtil.createAndAddAnnotationJar( env
+ .getJavaProject( projectPath ) );
+ IProject project = env.getProject( projName );
+ IFolder srcFolder = project.getFolder( "src" );
+ IPath srcRoot = srcFolder.getFullPath();
+
+ String a1Code = "package pkg; " + "\n"
+ + "import org.eclipse.jdt.apt.tests.annotations.apitest.SubtypeOf;\n"
+ + "public interface A1 {\n "
+ + "}\n"
+ + "class A2 implements A1 {\n"
+ + "}\n"
+ + "class A3 extends A2 {\n"
+ + " @SubtypeOf(A1.class) // yes\n"
+ + " A2 _foo;\n"
+ + " @SubtypeOf(A1.class) // yes\n"
+ + " A3 _bar;\n"
+ + " @SubtypeOf(A2.class) // yes\n"
+ + " A3 _baz;\n"
+ + " @SubtypeOf(A1.class) // yes\n"
+ + " A1 _quux;\n"
+ + " @SubtypeOf(A2.class) // no\n"
+ + " A1 _yuzz;\n"
+ + " @SubtypeOf(String.class) // no\n"
+ + " A2 _wum;\n"
+ + "}\n"
+ + "class A4 extends A2 implements A1 {\n"
+ + " @SubtypeOf(A1.class) // yes\n"
+ + " A4 _humpf;\n"
+ + " @SubtypeOf(A2.class) // yes\n"
+ + " A4 _fuddle;\n"
+ + " @SubtypeOf(A5.class) // no\n"
+ + " A4 _snee;\n"
+ + "}\n"
+ + "class A5 {\n"
+ + "}\n";
+
+ final IPath a1Path = env.addClass( srcRoot, "pkg", "A1", a1Code ); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Set some per-project preferences
+ IJavaProject jproj = env.getJavaProject( projName );
+ AptConfig.setEnabled(jproj, true);
+ fullBuild( project.getFullPath() );
+ expectingSpecificProblemsFor(a1Path, new ExpectedProblem[]{
+ new ExpectedProblem("", "pkg.A2 is a subtype of pkg.A1", a1Path),
+ new ExpectedProblem("", "pkg.A3 is a subtype of pkg.A1", a1Path),
+ new ExpectedProblem("", "pkg.A3 is a subtype of pkg.A2", a1Path),
+ new ExpectedProblem("", "pkg.A1 is a subtype of pkg.A1", a1Path),
+ new ExpectedProblem("", "pkg.A1 is not a subtype of pkg.A2", a1Path),
+ new ExpectedProblem("", "pkg.A2 is not a subtype of java.lang.String", a1Path),
+ new ExpectedProblem("", "pkg.A4 is a subtype of pkg.A1", a1Path),
+ new ExpectedProblem("", "pkg.A4 is a subtype of pkg.A2", a1Path),
+ new ExpectedProblem("", "pkg.A4 is not a subtype of pkg.A5", a1Path),
+ }
+ );
+ }
+
+ /**
+ * Test the Types.isAssignable() API, in various inheritance scenarios
+ * @throws Exception
+ */
+ public void testBugzilla206591B() throws Exception {
+ final String projName = RegressionTests.class.getName() + "206591.Project"; //$NON-NLS-1$
+ IPath projectPath = env.addProject( projName, "1.5" ); //$NON-NLS-1$
+ env.addExternalJars( projectPath, Util.getJavaClassLibs() );
+
+ env.removePackageFragmentRoot( projectPath, "" ); //$NON-NLS-1$
+ env.addPackageFragmentRoot( projectPath, "src" ); //$NON-NLS-1$
+ env.setOutputFolder( projectPath, "bin" ); //$NON-NLS-1$
+
+ TestUtil.createAndAddAnnotationJar( env
+ .getJavaProject( projectPath ) );
+ IProject project = env.getProject( projName );
+ IFolder srcFolder = project.getFolder( "src" );
+ IPath srcRoot = srcFolder.getFullPath();
+
+ String a1Code = "package pkg; " + "\n"
+ + "import org.eclipse.jdt.apt.tests.annotations.apitest.AssignableTo;\n"
+ + "public interface A1 {\n "
+ + "}\n"
+ + "class A2 implements A1 {\n"
+ + "}\n"
+ + "class A3 extends A2 {\n"
+ + " @AssignableTo(A1.class) // yes\n"
+ + " A2 _foo;\n"
+ + " @AssignableTo(int.class) // yes\n"
+ + " byte _bar;\n"
+ + " @AssignableTo(A1.class) // yes\n"
+ + " A3 _baz;\n"
+ + " @AssignableTo(A2.class) // no\n"
+ + " A1 _quux;\n"
+ + "}";
+
+ final IPath a1Path = env.addClass( srcRoot, "pkg", "A1", a1Code ); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Set some per-project preferences
+ IJavaProject jproj = env.getJavaProject( projName );
+ AptConfig.setEnabled(jproj, true);
+ fullBuild( project.getFullPath() );
+ expectingSpecificProblemsFor(a1Path, new ExpectedProblem[]{
+ new ExpectedProblem("", "pkg.A2 is assignable to pkg.A1", a1Path),
+ new ExpectedProblem("", "byte is assignable to int", a1Path),
+ new ExpectedProblem("", "pkg.A3 is assignable to pkg.A1", a1Path),
+ new ExpectedProblem("", "pkg.A1 is not assignable to pkg.A2", a1Path),
+ }
+ );
+ }
+
}
diff --git a/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/APIAnnotationProcessorFactory.java b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/APIAnnotationProcessorFactory.java
index 3b94743..0ff3131 100644
--- a/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/APIAnnotationProcessorFactory.java
+++ b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/APIAnnotationProcessorFactory.java
@@ -11,7 +11,9 @@
package org.eclipse.jdt.apt.tests.annotations.apitest;
import java.util.Collection;
+import java.util.Map;
import java.util.Set;
+import java.util.Map.Entry;
import org.eclipse.jdt.apt.tests.annotations.BaseFactory;
import org.eclipse.jdt.apt.tests.annotations.BaseProcessor;
@@ -19,7 +21,10 @@
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.Messager;
+import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
+import com.sun.mirror.declaration.AnnotationTypeElementDeclaration;
+import com.sun.mirror.declaration.AnnotationValue;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.FieldDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
@@ -27,12 +32,13 @@
import com.sun.mirror.declaration.TypeParameterDeclaration;
import com.sun.mirror.type.DeclaredType;
import com.sun.mirror.type.TypeMirror;
+import com.sun.mirror.util.Types;
public class APIAnnotationProcessorFactory extends BaseFactory {
public APIAnnotationProcessorFactory(){
- super(Common.class.getName());
+ super(Common.class.getName(), SubtypeOf.class.getName(), AssignableTo.class.getName());
}
public AnnotationProcessor getProcessorFor(
@@ -43,12 +49,26 @@
public static class APIAnnotationProcessor extends BaseProcessor{
+ private Messager _msgr;
+ private Types _types;
+
public APIAnnotationProcessor(AnnotationProcessorEnvironment env){
super(env);
}
public void process() {
- final Messager msgr = _env.getMessager();
+ _msgr = _env.getMessager();
+ _types = _env.getTypeUtils();
+ checkCommon();
+ checkSubtypeOf();
+ checkAssignableTo();
+ }
+
+ /**
+ * validate instances of the Common annotation
+ */
+ private void checkCommon()
+ {
final AnnotationTypeDeclaration commonAnnoType =
(AnnotationTypeDeclaration)_env.getTypeDeclaration(Common.class.getName());
final Collection<Declaration> decls =
@@ -66,9 +86,9 @@
TypeMirror typeVar = typeVars.iterator().next();
boolean assignable = _env.getTypeUtils().isAssignable(typeVar, collectionType);
if( assignable )
- msgr.printError(typeVar + " is assignable to " + collectionType );
+ _msgr.printError(typeVar + " is assignable to " + collectionType );
else
- msgr.printError(typeVar + " is not assignable to " + collectionType );
+ _msgr.printError(typeVar + " is not assignable to " + collectionType );
}
}
}else if(decl instanceof TypeDeclaration){
@@ -77,7 +97,7 @@
typeDecl.getFormalTypeParameters();
for(TypeParameterDeclaration typeParam : typeParams){
Declaration owner = typeParam.getOwner();
- msgr.printError("Type parameter '" + typeParam + "' belongs to " + owner.getClass().getName() + " " + owner.getSimpleName() );
+ _msgr.printError("Type parameter '" + typeParam + "' belongs to " + owner.getClass().getName() + " " + owner.getSimpleName() );
}
}
else if( decl instanceof MethodDeclaration ){
@@ -86,10 +106,95 @@
methodDecl.getFormalTypeParameters();
for(TypeParameterDeclaration typeParam : typeParams){
Declaration owner = typeParam.getOwner();
- msgr.printError("Type parameter '" + typeParam + "' belongs to " + owner.getClass().getName() + " " + owner.getSimpleName() );
+ _msgr.printError("Type parameter '" + typeParam + "' belongs to " + owner.getClass().getName() + " " + owner.getSimpleName() );
}
}
}
}
+
+ /**
+ * Validate all the fields annotated with @SubtypeOf, in order to test
+ * the Types.subtypeOf() method.
+ * We ignore anything but fields, out of laziness.
+ */
+ private void checkSubtypeOf() {
+ final AnnotationTypeDeclaration annoType =
+ (AnnotationTypeDeclaration)_env.getTypeDeclaration(SubtypeOf.class.getName());
+ final Collection<Declaration> decls =
+ _env.getDeclarationsAnnotatedWith(annoType);
+ for( Declaration decl : decls ){
+ if(decl instanceof FieldDeclaration ) {
+ AnnotationMirror mirror = findMirror(decl, annoType);
+ if (mirror == null) {
+ return;
+ }
+ TypeMirror valueType = getTypeValue(mirror);
+ final FieldDeclaration field = (FieldDeclaration)decl;
+ final TypeMirror fieldType = field.getType();
+ boolean isSubtype = _types.isSubtype(fieldType, valueType);
+ if( isSubtype )
+ _msgr.printError(fieldType + " is a subtype of " + valueType );
+ else
+ _msgr.printError(fieldType + " is not a subtype of " + valueType );
+ }
+ }
+ }
+
+ /**
+ * Validate all the fields annotated with @AssignableTo.
+ * We ignore anything but fields, out of laziness.
+ */
+ private void checkAssignableTo() {
+ final AnnotationTypeDeclaration annoType =
+ (AnnotationTypeDeclaration)_env.getTypeDeclaration(AssignableTo.class.getName());
+ final Collection<Declaration> decls =
+ _env.getDeclarationsAnnotatedWith(annoType);
+ for( Declaration decl : decls ){
+ if(decl instanceof FieldDeclaration ) {
+ AnnotationMirror mirror = findMirror(decl, annoType);
+ if (mirror == null) {
+ return;
+ }
+ TypeMirror valueType = getTypeValue(mirror);
+ final FieldDeclaration field = (FieldDeclaration)decl;
+ final TypeMirror fieldType = field.getType();
+ boolean isAssignableTo = _types.isAssignable(fieldType, valueType);
+ if( isAssignableTo )
+ _msgr.printError(fieldType + " is assignable to " + valueType );
+ else
+ _msgr.printError(fieldType + " is not assignable to " + valueType );
+ }
+ }
+ }
+
+ /**
+ * @return a mirror for the instance of the specified annotation on the specified
+ * declaration, or null if one is not present.
+ */
+ private AnnotationMirror findMirror(Declaration decl, AnnotationTypeDeclaration at) {
+ for (AnnotationMirror mirror : decl.getAnnotationMirrors()) {
+ if (mirror.getAnnotationType().equals(at)) {
+ return mirror;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the value() of an annotation instance <code>mirror</code>, if it is a
+ * class value, or null if not.
+ */
+ private TypeMirror getTypeValue(AnnotationMirror mirror) {
+ Map<AnnotationTypeElementDeclaration, AnnotationValue> values = mirror.getElementValues();
+ for (Entry<AnnotationTypeElementDeclaration, AnnotationValue> entry : values.entrySet()) {
+ if ("value".equals(entry.getKey().getSimpleName())) {
+ if (entry.getValue().getValue() instanceof TypeMirror)
+ return (TypeMirror)entry.getValue().getValue();
+ else
+ return null;
+ }
+ }
+ return null;
+ }
}
}
diff --git a/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/AssignableTo.java b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/AssignableTo.java
new file mode 100644
index 0000000..4c87f8c
--- /dev/null
+++ b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/AssignableTo.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2007 BEA Systems, Inc.
+ * 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:
+ * wharley@bea.com - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.apt.tests.annotations.apitest;
+
+/**
+ * Triggers the APIAnnotationProcessor, which will verify that the annotated object
+ * is assignable to an object of the specified type.
+ */
+public @interface AssignableTo
+{
+ Class<?> value();
+}
diff --git a/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/SubtypeOf.java b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/SubtypeOf.java
new file mode 100644
index 0000000..1d527cb
--- /dev/null
+++ b/org.eclipse.jdt.apt.tests/src/org/eclipse/jdt/apt/tests/annotations/apitest/SubtypeOf.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2007 BEA Systems, Inc.
+ * 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:
+ * wharley@bea.com - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.apt.tests.annotations.apitest;
+
+/**
+ * Triggers the APIAnnotationProcessor, which will validate that the annotated object
+ * is of a type that is a subtype of the specified value.
+ */
+public @interface SubtypeOf
+{
+ Class<?> value();
+}