--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2012 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
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.fix;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.text.edits.TextEditGroup;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+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.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+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.ImportDeclaration;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PostfixExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+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.ASTNodes;
+import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
+import org.eclipse.jdt.internal.corext.dom.NecessaryParenthesesChecker;
+import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
+import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+
+import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp;
+import org.eclipse.jdt.internal.ui.text.correction.JavadocTagsSubProcessor;
+import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation;
+import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
+
+/**
+ * Fix which removes unused code.
+ */
+public class UnusedCodeFix extends CompilationUnitRewriteOperationsFix {
+
+ private static class SideEffectFinder extends ASTVisitor {
+
+ private final ArrayList<Expression> fSideEffectNodes;
+
+ public SideEffectFinder(ArrayList<Expression> res) {
+ fSideEffectNodes= res;
+ }
+
+ @Override
+ public boolean visit(Assignment node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(PostfixExpression node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(PrefixExpression node) {
+ Object operator= node.getOperator();
+ if (operator == PrefixExpression.Operator.INCREMENT || operator == PrefixExpression.Operator.DECREMENT) {
+ fSideEffectNodes.add(node);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(ClassInstanceCreation node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ fSideEffectNodes.add(node);
+ return false;
+ }
+ }
+
+ private static class RemoveImportOperation extends CompilationUnitRewriteOperation {
+
+ private final ImportDeclaration fImportDeclaration;
+
+ public RemoveImportOperation(ImportDeclaration importDeclaration) {
+ fImportDeclaration= importDeclaration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
+ ImportDeclaration node= fImportDeclaration;
+ TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveImport_description, cuRewrite);
+ cuRewrite.getASTRewrite().remove(node, group);
+ }
+
+ }
+
+ private static class RemoveUnusedMemberOperation extends CompilationUnitRewriteOperation {
+
+ private final SimpleName[] fUnusedNames;
+ private boolean fForceRemove;
+ private int fRemovedAssignmentsCount;
+ private int fAlteredAssignmentsCount;
+
+ public RemoveUnusedMemberOperation(SimpleName[] unusedNames, boolean removeAllAsignements) {
+ fUnusedNames= unusedNames;
+ fForceRemove= removeAllAsignements;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
+ for (int i= 0; i < fUnusedNames.length; i++) {
+ removeUnusedName(cuRewrite, fUnusedNames[i]);
+ }
+ }
+
+ private void removeUnusedName(CompilationUnitRewrite cuRewrite, SimpleName simpleName) {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+ CompilationUnit completeRoot= cuRewrite.getRoot();
+
+ IBinding binding= simpleName.resolveBinding();
+ CompilationUnit root= (CompilationUnit) simpleName.getRoot();
+ String displayString= getDisplayString(binding);
+ TextEditGroup group= createTextEditGroup(displayString, cuRewrite);
+ if (binding.getKind() == IBinding.METHOD) {
+ IMethodBinding decl= ((IMethodBinding) binding).getMethodDeclaration();
+ ASTNode declaration= root.findDeclaringNode(decl);
+ rewrite.remove(declaration, group);
+ } else if (binding.getKind() == IBinding.TYPE) {
+ ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration();
+ ASTNode declaration= root.findDeclaringNode(decl);
+ if (declaration.getParent() instanceof TypeDeclarationStatement) {
+ declaration= declaration.getParent();
+ }
+ rewrite.remove(declaration, group);
+ } else if (binding.getKind() == IBinding.VARIABLE) {
+ SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
+ SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
+ for (int i= 0; i < references.length; i++) {
+ removeVariableReferences(rewrite, references[i], group);
+ }
+
+ IVariableBinding bindingDecl= ((IVariableBinding) nameNode.resolveBinding()).getVariableDeclaration();
+ ASTNode declaringNode= completeRoot.findDeclaringNode(bindingDecl);
+ if (declaringNode instanceof SingleVariableDeclaration) {
+ removeParamTag(rewrite, (SingleVariableDeclaration) declaringNode, group);
+ }
+ } else {
+ // unexpected
+ }
+ }
+
+ private String getDisplayString(IBinding binding) {
+ switch (binding.getKind()) {
+ case IBinding.TYPE:
+ return FixMessages.UnusedCodeFix_RemoveUnusedType_description;
+ case IBinding.METHOD:
+ if (((IMethodBinding) binding).isConstructor()) {
+ return FixMessages.UnusedCodeFix_RemoveUnusedConstructor_description;
+ } else {
+ return FixMessages.UnusedCodeFix_RemoveUnusedPrivateMethod_description;
+ }
+ case IBinding.VARIABLE:
+ if (((IVariableBinding) binding).isField()) {
+ return FixMessages.UnusedCodeFix_RemoveUnusedField_description;
+ } else {
+ return FixMessages.UnusedCodeFix_RemoveUnusedVariabl_description;
+ }
+ default:
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl, TextEditGroup group) {
+ if (varDecl.getParent() instanceof MethodDeclaration) {
+ Javadoc javadoc= ((MethodDeclaration) varDecl.getParent()).getJavadoc();
+ if (javadoc != null) {
+ TagElement tagElement= JavadocTagsSubProcessor.findParamTag(javadoc, varDecl.getName().getIdentifier());
+ if (tagElement != null) {
+ rewrite.remove(tagElement, group);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the field or variable declaration including the initializer.
+ * @param rewrite the AST rewriter to use
+ * @param reference a reference to the variable to remove
+ * @param group the text edit group to use
+ */
+ private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference, TextEditGroup group) {
+ ASTNode parent= reference.getParent();
+ while (parent instanceof QualifiedName) {
+ parent= parent.getParent();
+ }
+ if (parent instanceof FieldAccess) {
+ parent= parent.getParent();
+ }
+
+ int nameParentType= parent.getNodeType();
+ if (nameParentType == ASTNode.ASSIGNMENT) {
+ Assignment assignment= (Assignment) parent;
+ Expression rightHand= assignment.getRightHandSide();
+
+ ASTNode assignParent= assignment.getParent();
+ if (assignParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT && rightHand.getNodeType() != ASTNode.ASSIGNMENT) {
+ removeVariableWithInitializer(rewrite, rightHand, assignParent, group);
+ } else {
+ rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), group);
+ }
+ } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
+ rewrite.remove(parent, group);
+ } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ VariableDeclarationFragment frag= (VariableDeclarationFragment) parent;
+ ASTNode varDecl= frag.getParent();
+ List<VariableDeclarationFragment> fragments;
+ if (varDecl instanceof VariableDeclarationExpression) {
+ fragments= ((VariableDeclarationExpression) varDecl).fragments();
+ } else if (varDecl instanceof FieldDeclaration) {
+ fragments= ((FieldDeclaration) varDecl).fragments();
+ } else {
+ fragments= ((VariableDeclarationStatement) varDecl).fragments();
+ }
+ Expression initializer = frag.getInitializer();
+ if (initializer instanceof CastExpression) {
+ initializer= ((CastExpression) initializer).getExpression();
+ }
+ boolean sideEffectInitializer = initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation;
+ if (fragments.size() == fUnusedNames.length) {
+ if (fForceRemove) {
+ rewrite.remove(varDecl, group);
+ return;
+ }
+ if (parent.getParent() instanceof FieldDeclaration) {
+ rewrite.remove(varDecl, group);
+ return;
+ }
+ if (sideEffectInitializer){
+ Expression movedInit = (Expression) rewrite.createMoveTarget(initializer);
+ ExpressionStatement wrapped = rewrite.getAST().newExpressionStatement(movedInit);
+ rewrite.replace(varDecl, wrapped, group);
+ } else {
+ rewrite.remove(varDecl, group);
+ }
+ } else {
+ if (fForceRemove) {
+ rewrite.remove(frag, group);
+ return;
+ }
+ //multiple declarations in one line
+ ASTNode declaration = parent.getParent();
+ if (declaration instanceof FieldDeclaration) {
+ rewrite.remove(frag, group);
+ return;
+ }
+ if (declaration instanceof VariableDeclarationStatement) {
+ ASTNode lst = declaration.getParent();
+ ListRewrite listRewrite= null;
+ if (lst instanceof Block) {
+ listRewrite= rewrite.getListRewrite(lst, Block.STATEMENTS_PROPERTY);
+ } else if (lst instanceof SwitchStatement) {
+ listRewrite= rewrite.getListRewrite(lst, SwitchStatement.STATEMENTS_PROPERTY);
+ } else {
+ Assert.isTrue(false);
+ }
+ splitUpDeclarations(rewrite, group, frag, listRewrite, (VariableDeclarationStatement) declaration);
+ rewrite.remove(frag, group);
+ return;
+ }
+ if (declaration instanceof VariableDeclarationExpression) {
+ //keep constructors and method invocations
+ if (!sideEffectInitializer){
+ rewrite.remove(frag, group);
+ }
+ }
+ }
+ } else if (nameParentType == ASTNode.POSTFIX_EXPRESSION || nameParentType == ASTNode.PREFIX_EXPRESSION) {
+ Expression expression= (Expression)parent;
+ ASTNode expressionParent= expression.getParent();
+ if (expressionParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
+ removeStatement(rewrite, expressionParent, group);
+ } else {
+ rewrite.remove(expression, group);
+ }
+ }
+ }
+
+ private void splitUpDeclarations(ASTRewrite rewrite, TextEditGroup group, VariableDeclarationFragment frag, ListRewrite statementRewrite, VariableDeclarationStatement originalStatement) {
+ Expression initializer = frag.getInitializer();
+ //keep constructors and method invocations
+ if (initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation){
+ Expression movedInitializer= (Expression) rewrite.createMoveTarget(initializer);
+
+ ExpressionStatement newInitializer= rewrite.getAST().newExpressionStatement( movedInitializer);
+ statementRewrite.insertAfter(newInitializer, originalStatement, group);
+
+ VariableDeclarationStatement newDeclaration= null;
+ List<VariableDeclarationFragment> fragments= originalStatement.fragments();
+ int fragIndex= fragments.indexOf(frag);
+ ListIterator<VariableDeclarationFragment> fragmentIterator= fragments.listIterator(fragIndex+1);
+ while (fragmentIterator.hasNext()) {
+ VariableDeclarationFragment currentFragment= fragmentIterator.next();
+ VariableDeclarationFragment movedFragment= (VariableDeclarationFragment) rewrite.createMoveTarget(currentFragment);
+ if (newDeclaration == null) {
+ newDeclaration= rewrite.getAST().newVariableDeclarationStatement(movedFragment);
+ Type copiedType= (Type) rewrite.createCopyTarget(originalStatement.getType());
+ newDeclaration.setType(copiedType);
+ } else
+ newDeclaration.fragments().add(movedFragment);
+ }
+ if (newDeclaration != null){
+ statementRewrite.insertAfter(newDeclaration, newInitializer, group);
+ }
+ if (originalStatement.fragments().size() == newDeclaration.fragments().size() + 1){
+ rewrite.remove(originalStatement, group);
+ }
+ }
+ }
+
+ private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode, TextEditGroup group) {
+ boolean performRemove= fForceRemove;
+ if (!performRemove) {
+ ArrayList<Expression> sideEffectNodes= new ArrayList<Expression>();
+ initializerNode.accept(new SideEffectFinder(sideEffectNodes));
+ performRemove= sideEffectNodes.isEmpty();
+ }
+ if (performRemove) {
+ removeStatement(rewrite, statementNode, group);
+ fRemovedAssignmentsCount++;
+ } else {
+ ASTNode initNode = rewrite.createMoveTarget(initializerNode);
+ ExpressionStatement statement = rewrite.getAST().newExpressionStatement((Expression) initNode);
+ rewrite.replace(statementNode, statement, null);
+ fAlteredAssignmentsCount++;
+ }
+ }
+
+ private void removeStatement(ASTRewrite rewrite, ASTNode statementNode, TextEditGroup group) {
+ if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) {
+ rewrite.replace(statementNode, rewrite.getAST().newBlock(), group);
+ } else {
+ rewrite.remove(statementNode, group);
+ }
+ }
+
+ @Override
+ public String getAdditionalInfo() {
+ StringBuffer sb=new StringBuffer();
+ if (fRemovedAssignmentsCount == 1) {
+ sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_singular);
+ } else if (fRemovedAssignmentsCount > 1) {
+ sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_plural, String.valueOf(fRemovedAssignmentsCount)));
+ }
+ if (fAlteredAssignmentsCount == 1) {
+ sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_singular);
+ } else if (fAlteredAssignmentsCount > 1) {
+ sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_plural, String.valueOf(fAlteredAssignmentsCount)));
+ }
+ if (sb.length()>0) {
+ return sb.toString();
+ } else
+ return null;
+ }
+ }
+
+ private static class RemoveCastOperation extends CompilationUnitRewriteOperation {
+
+ private final CastExpression fCast;
+
+ public RemoveCastOperation(CastExpression cast) {
+ fCast= cast;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
+
+ TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
+
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+
+ CastExpression cast= fCast;
+ Expression expression= cast.getExpression();
+ if (expression instanceof ParenthesizedExpression) {
+ Expression childExpression= ((ParenthesizedExpression) expression).getExpression();
+ if (NecessaryParenthesesChecker.needsParentheses(childExpression, cast, CastExpression.EXPRESSION_PROPERTY)) {
+ expression= childExpression;
+ }
+ }
+
+ replaceCast(cast, expression, rewrite, group);
+ }
+ }
+
+ private static class RemoveAllCastOperation extends CompilationUnitRewriteOperation {
+
+ private final LinkedHashSet<CastExpression> fUnnecessaryCasts;
+
+ public RemoveAllCastOperation(LinkedHashSet<CastExpression> unnecessaryCasts) {
+ fUnnecessaryCasts= unnecessaryCasts;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
+ ASTRewrite rewrite= cuRewrite.getASTRewrite();
+
+ TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
+
+ while (fUnnecessaryCasts.size() > 0) {
+ CastExpression castExpression= fUnnecessaryCasts.iterator().next();
+ fUnnecessaryCasts.remove(castExpression);
+
+ /*
+ * ASTRewrite doesn't allow replacing (deleting) of moved nodes. To solve problems
+ * with nested casts, we need to replace all casts at once.
+ *
+ * The loop proceeds downwards to find the innermost expression that stays in the result (downChild)
+ * and it also skips necessary parentheses.
+ */
+ CastExpression down= castExpression;
+ Expression downChild= down.getExpression();
+ while (true) {
+ if (fUnnecessaryCasts.contains(downChild)) {
+ down= (CastExpression) downChild;
+ fUnnecessaryCasts.remove(down);
+ downChild= down.getExpression();
+ } else if (downChild instanceof ParenthesizedExpression) {
+ Expression downChildExpression= ((ParenthesizedExpression) downChild).getExpression();
+ // is it justified that downChild is a ParenthesizedExpression?
+ if (NecessaryParenthesesChecker.needsParentheses(downChildExpression, down, CastExpression.EXPRESSION_PROPERTY)) {
+ // yes => continue walking down
+ downChild= downChildExpression;
+ } else {
+ // no => stop walking
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // downChild is the innermost CastExpression's expression, stripped of a necessary surrounding ParenthesizedExpression
+ // Move either downChild (if it doesn't need parentheses), or a parenthesized version if necessary
+
+ replaceCast(castExpression, downChild, rewrite, group);
+ }
+ }
+ }
+
+ public static UnusedCodeFix createRemoveUnusedImportFix(CompilationUnit compilationUnit, IProblemLocation problem) {
+ if (isUnusedImport(problem)) {
+ ImportDeclaration node= getImportDeclaration(problem, compilationUnit);
+ if (node != null) {
+ String label= FixMessages.UnusedCodeFix_RemoveImport_description;
+ RemoveImportOperation operation= new RemoveImportOperation(node);
+ Map<String, String> options= new Hashtable<String, String>();
+ options.put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
+ return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[] {operation}, options);
+ }
+ }
+ return null;
+ }
+
+ public static boolean isUnusedImport(IProblemLocation problem) {
+ int id= problem.getProblemId();
+ return id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport || id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound;
+ }
+
+ public static UnusedCodeFix createUnusedMemberFix(CompilationUnit compilationUnit, IProblemLocation problem, boolean removeAllAssignements) {
+ if (isUnusedMember(problem)) {
+ SimpleName name= getUnusedName(compilationUnit, problem);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding != null) {
+ if (isFormalParameterInEnhancedForStatement(name))
+ return null;
+
+ String label= getDisplayString(name, binding, removeAllAssignements);
+ RemoveUnusedMemberOperation operation= new RemoveUnusedMemberOperation(new SimpleName[] { name }, removeAllAssignements);
+ return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[] { operation }, getCleanUpOptions(binding, removeAllAssignements));
+ }
+ }
+ }
+ return null;
+ }
+
+ public static boolean isUnusedMember(IProblemLocation problem) {
+ int id= problem.getProblemId();
+ return id == IProblem.UnusedPrivateMethod || id == IProblem.UnusedPrivateConstructor || id == IProblem.UnusedPrivateField || id == IProblem.UnusedPrivateType
+ || id == IProblem.LocalVariableIsNeverUsed || id == IProblem.ArgumentIsNeverUsed;
+ }
+
+ public static UnusedCodeFix createRemoveUnusedCastFix(CompilationUnit compilationUnit, IProblemLocation problem) {
+ if (problem.getProblemId() != IProblem.UnnecessaryCast)
+ return null;
+
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ ASTNode curr= selectedNode;
+ while (curr instanceof ParenthesizedExpression) {
+ curr= ((ParenthesizedExpression) curr).getExpression();
+ }
+
+ if (!(curr instanceof CastExpression))
+ return null;
+
+ return new UnusedCodeFix(FixMessages.UnusedCodeFix_RemoveCast_description, compilationUnit, new CompilationUnitRewriteOperation[] {new RemoveCastOperation((CastExpression)curr)});
+ }
+
+ public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit,
+ boolean removeUnusedPrivateMethods,
+ boolean removeUnusedPrivateConstructors,
+ boolean removeUnusedPrivateFields,
+ boolean removeUnusedPrivateTypes,
+ boolean removeUnusedLocalVariables,
+ boolean removeUnusedImports,
+ boolean removeUnusedCast) {
+
+ IProblem[] problems= compilationUnit.getProblems();
+ IProblemLocation[] locations= new IProblemLocation[problems.length];
+ for (int i= 0; i < problems.length; i++) {
+ locations[i]= new ProblemLocation(problems[i]);
+ }
+
+ return createCleanUp(compilationUnit, locations,
+ removeUnusedPrivateMethods,
+ removeUnusedPrivateConstructors,
+ removeUnusedPrivateFields,
+ removeUnusedPrivateTypes,
+ removeUnusedLocalVariables,
+ removeUnusedImports,
+ removeUnusedCast);
+ }
+
+ public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, IProblemLocation[] problems,
+ boolean removeUnusedPrivateMethods,
+ boolean removeUnusedPrivateConstructors,
+ boolean removeUnusedPrivateFields,
+ boolean removeUnusedPrivateTypes,
+ boolean removeUnusedLocalVariables,
+ boolean removeUnusedImports,
+ boolean removeUnusedCast) {
+
+ List<CompilationUnitRewriteOperation> result= new ArrayList<CompilationUnitRewriteOperation>();
+ Hashtable<ASTNode, List<SimpleName>> variableDeclarations= new Hashtable<ASTNode, List<SimpleName>>();
+ LinkedHashSet<CastExpression> unnecessaryCasts= new LinkedHashSet<CastExpression>();
+ for (int i= 0; i < problems.length; i++) {
+ IProblemLocation problem= problems[i];
+ int id= problem.getProblemId();
+
+ if (removeUnusedImports && (id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport ||
+ id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound))
+ {
+ ImportDeclaration node= UnusedCodeFix.getImportDeclaration(problem, compilationUnit);
+ if (node != null) {
+ result.add(new RemoveImportOperation(node));
+ }
+ }
+
+ if ((removeUnusedPrivateMethods && id == IProblem.UnusedPrivateMethod) || (removeUnusedPrivateConstructors && id == IProblem.UnusedPrivateConstructor) ||
+ (removeUnusedPrivateTypes && id == IProblem.UnusedPrivateType)) {
+
+ SimpleName name= getUnusedName(compilationUnit, problem);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding != null) {
+ result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
+ }
+ }
+ }
+
+ if ((removeUnusedLocalVariables && id == IProblem.LocalVariableIsNeverUsed) || (removeUnusedPrivateFields && id == IProblem.UnusedPrivateField)) {
+ SimpleName name= getUnusedName(compilationUnit, problem);
+ if (name != null) {
+ IBinding binding= name.resolveBinding();
+ if (binding instanceof IVariableBinding && !isFormalParameterInEnhancedForStatement(name) && (!((IVariableBinding) binding).isField() || isSideEffectFree(name, compilationUnit))) {
+ VariableDeclarationFragment parent= (VariableDeclarationFragment) ASTNodes.getParent(name, VariableDeclarationFragment.class);
+ if (parent != null) {
+ ASTNode varDecl= parent.getParent();
+ if (!variableDeclarations.containsKey(varDecl)) {
+ variableDeclarations.put(varDecl, new ArrayList<SimpleName>());
+ }
+ variableDeclarations.get(varDecl).add(name);
+ } else {
+ result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
+ }
+ }
+ }
+ }
+
+ if (removeUnusedCast && id == IProblem.UnnecessaryCast) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ ASTNode curr= selectedNode;
+ while (curr instanceof ParenthesizedExpression) {
+ curr= ((ParenthesizedExpression) curr).getExpression();
+ }
+
+ if (curr instanceof CastExpression) {
+ unnecessaryCasts.add((CastExpression) curr);
+ }
+ }
+ }
+ for (Iterator<ASTNode> iter= variableDeclarations.keySet().iterator(); iter.hasNext();) {
+ ASTNode node= iter.next();
+ List<SimpleName> names= variableDeclarations.get(node);
+ result.add(new RemoveUnusedMemberOperation(names.toArray(new SimpleName[names.size()]), false));
+ }
+ if (unnecessaryCasts.size() > 0)
+ result.add(new RemoveAllCastOperation(unnecessaryCasts));
+
+ if (result.size() == 0)
+ return null;
+
+ return new UnusedCodeFix(FixMessages.UnusedCodeFix_change_name, compilationUnit, result.toArray(new CompilationUnitRewriteOperation[result.size()]));
+ }
+
+ private static boolean isFormalParameterInEnhancedForStatement(SimpleName name) {
+ return name.getParent() instanceof SingleVariableDeclaration && name.getParent().getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY;
+ }
+
+ private static boolean isSideEffectFree(SimpleName simpleName, CompilationUnit completeRoot) {
+ SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
+ SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
+ for (int i= 0; i < references.length; i++) {
+ if (hasSideEffect(references[i]))
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean hasSideEffect(SimpleName reference) {
+ ASTNode parent= reference.getParent();
+ while (parent instanceof QualifiedName) {
+ parent= parent.getParent();
+ }
+ if (parent instanceof FieldAccess) {
+ parent= parent.getParent();
+ }
+
+ ASTNode node= null;
+ int nameParentType= parent.getNodeType();
+ if (nameParentType == ASTNode.ASSIGNMENT) {
+ Assignment assignment= (Assignment) parent;
+ node= assignment.getRightHandSide();
+ } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
+ SingleVariableDeclaration decl= (SingleVariableDeclaration)parent;
+ node= decl.getInitializer();
+ if (node == null)
+ return false;
+ } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
+ node= parent;
+ } else {
+ return false;
+ }
+
+ ArrayList<Expression> sideEffects= new ArrayList<Expression>();
+ node.accept(new SideEffectFinder(sideEffects));
+ return sideEffects.size() > 0;
+ }
+
+ private static SimpleName getUnusedName(CompilationUnit compilationUnit, IProblemLocation problem) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+
+ if (selectedNode instanceof MethodDeclaration) {
+ return ((MethodDeclaration) selectedNode).getName();
+ } else if (selectedNode instanceof SimpleName) {
+ return (SimpleName) selectedNode;
+ }
+
+ return null;
+ }
+
+ private static String getDisplayString(SimpleName simpleName, IBinding binding, boolean removeAllAssignements) {
+ String name= BasicElementLabels.getJavaElementName(simpleName.getIdentifier());
+ switch (binding.getKind()) {
+ case IBinding.TYPE:
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveType_description, name);
+ case IBinding.METHOD:
+ if (((IMethodBinding) binding).isConstructor()) {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveConstructor_description, name);
+ } else {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveMethod_description, name);
+ }
+ case IBinding.VARIABLE:
+ if (removeAllAssignements) {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocalWithInitializer_description, name);
+ } else {
+ return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_description, name);
+ }
+ default:
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ private static Map<String, String> getCleanUpOptions(IBinding binding, boolean removeAll) {
+ Map<String, String> result= new Hashtable<String, String>();
+
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptions.TRUE);
+ switch (binding.getKind()) {
+ case IBinding.TYPE:
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_TYPES, CleanUpOptions.TRUE);
+ break;
+ case IBinding.METHOD:
+ if (((IMethodBinding) binding).isConstructor()) {
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS, CleanUpOptions.TRUE);
+ } else {
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_METHODS, CleanUpOptions.TRUE);
+ }
+ break;
+ case IBinding.VARIABLE:
+ if (removeAll)
+ return null;
+
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_FELDS, CleanUpOptions.TRUE);
+ result.put(CleanUpConstants.REMOVE_UNUSED_CODE_LOCAL_VARIABLES, CleanUpOptions.TRUE);
+ break;
+ }
+
+ return result;
+ }
+
+ private static ImportDeclaration getImportDeclaration(IProblemLocation problem, CompilationUnit compilationUnit) {
+ ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
+ if (selectedNode != null) {
+ ASTNode node= ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
+ if (node instanceof ImportDeclaration) {
+ return (ImportDeclaration)node;
+ }
+ }
+ return null;
+ }
+
+ private static void replaceCast(CastExpression castExpression, Expression replacement, ASTRewrite rewrite, TextEditGroup group) {
+ boolean castEnclosedInNecessaryParentheses= castExpression.getParent() instanceof ParenthesizedExpression
+ && NecessaryParenthesesChecker.needsParentheses(castExpression, castExpression.getParent().getParent(), castExpression.getParent().getLocationInParent());
+
+ ASTNode toReplace= castEnclosedInNecessaryParentheses ? castExpression.getParent() : castExpression;
+ ASTNode move;
+ if (NecessaryParenthesesChecker.needsParentheses(replacement, toReplace.getParent(), toReplace.getLocationInParent())) {
+ if (replacement.getParent() instanceof ParenthesizedExpression) {
+ move= rewrite.createMoveTarget(replacement.getParent());
+ } else if (castEnclosedInNecessaryParentheses) {
+ toReplace= castExpression;
+ move= rewrite.createMoveTarget(replacement);
+ } else {
+ ParenthesizedExpression parentheses= replacement.getAST().newParenthesizedExpression();
+ parentheses.setExpression((Expression) rewrite.createMoveTarget(replacement));
+ move= parentheses;
+ }
+ } else {
+ move= rewrite.createMoveTarget(replacement);
+ }
+ rewrite.replace(toReplace, move, group);
+ }
+
+ private final Map<String, String> fCleanUpOptions;
+
+ private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
+ this(name, compilationUnit, fixRewriteOperations, null);
+ }
+
+ private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations, Map<String, String> options) {
+ super(name, compilationUnit, fixRewriteOperations);
+ fCleanUpOptions= options;
+ }
+
+ public UnusedCodeCleanUp getCleanUp() {
+ if (fCleanUpOptions == null)
+ return null;
+
+ return new UnusedCodeCleanUp(fCleanUpOptions);
+ }
+
+}