/*******************************************************************************
* Copyright (c) 2011, 2013 GK Software AG and others.
* 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:
* Stephan Herrmann - [quick fix] Add quick fixes for null annotations - https://bugs.eclipse.org/337977
* IBM Corporation - bug fixes
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.correction;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.swt.graphics.Image;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.fix.NullAnnotationsFix;
import org.eclipse.jdt.internal.corext.fix.NullAnnotationsRewriteOperations.ChangeKind;
import org.eclipse.jdt.ui.text.java.IInvocationContext;
import org.eclipse.jdt.ui.text.java.IProblemLocation;
import org.eclipse.jdt.ui.text.java.correction.ICommandAccess;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.fix.NullAnnotationsCleanUp;
import org.eclipse.jdt.internal.ui.text.correction.proposals.ExtractToNullCheckedLocalProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposal;
/**
* Quick Fixes for null-annotation related problems.
*/
public class NullAnnotationsCorrectionProcessor {
// pre: changeKind != OVERRIDDEN
public static void addReturnAndArgumentTypeProposal(IInvocationContext context, IProblemLocation problem, ChangeKind changeKind,
Collection<ICommandAccess> proposals) {
CompilationUnit astRoot= context.getASTRoot();
ASTNode selectedNode= problem.getCoveringNode(astRoot);
boolean isArgumentProblem= NullAnnotationsFix.isComplainingAboutArgument(selectedNode);
if (isArgumentProblem || NullAnnotationsFix.isComplainingAboutReturn(selectedNode))
addNullAnnotationInSignatureProposal(context, problem, proposals, changeKind, isArgumentProblem);
}
public static void addNullAnnotationInSignatureProposal(IInvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals, ChangeKind changeKind, boolean isArgumentProblem) {
NullAnnotationsFix fix= NullAnnotationsFix.createNullAnnotationInSignatureFix(context.getASTRoot(), problem,
changeKind, isArgumentProblem);
if (fix != null) {
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
Map<String, String> options= new Hashtable<String, String>();
if (fix.getCu() != context.getASTRoot()) {
// workaround: adjust the unit to operate on, depending on the findings of RewriteOperations.createAddAnnotationOperation(..)
final CompilationUnit cu= fix.getCu();
final IInvocationContext originalContext= context;
context= new IInvocationContext() {
public int getSelectionOffset() {
return originalContext.getSelectionOffset();
}
public int getSelectionLength() {
return originalContext.getSelectionLength();
}
public ASTNode getCoveringNode() {
return originalContext.getCoveringNode();
}
public ASTNode getCoveredNode() {
return originalContext.getCoveredNode();
}
public ICompilationUnit getCompilationUnit() {
return (ICompilationUnit) cu.getJavaElement();
}
public CompilationUnit getASTRoot() {
return cu;
}
};
}
int relevance= (changeKind == ChangeKind.OVERRIDDEN) ? IProposalRelevance.CHANGE_NULLNESS_ANNOTATION_IN_OVERRIDDEN_METHOD : IProposalRelevance.CHANGE_NULLNESS_ANNOTATION; //raise local change above change in overridden method
FixCorrectionProposal proposal= new FixCorrectionProposal(fix, new NullAnnotationsCleanUp(options, problem.getProblemId()), relevance, image, context);
proposals.add(proposal);
}
}
public static void addRemoveRedundantAnnotationProposal(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) {
NullAnnotationsFix fix= NullAnnotationsFix.createRemoveRedundantNullAnnotationsFix(context.getASTRoot(), problem);
if (fix == null)
return;
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
Map<String, String> options= new Hashtable<String, String>();
FixCorrectionProposal proposal= new FixCorrectionProposal(fix, new NullAnnotationsCleanUp(options, problem.getProblemId()), IProposalRelevance.REMOVE_REDUNDANT_NULLNESS_ANNOTATION, image, context);
proposals.add(proposal);
}
/**
* Fix for {@link IProblem#NullableFieldReference}
* @param context context
* @param problem problem to be fixed
* @param proposals accumulator for computed proposals
*/
public static void addExtractCheckedLocalProposal(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) {
CompilationUnit compilationUnit = context.getASTRoot();
ICompilationUnit cu= (ICompilationUnit) compilationUnit.getJavaElement();
ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
SimpleName name= findProblemFieldName(selectedNode, problem.getProblemId());
if (name == null)
return;
ASTNode method= ASTNodes.getParent(selectedNode, MethodDeclaration.class);
if (method == null)
method= ASTNodes.getParent(selectedNode, Initializer.class);
if (method == null)
return;
proposals.add(new ExtractToNullCheckedLocalProposal(cu, compilationUnit, name, method));
}
private static SimpleName findProblemFieldName(ASTNode selectedNode, int problemID) {
// if a field access occurs in an compatibility situation (assignment/return/argument)
// with expected type declared @NonNull we first need to find the SimpleName inside:
if (selectedNode instanceof FieldAccess)
selectedNode= ((FieldAccess) selectedNode).getName();
else if (selectedNode instanceof QualifiedName)
selectedNode= ((QualifiedName) selectedNode).getName();
if (selectedNode instanceof SimpleName) {
SimpleName name= (SimpleName) selectedNode;
if (problemID == IProblem.NullableFieldReference)
return name;
// not field dereference, but compatibility issue - is value a field reference?
IBinding binding= name.resolveBinding();
if ((binding instanceof IVariableBinding) && ((IVariableBinding) binding).isField())
return name;
}
return null;
}
}