| /******************************************************************************* |
| * Copyright (c) 2000, 2016 IBM Corporation 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: |
| * IBM Corporation - 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.core.runtime.NullProgressMonitor; |
| |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.compiler.CategorizedProblem; |
| 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.ArrayType; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| 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.FieldDeclaration; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.MethodInvocation; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.ParameterizedType; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SimpleType; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.dom.VariableDeclarationStatement; |
| import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ListRewrite; |
| |
| import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; |
| import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsConstraintCreator; |
| import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsConstraintsSolver; |
| import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsRefactoring; |
| import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsTCModel; |
| import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsUpdate; |
| 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.ui.cleanup.ICleanUpFix; |
| import org.eclipse.jdt.ui.text.java.IProblemLocation; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation; |
| import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; |
| |
| /** |
| * Fix which introduce new language constructs to pre Java50 code. |
| * Requires a compiler level setting of 5.0+ |
| * Supported: |
| * Add missing @Override annotation |
| * Add missing @Deprecated annotation |
| * Convert for loop to enhanced for loop |
| */ |
| public class Java50Fix extends CompilationUnitRewriteOperationsFix { |
| |
| private static final String OVERRIDE= "Override"; //$NON-NLS-1$ |
| private static final String DEPRECATED= "Deprecated"; //$NON-NLS-1$ |
| |
| private static class AnnotationRewriteOperation extends CompilationUnitRewriteOperation { |
| private final BodyDeclaration fBodyDeclaration; |
| private final String fAnnotation; |
| |
| public AnnotationRewriteOperation(BodyDeclaration bodyDeclaration, String annotation) { |
| fBodyDeclaration= bodyDeclaration; |
| fAnnotation= annotation; |
| } |
| |
| @Override |
| public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException { |
| AST ast= cuRewrite.getRoot().getAST(); |
| ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(fBodyDeclaration, fBodyDeclaration.getModifiersProperty()); |
| Annotation newAnnotation= ast.newMarkerAnnotation(); |
| newAnnotation.setTypeName(ast.newSimpleName(fAnnotation)); |
| TextEditGroup group= createTextEditGroup(Messages.format(FixMessages.Java50Fix_AddMissingAnnotation_description, BasicElementLabels.getJavaElementName(fAnnotation)), cuRewrite); |
| listRewrite.insertFirst(newAnnotation, group); |
| } |
| } |
| |
| private static class AddTypeParametersOperation extends CompilationUnitRewriteOperation { |
| |
| private final SimpleType[] fTypes; |
| |
| public AddTypeParametersOperation(SimpleType[] types) { |
| fTypes= types; |
| } |
| |
| @Override |
| public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel positionGroups) throws CoreException { |
| InferTypeArgumentsTCModel model= new InferTypeArgumentsTCModel(); |
| InferTypeArgumentsConstraintCreator creator= new InferTypeArgumentsConstraintCreator(model, true); |
| |
| CompilationUnit root= cuRewrite.getRoot(); |
| root.accept(creator); |
| |
| InferTypeArgumentsConstraintsSolver solver= new InferTypeArgumentsConstraintsSolver(model); |
| InferTypeArgumentsUpdate update= solver.solveConstraints(new NullProgressMonitor()); |
| solver= null; //free caches |
| |
| ParameterizedType[] nodes= InferTypeArgumentsRefactoring.inferArguments(fTypes, update, model, cuRewrite); |
| if (nodes.length == 0) |
| return; |
| |
| ASTRewrite astRewrite= cuRewrite.getASTRewrite(); |
| for (int i= 0; i < nodes.length; i++) { |
| ParameterizedType type= nodes[i]; |
| List<Type> args= type.typeArguments(); |
| int j= 0; |
| for (Type argType : args) { |
| LinkedProposalPositionGroup group= new LinkedProposalPositionGroup("G" + i + "_" + j); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (!positionGroups.hasLinkedPositions()) { |
| group.addPosition(astRewrite.track(argType), true); |
| } else { |
| group.addPosition(astRewrite.track(argType), false); |
| } |
| positionGroups.addPositionGroup(group); |
| j++; |
| } |
| } |
| positionGroups.setEndPosition(astRewrite.track(nodes[0])); |
| } |
| } |
| |
| public static Java50Fix createAddOverrideAnnotationFix(CompilationUnit compilationUnit, IProblemLocation problem) { |
| if (!isMissingOverrideAnnotationProblem(problem.getProblemId())) |
| return null; |
| |
| return createFix(compilationUnit, problem, OVERRIDE, FixMessages.Java50Fix_AddOverride_description); |
| } |
| |
| public static boolean isMissingOverrideAnnotationInterfaceProblem(int id) { |
| return id == IProblem.MissingOverrideAnnotationForInterfaceMethodImplementation; |
| } |
| |
| public static boolean isMissingOverrideAnnotationProblem(int id) { |
| return id == IProblem.MissingOverrideAnnotation || id == IProblem.MissingOverrideAnnotationForInterfaceMethodImplementation; |
| } |
| |
| public static Java50Fix createAddDeprectatedAnnotation(CompilationUnit compilationUnit, IProblemLocation problem) { |
| if (!isMissingDeprecationProblem(problem.getProblemId())) |
| return null; |
| |
| return createFix(compilationUnit, problem, DEPRECATED, FixMessages.Java50Fix_AddDeprecated_description); |
| } |
| |
| public static boolean isMissingDeprecationProblem(int id) { |
| return id == IProblem.FieldMissingDeprecatedAnnotation |
| || id == IProblem.MethodMissingDeprecatedAnnotation |
| || id == IProblem.TypeMissingDeprecatedAnnotation; |
| } |
| |
| private static Java50Fix createFix(CompilationUnit compilationUnit, IProblemLocation problem, String annotation, String label) { |
| ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement(); |
| if (!JavaModelUtil.is50OrHigher(cu.getJavaProject())) |
| return null; |
| |
| ASTNode selectedNode= problem.getCoveringNode(compilationUnit); |
| if (selectedNode == null) |
| return null; |
| |
| ASTNode declaringNode= getDeclaringNode(selectedNode); |
| if (!(declaringNode instanceof BodyDeclaration)) |
| return null; |
| |
| BodyDeclaration declaration= (BodyDeclaration) declaringNode; |
| |
| AnnotationRewriteOperation operation= new AnnotationRewriteOperation(declaration, annotation); |
| |
| return new Java50Fix(label, compilationUnit, new CompilationUnitRewriteOperation[] {operation}); |
| } |
| |
| public static Java50Fix createRawTypeReferenceFix(CompilationUnit compilationUnit, IProblemLocation problem) { |
| List<CompilationUnitRewriteOperation> operations= new ArrayList<>(); |
| SimpleType node= createRawTypeReferenceOperations(compilationUnit, new IProblemLocation[] {problem}, operations); |
| if (operations.isEmpty()) |
| return null; |
| |
| return new Java50Fix(Messages.format(FixMessages.Java50Fix_AddTypeArguments_description, BasicElementLabels.getJavaElementName(node.getName().getFullyQualifiedName())), compilationUnit, operations.toArray(new CompilationUnitRewriteOperation[operations.size()])); |
| } |
| |
| public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, |
| boolean addOverrideAnnotation, |
| boolean addOverrideInterfaceAnnotation, |
| boolean addDeprecatedAnnotation, |
| boolean rawTypeReference) { |
| |
| ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement(); |
| if (!JavaModelUtil.is50OrHigher(cu.getJavaProject())) |
| return null; |
| |
| if (!addOverrideAnnotation && !addDeprecatedAnnotation && !rawTypeReference) |
| return null; |
| |
| List<CompilationUnitRewriteOperation> operations= new ArrayList<>(); |
| |
| IProblem[] problems= compilationUnit.getProblems(); |
| IProblemLocation[] locations= new IProblemLocation[problems.length]; |
| for (int i= 0; i < problems.length; i++) { |
| locations[i]= new ProblemLocation(problems[i]); |
| } |
| |
| if (addOverrideAnnotation) |
| createAddOverrideAnnotationOperations(compilationUnit, addOverrideInterfaceAnnotation, locations, operations); |
| |
| if (addDeprecatedAnnotation) |
| createAddDeprecatedAnnotationOperations(compilationUnit, locations, operations); |
| |
| if (rawTypeReference) |
| createRawTypeReferenceOperations(compilationUnit, locations, operations); |
| |
| if (operations.isEmpty()) |
| return null; |
| |
| String fixName; |
| if (rawTypeReference) { |
| fixName= FixMessages.Java50Fix_add_type_parameters_change_name; |
| } else { |
| fixName= FixMessages.Java50Fix_add_annotations_change_name; |
| } |
| |
| CompilationUnitRewriteOperation[] operationsArray= operations.toArray(new CompilationUnitRewriteOperation[operations.size()]); |
| return new Java50Fix(fixName, compilationUnit, operationsArray); |
| } |
| |
| public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, IProblemLocation[] problems, |
| boolean addOverrideAnnotation, |
| boolean addOverrideInterfaceAnnotation, |
| boolean addDeprecatedAnnotation, |
| boolean rawTypeReferences) { |
| |
| ICompilationUnit cu= (ICompilationUnit)compilationUnit.getJavaElement(); |
| if (!JavaModelUtil.is50OrHigher(cu.getJavaProject())) |
| return null; |
| |
| if (!addOverrideAnnotation && !addDeprecatedAnnotation && !rawTypeReferences) |
| return null; |
| |
| List<CompilationUnitRewriteOperation> operations= new ArrayList<>(); |
| |
| if (addOverrideAnnotation) |
| createAddOverrideAnnotationOperations(compilationUnit, addOverrideInterfaceAnnotation, problems, operations); |
| |
| if (addDeprecatedAnnotation) |
| createAddDeprecatedAnnotationOperations(compilationUnit, problems, operations); |
| |
| if (rawTypeReferences) |
| createRawTypeReferenceOperations(compilationUnit, problems, operations); |
| |
| |
| if (operations.isEmpty()) |
| return null; |
| |
| CompilationUnitRewriteOperation[] operationsArray= operations.toArray(new CompilationUnitRewriteOperation[operations.size()]); |
| return new Java50Fix(FixMessages.Java50Fix_add_annotations_change_name, compilationUnit, operationsArray); |
| } |
| |
| private static void createAddDeprecatedAnnotationOperations(CompilationUnit compilationUnit, IProblemLocation[] locations, List<CompilationUnitRewriteOperation> result) { |
| for (IProblemLocation problem : locations) { |
| if (isMissingDeprecationProblem(problem.getProblemId())) { |
| ASTNode selectedNode= problem.getCoveringNode(compilationUnit); |
| if (selectedNode != null) { |
| |
| ASTNode declaringNode= getDeclaringNode(selectedNode); |
| if (declaringNode instanceof BodyDeclaration) { |
| BodyDeclaration declaration= (BodyDeclaration) declaringNode; |
| AnnotationRewriteOperation operation= new AnnotationRewriteOperation(declaration, DEPRECATED); |
| result.add(operation); |
| } |
| } |
| } |
| } |
| } |
| |
| private static void createAddOverrideAnnotationOperations(CompilationUnit compilationUnit, boolean addOverrideInterfaceAnnotation, IProblemLocation[] locations, List<CompilationUnitRewriteOperation> result) { |
| for (IProblemLocation problem : locations) { |
| int problemId= problem.getProblemId(); |
| |
| if (isMissingOverrideAnnotationProblem(problemId)) { |
| if (!isMissingOverrideAnnotationInterfaceProblem(problemId) || addOverrideInterfaceAnnotation) { |
| ASTNode selectedNode= problem.getCoveringNode(compilationUnit); |
| if (selectedNode != null) { |
| |
| ASTNode declaringNode= getDeclaringNode(selectedNode); |
| if (declaringNode instanceof BodyDeclaration) { |
| BodyDeclaration declaration= (BodyDeclaration) declaringNode; |
| AnnotationRewriteOperation operation= new AnnotationRewriteOperation(declaration, OVERRIDE); |
| result.add(operation); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private static SimpleType createRawTypeReferenceOperations(CompilationUnit compilationUnit, IProblemLocation[] locations, List<CompilationUnitRewriteOperation> operations) { |
| if (hasFatalError(compilationUnit)) |
| return null; |
| |
| List<SimpleType> result= new ArrayList<>(); |
| for (IProblemLocation problem : locations) { |
| if (isRawTypeReferenceProblem(problem.getProblemId())) { |
| ASTNode node= problem.getCoveredNode(compilationUnit); |
| if (node instanceof ClassInstanceCreation) { |
| Type rawReference= (Type)node.getStructuralProperty(ClassInstanceCreation.TYPE_PROPERTY); |
| if (isRawTypeReference(rawReference)) { |
| result.add((SimpleType) rawReference); |
| } |
| } else if (node instanceof SimpleName) { |
| ASTNode rawReference= node.getParent(); |
| if (isRawTypeReference(rawReference)) { |
| ASTNode parent= rawReference.getParent(); |
| if (!(parent instanceof ArrayType) |
| && !(parent instanceof ParameterizedType)) |
| result.add((SimpleType) rawReference); |
| } |
| } else if (node instanceof MethodInvocation) { |
| MethodInvocation invocation= (MethodInvocation)node; |
| |
| SimpleType rawReference= getRawReference(invocation, compilationUnit); |
| if (rawReference != null) { |
| result.add(rawReference); |
| } |
| } |
| } |
| } |
| |
| if (result.isEmpty()) |
| return null; |
| |
| SimpleType[] types= result.toArray(new SimpleType[result.size()]); |
| operations.add(new AddTypeParametersOperation(types)); |
| return types[0]; |
| } |
| |
| private static boolean hasFatalError(CompilationUnit compilationUnit) { |
| try { |
| if (!((ICompilationUnit) compilationUnit.getJavaElement()).isStructureKnown()) |
| return true; |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| return true; |
| } |
| |
| for (IProblem problem : compilationUnit.getProblems()) { |
| if (problem.isError()) { |
| if (!(problem instanceof CategorizedProblem)) { |
| return true; |
| } |
| CategorizedProblem categorizedProblem= (CategorizedProblem) problem; |
| int categoryID= categorizedProblem.getCategoryID(); |
| switch (categoryID) { |
| case CategorizedProblem.CAT_BUILDPATH: |
| case CategorizedProblem.CAT_SYNTAX: |
| case CategorizedProblem.CAT_IMPORT: |
| case CategorizedProblem.CAT_TYPE: |
| case CategorizedProblem.CAT_MEMBER: |
| case CategorizedProblem.CAT_INTERNAL: |
| case CategorizedProblem.CAT_MODULE: |
| return true; |
| default: |
| break; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean isRawTypeReferenceProblem(int id) { |
| switch (id) { |
| case IProblem.UnsafeTypeConversion: |
| case IProblem.UnsafeElementTypeConversion: |
| case IProblem.RawTypeReference: |
| case IProblem.UnsafeRawMethodInvocation: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| private static SimpleType getRawReference(MethodInvocation invocation, CompilationUnit compilationUnit) { |
| Name name1= (Name)invocation.getStructuralProperty(MethodInvocation.NAME_PROPERTY); |
| if (name1 instanceof SimpleName) { |
| SimpleType rawReference= getRawReference((SimpleName)name1, compilationUnit); |
| if (rawReference != null) { |
| return rawReference; |
| } |
| } |
| |
| Expression expr= (Expression)invocation.getStructuralProperty(MethodInvocation.EXPRESSION_PROPERTY); |
| if (expr instanceof SimpleName) { |
| SimpleType rawReference= getRawReference((SimpleName)expr, compilationUnit); |
| if (rawReference != null) { |
| return rawReference; |
| } |
| } else if (expr instanceof QualifiedName) { |
| Name name= (Name)expr; |
| while (name instanceof QualifiedName) { |
| SimpleName simpleName= (SimpleName)name.getStructuralProperty(QualifiedName.NAME_PROPERTY); |
| SimpleType rawReference= getRawReference(simpleName, compilationUnit); |
| if (rawReference != null) { |
| return rawReference; |
| } |
| name= (Name)name.getStructuralProperty(QualifiedName.QUALIFIER_PROPERTY); |
| } |
| if (name instanceof SimpleName) { |
| SimpleType rawReference= getRawReference((SimpleName)name, compilationUnit); |
| if (rawReference != null) { |
| return rawReference; |
| } |
| } |
| } else if (expr instanceof MethodInvocation) { |
| SimpleType rawReference= getRawReference((MethodInvocation)expr, compilationUnit); |
| if (rawReference != null) { |
| return rawReference; |
| } |
| } |
| return null; |
| } |
| |
| private static SimpleType getRawReference(SimpleName name, CompilationUnit compilationUnit) { |
| for (SimpleName n : LinkedNodeFinder.findByNode(compilationUnit, name)) { |
| if (n.getParent() instanceof VariableDeclarationFragment) { |
| VariableDeclarationFragment fragment= (VariableDeclarationFragment) n.getParent(); |
| if (fragment.getParent() instanceof VariableDeclarationStatement) { |
| VariableDeclarationStatement statement= (VariableDeclarationStatement)fragment.getParent(); |
| ASTNode result= (ASTNode)statement.getStructuralProperty(VariableDeclarationStatement.TYPE_PROPERTY); |
| if (isRawTypeReference(result)) |
| return (SimpleType) result; |
| } else if (fragment.getParent() instanceof FieldDeclaration) { |
| FieldDeclaration declaration= (FieldDeclaration)fragment.getParent(); |
| ASTNode result= (ASTNode)declaration.getStructuralProperty(FieldDeclaration.TYPE_PROPERTY); |
| if (isRawTypeReference(result)) |
| return (SimpleType) result; |
| } |
| } else if (n.getParent() instanceof SingleVariableDeclaration) { |
| SingleVariableDeclaration declaration= (SingleVariableDeclaration) n.getParent(); |
| ASTNode result= (ASTNode)declaration.getStructuralProperty(SingleVariableDeclaration.TYPE_PROPERTY); |
| if (isRawTypeReference(result)) |
| return (SimpleType) result; |
| } else if (n.getParent() instanceof MethodDeclaration) { |
| MethodDeclaration methodDecl= (MethodDeclaration) n.getParent(); |
| ASTNode result= (ASTNode)methodDecl.getStructuralProperty(MethodDeclaration.RETURN_TYPE2_PROPERTY); |
| if (isRawTypeReference(result)) |
| return (SimpleType) result; |
| } |
| } |
| return null; |
| } |
| |
| private static boolean isRawTypeReference(ASTNode node) { |
| if (!(node instanceof SimpleType)) |
| return false; |
| |
| ITypeBinding typeBinding= ((SimpleType) node).resolveBinding(); |
| if (typeBinding == null) |
| return false; |
| |
| ITypeBinding binding= typeBinding.getTypeDeclaration(); |
| if (binding == null) |
| return false; |
| |
| ITypeBinding[] parameters= binding.getTypeParameters(); |
| if (parameters.length == 0) |
| return false; |
| |
| return true; |
| } |
| |
| private static ASTNode getDeclaringNode(ASTNode selectedNode) { |
| ASTNode declaringNode= null; |
| if (selectedNode instanceof MethodDeclaration) { |
| declaringNode= selectedNode; |
| } else if (selectedNode instanceof SimpleName) { |
| StructuralPropertyDescriptor locationInParent= selectedNode.getLocationInParent(); |
| if (locationInParent == MethodDeclaration.NAME_PROPERTY || locationInParent == TypeDeclaration.NAME_PROPERTY) { |
| declaringNode= selectedNode.getParent(); |
| } else if (locationInParent == VariableDeclarationFragment.NAME_PROPERTY) { |
| declaringNode= selectedNode.getParent().getParent(); |
| } |
| } |
| return declaringNode; |
| } |
| |
| private Java50Fix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewrites) { |
| super(name, compilationUnit, fixRewrites); |
| } |
| |
| } |