/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [quick fix] proposes wrong cast from Object to primitive int - https://bugs
* .eclipse.org/bugs/show_bug.cgi?id=100593
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [quick fix] "Add exceptions to..." quickfix does nothing - https://bugs.eclipse
* .org/bugs/show_bug.cgi?id=107924
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdt.internal.text.correction;
import org.eclipse.che.ide.ext.java.jdt.Images;
import org.eclipse.che.ide.ext.java.jdt.codeassistant.api.IProblemLocation;
import org.eclipse.che.ide.ext.java.jdt.core.dom.AST;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayInitializer;
import org.eclipse.che.ide.ext.java.jdt.core.dom.Assignment;
import org.eclipse.che.ide.ext.java.jdt.core.dom.BodyDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.CastExpression;
import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit;
import org.eclipse.che.ide.ext.java.jdt.core.dom.EnhancedForStatement;
import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression;
import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldAccess;
import org.eclipse.che.ide.ext.java.jdt.core.dom.IBinding;
import org.eclipse.che.ide.ext.java.jdt.core.dom.IMethodBinding;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding;
import org.eclipse.che.ide.ext.java.jdt.core.dom.IVariableBinding;
import org.eclipse.che.ide.ext.java.jdt.core.dom.InfixExpression;
import org.eclipse.che.ide.ext.java.jdt.core.dom.MemberValuePair;
import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.Name;
import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.Type;
import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ASTResolving;
import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.Bindings;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ASTRewriteCorrectionProposal;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.CastCorrectionProposal;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ChangeMethodSignatureProposal;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ChangeMethodSignatureProposal.ChangeDescription;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ChangeMethodSignatureProposal.RemoveDescription;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.LinkedCorrectionProposal;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.TypeChangeCorrectionProposal;
import org.eclipse.che.ide.ext.java.jdt.internal.ui.BindingLabelProvider;
import org.eclipse.che.ide.ext.java.jdt.internal.ui.JavaElementLabels;
import org.eclipse.che.ide.ext.java.jdt.quickassist.api.InvocationContext;
import org.eclipse.che.ide.runtime.CoreException;
import java.util.ArrayList;
import java.util.Collection;
public class TypeMismatchSubProcessor {
private TypeMismatchSubProcessor() {
}
public static void addTypeMismatchProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) throws CoreException {
String[] args = problem.getProblemArguments();
if (args.length != 2) {
return;
}
CompilationUnit astRoot = context.getASTRoot();
AST ast = astRoot.getAST();
ASTNode selectedNode = problem.getCoveredNode(astRoot);
if (!(selectedNode instanceof Expression)) {
return;
}
Expression nodeToCast = (Expression)selectedNode;
Name receiverNode = null;
ITypeBinding castTypeBinding = null;
int parentNodeType = selectedNode.getParent().getNodeType();
if (parentNodeType == ASTNode.ASSIGNMENT) {
Assignment assign = (Assignment)selectedNode.getParent();
Expression leftHandSide = assign.getLeftHandSide();
if (selectedNode.equals(leftHandSide)) {
nodeToCast = assign.getRightHandSide();
}
castTypeBinding = assign.getLeftHandSide().resolveTypeBinding();
if (leftHandSide instanceof Name) {
receiverNode = (Name)leftHandSide;
} else if (leftHandSide instanceof FieldAccess) {
receiverNode = ((FieldAccess)leftHandSide).getName();
}
} else if (parentNodeType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
VariableDeclarationFragment frag = (VariableDeclarationFragment)selectedNode.getParent();
if (selectedNode.equals(frag.getName()) || selectedNode.equals(frag.getInitializer())) {
nodeToCast = frag.getInitializer();
castTypeBinding = ASTNodes.getType(frag).resolveBinding();
receiverNode = frag.getName();
}
} else if (parentNodeType == ASTNode.MEMBER_VALUE_PAIR) {
receiverNode = ((MemberValuePair)selectedNode.getParent()).getName();
castTypeBinding = ASTResolving.guessBindingForReference(nodeToCast);
} else if (parentNodeType == ASTNode.SINGLE_MEMBER_ANNOTATION) {
receiverNode = ((SingleMemberAnnotation)selectedNode.getParent()).getTypeName(); // use the type name
castTypeBinding = ASTResolving.guessBindingForReference(nodeToCast);
} else {
// try to find the binding corresponding to 'castTypeName'
castTypeBinding = ASTResolving.guessBindingForReference(nodeToCast);
}
if (castTypeBinding == null) {
return;
}
ITypeBinding currBinding = nodeToCast.resolveTypeBinding();
if (!(nodeToCast instanceof ArrayInitializer)) {
ITypeBinding castFixType = null;
if (currBinding == null || castTypeBinding.isCastCompatible(currBinding)
|| nodeToCast instanceof CastExpression) {
castFixType = castTypeBinding;
} else if (true /*JavaModelUtil.is50OrHigher(cu.getJavaProject())*/) {
ITypeBinding boxUnboxedTypeBinding = boxUnboxPrimitives(castTypeBinding, currBinding, ast);
if (boxUnboxedTypeBinding != castTypeBinding && boxUnboxedTypeBinding.isCastCompatible(currBinding)) {
castFixType = boxUnboxedTypeBinding;
}
}
if (castFixType != null) {
proposals.add(createCastProposal(context, castFixType, nodeToCast, 7));
}
}
boolean nullOrVoid = currBinding == null || "void".equals(currBinding.getName()); //$NON-NLS-1$
// change method return statement to actual type
if (!nullOrVoid && parentNodeType == ASTNode.RETURN_STATEMENT) {
BodyDeclaration decl = ASTResolving.findParentBodyDeclaration(selectedNode);
if (decl instanceof MethodDeclaration) {
MethodDeclaration methodDeclaration = (MethodDeclaration)decl;
currBinding = Bindings.normalizeTypeBinding(currBinding);
if (currBinding == null) {
currBinding = ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
if (currBinding.isWildcardType()) {
currBinding = ASTResolving.normalizeWildcardType(currBinding, true, ast);
}
ASTRewrite rewrite = ASTRewrite.create(ast);
String label =
CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_changereturntype_description(currBinding.getName());
Images image = Images.correction_change;
LinkedCorrectionProposal proposal =
new LinkedCorrectionProposal(label, rewrite, 6, context.getDocument(), image);
ImportRewrite imports = proposal.createImportRewrite(astRoot);
ImportRewriteContext importRewriteContext = new ContextSensitiveImportRewriteContext(decl, imports);
Type newReturnType = imports.addImport(currBinding, ast, importRewriteContext);
rewrite.replace(methodDeclaration.getReturnType2(), newReturnType, null);
String returnKey = "return"; //$NON-NLS-1$
// proposal.addLinkedPosition(rewrite.track(newReturnType), true, returnKey);
ITypeBinding[] typeSuggestions = ASTResolving.getRelaxingTypes(ast, currBinding);
// for (int i = 0; i < typeSuggestions.length; i++)
// {
// proposal.addLinkedPositionProposal(returnKey, typeSuggestions[i]);
// }
proposals.add(proposal);
}
}
if (!nullOrVoid && receiverNode != null) {
currBinding = Bindings.normalizeTypeBinding(currBinding);
if (currBinding == null) {
currBinding = ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
if (currBinding.isWildcardType()) {
currBinding = ASTResolving.normalizeWildcardType(currBinding, true, ast);
}
addChangeSenderTypeProposals(context, receiverNode, currBinding, true, 6, proposals);
}
addChangeSenderTypeProposals(context, nodeToCast, castTypeBinding, false, 5, proposals);
if (castTypeBinding == ast.resolveWellKnownType("boolean") && currBinding != null && !currBinding.isPrimitive() &&
!Bindings.isVoidType(currBinding)) { //$NON-NLS-1$
String label = CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_insertnullcheck_description();
Images image = Images.correction_change;
ASTRewrite rewrite = ASTRewrite.create(astRoot.getAST());
InfixExpression expression = ast.newInfixExpression();
expression.setLeftOperand((Expression)rewrite.createMoveTarget(nodeToCast));
expression.setRightOperand(ast.newNullLiteral());
expression.setOperator(InfixExpression.Operator.NOT_EQUALS);
rewrite.replace(nodeToCast, expression, null);
proposals.add(new ASTRewriteCorrectionProposal(label, rewrite, 2, context.getDocument(), image));
}
}
public static ITypeBinding boxUnboxPrimitives(ITypeBinding castType, ITypeBinding toCast, AST ast) {
/*
* e.g:
* void m(toCast var) {
* castType i= var;
* }
*/
if (castType.isPrimitive() && !toCast.isPrimitive()) {
return Bindings.getBoxedTypeBinding(castType, ast);
} else if (!castType.isPrimitive() && toCast.isPrimitive()) {
return Bindings.getUnboxedTypeBinding(castType, ast);
} else {
return castType;
}
}
public static void addChangeSenderTypeProposals(InvocationContext context, Expression nodeToCast,
ITypeBinding castTypeBinding, boolean isAssignedNode, int relevance,
Collection<ICommandAccess> proposals) {
IBinding callerBinding = Bindings.resolveExpressionBinding(nodeToCast, false);
CompilationUnit astRoot = context.getASTRoot();
boolean isThisCu = false;
ITypeBinding declaringType = null;
IBinding callerBindingDecl = callerBinding;
if (callerBinding instanceof IVariableBinding) {
IVariableBinding variableBinding = (IVariableBinding)callerBinding;
if (variableBinding.isEnumConstant()) {
return;
}
if (!variableBinding.isField()) {
isThisCu = true;
} else {
callerBindingDecl = variableBinding.getVariableDeclaration();
ITypeBinding declaringClass = variableBinding.getDeclaringClass();
if (declaringClass == null) {
return; // array length
}
declaringType = declaringClass.getTypeDeclaration();
}
} else if (callerBinding instanceof IMethodBinding) {
IMethodBinding methodBinding = (IMethodBinding)callerBinding;
if (!methodBinding.isConstructor()) {
declaringType = methodBinding.getDeclaringClass().getTypeDeclaration();
callerBindingDecl = methodBinding.getMethodDeclaration();
}
} else if (callerBinding instanceof ITypeBinding
&& nodeToCast.getLocationInParent() == SingleMemberAnnotation.TYPE_NAME_PROPERTY) {
declaringType = (ITypeBinding)callerBinding;
callerBindingDecl = Bindings.findMethodInType(declaringType, "value", (String[])null); //$NON-NLS-1$
if (callerBindingDecl == null) {
return;
}
}
if (declaringType != null && declaringType.isFromSource()) {
//TODO load class file
// targetCu = ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringType);
isThisCu = true;
}
if (isThisCu && ASTResolving.isUseableTypeInContext(castTypeBinding, callerBindingDecl, false)) {
proposals.add(new TypeChangeCorrectionProposal(callerBindingDecl, astRoot, castTypeBinding, isAssignedNode,
relevance, context.getDocument()));
}
// add interface to resulting type
if (!isAssignedNode) {
ITypeBinding nodeType = nodeToCast.resolveTypeBinding();
if (castTypeBinding.isInterface() && nodeType != null && nodeType.isClass() && !nodeType.isAnonymous()
&& nodeType.isFromSource()) {
//TODO load class file
// ITypeBinding typeDecl = nodeType.getTypeDeclaration();
// // ICompilationUnit nodeCu = ASTResolving.findCompilationUnitForBinding(cu, astRoot, typeDecl);
// if (ASTResolving.isUseableTypeInContext(castTypeBinding, typeDecl, true))
// {
// proposals.add(new ImplementInterfaceProposal(typeDecl, astRoot, castTypeBinding, relevance - 1, context
// .getDocument()));
// }
}
}
}
public static ASTRewriteCorrectionProposal createCastProposal(InvocationContext context,
ITypeBinding castTypeBinding, Expression nodeToCast, int relevance) {
String label;
String castType = BindingLabelProvider.getBindingLabel(castTypeBinding, JavaElementLabels.ALL_DEFAULT);
if (nodeToCast.getNodeType() == ASTNode.CAST_EXPRESSION) {
label = CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_changecast_description(castType);
} else {
label = CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_addcast_description(castType);
}
return new CastCorrectionProposal(label, nodeToCast, castTypeBinding, relevance, context.getDocument());
}
public static void addIncompatibleReturnTypeProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) {
CompilationUnit astRoot = context.getASTRoot();
ASTNode selectedNode = problem.getCoveringNode(astRoot);
if (selectedNode == null) {
return;
}
MethodDeclaration decl = ASTResolving.findParentMethodDeclaration(selectedNode);
if (decl == null) {
return;
}
IMethodBinding methodDeclBinding = decl.resolveBinding();
if (methodDeclBinding == null) {
return;
}
ITypeBinding returnType = methodDeclBinding.getReturnType();
IMethodBinding overridden = Bindings.findOverriddenMethod(methodDeclBinding, false);
if (overridden == null || overridden.getReturnType() == returnType) {
return;
}
IMethodBinding methodDecl = methodDeclBinding.getMethodDeclaration();
ITypeBinding overriddenReturnType = overridden.getReturnType();
// if (!JavaModelUtil.is50OrHigher(context.getCompilationUnit().getJavaProject()))
// {
// overriddenReturnType = overriddenReturnType.getErasure();
// }
proposals.add(new TypeChangeCorrectionProposal(methodDecl, astRoot, overriddenReturnType, false, 8, context
.getDocument()));
IMethodBinding overriddenDecl = overridden.getMethodDeclaration();
ITypeBinding overridenDeclType = overriddenDecl.getDeclaringClass();
if (overridenDeclType.isFromSource()) {
//TODO load class
// // targetCu = ASTResolving.findCompilationUnitForBinding(cu, astRoot, overridenDeclType);
// if (ASTResolving.isUseableTypeInContext(returnType, overriddenDecl, false))
// {
// TypeChangeCorrectionProposal proposal =
// new TypeChangeCorrectionProposal(overriddenDecl, astRoot, returnType, false, 7, context.getDocument());
// if (overridenDeclType.isInterface())
// {
// proposal.setDisplayName(CorrectionMessages.INSTANCE
// .TypeMismatchSubProcessor_changereturnofimplemented_description(overriddenDecl.getName()));
// }
// else
// {
// proposal.setDisplayName(CorrectionMessages.INSTANCE
// .TypeMismatchSubProcessor_changereturnofoverridden_description(overriddenDecl.getName()));
// }
// proposals.add(proposal);
// }
}
}
public static void addIncompatibleThrowsProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) {
CompilationUnit astRoot = context.getASTRoot();
ASTNode selectedNode = problem.getCoveringNode(astRoot);
if (!(selectedNode instanceof MethodDeclaration)) {
return;
}
MethodDeclaration decl = (MethodDeclaration)selectedNode;
IMethodBinding methodDeclBinding = decl.resolveBinding();
if (methodDeclBinding == null) {
return;
}
IMethodBinding overridden = Bindings.findOverriddenMethod(methodDeclBinding, false);
if (overridden == null) {
return;
}
ITypeBinding[] methodExceptions = methodDeclBinding.getExceptionTypes();
ITypeBinding[] definedExceptions = overridden.getExceptionTypes();
ArrayList<ITypeBinding> undeclaredExceptions = new ArrayList<ITypeBinding>();
{
ChangeDescription[] changes = new ChangeDescription[methodExceptions.length];
for (int i = 0; i < methodExceptions.length; i++) {
if (!isDeclaredException(methodExceptions[i], definedExceptions)) {
changes[i] = new RemoveDescription();
undeclaredExceptions.add(methodExceptions[i]);
}
}
String label =
CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_removeexceptions_description(methodDeclBinding
.getName());
Images image = Images.remove_correction;
proposals.add(new ChangeMethodSignatureProposal(label, astRoot, methodDeclBinding, null, changes, 8, context
.getDocument(), image));
}
//TODO load class file
//ITypeBinding declaringType = overridden.getDeclaringClass();
// if (declaringType.isFromSource())
// {
// targetCu = ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringType);
// }
// if (targetCu != null)
// {
// ChangeDescription[] changes = new ChangeDescription[definedExceptions.length + undeclaredExceptions.size()];
//
// for (int i = 0; i < undeclaredExceptions.size(); i++)
// {
// changes[i + definedExceptions.length] = new InsertDescription(undeclaredExceptions.get(i), ""); //$NON-NLS-1$
// }
// IMethodBinding overriddenDecl = overridden.getMethodDeclaration();
// String label =
// CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_addexceptions_description(declaringType.getName(),
// overridden.getName());
// Image image = new Image(JavaClientBundle.INSTANCE.add_obj());
// proposals.add(new ChangeMethodSignatureProposal(label, astRoot, overriddenDecl, null, changes, 7, context
// .getDocument(), image));
// }
}
private static boolean isDeclaredException(ITypeBinding curr, ITypeBinding[] declared) {
for (int i = 0; i < declared.length; i++) {
if (Bindings.isSuperType(declared[i], curr)) {
return true;
}
}
return false;
}
public static void addTypeMismatchInForEachProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) {
CompilationUnit astRoot = context.getASTRoot();
ASTNode selectedNode = problem.getCoveringNode(astRoot);
if (selectedNode == null || selectedNode.getLocationInParent() != EnhancedForStatement.EXPRESSION_PROPERTY) {
return;
}
EnhancedForStatement forStatement = (EnhancedForStatement)selectedNode.getParent();
ITypeBinding expressionBinding = forStatement.getExpression().resolveTypeBinding();
if (expressionBinding == null) {
return;
}
ITypeBinding expectedBinding;
if (expressionBinding.isArray()) {
expectedBinding = expressionBinding.getComponentType();
} else {
IMethodBinding iteratorMethod = Bindings.findMethodInHierarchy(expressionBinding, "iterator", new String[0]); //$NON-NLS-1$
if (iteratorMethod == null) {
return;
}
ITypeBinding[] typeArguments = iteratorMethod.getReturnType().getTypeArguments();
if (typeArguments.length != 1) {
return;
}
expectedBinding = typeArguments[0];
}
AST ast = astRoot.getAST();
expectedBinding = Bindings.normalizeForDeclarationUse(expectedBinding, ast);
SingleVariableDeclaration parameter = forStatement.getParameter();
String label =
CorrectionMessages.INSTANCE.TypeMismatchSubProcessor_incompatible_for_each_type_description(parameter
.getName()
.getIdentifier(),
BindingLabelProvider
.getBindingLabel(
expectedBinding,
BindingLabelProvider.DEFAULT_TEXTFLAGS));
Images image = Images.correction_change;
ASTRewrite rewrite = ASTRewrite.create(ast);
ASTRewriteCorrectionProposal proposal =
new ASTRewriteCorrectionProposal(label, rewrite, 5, context.getDocument(), image);
ImportRewrite importRewrite = proposal.createImportRewrite(astRoot);
ImportRewriteContext importRewriteContext =
new ContextSensitiveImportRewriteContext(ASTResolving.findParentBodyDeclaration(selectedNode), importRewrite);
Type newType = importRewrite.addImport(expectedBinding, ast, importRewriteContext);
rewrite.replace(parameter.getType(), newType, null);
proposals.add(proposal);
}
}