Bug 466232 - [null] Annotate command should be resilient to "broken
classfiles"

Change-Id: I721ffc45cf5bf4dcb9da2d9af1baab2d58d98141
Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de>
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AbstractAnnotateAssistTests.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AbstractAnnotateAssistTests.java
index 7fa0570..f80de0b 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AbstractAnnotateAssistTests.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AbstractAnnotateAssistTests.java
@@ -41,6 +41,7 @@
 import org.eclipse.jdt.internal.corext.util.Strings;
 
 import org.eclipse.jdt.ui.tests.core.ProjectTestSetup;
+import org.eclipse.jdt.ui.tests.quickfix.JarUtil.ClassFileFilter;
 
 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
 import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer;
@@ -86,9 +87,9 @@
 	// === from jdt.core.tests.model: ===
 
 	protected void addLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents,
-								String annotationpath, String compliance) throws CoreException, IOException 
-	{	
-		IProject project= createLibrary(javaProject, jarName, sourceZipName, pathAndContents, compliance);
+								String annotationpath, String compliance, ClassFileFilter classFileFilter) throws CoreException, IOException
+	{
+		IProject project= createLibrary(javaProject, jarName, sourceZipName, pathAndContents, compliance, classFileFilter);
 	
 		String projectPath= '/' + project.getName() + '/';
 		IClasspathEntry entry= JavaCore.newLibraryEntry(
@@ -106,13 +107,13 @@
 	}
 
 	protected IProject createLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents,
-								String compliance) throws IOException, CoreException
+								String compliance, ClassFileFilter classFileFilter) throws IOException, CoreException
 	{
 		IProject project= javaProject.getProject();
 		String projectLocation= project.getLocation().toOSString();
 		String jarPath= projectLocation + File.separator + jarName;
 		String[] claspath= new String[] { javaProject.getResolvedClasspath(true)[0].getPath().toOSString() };
-		JarUtil.createJar(pathAndContents, null, jarPath, claspath, compliance, null);
+		JarUtil.createJar(pathAndContents, null, jarPath, claspath, compliance, null, classFileFilter);
 		if (pathAndContents != null && pathAndContents.length != 0) {
 			String sourceZipPath= projectLocation + File.separator + sourceZipName;
 			JarUtil.createSourceZip(pathAndContents, sourceZipPath);
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest15.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest15.java
index 6e0fe9d..46ed0ef 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest15.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest15.java
@@ -13,11 +13,10 @@
 import java.io.ByteArrayInputStream;
 import java.util.List;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
 import org.eclipse.jdt.testplugin.JavaProjectHelper;
 
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Path;
 
 import org.eclipse.core.resources.IFile;
@@ -32,13 +31,19 @@
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaCore;
 
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+
 import org.eclipse.jdt.ui.JavaUI;
 import org.eclipse.jdt.ui.tests.core.ProjectTestSetup;
+import org.eclipse.jdt.ui.tests.quickfix.JarUtil.ClassFileFilter;
 
 import org.eclipse.jdt.internal.ui.JavaPlugin;
 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
 import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer;
 
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
 public class AnnotateAssistTest15 extends AbstractAnnotateAssistTests {
 
 	protected static final String ANNOTATION_PATH= "annots";
@@ -67,19 +72,19 @@
 
 	/**
 	 * Assert that the "Annotate" command can be invoked on a ClassFileEditor
-	 * @throws Exception 
+	 * @throws Exception
 	 */
 	public void testAnnotateReturn() throws Exception {
 		
 		String MY_MAP_PATH= "pack/age/MyMap";
-		String[] pathAndContents= new String[] { 
-					MY_MAP_PATH+".java", 
+		String[] pathAndContents= new String[] {
+					MY_MAP_PATH+".java",
 					"package pack.age;\n" +
 					"public interface MyMap<K,V> {\n" +
 					"    public V get(K key);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, null);
 		IType type= fJProject1.findType(MY_MAP_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
 
@@ -91,12 +96,12 @@
 			viewer.getQuickAssistAssistant().addCompletionListener(new ICompletionListener() {
 				public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
 					proposalBox[0]= proposal;
-				}				
+				}
 				public void assistSessionStarted(ContentAssistEvent event) { /* nop */ }
 				public void assistSessionEnded(ContentAssistEvent event) { /* nop */ }
 			});
 
-			int offset= pathAndContents[1].indexOf("V get");		
+			int offset= pathAndContents[1].indexOf("V get");
 			viewer.setSelection(new TextSelection(offset, 0));
 			viewer.doOperation(JavaSourceViewer.ANNOTATE_CLASS_FILE);
 
@@ -142,14 +147,14 @@
 	public void testAnnotateReturn2() throws Exception {
 		
 		String MY_MAP_PATH= "pack/age/MyMap";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					MY_MAP_PATH+".java",
 					"package pack.age;\n" +
 					"public interface MyMap<K,V> {\n" +
 					"    public V get(K key);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, null);
 		IType type= fJProject1.findType(MY_MAP_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
 
@@ -203,14 +208,14 @@
 		
 		String MY_MAP_PATH= "pack/age/MyMap";
 
-		String[] pathAndContents= new String[] { 
-				MY_MAP_PATH+".java", 
+		String[] pathAndContents= new String[] {
+				MY_MAP_PATH+".java",
 				"package pack.age;\n" +
 				"public interface MyMap<K,V> {\n" +
 				"    public V get(K key);\n" +
 				"}\n"
 			};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, null);
 		IType type= fJProject1.findType(MY_MAP_PATH.replace('/', '.'));
 		IFile annotationFile= fJProject1.getProject().getFile(new Path(ANNOTATION_PATH).append(MY_MAP_PATH+".eea"));
 		String initialContent=
@@ -273,7 +278,7 @@
 	public void testAnnotateParameter_Array1() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -281,7 +286,7 @@
 					"    public String test(int[][] ints, List<String> list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 		
 		IFile annotationFile= fJProject1.getProject().getFile(new Path(ANNOTATION_PATH).append(X_PATH+".eea"));
 		String initialContent=
@@ -343,14 +348,14 @@
 	public void testAnnotateField1() throws Exception {
 		
 		String NODE_PATH= "pack/age/Node";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					NODE_PATH+".java",
 					"package pack.age;\n" +
 					"public class Node<V> {\n" +
 					"    V value;\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, null);
 		IType type= fJProject1.findType(NODE_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
 
@@ -403,14 +408,14 @@
 	public void testAnnotateField2() throws Exception {
 		
 		String NODE_PATH= "pack/age/Node";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					NODE_PATH+".java",
 					"package pack.age;\n" +
 					"public class Node<V> {\n" +
 					"    Node<String> next;\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, null);
 		IType type= fJProject1.findType(NODE_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
 
@@ -455,4 +460,64 @@
 		}
 	}
 
+	public void testBug466232() throws Exception {
+		final String MISSINGPATH= "pack/age/Missing";
+		String CLASS1_PATH= "pack/age/Class1";
+		String CLASS2_PATH= "pack/age/Class2";
+		String[] pathAndContents= new String[] {
+					MISSINGPATH+".java",
+					"package pack.age;\n" +
+					"@interface Missing {}\n",
+					CLASS1_PATH+".java",
+					"package pack.age;\n" +
+					"import pack.age.Missing;\n" +
+					"@Missing\n" +
+					"public class Class1 {\n" +
+					"    Class1 foo() { return this; }\n" +
+					"}\n",
+					CLASS2_PATH+".java",
+					"package pack.age;\n" +
+					"import pack.age.Class1;\n" + // creates UnresolvedReferenceBinding for transitively referenced "Missing"
+					"import pack.age.Missing;\n" + // throws AbortCompilation for unresolvable UnresolvedReferenceBinding "Missing"
+					"public class Class2 {\n" +
+					"    void test(Class1 c1) {\n" +
+					"        c1 = c1.foo();\n" +
+					"    }\n" +
+					"}\n"
+				};
+		
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, new ClassFileFilter() {
+			public boolean include(CompilationResult unitResult) {
+				return !new Path(MISSINGPATH + ".java").equals(new Path(String.valueOf(unitResult.getFileName())));
+			}
+		});
+		IType type= fJProject1.findType(CLASS2_PATH.replace('/', '.'));
+		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
+
+		ILogListener logListener= null;
+		try {
+			int offset= pathAndContents[5].indexOf("Class1 c1");
+
+			// not expecting proposals, but a log message, due to incomplete AST (no binding information available).
+			final IStatus[] resultingStatus= new IStatus[1];
+			logListener= new ILogListener() {
+				public void logging(IStatus status, String plugin) {
+					assertTrue("Only one status", resultingStatus[0] == null);
+					assertEquals("Expected status message",
+							"Error during computation of Annotate proposals: " +
+							"The type pack.age.Missing cannot be resolved.\n It is indirectly referenced from required .class files",
+							status.getMessage());
+				}
+			};
+			JavaPlugin.getDefault().getLog().addLogListener(logListener);
+			List<ICompletionProposal> list= collectAnnotateProposals(javaEditor, offset);
+			
+			assertEquals("Expected number of proposals", 0, list.size());
+		} finally {
+			if (logListener != null) {
+				JavaPlugin.getDefault().getLog().removeLogListener(logListener);
+			}
+			JavaPlugin.getActivePage().closeAllEditors(false);
+		}
+	}
 }
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest18.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest18.java
index d3fabd8..afe0ecc 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest18.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AnnotateAssistTest18.java
@@ -64,7 +64,7 @@
 	public void testAnnotateParameter_TypeArgument() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -72,7 +72,7 @@
 					"    public String test(int[] ints, List<String> list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 		
 		IFile annotationFile= fJProject1.getProject().getFile(new Path(ANNOTATION_PATH).append(X_PATH+".eea"));
 		String initialContent=
@@ -134,7 +134,7 @@
 	public void testAnnotateParameter_ArrayOfPrimitive() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -142,7 +142,7 @@
 					"    public String test(int[] ints, List<String> list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 		IType type= fJProject1.findType(X_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
 
@@ -167,7 +167,7 @@
 	public void testAnnotateParameter_WildcardBound() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -175,7 +175,7 @@
 					"    public String test(Object[] objects, List<? extends Number> list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 		
 		IFile annotationFile= fJProject1.getProject().getFile(new Path(ANNOTATION_PATH).append(X_PATH+".eea"));
 		String initialContent=
@@ -241,7 +241,7 @@
 	public void testAnnotateParameter_Array2() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -249,7 +249,7 @@
 					"    public String test(int[][] ints, List<String> list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 		
 		IFile annotationFile= fJProject1.getProject().getFile(new Path(ANNOTATION_PATH).append(X_PATH+".eea"));
 		String initialContent=
@@ -313,7 +313,7 @@
 	public void testAnnotateParameter_Array3() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -321,7 +321,7 @@
 					"    public String test(int[][] ints, List<String> list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 		
 		IFile annotationFile= fJProject1.getProject().getFile(new Path(ANNOTATION_PATH).append(X_PATH+".eea"));
 		String initialContent=
@@ -387,7 +387,7 @@
 	public void _testAnnotateParameter_TypeParameter() throws Exception {
 		
 		String X_PATH= "pack/age/X";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					X_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -395,7 +395,7 @@
 					"    public <X, T extends List<X>> X test(T list);\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_8, null);
 
 		IType type= fJProject1.findType(X_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
@@ -449,7 +449,7 @@
 	public void testAnnotateField1() throws Exception {
 		
 		String NODE_PATH= "pack/age/Node";
-		String[] pathAndContents= new String[] { 
+		String[] pathAndContents= new String[] {
 					NODE_PATH+".java",
 					"package pack.age;\n" +
 					"import java.util.List;\n" +
@@ -457,7 +457,7 @@
 					"    List<Object[]> value;\n" +
 					"}\n"
 				};
-		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5);
+		addLibrary(fJProject1, "lib.jar", "lib.zip", pathAndContents, ANNOTATION_PATH, JavaCore.VERSION_1_5, null);
 		IType type= fJProject1.findType(NODE_PATH.replace('/', '.'));
 		JavaEditor javaEditor= (JavaEditor) JavaUI.openInEditor(type);
 
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/JarUtil.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/JarUtil.java
index 275f2b2..02f2fe4 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/JarUtil.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/JarUtil.java
@@ -146,11 +146,30 @@
     }
     return result;
 }
-/* inlined and simplified for JDT/UI */
+
+// should eventually be replaced by use of java.util.function.Predicate<CompilationResult>
+public interface ClassFileFilter {
+	boolean include(CompilationResult unitResult);
+}
+/* inlined and simplified / modified for JDT/UI */
 private static class Requestor implements ICompilerRequestor {
 	public boolean hasErrors = false;
 	public String outputPath;
 	public String problemLog = "";
+	private ClassFileFilter classFileFilter= null;
+	
+	public Requestor(ClassFileFilter classFileFilter) {
+		if (classFileFilter != null) {
+			this.classFileFilter = classFileFilter;
+		} else {
+			// default: all without errors
+			this.classFileFilter = new ClassFileFilter() {
+				public boolean include(CompilationResult unitResult) {
+					return (unitResult != null) && !unitResult.hasErrors();
+				}
+			};
+		}
+	}
 
 	public void acceptResult(CompilationResult compilationResult) {
 		this.hasErrors |= compilationResult.hasErrors();
@@ -158,7 +177,7 @@
 		outputClassFiles(compilationResult);
 	}
 	protected void outputClassFiles(CompilationResult unitResult) {
-		if ((unitResult != null) && !unitResult.hasErrors()) {
+		if (this.classFileFilter.include(unitResult)) {
 			ClassFile[] classFiles = unitResult.getClassFiles();
 			if (this.outputPath != null) {
 				for (int i = 0, fileCount = classFiles.length; i < fileCount; i++) {
@@ -176,9 +195,9 @@
 		}
 	}
 }
-public static void compile(String[] pathsAndContents, Map options, String[] classpath, String outputPath) {
+public static void compile(String[] pathsAndContents, Map options, String[] classpath, String outputPath, ClassFileFilter classFileFilter) {
         IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault());
-        Requestor requestor = new Requestor();
+        Requestor requestor = new Requestor(classFileFilter);
         requestor.outputPath = outputPath.endsWith(File.separator) ? outputPath : outputPath + File.separator;
 
         String[] classLibs = getJavaClassLibs();
@@ -229,12 +248,12 @@
         output.close();
     }
 }
-public static void createJar(String[] pathsAndContents, String[] extraPathsAndContents, Map options, String[] classpath, String jarPath) throws IOException {
+public static void createJar(String[] pathsAndContents, String[] extraPathsAndContents, Map options, ClassFileFilter classFileFilter, String[] classpath, String jarPath) throws IOException {
     String classesPath = getOutputDirectory() + File.separator + "classes";
     File classesDir = new File(classesPath);
     flushDirectoryContent(classesDir);
 	if (pathsAndContents != null) {
-		compile(pathsAndContents, options, classpath, classesPath);
+		compile(pathsAndContents, options, classpath, classesPath, classFileFilter);
 	}
 	if (extraPathsAndContents != null) {
 		for (int i = 0, l = extraPathsAndContents.length; i < l; /* inc in loop */) {
@@ -245,12 +264,12 @@
 	}
     zip(classesDir, jarPath);
 }
-public static void createJar(String[] javaPathsAndContents, String[] extraPathsAndContents, String jarPath, String[] classpath, String compliance, Map options) throws IOException {
+public static void createJar(String[] javaPathsAndContents, String[] extraPathsAndContents, String jarPath, String[] classpath, String compliance, Map options, ClassFileFilter classFileFilter) throws IOException {
 	Map compileOptions = getCompileOptions(compliance);
 	if (options != null) {
 		compileOptions.putAll(options);
 	}
-	createJar(javaPathsAndContents, extraPathsAndContents, compileOptions, classpath, jarPath);
+	createJar(javaPathsAndContents, extraPathsAndContents, compileOptions, classFileFilter, classpath, jarPath);
 }
 public static void createSourceZip(String[] pathsAndContents, String zipPath) throws IOException {
     String sourcesPath = getOutputDirectory() + File.separator + "sources";
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/ExternalNullAnnotationChangeProposals.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/ExternalNullAnnotationChangeProposals.java
index 670d2c4..501918b 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/ExternalNullAnnotationChangeProposals.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/ExternalNullAnnotationChangeProposals.java
@@ -31,6 +31,7 @@
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Path;
 
@@ -49,9 +50,11 @@
 import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTVisitor;
 import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.Dimension;
 import org.eclipse.jdt.core.dom.FieldDeclaration;
 import org.eclipse.jdt.core.dom.IMethodBinding;
@@ -65,6 +68,7 @@
 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
 import org.eclipse.jdt.core.dom.Type;
 import org.eclipse.jdt.core.dom.TypeParameter;
+import org.eclipse.jdt.core.dom.VariableDeclaration;
 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
 import org.eclipse.jdt.core.dom.WildcardType;
 import org.eclipse.jdt.core.util.ExternalAnnotationUtil;
@@ -79,6 +83,7 @@
 
 import org.eclipse.jdt.internal.ui.JavaPlugin;
 import org.eclipse.jdt.internal.ui.JavaPluginImages;
+import org.eclipse.jdt.internal.ui.JavaUIStatus;
 import org.eclipse.jdt.internal.ui.text.correction.ExternalNullAnnotationQuickAssistProcessor;
 import org.eclipse.jdt.internal.ui.text.correction.IProposalRelevance;
 
@@ -277,6 +282,57 @@
 		}
 	}
 
+	static class MissingBindingException extends RuntimeException {
+		private static final long serialVersionUID= 1L;
+		ASTNode fNode;
+		MissingBindingException(ASTNode/*Type or TypeParameter or MethodDeclaration*/ node) {
+			fNode= node;
+		}
+		@Override
+		public String getMessage() {
+			// check if compilation may have been aborted due to classpath trouble / unreportable problem:
+			ASTNode cu= ASTNodes.getParent(fNode, ASTNode.COMPILATION_UNIT);
+			if (cu instanceof CompilationUnit) {
+				for (IProblem problem : ((CompilationUnit) cu).getProblems()) {
+					if (problem.getID() == IProblem.IsClassPathCorrect || problem.getOriginatingFileName() == null)
+						return problem.getMessage();
+				}
+			}
+			switch (fNode.getNodeType()) {
+				case ASTNode.METHOD_DECLARATION:
+					return "Could not resolve method "+fNode.toString(); //$NON-NLS-1$
+				case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
+					return "Could not resolve field "+fNode.toString(); //$NON-NLS-1$
+				default:
+					return "Could not resolve type "+fNode.toString(); //$NON-NLS-1$
+			}
+		}
+	}
+
+	static ITypeBinding resolveBinding(TypeParameter type) {
+		ITypeBinding binding= type.resolveBinding();
+		if (binding == null) throw new MissingBindingException(type);
+		return binding;
+	}
+
+	static ITypeBinding resolveBinding(Type type) {
+		ITypeBinding binding= type.resolveBinding();
+		if (binding == null) throw new MissingBindingException(type);
+		return binding;
+	}
+	
+	static IMethodBinding resolveBinding(MethodDeclaration method) {
+		IMethodBinding binding= method.resolveBinding();
+		if (binding == null) throw new MissingBindingException(method);
+		return binding;
+	}
+
+	static IVariableBinding resolveBinding(VariableDeclaration variable) {
+		IVariableBinding binding= variable.resolveBinding();
+		if (binding == null) throw new MissingBindingException(variable);
+		return binding;
+	}
+
 	/* Quick assist on class file, propose changes an any type detail. */
 	public static void collectExternalAnnotationProposals(ICompilationUnit cu, ASTNode coveringNode, int offset, ArrayList<IJavaCompletionProposal> resultingCollection) {
 
@@ -316,42 +372,47 @@
 			if (!(outer.getNodeType() == ASTNode.PARAMETERIZED_TYPE && inner.getParent() == outer))
 				return;
 		}
-		if (outer instanceof Type) {
-			ITypeBinding typeBinding= ((Type) outer).resolveBinding();
-			if (typeBinding != null && typeBinding.isPrimitive())
-				return;
-			outer.accept(rendererNonNull);
-			outer.accept(rendererNullable);
-			outer.accept(rendererRemove);
-		} else {
-			List<?> siblingList= (List<?>) outer.getParent().getStructuralProperty(outer.getLocationInParent());
-			rendererNonNull.visitTypeParameters(siblingList);
-			rendererNullable.visitTypeParameters(siblingList);
-			rendererRemove.visitTypeParameters(siblingList);
-		}
-
-		StructuralPropertyDescriptor locationInParent= outer.getLocationInParent();
-		ProposalCreator creator= null;
-		if (locationInParent == MethodDeclaration.RETURN_TYPE2_PROPERTY) {
-			MethodDeclaration method= (MethodDeclaration) ASTNodes.getParent(coveringNode, MethodDeclaration.class);
-			creator= new ReturnProposalCreator(cu, method.resolveBinding());
-		} else if (locationInParent == SingleVariableDeclaration.TYPE_PROPERTY) {
-			ASTNode param= outer.getParent();
-			if (param.getLocationInParent() == MethodDeclaration.PARAMETERS_PROPERTY) {
+		try {
+			if (outer instanceof Type) {
+				ITypeBinding typeBinding= resolveBinding((Type) outer);
+				if (typeBinding.isPrimitive())
+					return;
+				outer.accept(rendererNonNull);
+				outer.accept(rendererNullable);
+				outer.accept(rendererRemove);
+			} else {
+				List<?> siblingList= (List<?>) outer.getParent().getStructuralProperty(outer.getLocationInParent());
+				rendererNonNull.visitTypeParameters(siblingList);
+				rendererNullable.visitTypeParameters(siblingList);
+				rendererRemove.visitTypeParameters(siblingList);
+			}
+		
+			StructuralPropertyDescriptor locationInParent= outer.getLocationInParent();
+			ProposalCreator creator= null;
+			if (locationInParent == MethodDeclaration.RETURN_TYPE2_PROPERTY) {
 				MethodDeclaration method= (MethodDeclaration) ASTNodes.getParent(coveringNode, MethodDeclaration.class);
-				int paramIdx= method.parameters().indexOf(param);
-				if (paramIdx != -1)
-					creator= new ParameterProposalCreator(cu, method.resolveBinding(), paramIdx);
+				creator= new ReturnProposalCreator(cu, resolveBinding(method));
+			} else if (locationInParent == SingleVariableDeclaration.TYPE_PROPERTY) {
+				ASTNode param= outer.getParent();
+				if (param.getLocationInParent() == MethodDeclaration.PARAMETERS_PROPERTY) {
+					MethodDeclaration method= (MethodDeclaration) ASTNodes.getParent(coveringNode, MethodDeclaration.class);
+					int paramIdx= method.parameters().indexOf(param);
+					if (paramIdx != -1)
+						creator= new ParameterProposalCreator(cu, resolveBinding(method), paramIdx);
+				}
+			} else if (locationInParent == FieldDeclaration.TYPE_PROPERTY) {
+				FieldDeclaration field= (FieldDeclaration) ASTNodes.getParent(coveringNode, FieldDeclaration.class);
+				if (field.fragments().size() > 0) {
+					VariableDeclarationFragment fragment= (VariableDeclarationFragment) field.fragments().get(0);
+					creator= new FieldProposalCreator(cu, resolveBinding(fragment));
+				}
 			}
-		} else if (locationInParent == FieldDeclaration.TYPE_PROPERTY) {
-			FieldDeclaration field= (FieldDeclaration) ASTNodes.getParent(coveringNode, FieldDeclaration.class);
-			if (field.fragments().size() > 0) {
-				VariableDeclarationFragment fragment= (VariableDeclarationFragment) field.fragments().get(0);
-				creator= new FieldProposalCreator(cu, fragment.resolveBinding());
+			if (creator != null) {
+				createProposalsForType(cu, inner, offset, rendererNonNull, rendererNullable, rendererRemove, creator, resultingCollection);
 			}
-		}
-		if (creator != null) {
-			createProposalsForType(cu, inner, offset, rendererNonNull, rendererNullable, rendererRemove, creator, resultingCollection);
+		} catch (MissingBindingException mbe) {
+			JavaPlugin.log(JavaUIStatus.createError(IStatus.ERROR, "Error during computation of Annotate proposals: "+mbe.getMessage(), mbe)); //$NON-NLS-1$
+			return;
 		}
 	}
 
@@ -550,7 +611,7 @@
 			fBuffer.append('L');
 			if (type == fFocusType || type.getType() == fFocusType)
 				fBuffer.append(fAnnotation);
-			fBuffer.append(binaryName(type.resolveBinding()));
+			fBuffer.append(binaryName(resolveBinding(type)));
 			fBuffer.append('<');
 			for (Object arg : type.typeArguments())
 				((Type) arg).accept(this);
@@ -601,7 +662,7 @@
 			Type classBound= null;
 			for (Object bound : parameter.typeBounds()) {
 				Type typeBound= (Type) bound;
-				if (typeBound.resolveBinding().isClass()) {
+				if (resolveBinding(typeBound).isClass()) {
 					classBound= typeBound;
 					break;
 				}
@@ -610,7 +671,7 @@
 				fBuffer.append(':');
 				classBound.accept(this);
 			} else {
-				ITypeBinding typeBinding= parameter.resolveBinding();
+				ITypeBinding typeBinding= resolveBinding(parameter);
 				fBuffer.append(":L").append(binaryName(typeBinding.getSuperclass())).append(';'); //$NON-NLS-1$
 			}
 			for (Object bound : parameter.typeBounds()) {
@@ -625,7 +686,7 @@
 
 		@Override
 		public boolean visit(SimpleType type) {
-			ITypeBinding typeBinding= type.resolveBinding();
+			ITypeBinding typeBinding= resolveBinding(type);
 			if (typeBinding.isTypeVariable()) {
 				fBuffer.append('T');
 				if (fFocusType == type)
@@ -643,10 +704,10 @@
 		@Override
 		public boolean visit(PrimitiveType node) {
 			// not a legal focus type, but could be array element type
-			fBuffer.append(node.resolveBinding().getBinaryName());
+			fBuffer.append(resolveBinding(node).getBinaryName());
 			return false;
 		}
-
+	
 		String binaryName(ITypeBinding type) {
 			return type.getBinaryName().replace('.', '/');
 		}