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();
+}