New quickfixes for adjusting annotations of overridden methods
Refactor to better accommodate the growing amount of code

diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.java b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.java
index f055c61..a110b52 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.java
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.java
@@ -24,6 +24,12 @@
 
 	public static String QuickFixes_declare_method_return_nullable;
 
+
+	public static String QuickFixes_declare_overridden_parameter_as_nonnull;
+
+
+	public static String QuickFixes_declare_overridden_return_as_nullable;
+
 	static {
 		// initialize resource bundle
 		NLS.initializeMessages(BUNDLE_NAME, FixMessages.class);
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.properties b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.properties
index 985ec90..1493f08 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.properties
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/FixMessages.properties
@@ -2,3 +2,5 @@
 QuickFixes_add_annotation_change_name=Add Annotations
 QuickFixes_declare_method_parameter_nullable=Declare method parameter as @{0}
 QuickFixes_declare_method_return_nullable=Declare method return as @{0}
+QuickFixes_declare_overridden_parameter_as_nonnull=Adjust overridden method from {1}, mark parameter as @{0}
+QuickFixes_declare_overridden_return_as_nullable=Adjust overridden method from {1}, mark as returning @{0}
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/NullAnnotationsCleanUp.java b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/NullAnnotationsCleanUp.java
index ddb34ce..e20c632 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/NullAnnotationsCleanUp.java
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/NullAnnotationsCleanUp.java
@@ -144,7 +144,7 @@
 	public boolean canFix(ICompilationUnit compilationUnit, IProblemLocation problem) {
 		int id= problem.getProblemId();
 		
-		if (QuickFixes.isMissingNullableAnnotationProblem(id)) {
+		if (QuickFixes.isMissingNullAnnotationProblem(id)) {
 			if (   isEnabled(CleanUpConstants.ADD_MISSING_ANNOTATIONS) 
 				&& (   ((id == DefiniteNullFromNonNullMethod) && isEnabled(CleanUpConstants.ADD_DEFINITELY_MISSING_RETURN_ANNOTATION_NULLABLE))
 					|| ((id == PotentialNullFromNonNullMethod) && isEnabled(CleanUpConstants.ADD_POTENTIALLY_MISSING_RETURN_ANNOTATION_NULLABLE))
@@ -178,7 +178,7 @@
 			if (   (addDefinitelyMissingReturnNullable && id == DefiniteNullFromNonNullMethod)
 				|| (addPotentiallyMissingReturnNullable && id == PotentialNullFromNonNullMethod)
 				|| (addDefinitelyMissingParamNullable && QuickFixes.mayIndicateParameterNullcheck(id)))
-				if (!QuickFixes.hasExplicitNullnessAnnotation((ICompilationUnit) compilationUnit.getJavaElement(), problems[i].getSourceStart()))
+				if (!QuickFixes.hasExplicitNullAnnotation((ICompilationUnit) compilationUnit.getJavaElement(), problems[i].getSourceStart()))
 					result++;
 		}
 		return result;
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/QuickFixes.java b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/QuickFixes.java
index d6e21a3..af11492 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/QuickFixes.java
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/QuickFixes.java
@@ -29,20 +29,13 @@
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.IBinding;
-import org.eclipse.jdt.core.dom.IMethodBinding;
-import org.eclipse.jdt.core.dom.ITypeBinding;
 import org.eclipse.jdt.core.dom.IVariableBinding;
-import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.MethodInvocation;
 import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix;
 import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.CompilationUnitRewriteOperation;
 import org.eclipse.jdt.internal.corext.fix.IProposableFix;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
-import org.eclipse.jdt.internal.corext.util.Messages;
 import org.eclipse.jdt.internal.ui.JavaPluginImages;
-import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
 import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation;
 import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposal;
 import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
@@ -74,6 +67,7 @@
 			case PotentialNullFromNonNullMethod:
 			case DefiniteNullToNonNullParameter:
 			case PotentialNullToNonNullParameter:
+			case IllegalRedefinitionToNullableReturn:
 			case IProblem.NonNullLocalVariableComparisonYieldsFalse:
 			case IProblem.RedundantNullCheckOnNonNullLocalVariable:
 					return true;
@@ -97,33 +91,46 @@
 			case PotentialNullFromNonNullMethod:
 			case DefiniteNullToNonNullParameter:
 			case PotentialNullToNonNullParameter:
-				addNullableAnnotationInSignatureProposal(context, problem, proposals);
+			case IllegalRedefinitionToNullableReturn:
+			case IllegalDefinitionToNonNullParameter:
+			case IllegalRedefinitionToNonNullParameter:
+				addNullAnnotationInSignatureProposal(context, problem, proposals);
 				break;			
 			case IProblem.NonNullLocalVariableComparisonYieldsFalse:
 			case IProblem.RedundantNullCheckOnNonNullLocalVariable:
 				if (isComplainingAboutArgument(context, problem))
-					addNullableAnnotationInSignatureProposal(context, problem, proposals);
+					addNullAnnotationInSignatureProposal(context, problem, proposals);
 				break;
 			}			
 		}		
 	}
 		
 	@SuppressWarnings("unchecked")
-	void addNullableAnnotationInSignatureProposal(IInvocationContext context, IProblemLocation problem, @SuppressWarnings("rawtypes") Collection proposals) {
-		IProposableFix fix= createNullableInSignatureFix(context.getASTRoot(), problem);
+	void addNullAnnotationInSignatureProposal(IInvocationContext context, IProblemLocation problem, @SuppressWarnings("rawtypes") Collection proposals)
+	{
+		IProposableFix fix= createNullAnnotationInSignatureFix(context.getASTRoot(), problem);
 		
 		if (fix != null) {
 			Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
 			Map<String, String> options= new Hashtable<String, String>();
 			options.put(CleanUpConstants.ADD_MISSING_ANNOTATIONS, CleanUpOptions.TRUE);
-			if (problem.getProblemId() == DefiniteNullFromNonNullMethod)
+			switch (problem.getProblemId()) {
+			case DefiniteNullFromNonNullMethod:
+			case IllegalRedefinitionToNullableReturn:
 				options.put(CleanUpConstants.ADD_DEFINITELY_MISSING_RETURN_ANNOTATION_NULLABLE, CleanUpOptions.TRUE);
-			if (problem.getProblemId() == PotentialNullFromNonNullMethod)
+				break;
+			case PotentialNullFromNonNullMethod:
 				options.put(CleanUpConstants.ADD_POTENTIALLY_MISSING_RETURN_ANNOTATION_NULLABLE, CleanUpOptions.TRUE);
-			if (mayIndicateParameterNullcheck(problem.getProblemId()))
+				break;
+			case IProblem.NonNullLocalVariableComparisonYieldsFalse:
+			case IProblem.RedundantNullCheckOnNonNullLocalVariable:
+			    // may indicate a parameter null-check
+			case DefiniteNullToNonNullParameter:
+			case IllegalDefinitionToNonNullParameter:
+			case IllegalRedefinitionToNonNullParameter:
 				options.put(CleanUpConstants.ADD_DEFINITELY_MISSING_PARAMETER_ANNOTATION_NULLABLE, CleanUpOptions.TRUE);
-			if (problem.getProblemId() == DefiniteNullToNonNullParameter)
-				options.put(CleanUpConstants.ADD_DEFINITELY_MISSING_PARAMETER_ANNOTATION_NULLABLE, CleanUpOptions.TRUE);
+				break;
+			}
 			FixCorrectionProposal proposal= new FixCorrectionProposal(fix, new NullAnnotationsCleanUp(options, this), 15, image, context);
 			proposals.add(proposal);
 		}		
@@ -144,40 +151,39 @@
 		return false;
 	}
 
-	CompilationUnitRewriteOperationsFix createNullableInSignatureFix(CompilationUnit compilationUnit, IProblemLocation problem) {
+	CompilationUnitRewriteOperationsFix createNullAnnotationInSignatureFix(CompilationUnit compilationUnit, IProblemLocation problem)
+	{
 		String nullableAnnotationName = getNullableAnnotationName(compilationUnit.getJavaElement(), false);
 		String nonNullAnnotationName = getNonNullAnnotationName(compilationUnit.getJavaElement(), false);
-		RewriteOperations.SignatureAnnotationRewriteOperation operation = createAddNullableOperation(
-				compilationUnit, problem, nullableAnnotationName, nonNullAnnotationName, null, false, true);
+		String annotationToAdd = nullableAnnotationName;
+		String annotationToRemove = nonNullAnnotationName;
+		
+		switch (problem.getProblemId()) {
+		case IllegalDefinitionToNonNullParameter:
+		case IllegalRedefinitionToNonNullParameter:
+			annotationToAdd = nonNullAnnotationName;
+			annotationToRemove = nullableAnnotationName;
+			break;
+		// all others propose to add @Nullable
+		}
+		
+		RewriteOperations.SignatureAnnotationRewriteOperation operation = 
+			RewriteOperations.createAddAnnotationOperation(
+				compilationUnit, problem, annotationToAdd, annotationToRemove, null, false/*thisUnitOnly*/, true/*allowRemove*/);
 		if (operation == null)
 			return null;
 		
-		int lastDot = nullableAnnotationName.lastIndexOf('.');
-		if (lastDot != -1)
-			nullableAnnotationName = nullableAnnotationName.substring(lastDot+1);
-		String messageTemplate = null;
-		switch (problem.getProblemId()) {
-		case DefiniteNullFromNonNullMethod:
-		case PotentialNullFromNonNullMethod:
-			messageTemplate = FixMessages.QuickFixes_declare_method_return_nullable;
-			break;
-		case DefiniteNullToNonNullParameter:
-		case PotentialNullToNonNullParameter:
-		case IProblem.NonNullLocalVariableComparisonYieldsFalse:
-		case IProblem.RedundantNullCheckOnNonNullLocalVariable:
-			messageTemplate = FixMessages.QuickFixes_declare_method_parameter_nullable;
-			break;
-		}
-		return new CompilationUnitRewriteOperationsFix(Messages.format(messageTemplate, nullableAnnotationName),
-														operation.getCompilationUnit(), 
-														new CompilationUnitRewriteOperation[] {operation});
+		return new CompilationUnitRewriteOperationsFix(operation.getMessage(),
+													   operation.getCompilationUnit(), 
+													   new CompilationUnitRewriteOperation[] {operation});
 	}
 
-	ICleanUpFix createCleanUp(CompilationUnit compilationUnit, 
-								boolean addDefinitelyMissingReturnAnnotations, 
-								boolean addPotentiallyMissingReturnAnnotations, 
-								boolean addDefinitelyMissingParamAnnotations, 
-								IProblemLocation[] locations) 
+	// Entry for NullAnnotationsCleanup:
+	public ICleanUpFix createCleanUp(CompilationUnit compilationUnit, 
+									 boolean addDefinitelyMissingReturnAnnotations, 
+									 boolean addPotentiallyMissingReturnAnnotations, 
+									 boolean addDefinitelyMissingParamAnnotations, 
+									 IProblemLocation[] locations) 
 	{
 		
 		ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement();
@@ -200,7 +206,7 @@
 			}
 		}
 		
-		createAddNullableAnnotationOperations(compilationUnit, locations, operations);
+		createAddNullAnnotationOperations(compilationUnit, locations, operations);
 		
 		if (operations.size() == 0)
 			return null;
@@ -210,115 +216,40 @@
 	}
 
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	void createAddNullableAnnotationOperations(CompilationUnit compilationUnit, IProblemLocation[] locations, List result) {
+	void createAddNullAnnotationOperations(CompilationUnit compilationUnit, IProblemLocation[] locations, List result) {
 		String nullableAnnotationName = getNullableAnnotationName(compilationUnit.getJavaElement(), false);
 		String nonNullAnnotationName = getNonNullAnnotationName(compilationUnit.getJavaElement(), false);
 		Set<String> handledPositions = new HashSet<String>();
 		for (int i= 0; i < locations.length; i++) {
 			IProblemLocation problem= locations[i];
 			if (problem == null) continue; // problem was filtered out by createCleanUp()
-			CompilationUnitRewriteOperation fix = createAddNullableOperation(
-						compilationUnit, problem, nullableAnnotationName, nonNullAnnotationName, handledPositions, true, false);
+			String annotationToAdd = nullableAnnotationName;
+			String annotationToRemove = nonNullAnnotationName;
+			switch (problem.getProblemId()) {
+			case IllegalDefinitionToNonNullParameter:
+			case IllegalRedefinitionToNonNullParameter:
+				annotationToAdd = nonNullAnnotationName;
+				annotationToRemove = nullableAnnotationName;
+			}
+			CompilationUnitRewriteOperation fix = RewriteOperations.createAddAnnotationOperation(
+						compilationUnit, problem, annotationToAdd, annotationToRemove, handledPositions, true, false);
 			if (fix != null)
 				result.add(fix);
 		}
 	}
 	
-	RewriteOperations.SignatureAnnotationRewriteOperation createAddNullableOperation(CompilationUnit compilationUnit,
-			IProblemLocation problem, String nullableAnnotationName, String nonNullAnnotationName, Set<String> handledPositions,
-			boolean thisUnitOnly, boolean allowRemove) 
-	{
-		ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement();
-		if (!JavaModelUtil.is50OrHigher(cu.getJavaProject()))
-			return null;
-		
-		if (!isMissingNullableAnnotationProblem(problem.getProblemId())) // TODO is this obsolete?
-			return null;
-		
-		if (hasExplicitNullnessAnnotation(cu, problem.getOffset()))
-			return null;
-		
-		ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
-		if (selectedNode == null)
-			return null;
-			
-		RewriteOperations.SignatureAnnotationRewriteOperation result = null;
-		ASTNode declaringNode= getDeclaringNode(selectedNode);
-		if (selectedNode.getParent() instanceof MethodInvocation) {
-			// check problem ID!
-			MethodInvocation methodInvocation = (MethodInvocation)selectedNode.getParent();
-			int paramIdx = methodInvocation.arguments().indexOf(selectedNode);
-			IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
-			ASTNode methodDecl= compilationUnit.findDeclaringNode(methodBinding.getMethodDeclaration());
-			if (methodDecl == null) {
-				// is methodDecl defined in another CU?
-				ITypeBinding declaringTypeDecl= methodBinding.getDeclaringClass().getTypeDeclaration();
-				if (declaringTypeDecl.isFromSource()) {
-					ICompilationUnit targetCU = null;
-					try {
-						targetCU = ASTResolving.findCompilationUnitForBinding(cu, compilationUnit, declaringTypeDecl);
-					} catch (JavaModelException e) { /* can't do better */ }
-					if (targetCU != null) {
-						compilationUnit = ASTResolving.createQuickFixAST(targetCU, null);
-						methodDecl= compilationUnit.findDeclaringNode(methodBinding.getKey());
-					}
-				}
-			}
-			if (methodDecl != null)
-				result = new RewriteOperations.ParameterAnnotationRewriteOperation(compilationUnit,
-																				   (MethodDeclaration) methodDecl,
-																				   nullableAnnotationName,
-																				   nonNullAnnotationName,
-																				   paramIdx,
-																				   allowRemove);
-		} else if (declaringNode instanceof MethodDeclaration) {
-		
-			MethodDeclaration declaration= (MethodDeclaration) declaringNode;
-			
-			if (mayIndicateParameterNullcheck(problem.getProblemId())) {
-				if (selectedNode.getNodeType() == ASTNode.SIMPLE_NAME && declaration.getNodeType() == ASTNode.METHOD_DECLARATION) {
-					IBinding binding = ((SimpleName)selectedNode).resolveBinding();
-					if (binding.getKind() == IBinding.VARIABLE && ((IVariableBinding)binding).isParameter())
-						result = new RewriteOperations.ParameterAnnotationRewriteOperation(compilationUnit,
-																						   declaration,
-																						   nullableAnnotationName,
-																						   nonNullAnnotationName,
-																						   ((SimpleName) selectedNode).getIdentifier(),
-																						   allowRemove);
-				}
-			} else {
-				result = new RewriteOperations.ReturnAnnotationRewriteOperation(compilationUnit,
-																				declaration,
-																				nullableAnnotationName,
-																				nonNullAnnotationName,
-																				allowRemove);
-			}
-			
-		}
-		if (handledPositions != null && result != null) {
-			if (handledPositions.contains(result.getKey()))
-				return null;
-			handledPositions.add(result.getKey());
-		}
-		return result;
-	}
-
-	/** The relevant declaring node of a return statement is the enclosing method. */
-	ASTNode getDeclaringNode(ASTNode selectedNode) {
-		return ASTNodes.getParent(selectedNode, ASTNode.METHOD_DECLARATION);
-	}
-
-	static boolean isMissingNullableAnnotationProblem(int id) {
+	public static boolean isMissingNullAnnotationProblem(int id) {
 		return id == DefiniteNullFromNonNullMethod || id == PotentialNullFromNonNullMethod 
 				|| id == DefiniteNullToNonNullParameter || id == PotentialNullToNonNullParameter
+				|| id == IllegalRedefinitionToNullableReturn
 				|| mayIndicateParameterNullcheck(id);
 	}
 	
-	static boolean mayIndicateParameterNullcheck(int problemId) {
+	public static boolean mayIndicateParameterNullcheck(int problemId) {
 		return problemId == IProblem.NonNullLocalVariableComparisonYieldsFalse || problemId == IProblem.RedundantNullCheckOnNonNullLocalVariable;
 	}
 	
-	static boolean hasExplicitNullnessAnnotation(ICompilationUnit compilationUnit, int offset) {
+	public static boolean hasExplicitNullAnnotation(ICompilationUnit compilationUnit, int offset) {
 		try {
 			IJavaElement problemElement = compilationUnit.getElementAt(offset);
 			if (problemElement.getElementType() == IJavaElement.METHOD) {
@@ -337,7 +268,7 @@
 		return false;
 	}
 
-	static String getNullableAnnotationName(IJavaElement javaElement, boolean makeSimple) {
+	public static String getNullableAnnotationName(IJavaElement javaElement, boolean makeSimple) {
 		String qualifiedName = javaElement.getJavaProject().getOption(NullCompilerOptions.OPTION_NullableAnnotationName, true);
 		int lastDot;
 		if (makeSimple && qualifiedName != null && (lastDot = qualifiedName.lastIndexOf('.')) != -1)
@@ -345,7 +276,7 @@
 		return qualifiedName;
 	}
 
-	static String getNonNullAnnotationName(IJavaElement javaElement, boolean makeSimple) {
+	public static String getNonNullAnnotationName(IJavaElement javaElement, boolean makeSimple) {
 		String qualifiedName = javaElement.getJavaProject().getOption(NullCompilerOptions.OPTION_NonNullAnnotationName, true);
 		int lastDot;
 		if (makeSimple && qualifiedName != null && (lastDot = qualifiedName.lastIndexOf('.')) != -1)
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/RewriteOperations.java b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/RewriteOperations.java
index 01b20e2..6f6e7fe 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/RewriteOperations.java
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/quickfix/RewriteOperations.java
@@ -10,23 +10,41 @@
  *******************************************************************************/
 package org.eclipse.objectteams.internal.jdt.nullity.quickfix;
 
+import static org.eclipse.objectteams.internal.jdt.nullity.IConstants.IProblem.*;
+
 import java.util.List;
+import java.util.Set;
 
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.Annotation;
 import org.eclipse.jdt.core.dom.BodyDeclaration;
 import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
 import org.eclipse.jdt.core.dom.MarkerAnnotation;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
 import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFix.CompilationUnitRewriteOperation;
 import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 import org.eclipse.jdt.internal.corext.util.Messages;
+import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
 import org.eclipse.text.edits.TextEditGroup;
 
 @SuppressWarnings("restriction")
@@ -39,6 +57,7 @@
 		CompilationUnit fUnit;
 		
 		protected String fKey;
+		protected String fMessage; 
 		
 		/** A globally unique key that identifies the position being annotated (for avoiding double annotations). */
 		public String getKey() { return this.fKey; }
@@ -76,6 +95,10 @@
 			}
 			return true;
 		}
+
+		public String getMessage() {
+			return fMessage;
+		}
 	}
 	
 	/**
@@ -83,17 +106,17 @@
 	 * 
 	 * Crafted after the lead of Java50Fix.AnnotationRewriteOperation
 	 * @author stephan
-	 *
 	 */
 	static class ReturnAnnotationRewriteOperation extends SignatureAnnotationRewriteOperation {
 
 		private final BodyDeclaration fBodyDeclaration;
 
-		public ReturnAnnotationRewriteOperation(CompilationUnit unit,
-												MethodDeclaration method,
-												String annotationToAdd,
-												String annotationToRemove,
-												boolean allowRemove)
+		ReturnAnnotationRewriteOperation(CompilationUnit unit,
+										 MethodDeclaration method,
+										 String annotationToAdd,
+										 String annotationToRemove,
+										 boolean allowRemove,
+										 String message)
 		{
 			fUnit = unit;
 			fKey= method.resolveBinding().getKey()+"<return>"; //$NON-NLS-1$
@@ -101,13 +124,13 @@
 			fAnnotationToAdd= annotationToAdd;
 			fAnnotationToRemove= annotationToRemove;
 			fAllowRemove= allowRemove;
+			fMessage = message;
 		}
 
 		public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
 			AST ast= cuRewrite.getRoot().getAST();
 			ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(fBodyDeclaration, fBodyDeclaration.getModifiersProperty());
-			TextEditGroup group= createTextEditGroup(Messages.format(FixMessages.QuickFixes_declare_method_return_nullable, 
-													 BasicElementLabels.getJavaElementName(fAnnotationToAdd)), cuRewrite);
+			TextEditGroup group= createTextEditGroup(fMessage, cuRewrite);
 			if (!checkExisting(fBodyDeclaration.modifiers(), listRewrite, group))
 				return;
 			Annotation newAnnotation= ast.newMarkerAnnotation();
@@ -122,18 +145,20 @@
 
 		private SingleVariableDeclaration fArgument;
 
-		public ParameterAnnotationRewriteOperation(CompilationUnit unit,
-												   MethodDeclaration method,
-												   String annotationToAdd,
-												   String annotationToRemove,
-												   String paramName,
-												   boolean allowRemove)
+		ParameterAnnotationRewriteOperation(CompilationUnit unit,
+											MethodDeclaration method,
+											String annotationToAdd,
+											String annotationToRemove,
+											String paramName,
+											boolean allowRemove,
+											String message)
 		{
 			fUnit= unit;
 			fKey= method.resolveBinding().getKey();
 			fAnnotationToAdd= annotationToAdd;
 			fAnnotationToRemove= annotationToRemove;
 			fAllowRemove= allowRemove;
+			fMessage = message;
 			for (Object param : method.parameters()) {
 				SingleVariableDeclaration argument = (SingleVariableDeclaration) param;
 				if (argument.getName().getIdentifier().equals(paramName)) {
@@ -146,12 +171,13 @@
 			throw new RuntimeException("Argument "+paramName+" not found in method "+method.getName().getIdentifier()); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 
-		public ParameterAnnotationRewriteOperation(CompilationUnit unit,
-												   MethodDeclaration method,
-												   String annotationToAdd,
-												   String annotationToRemove,
-												   int paramIdx,
-												   boolean allowRemove)
+		ParameterAnnotationRewriteOperation(CompilationUnit unit,
+											MethodDeclaration method,
+											String annotationToAdd,
+											String annotationToRemove,
+											int paramIdx,
+											boolean allowRemove,
+											String message)
 		{
 			fUnit= unit;
 			fKey= method.resolveBinding().getKey();
@@ -160,13 +186,13 @@
 			fAllowRemove= allowRemove;
 			fArgument = (SingleVariableDeclaration) method.parameters().get(paramIdx);
 			fKey += fArgument.getName().getIdentifier();
+			fMessage = message;
 		}
 
 		public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel linkedModel) throws CoreException {
 			AST ast= cuRewrite.getRoot().getAST();
 			ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(fArgument, SingleVariableDeclaration.MODIFIERS2_PROPERTY);
-			TextEditGroup group= createTextEditGroup(Messages.format(FixMessages.QuickFixes_declare_method_parameter_nullable, 
-													 BasicElementLabels.getJavaElementName(fAnnotationToAdd)), cuRewrite);
+			TextEditGroup group= createTextEditGroup(fMessage, cuRewrite);
 			if (!checkExisting(fArgument.modifiers(), listRewrite, group))
 				return;
 			Annotation newAnnotation= ast.newMarkerAnnotation();
@@ -176,4 +202,220 @@
 			listRewrite.insertLast(newAnnotation, group); // null annotation is last modifier, directly preceding the return type
 		}
 	}
+
+	// Entry for QuickFixes:
+	public static SignatureAnnotationRewriteOperation createAddAnnotationOperation(CompilationUnit compilationUnit, 
+																				   IProblemLocation problem, 
+																				   String annotationToAdd, 
+																				   String annotationToRemove, 
+																				   Set<String> handledPositions,
+																				   boolean thisUnitOnly, 
+																				   boolean allowRemove) 
+	{
+		ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement();
+		if (!JavaModelUtil.is50OrHigher(cu.getJavaProject()))
+			return null;
+				
+		switch (problem.getProblemId()) {
+		case DefiniteNullToNonNullParameter:
+		case PotentialNullToNonNullParameter:
+		case IllegalRedefinitionToNullableReturn:
+		case IllegalDefinitionToNonNullParameter:
+		case IllegalRedefinitionToNonNullParameter:
+			// these affect another method
+			break;
+		default:
+			// if this method has annotations, don't change'em
+			if (QuickFixes.hasExplicitNullAnnotation(cu, problem.getOffset()))
+				return null;
+		}
+		
+		ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+		if (selectedNode == null)
+			return null;
+			
+		SignatureAnnotationRewriteOperation result = null;
+		ASTNode declaringNode= getDeclaringNode(selectedNode);
+		String message = null;
+		String annotationNameLabel = annotationToAdd; 
+		int lastDot = annotationToAdd.lastIndexOf('.');
+		if (lastDot != -1)
+			annotationNameLabel = annotationToAdd.substring(lastDot+1);
+		annotationNameLabel = BasicElementLabels.getJavaElementName(annotationNameLabel);
+	
+		if (selectedNode.getParent() instanceof MethodInvocation) {
+			// DefiniteNullToNonNullParameter || PotentialNullToNonNullParameter
+			MethodInvocation methodInvocation = (MethodInvocation)selectedNode.getParent();
+			int paramIdx = methodInvocation.arguments().indexOf(selectedNode);
+			IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
+			compilationUnit = findCUForMethod(compilationUnit, cu, methodBinding);
+			if (compilationUnit == null)
+				return null;
+			ASTNode methodDecl = compilationUnit.findDeclaringNode(methodBinding.getKey());
+			if (methodDecl == null)
+				return null;
+			message = Messages.format(FixMessages.QuickFixes_declare_method_parameter_nullable, annotationNameLabel);
+			result = new ParameterAnnotationRewriteOperation(compilationUnit,
+														     (MethodDeclaration) methodDecl,
+														     annotationToAdd,
+														     annotationToRemove,
+														     paramIdx,
+														     allowRemove,
+														     message);
+		} else if (declaringNode instanceof MethodDeclaration) {
+			
+			// complaint is in signature of this method
+			
+			MethodDeclaration declaration= (MethodDeclaration) declaringNode;
+			
+			switch (problem.getProblemId()) {
+				case IProblem.NonNullLocalVariableComparisonYieldsFalse:
+				case IProblem.RedundantNullCheckOnNonNullLocalVariable:
+					// statements suggest changing parameters:
+					if (selectedNode.getNodeType() == ASTNode.SIMPLE_NAME && declaration.getNodeType() == ASTNode.METHOD_DECLARATION) {
+						IBinding binding = ((SimpleName)selectedNode).resolveBinding();
+						if (binding.getKind() == IBinding.VARIABLE && ((IVariableBinding)binding).isParameter()) {
+							message = Messages.format(FixMessages.QuickFixes_declare_method_parameter_nullable, annotationNameLabel);
+							result = new ParameterAnnotationRewriteOperation(compilationUnit,
+																							   declaration,
+																							   annotationToAdd,
+																							   annotationToRemove,
+																							   ((SimpleName) selectedNode).getIdentifier(),
+																							   allowRemove,
+																							   message);
+						}
+					}
+					break;
+				case IllegalDefinitionToNonNullParameter:
+				case IllegalRedefinitionToNonNullParameter:
+					result = createChangeOverriddenParameterOperation(compilationUnit, cu, declaration, selectedNode, allowRemove,
+																	  annotationToAdd, annotationToRemove, annotationNameLabel);
+					break;
+				case IllegalRedefinitionToNullableReturn:
+					result = createChangeOverriddenReturnOperation(compilationUnit, cu, declaration, allowRemove,
+																   annotationToAdd, annotationToRemove, annotationNameLabel);
+					break;
+				case DefiniteNullFromNonNullMethod:
+				case PotentialNullFromNonNullMethod:
+					message = Messages.format(FixMessages.QuickFixes_declare_method_parameter_nullable, annotationNameLabel);
+					result = new ReturnAnnotationRewriteOperation(compilationUnit,
+																				    declaration,
+																				    annotationToAdd,
+																				    annotationToRemove,
+																				    allowRemove,
+																				    message);
+					break;
+			}
+			
+		}
+		if (handledPositions != null && result != null) {
+			if (handledPositions.contains(result.getKey()))
+				return null;
+			handledPositions.add(result.getKey());
+		}
+		return result;
+	}
+
+	static SignatureAnnotationRewriteOperation createChangeOverriddenParameterOperation(CompilationUnit compilationUnit,
+																						ICompilationUnit cu,
+																						MethodDeclaration declaration,
+																						ASTNode selectedNode,
+																						boolean allowRemove,
+																						String annotationToAdd,
+																						String annotationToRemove,
+																						String annotationNameLabel)
+	{
+		SignatureAnnotationRewriteOperation result;
+		String message;
+		IMethodBinding methodDeclBinding= declaration.resolveBinding();
+		if (methodDeclBinding == null)
+			return null;
+		
+		IMethodBinding overridden= Bindings.findOverriddenMethod(methodDeclBinding, false);
+		if (overridden == null)
+			return null;
+		compilationUnit = findCUForMethod(compilationUnit, cu, overridden);
+		if (compilationUnit == null)
+			return null;
+		ASTNode methodDecl = compilationUnit.findDeclaringNode(overridden.getKey());
+		if (methodDecl == null)
+			return null;
+		declaration = (MethodDeclaration) methodDecl;
+		message = Messages.format(FixMessages.QuickFixes_declare_overridden_parameter_as_nonnull, 
+				                  new String[] {
+									annotationNameLabel,
+									BasicElementLabels.getJavaElementName(overridden.getDeclaringClass().getName())
+								  });
+		result = new ParameterAnnotationRewriteOperation(compilationUnit,
+													     declaration,
+													     annotationToAdd,
+													     annotationToRemove,
+													     ((SimpleName) selectedNode).getIdentifier(),
+													     allowRemove,
+													     message);
+		return result;
+	}
+
+	static SignatureAnnotationRewriteOperation createChangeOverriddenReturnOperation(CompilationUnit compilationUnit,
+																					 ICompilationUnit cu,
+																					 MethodDeclaration declaration,
+																					 boolean allowRemove,
+																					 String annotationToAdd,
+																					 String annotationToRemove,
+																					 String annotationNameLabel) 
+	{
+		SignatureAnnotationRewriteOperation result;
+		String message;
+		IMethodBinding methodDeclBinding= declaration.resolveBinding();
+		if (methodDeclBinding == null)
+			return null;
+		
+		IMethodBinding overridden= Bindings.findOverriddenMethod(methodDeclBinding, false);
+		if (overridden == null)
+			return null;
+		compilationUnit = findCUForMethod(compilationUnit, cu, overridden);
+		if (compilationUnit == null)
+			return null;
+		ASTNode methodDecl = compilationUnit.findDeclaringNode(overridden.getKey());
+		if (methodDecl == null)
+			return null;
+		declaration = (MethodDeclaration) methodDecl;
+		message = Messages.format(FixMessages.QuickFixes_declare_overridden_return_as_nullable, 
+				                  new String[] {
+									annotationNameLabel,
+									BasicElementLabels.getJavaElementName(overridden.getDeclaringClass().getName())
+								  });
+		result = new ReturnAnnotationRewriteOperation(compilationUnit,
+												      declaration,
+												      annotationToAdd,
+												      annotationToRemove,
+												      allowRemove,
+												      message);
+		return result;
+	}
+
+	static CompilationUnit findCUForMethod(CompilationUnit compilationUnit, ICompilationUnit cu, IMethodBinding methodBinding) 
+	{
+		ASTNode methodDecl= compilationUnit.findDeclaringNode(methodBinding.getMethodDeclaration());
+		if (methodDecl == null) {
+			// is methodDecl defined in another CU?
+			ITypeBinding declaringTypeDecl= methodBinding.getDeclaringClass().getTypeDeclaration();
+			if (declaringTypeDecl.isFromSource()) {
+				ICompilationUnit targetCU = null;
+				try {
+					targetCU = ASTResolving.findCompilationUnitForBinding(cu, compilationUnit, declaringTypeDecl);
+				} catch (JavaModelException e) { /* can't do better */ }
+				if (targetCU != null) {
+					return ASTResolving.createQuickFixAST(targetCU, null);
+				}
+			}
+			return null;
+		}
+		return compilationUnit;
+	}
+
+	/** The relevant declaring node of a return statement is the enclosing method. */
+	static ASTNode getDeclaringNode(ASTNode selectedNode) {
+		return ASTNodes.getParent(selectedNode, ASTNode.METHOD_DECLARATION);
+	}
 }