blob: 6f3f6115d88bace524a7e59a0b955ba9e6961d35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2021 Fabrice TIERCELIN and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Fabrice TIERCELIN - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.fix;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer;
import org.eclipse.jdt.core.manipulation.ICleanUpFixCore;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.ui.fix.MultiFixMessages;
public class ValueOfRatherThanInstantiationFixCore extends CompilationUnitRewriteOperationsFixCore {
public static final class ValueOfRatherThanInstantiationFinder extends ASTVisitor {
private List<CompilationUnitRewriteOperation> fResult;
public ValueOfRatherThanInstantiationFinder(List<CompilationUnitRewriteOperation> ops) {
fResult= ops;
}
@Override
public boolean visit(final ClassInstanceCreation visited) {
ITypeBinding typeBinding= visited.getType().resolveBinding();
List<Expression> args= visited.arguments();
if (args.size() == 1) {
Expression arg0= args.get(0);
if (ASTNodes.hasType(typeBinding, Float.class.getCanonicalName())) {
if (ASTNodes.hasType(arg0, double.class.getSimpleName())) {
fResult.add(new ValueOfRatherThanInstantiationFloatWithValueOfFixOperation(visited, typeBinding, arg0));
return false;
}
if (ASTNodes.hasType(arg0, Double.class.getCanonicalName())) {
fResult.add(new ValueOfRatherThanInstantiationFloatWithFloatValueFixOperation(visited, arg0));
return false;
}
}
if (ASTNodes.hasType(typeBinding, Boolean.class.getCanonicalName(), Integer.class.getCanonicalName(), Long.class.getCanonicalName(), Double.class.getCanonicalName(),
Short.class.getCanonicalName(), Float.class.getCanonicalName(), Byte.class.getCanonicalName(), Character.class.getCanonicalName())) {
ITypeBinding destinationTypeBinding= ASTNodes.getTargetType(visited);
if (destinationTypeBinding != null
&& destinationTypeBinding.isPrimitive()
&& !ASTNodes.hasType(arg0, String.class.getCanonicalName())) {
fResult.add(new ValueOfRatherThanInstantiationWithTheSingleArgumentFixOperation(visited));
return false;
}
if (JavaModelUtil.is50OrHigher(((CompilationUnit) visited.getRoot()).getJavaElement().getJavaProject())
|| ASTNodes.hasType(typeBinding, String.class.getCanonicalName())) {
fResult.add(new ValueOfRatherThanInstantiationWithValueOfFixOperation(visited, typeBinding, arg0));
return false;
}
}
}
return true;
}
}
public static class ValueOfRatherThanInstantiationFloatWithValueOfFixOperation extends CompilationUnitRewriteOperation {
private final ClassInstanceCreation visited;
private final ITypeBinding typeBinding;
private final Expression arg0;
public ValueOfRatherThanInstantiationFloatWithValueOfFixOperation(final ClassInstanceCreation visited, final ITypeBinding typeBinding, final Expression arg0) {
this.visited= visited;
this.typeBinding= typeBinding;
this.arg0= arg0;
}
@Override
public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException {
ASTRewrite rewrite= cuRewrite.getASTRewrite();
AST ast= cuRewrite.getRoot().getAST();
TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_float_with_valueof, cuRewrite);
rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() {
@Override
public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) {
return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength());
}
return super.computeSourceRange(nodeWithComment);
}
});
MethodInvocation valueOfMethod= ast.newMethodInvocation();
valueOfMethod.setExpression(ASTNodeFactory.newName(ast, typeBinding.getName()));
valueOfMethod.setName(ast.newSimpleName("valueOf")); //$NON-NLS-1$
// remove all casts and parentheses from the argument
Expression newArgument= arg0;
while (true) {
if (newArgument instanceof CastExpression) {
newArgument= ((CastExpression) newArgument).getExpression();
continue;
}
if (newArgument instanceof ParenthesizedExpression) {
newArgument= ((ParenthesizedExpression) newArgument).getExpression();
continue;
}
break;
}
if (!ASTNodes.hasType(newArgument, float.class.getSimpleName())) {
CastExpression newCastExpression= ast.newCastExpression();
newCastExpression.setType(ast.newPrimitiveType(PrimitiveType.FLOAT));
Expression moveTarget= ASTNodes.createMoveTarget(rewrite, newArgument);
if (newArgument.getNodeType() == ASTNode.INFIX_EXPRESSION || newArgument.getNodeType() == ASTNode.ASSIGNMENT) {
// those expressions could be numeric and have less precedence than a cast, so need to put parens
ParenthesizedExpression parens= ast.newParenthesizedExpression();
parens.setExpression(moveTarget);
moveTarget= parens;
}
newCastExpression.setExpression(moveTarget);
newArgument= newCastExpression;
}
valueOfMethod.arguments().add(newArgument);
ASTNodes.replaceButKeepComment(rewrite, visited, valueOfMethod, group);
}
}
public static class ValueOfRatherThanInstantiationFloatWithFloatValueFixOperation extends CompilationUnitRewriteOperation {
private final ClassInstanceCreation visited;
private final Expression arg0;
public ValueOfRatherThanInstantiationFloatWithFloatValueFixOperation(final ClassInstanceCreation visited, final Expression arg0) {
this.visited= visited;
this.arg0= arg0;
}
@Override
public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException {
ASTRewrite rewrite= cuRewrite.getASTRewrite();
AST ast= cuRewrite.getRoot().getAST();
TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_float_with_float_value, cuRewrite);
rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() {
@Override
public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) {
return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength());
}
return super.computeSourceRange(nodeWithComment);
}
});
MethodInvocation floatValueMethod= ast.newMethodInvocation();
floatValueMethod.setExpression(ASTNodes.createMoveTarget(rewrite, arg0));
floatValueMethod.setName(ast.newSimpleName("floatValue")); //$NON-NLS-1$
ASTNodes.replaceButKeepComment(rewrite, visited, floatValueMethod, group);
}
}
public static class ValueOfRatherThanInstantiationWithTheSingleArgumentFixOperation extends CompilationUnitRewriteOperation {
private final ClassInstanceCreation visited;
public ValueOfRatherThanInstantiationWithTheSingleArgumentFixOperation(final ClassInstanceCreation visited) {
this.visited= visited;
}
@Override
public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException {
ASTRewrite rewrite= cuRewrite.getASTRewrite();
TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_single_argument, cuRewrite);
rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() {
@Override
public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) {
return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength());
}
return super.computeSourceRange(nodeWithComment);
}
});
ASTNodes.replaceButKeepComment(rewrite, visited, ASTNodes.createMoveTarget(rewrite, (Expression) visited.arguments().get(0)), group);
}
}
public static class ValueOfRatherThanInstantiationWithValueOfFixOperation extends CompilationUnitRewriteOperation {
private final ClassInstanceCreation visited;
private final ITypeBinding typeBinding;
private final Expression arg0;
public ValueOfRatherThanInstantiationWithValueOfFixOperation(final ClassInstanceCreation visited, final ITypeBinding typeBinding, final Expression arg0) {
this.visited= visited;
this.typeBinding= typeBinding;
this.arg0= arg0;
}
@Override
public void rewriteAST(final CompilationUnitRewrite cuRewrite, final LinkedProposalModelCore linkedModel) throws CoreException {
ASTRewrite rewrite= cuRewrite.getASTRewrite();
AST ast= cuRewrite.getRoot().getAST();
TextEditGroup group= createTextEditGroup(MultiFixMessages.ValueOfRatherThanInstantiationCleanup_description_valueof, cuRewrite);
rewrite.setTargetSourceRangeComputer(new TargetSourceRangeComputer() {
@Override
public SourceRange computeSourceRange(final ASTNode nodeWithComment) {
if (Boolean.TRUE.equals(nodeWithComment.getProperty(ASTNodes.UNTOUCH_COMMENT))) {
return new SourceRange(nodeWithComment.getStartPosition(), nodeWithComment.getLength());
}
return super.computeSourceRange(nodeWithComment);
}
});
MethodInvocation valueOfMethod= ast.newMethodInvocation();
valueOfMethod.setExpression(ASTNodeFactory.newName(ast, typeBinding.getName()));
valueOfMethod.setName(ast.newSimpleName("valueOf")); //$NON-NLS-1$
valueOfMethod.arguments().add(ASTNodes.createMoveTarget(rewrite, ASTNodes.getUnparenthesedExpression(arg0)));
ASTNodes.replaceButKeepComment(rewrite, visited, valueOfMethod, group);
}
}
public static ICleanUpFixCore createCleanUp(final CompilationUnit compilationUnit) {
List<CompilationUnitRewriteOperation> operations= new ArrayList<>();
ValueOfRatherThanInstantiationFinder finder= new ValueOfRatherThanInstantiationFinder(operations);
compilationUnit.accept(finder);
if (operations.isEmpty()) {
return null;
}
CompilationUnitRewriteOperation[] ops= operations.toArray(new CompilationUnitRewriteOperation[0]);
return new ValueOfRatherThanInstantiationFixCore(FixMessages.ValueOfRatherThanInstantiationFix_description, compilationUnit, ops);
}
protected ValueOfRatherThanInstantiationFixCore(final String name, final CompilationUnit compilationUnit, final CompilationUnitRewriteOperation[] fixRewriteOperations) {
super(name, compilationUnit, fixRewriteOperations);
}
}