]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/fix/UnusedCodeFix.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / fix / UnusedCodeFix.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2012 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.fix;
12
13 import java.util.ArrayList;
14 import java.util.Hashtable;
15 import java.util.Iterator;
16 import java.util.LinkedHashSet;
17 import java.util.List;
18 import java.util.ListIterator;
19 import java.util.Map;
20
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.CoreException;
23
24 import org.eclipse.text.edits.TextEditGroup;
25
26 import org.eclipse.jdt.core.compiler.IProblem;
27 import org.eclipse.jdt.core.dom.ASTNode;
28 import org.eclipse.jdt.core.dom.ASTVisitor;
29 import org.eclipse.jdt.core.dom.Assignment;
30 import org.eclipse.jdt.core.dom.Block;
31 import org.eclipse.jdt.core.dom.CastExpression;
32 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
33 import org.eclipse.jdt.core.dom.CompilationUnit;
34 import org.eclipse.jdt.core.dom.EnhancedForStatement;
35 import org.eclipse.jdt.core.dom.Expression;
36 import org.eclipse.jdt.core.dom.ExpressionStatement;
37 import org.eclipse.jdt.core.dom.FieldAccess;
38 import org.eclipse.jdt.core.dom.FieldDeclaration;
39 import org.eclipse.jdt.core.dom.IBinding;
40 import org.eclipse.jdt.core.dom.IMethodBinding;
41 import org.eclipse.jdt.core.dom.ITypeBinding;
42 import org.eclipse.jdt.core.dom.IVariableBinding;
43 import org.eclipse.jdt.core.dom.ImportDeclaration;
44 import org.eclipse.jdt.core.dom.Javadoc;
45 import org.eclipse.jdt.core.dom.MethodDeclaration;
46 import org.eclipse.jdt.core.dom.MethodInvocation;
47 import org.eclipse.jdt.core.dom.NodeFinder;
48 import org.eclipse.jdt.core.dom.ParenthesizedExpression;
49 import org.eclipse.jdt.core.dom.PostfixExpression;
50 import org.eclipse.jdt.core.dom.PrefixExpression;
51 import org.eclipse.jdt.core.dom.QualifiedName;
52 import org.eclipse.jdt.core.dom.SimpleName;
53 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
54 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
55 import org.eclipse.jdt.core.dom.SwitchStatement;
56 import org.eclipse.jdt.core.dom.TagElement;
57 import org.eclipse.jdt.core.dom.Type;
58 import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
59 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
60 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
61 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
62 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
63 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
64
65 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
66 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
67 import org.eclipse.jdt.internal.corext.dom.NecessaryParenthesesChecker;
68 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
69 import org.eclipse.jdt.internal.corext.util.Messages;
70
71 import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
72 import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
73 import org.eclipse.jdt.ui.text.java.IProblemLocation;
74
75 import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp;
76 import org.eclipse.jdt.internal.ui.text.correction.JavadocTagsSubProcessor;
77 import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation;
78 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
79
80 /**
81  * Fix which removes unused code.
82  */
83 public class UnusedCodeFix extends CompilationUnitRewriteOperationsFix {
84
85         private static class SideEffectFinder extends ASTVisitor {
86
87                 private final ArrayList<Expression> fSideEffectNodes;
88
89                 public SideEffectFinder(ArrayList<Expression> res) {
90                         fSideEffectNodes= res;
91                 }
92
93                 @Override
94                 public boolean visit(Assignment node) {
95                         fSideEffectNodes.add(node);
96                         return false;
97                 }
98
99                 @Override
100                 public boolean visit(PostfixExpression node) {
101                         fSideEffectNodes.add(node);
102                         return false;
103                 }
104
105                 @Override
106                 public boolean visit(PrefixExpression node) {
107                         Object operator= node.getOperator();
108                         if (operator == PrefixExpression.Operator.INCREMENT || operator == PrefixExpression.Operator.DECREMENT) {
109                                 fSideEffectNodes.add(node);
110                         }
111                         return false;
112                 }
113
114                 @Override
115                 public boolean visit(MethodInvocation node) {
116                         fSideEffectNodes.add(node);
117                         return false;
118                 }
119
120                 @Override
121                 public boolean visit(ClassInstanceCreation node) {
122                         fSideEffectNodes.add(node);
123                         return false;
124                 }
125
126                 @Override
127                 public boolean visit(SuperMethodInvocation node) {
128                         fSideEffectNodes.add(node);
129                         return false;
130                 }
131         }
132
133         private static class RemoveImportOperation extends CompilationUnitRewriteOperation {
134
135                 private final ImportDeclaration fImportDeclaration;
136
137                 public RemoveImportOperation(ImportDeclaration importDeclaration) {
138                         fImportDeclaration= importDeclaration;
139                 }
140
141                 /**
142                  * {@inheritDoc}
143                  */
144                 @Override
145                 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
146                         ImportDeclaration node= fImportDeclaration;
147                         TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveImport_description, cuRewrite);
148                         cuRewrite.getASTRewrite().remove(node, group);
149                 }
150
151         }
152
153         private static class RemoveUnusedMemberOperation extends CompilationUnitRewriteOperation {
154
155                 private final SimpleName[] fUnusedNames;
156                 private boolean fForceRemove;
157                 private int fRemovedAssignmentsCount;
158                 private int fAlteredAssignmentsCount;
159
160                 public RemoveUnusedMemberOperation(SimpleName[] unusedNames, boolean removeAllAsignements) {
161                         fUnusedNames= unusedNames;
162                         fForceRemove= removeAllAsignements;
163                 }
164
165                 /**
166                  * {@inheritDoc}
167                  */
168                 @Override
169                 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
170                         for (int i= 0; i < fUnusedNames.length; i++) {
171                                 removeUnusedName(cuRewrite, fUnusedNames[i]);
172                         }
173                 }
174
175                 private void removeUnusedName(CompilationUnitRewrite cuRewrite, SimpleName simpleName) {
176                         ASTRewrite rewrite= cuRewrite.getASTRewrite();
177                         CompilationUnit completeRoot= cuRewrite.getRoot();
178
179                         IBinding binding= simpleName.resolveBinding();
180                         CompilationUnit root= (CompilationUnit) simpleName.getRoot();
181                         String displayString= getDisplayString(binding);
182                         TextEditGroup group= createTextEditGroup(displayString, cuRewrite);
183                         if (binding.getKind() == IBinding.METHOD) {
184                                 IMethodBinding decl= ((IMethodBinding) binding).getMethodDeclaration();
185                                 ASTNode declaration= root.findDeclaringNode(decl);
186                                 rewrite.remove(declaration, group);
187                         } else if (binding.getKind() == IBinding.TYPE) {
188                                 ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration();
189                                 ASTNode declaration= root.findDeclaringNode(decl);
190                                 if (declaration.getParent() instanceof TypeDeclarationStatement) {
191                                         declaration= declaration.getParent();
192                                 }
193                                 rewrite.remove(declaration, group);
194                         } else if (binding.getKind() == IBinding.VARIABLE) {
195                                 SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
196                                 SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
197                                 for (int i= 0; i < references.length; i++) {
198                                         removeVariableReferences(rewrite, references[i], group);
199                                 }
200
201                                 IVariableBinding bindingDecl= ((IVariableBinding) nameNode.resolveBinding()).getVariableDeclaration();
202                                 ASTNode declaringNode= completeRoot.findDeclaringNode(bindingDecl);
203                                 if (declaringNode instanceof SingleVariableDeclaration) {
204                                         removeParamTag(rewrite, (SingleVariableDeclaration) declaringNode, group);
205                                 }
206                         } else {
207                                 // unexpected
208                         }
209                 }
210
211                 private String getDisplayString(IBinding binding) {
212                         switch (binding.getKind()) {
213                                 case IBinding.TYPE:
214                                         return FixMessages.UnusedCodeFix_RemoveUnusedType_description;
215                                 case IBinding.METHOD:
216                                         if (((IMethodBinding) binding).isConstructor()) {
217                                                 return FixMessages.UnusedCodeFix_RemoveUnusedConstructor_description;
218                                         } else {
219                                                 return FixMessages.UnusedCodeFix_RemoveUnusedPrivateMethod_description;
220                                         }
221                                 case IBinding.VARIABLE:
222                                         if (((IVariableBinding) binding).isField()) {
223                                                 return FixMessages.UnusedCodeFix_RemoveUnusedField_description;
224                                         } else {
225                                                 return FixMessages.UnusedCodeFix_RemoveUnusedVariabl_description;
226                                         }
227                                 default:
228                                         return ""; //$NON-NLS-1$
229                         }
230                 }
231
232                 private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl, TextEditGroup group) {
233                         if (varDecl.getParent() instanceof MethodDeclaration) {
234                                 Javadoc javadoc= ((MethodDeclaration) varDecl.getParent()).getJavadoc();
235                                 if (javadoc != null) {
236                                         TagElement tagElement= JavadocTagsSubProcessor.findParamTag(javadoc, varDecl.getName().getIdentifier());
237                                         if (tagElement != null) {
238                                                 rewrite.remove(tagElement, group);
239                                         }
240                                 }
241                         }
242                 }
243
244                 /**
245                  * Remove the field or variable declaration including the initializer.
246                  * @param rewrite the AST rewriter to use
247                  * @param reference a reference to the variable to remove
248                  * @param group the text edit group to use
249                  */
250                 private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference, TextEditGroup group) {
251                         ASTNode parent= reference.getParent();
252                         while (parent instanceof QualifiedName) {
253                                 parent= parent.getParent();
254                         }
255                         if (parent instanceof FieldAccess) {
256                                 parent= parent.getParent();
257                         }
258
259                         int nameParentType= parent.getNodeType();
260                         if (nameParentType == ASTNode.ASSIGNMENT) {
261                                 Assignment assignment= (Assignment) parent;
262                                 Expression rightHand= assignment.getRightHandSide();
263
264                                 ASTNode assignParent= assignment.getParent();
265                                 if (assignParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT && rightHand.getNodeType() != ASTNode.ASSIGNMENT) {
266                                         removeVariableWithInitializer(rewrite, rightHand, assignParent, group);
267                                 }       else {
268                                         rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), group);
269                                 }
270                         } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
271                                 rewrite.remove(parent, group);
272                         } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
273                                 VariableDeclarationFragment frag= (VariableDeclarationFragment) parent;
274                                 ASTNode varDecl= frag.getParent();
275                                 List<VariableDeclarationFragment> fragments;
276                                 if (varDecl instanceof VariableDeclarationExpression) {
277                                         fragments= ((VariableDeclarationExpression) varDecl).fragments();
278                                 } else if (varDecl instanceof FieldDeclaration) {
279                                         fragments= ((FieldDeclaration) varDecl).fragments();
280                                 } else {
281                                         fragments= ((VariableDeclarationStatement) varDecl).fragments();
282                                 }
283                                 Expression initializer = frag.getInitializer();
284                                 if (initializer instanceof CastExpression) {
285                                         initializer= ((CastExpression) initializer).getExpression();
286                                 }
287                                 boolean sideEffectInitializer = initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation;
288                                 if (fragments.size() == fUnusedNames.length) {
289                                         if (fForceRemove) {
290                                                 rewrite.remove(varDecl, group);
291                                                 return;
292                                         }
293                                         if (parent.getParent() instanceof FieldDeclaration) {
294                                                 rewrite.remove(varDecl, group);
295                                                 return;
296                                         }
297                                         if (sideEffectInitializer){
298                                                 Expression movedInit = (Expression) rewrite.createMoveTarget(initializer);
299                                                 ExpressionStatement wrapped = rewrite.getAST().newExpressionStatement(movedInit);
300                                                 rewrite.replace(varDecl, wrapped, group);
301                                         } else {
302                                                 rewrite.remove(varDecl, group);
303                                         }
304                                 } else {
305                                         if (fForceRemove) {
306                                                 rewrite.remove(frag, group);
307                                                 return;
308                                         }
309                                         //multiple declarations in one line
310                                         ASTNode declaration = parent.getParent();
311                                         if (declaration instanceof FieldDeclaration) {
312                                                 rewrite.remove(frag, group);
313                                                 return;
314                                         }
315                                         if (declaration instanceof VariableDeclarationStatement) {
316                                                 ASTNode lst = declaration.getParent();
317                                                 ListRewrite listRewrite= null;
318                                                 if (lst instanceof Block) {
319                                                         listRewrite= rewrite.getListRewrite(lst, Block.STATEMENTS_PROPERTY);
320                                                 } else if (lst instanceof SwitchStatement) {
321                                                         listRewrite= rewrite.getListRewrite(lst, SwitchStatement.STATEMENTS_PROPERTY);
322                                                 } else {
323                                                         Assert.isTrue(false);
324                                                 }
325                                                 splitUpDeclarations(rewrite, group, frag, listRewrite, (VariableDeclarationStatement) declaration);
326                                                 rewrite.remove(frag, group);
327                                                 return;
328                                         }
329                                         if (declaration instanceof VariableDeclarationExpression) {
330                                                 //keep constructors and method invocations
331                                                 if (!sideEffectInitializer){
332                                                         rewrite.remove(frag, group);
333                                                 }
334                                         }
335                                 }
336                         } else if (nameParentType == ASTNode.POSTFIX_EXPRESSION || nameParentType == ASTNode.PREFIX_EXPRESSION) {
337                                 Expression expression= (Expression)parent;
338                                 ASTNode expressionParent= expression.getParent();
339                                 if (expressionParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
340                                         removeStatement(rewrite, expressionParent, group);
341                                 } else {
342                                         rewrite.remove(expression, group);
343                                 }
344                         }
345                 }
346
347                 private void splitUpDeclarations(ASTRewrite rewrite, TextEditGroup group, VariableDeclarationFragment frag, ListRewrite statementRewrite, VariableDeclarationStatement originalStatement) {
348                         Expression initializer = frag.getInitializer();
349                         //keep constructors and method invocations
350                         if (initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation){
351                                 Expression movedInitializer= (Expression) rewrite.createMoveTarget(initializer);
352
353                                 ExpressionStatement newInitializer= rewrite.getAST().newExpressionStatement( movedInitializer);
354                                 statementRewrite.insertAfter(newInitializer, originalStatement, group);
355
356                                 VariableDeclarationStatement newDeclaration= null;
357                                 List<VariableDeclarationFragment> fragments= originalStatement.fragments();
358                                 int fragIndex= fragments.indexOf(frag);
359                                 ListIterator<VariableDeclarationFragment> fragmentIterator= fragments.listIterator(fragIndex+1);
360                                 while (fragmentIterator.hasNext()) {
361                                         VariableDeclarationFragment currentFragment= fragmentIterator.next();
362                                         VariableDeclarationFragment movedFragment= (VariableDeclarationFragment) rewrite.createMoveTarget(currentFragment);
363                                         if (newDeclaration == null) {
364                                                 newDeclaration= rewrite.getAST().newVariableDeclarationStatement(movedFragment);
365                                                 Type copiedType= (Type) rewrite.createCopyTarget(originalStatement.getType());
366                                                 newDeclaration.setType(copiedType);
367                                         } else
368                                                 newDeclaration.fragments().add(movedFragment);
369                                 }
370                                 if (newDeclaration != null){
371                                         statementRewrite.insertAfter(newDeclaration, newInitializer, group);
372                                 }
373                                 if (originalStatement.fragments().size() == newDeclaration.fragments().size() + 1){
374                                         rewrite.remove(originalStatement, group);
375                                 }
376                         }
377                 }
378
379                 private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode, TextEditGroup group) {
380                         boolean performRemove= fForceRemove;
381                         if (!performRemove) {
382                                 ArrayList<Expression> sideEffectNodes= new ArrayList<Expression>();
383                                 initializerNode.accept(new SideEffectFinder(sideEffectNodes));
384                                 performRemove= sideEffectNodes.isEmpty();
385                         }
386                         if (performRemove) {
387                                 removeStatement(rewrite, statementNode, group);
388                                 fRemovedAssignmentsCount++;
389                         } else {
390                                 ASTNode initNode = rewrite.createMoveTarget(initializerNode);
391                                 ExpressionStatement statement = rewrite.getAST().newExpressionStatement((Expression) initNode);
392                                 rewrite.replace(statementNode, statement, null);
393                                 fAlteredAssignmentsCount++;
394                         }
395                 }
396
397                 private void removeStatement(ASTRewrite rewrite, ASTNode statementNode, TextEditGroup group) {
398                         if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) {
399                                 rewrite.replace(statementNode, rewrite.getAST().newBlock(), group);
400                         } else {
401                                 rewrite.remove(statementNode, group);
402                         }
403                 }
404
405                 @Override
406                 public String getAdditionalInfo() {
407                         StringBuffer sb=new StringBuffer();
408                         if (fRemovedAssignmentsCount == 1) {
409                                 sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_singular);
410                         } else if (fRemovedAssignmentsCount > 1) {
411                                 sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_plural, String.valueOf(fRemovedAssignmentsCount)));
412                         }
413                         if (fAlteredAssignmentsCount == 1) {
414                                 sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_singular);
415                         } else if (fAlteredAssignmentsCount > 1) {
416                                 sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_plural, String.valueOf(fAlteredAssignmentsCount)));
417                         }
418                         if (sb.length()>0) {
419                                 return sb.toString();
420                         } else
421                                 return null;
422                 }
423         }
424
425         private static class RemoveCastOperation extends CompilationUnitRewriteOperation {
426
427                 private final CastExpression fCast;
428
429                 public RemoveCastOperation(CastExpression cast) {
430                         fCast= cast;
431                 }
432
433                 /**
434                  * {@inheritDoc}
435                  */
436                 @Override
437                 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
438
439                         TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
440
441                         ASTRewrite rewrite= cuRewrite.getASTRewrite();
442
443                         CastExpression cast= fCast;
444                         Expression expression= cast.getExpression();
445                         if (expression instanceof ParenthesizedExpression) {
446                                 Expression childExpression= ((ParenthesizedExpression) expression).getExpression();
447                                 if (NecessaryParenthesesChecker.needsParentheses(childExpression, cast, CastExpression.EXPRESSION_PROPERTY)) {
448                                         expression= childExpression;
449                                 }
450                         }
451                         
452                         replaceCast(cast, expression, rewrite, group);
453                 }
454         }
455
456         private static class RemoveAllCastOperation extends CompilationUnitRewriteOperation {
457
458                 private final LinkedHashSet<CastExpression> fUnnecessaryCasts;
459
460                 public RemoveAllCastOperation(LinkedHashSet<CastExpression> unnecessaryCasts) {
461                         fUnnecessaryCasts= unnecessaryCasts;
462                 }
463
464                 /**
465                  * {@inheritDoc}
466                  */
467                 @Override
468                 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
469                         ASTRewrite rewrite= cuRewrite.getASTRewrite();
470
471                         TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
472
473                         while (fUnnecessaryCasts.size() > 0) {
474                                 CastExpression castExpression= fUnnecessaryCasts.iterator().next();
475                                 fUnnecessaryCasts.remove(castExpression);
476                                 
477                                 /*
478                                  * ASTRewrite doesn't allow replacing (deleting) of moved nodes. To solve problems
479                                  * with nested casts, we need to replace all casts at once.
480                                  * 
481                                  * The loop proceeds downwards to find the innermost expression that stays in the result (downChild)
482                                  * and it also skips necessary parentheses.
483                                  */
484                                 CastExpression down= castExpression;
485                                 Expression downChild= down.getExpression();
486                                 while (true) {
487                                         if (fUnnecessaryCasts.contains(downChild)) {
488                                                 down= (CastExpression) downChild;
489                                                 fUnnecessaryCasts.remove(down);
490                                                 downChild= down.getExpression();
491                                         } else if (downChild instanceof ParenthesizedExpression) {
492                                                 Expression downChildExpression= ((ParenthesizedExpression) downChild).getExpression();
493                                                 // is it justified that downChild is a ParenthesizedExpression?
494                                                 if (NecessaryParenthesesChecker.needsParentheses(downChildExpression, down, CastExpression.EXPRESSION_PROPERTY)) {
495                                                         // yes => continue walking down
496                                                         downChild= downChildExpression;
497                                                 } else {
498                                                         // no => stop walking
499                                                         break;
500                                                 }
501                                         } else {
502                                                 break;
503                                         }
504                                 }
505                                 
506                                 // downChild is the innermost CastExpression's expression, stripped of a necessary surrounding ParenthesizedExpression
507                                 // Move either downChild (if it doesn't need parentheses), or a parenthesized version if necessary
508                                 
509                                 replaceCast(castExpression, downChild, rewrite, group);
510                         }
511                 }
512         }
513
514         public static UnusedCodeFix createRemoveUnusedImportFix(CompilationUnit compilationUnit, IProblemLocation problem) {
515                 if (isUnusedImport(problem)) {
516                         ImportDeclaration node= getImportDeclaration(problem, compilationUnit);
517                         if (node != null) {
518                                 String label= FixMessages.UnusedCodeFix_RemoveImport_description;
519                                 RemoveImportOperation operation= new RemoveImportOperation(node);
520                                 Map<String, String> options= new Hashtable<String, String>();
521                                 options.put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
522                                 return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[] {operation}, options);
523                         }
524                 }
525                 return null;
526         }
527
528         public static boolean isUnusedImport(IProblemLocation problem) {
529                 int id= problem.getProblemId();
530                 return id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport || id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound;
531         }
532
533         public static UnusedCodeFix createUnusedMemberFix(CompilationUnit compilationUnit, IProblemLocation problem, boolean removeAllAssignements) {
534                 if (isUnusedMember(problem)) {
535                         SimpleName name= getUnusedName(compilationUnit, problem);
536                         if (name != null) {
537                                 IBinding binding= name.resolveBinding();
538                                 if (binding != null) {
539                                         if (isFormalParameterInEnhancedForStatement(name))
540                                                 return null;
541
542                                         String label= getDisplayString(name, binding, removeAllAssignements);
543                                         RemoveUnusedMemberOperation operation= new RemoveUnusedMemberOperation(new SimpleName[] { name }, removeAllAssignements);
544                                         return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[] { operation }, getCleanUpOptions(binding, removeAllAssignements));
545                                 }
546                         }
547                 }
548                 return null;
549         }
550
551         public static boolean isUnusedMember(IProblemLocation problem) {
552                 int id= problem.getProblemId();
553                 return id == IProblem.UnusedPrivateMethod || id == IProblem.UnusedPrivateConstructor || id == IProblem.UnusedPrivateField || id == IProblem.UnusedPrivateType
554                                 || id == IProblem.LocalVariableIsNeverUsed || id == IProblem.ArgumentIsNeverUsed;
555         }
556
557         public static UnusedCodeFix createRemoveUnusedCastFix(CompilationUnit compilationUnit, IProblemLocation problem) {
558                 if (problem.getProblemId() != IProblem.UnnecessaryCast)
559                         return null;
560
561                 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
562
563                 ASTNode curr= selectedNode;
564                 while (curr instanceof ParenthesizedExpression) {
565                         curr= ((ParenthesizedExpression) curr).getExpression();
566                 }
567
568                 if (!(curr instanceof CastExpression))
569                         return null;
570
571                 return new UnusedCodeFix(FixMessages.UnusedCodeFix_RemoveCast_description, compilationUnit, new CompilationUnitRewriteOperation[] {new RemoveCastOperation((CastExpression)curr)});
572         }
573
574         public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit,
575                         boolean removeUnusedPrivateMethods,
576                         boolean removeUnusedPrivateConstructors,
577                         boolean removeUnusedPrivateFields,
578                         boolean removeUnusedPrivateTypes,
579                         boolean removeUnusedLocalVariables,
580                         boolean removeUnusedImports,
581                         boolean removeUnusedCast) {
582
583                 IProblem[] problems= compilationUnit.getProblems();
584                 IProblemLocation[] locations= new IProblemLocation[problems.length];
585                 for (int i= 0; i < problems.length; i++) {
586                         locations[i]= new ProblemLocation(problems[i]);
587                 }
588
589                 return createCleanUp(compilationUnit, locations,
590                                 removeUnusedPrivateMethods,
591                                 removeUnusedPrivateConstructors,
592                                 removeUnusedPrivateFields,
593                                 removeUnusedPrivateTypes,
594                                 removeUnusedLocalVariables,
595                                 removeUnusedImports,
596                                 removeUnusedCast);
597         }
598
599         public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, IProblemLocation[] problems,
600                         boolean removeUnusedPrivateMethods,
601                         boolean removeUnusedPrivateConstructors,
602                         boolean removeUnusedPrivateFields,
603                         boolean removeUnusedPrivateTypes,
604                         boolean removeUnusedLocalVariables,
605                         boolean removeUnusedImports,
606                         boolean removeUnusedCast) {
607
608                 List<CompilationUnitRewriteOperation> result= new ArrayList<CompilationUnitRewriteOperation>();
609                 Hashtable<ASTNode, List<SimpleName>> variableDeclarations= new Hashtable<ASTNode, List<SimpleName>>();
610                 LinkedHashSet<CastExpression> unnecessaryCasts= new LinkedHashSet<CastExpression>();
611                 for (int i= 0; i < problems.length; i++) {
612                         IProblemLocation problem= problems[i];
613                         int id= problem.getProblemId();
614
615                         if (removeUnusedImports && (id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport ||
616                                     id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound))
617                         {
618                                 ImportDeclaration node= UnusedCodeFix.getImportDeclaration(problem, compilationUnit);
619                                 if (node != null) {
620                                         result.add(new RemoveImportOperation(node));
621                                 }
622                         }
623
624                         if ((removeUnusedPrivateMethods && id == IProblem.UnusedPrivateMethod) || (removeUnusedPrivateConstructors && id == IProblem.UnusedPrivateConstructor) ||
625                             (removeUnusedPrivateTypes && id == IProblem.UnusedPrivateType)) {
626
627                                 SimpleName name= getUnusedName(compilationUnit, problem);
628                                 if (name != null) {
629                                         IBinding binding= name.resolveBinding();
630                                         if (binding != null) {
631                                                 result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
632                                         }
633                                 }
634                         }
635
636                         if ((removeUnusedLocalVariables && id == IProblem.LocalVariableIsNeverUsed) ||  (removeUnusedPrivateFields && id == IProblem.UnusedPrivateField)) {
637                                 SimpleName name= getUnusedName(compilationUnit, problem);
638                                 if (name != null) {
639                                         IBinding binding= name.resolveBinding();
640                                         if (binding instanceof IVariableBinding && !isFormalParameterInEnhancedForStatement(name) && (!((IVariableBinding) binding).isField() || isSideEffectFree(name, compilationUnit))) {
641                                                 VariableDeclarationFragment parent= (VariableDeclarationFragment) ASTNodes.getParent(name, VariableDeclarationFragment.class);
642                                                 if (parent != null) {
643                                                         ASTNode varDecl= parent.getParent();
644                                                         if (!variableDeclarations.containsKey(varDecl)) {
645                                                                 variableDeclarations.put(varDecl, new ArrayList<SimpleName>());
646                                                         }
647                                                         variableDeclarations.get(varDecl).add(name);
648                                                 } else {
649                                                         result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
650                                                 }
651                                         }
652                                 }
653                         }
654
655                         if (removeUnusedCast && id == IProblem.UnnecessaryCast) {
656                                 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
657
658                                 ASTNode curr= selectedNode;
659                                 while (curr instanceof ParenthesizedExpression) {
660                                         curr= ((ParenthesizedExpression) curr).getExpression();
661                                 }
662
663                                 if (curr instanceof CastExpression) {
664                                         unnecessaryCasts.add((CastExpression) curr);
665                                 }
666                         }
667                 }
668                 for (Iterator<ASTNode> iter= variableDeclarations.keySet().iterator(); iter.hasNext();) {
669                         ASTNode node= iter.next();
670                         List<SimpleName> names= variableDeclarations.get(node);
671                         result.add(new RemoveUnusedMemberOperation(names.toArray(new SimpleName[names.size()]), false));
672                 }
673                 if (unnecessaryCasts.size() > 0)
674                         result.add(new RemoveAllCastOperation(unnecessaryCasts));
675
676                 if (result.size() == 0)
677                         return null;
678
679                 return new UnusedCodeFix(FixMessages.UnusedCodeFix_change_name, compilationUnit, result.toArray(new CompilationUnitRewriteOperation[result.size()]));
680         }
681
682         private static boolean isFormalParameterInEnhancedForStatement(SimpleName name) {
683                 return name.getParent() instanceof SingleVariableDeclaration && name.getParent().getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY;
684         }
685
686         private static boolean isSideEffectFree(SimpleName simpleName, CompilationUnit completeRoot) {
687                 SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
688                 SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
689                 for (int i= 0; i < references.length; i++) {
690                         if (hasSideEffect(references[i]))
691                                 return false;
692                 }
693                 return true;
694         }
695
696         private static boolean hasSideEffect(SimpleName reference) {
697                 ASTNode parent= reference.getParent();
698                 while (parent instanceof QualifiedName) {
699                         parent= parent.getParent();
700                 }
701                 if (parent instanceof FieldAccess) {
702                         parent= parent.getParent();
703                 }
704
705                 ASTNode node= null;
706                 int nameParentType= parent.getNodeType();
707                 if (nameParentType == ASTNode.ASSIGNMENT) {
708                         Assignment assignment= (Assignment) parent;
709                         node= assignment.getRightHandSide();
710                 } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
711                         SingleVariableDeclaration decl= (SingleVariableDeclaration)parent;
712                         node= decl.getInitializer();
713                         if (node == null)
714                                 return false;
715                 } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
716                         node= parent;
717                 } else {
718                         return false;
719                 }
720
721                 ArrayList<Expression> sideEffects= new ArrayList<Expression>();
722                 node.accept(new SideEffectFinder(sideEffects));
723                 return sideEffects.size() > 0;
724         }
725
726         private static SimpleName getUnusedName(CompilationUnit compilationUnit, IProblemLocation problem) {
727                 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
728
729                 if (selectedNode instanceof MethodDeclaration) {
730                         return ((MethodDeclaration) selectedNode).getName();
731                 } else if (selectedNode instanceof SimpleName) {
732                         return (SimpleName) selectedNode;
733                 }
734
735                 return null;
736         }
737
738         private static String getDisplayString(SimpleName simpleName, IBinding binding, boolean removeAllAssignements) {
739                 String name= BasicElementLabels.getJavaElementName(simpleName.getIdentifier());
740                 switch (binding.getKind()) {
741                         case IBinding.TYPE:
742                                 return Messages.format(FixMessages.UnusedCodeFix_RemoveType_description, name);
743                         case IBinding.METHOD:
744                                 if (((IMethodBinding) binding).isConstructor()) {
745                                         return Messages.format(FixMessages.UnusedCodeFix_RemoveConstructor_description, name);
746                                 } else {
747                                         return Messages.format(FixMessages.UnusedCodeFix_RemoveMethod_description, name);
748                                 }
749                         case IBinding.VARIABLE:
750                                 if (removeAllAssignements) {
751                                         return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocalWithInitializer_description, name);
752                                 } else {
753                                         return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_description, name);
754                                 }
755                         default:
756                                 return ""; //$NON-NLS-1$
757                 }
758         }
759
760         private static Map<String, String> getCleanUpOptions(IBinding binding, boolean removeAll) {
761                 Map<String, String> result= new Hashtable<String, String>();
762
763                 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptions.TRUE);
764                 switch (binding.getKind()) {
765                         case IBinding.TYPE:
766                                 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_TYPES, CleanUpOptions.TRUE);
767                                 break;
768                         case IBinding.METHOD:
769                                 if (((IMethodBinding) binding).isConstructor()) {
770                                         result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS, CleanUpOptions.TRUE);
771                                 } else {
772                                         result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_METHODS, CleanUpOptions.TRUE);
773                                 }
774                                 break;
775                         case IBinding.VARIABLE:
776                                 if (removeAll)
777                                         return null;
778
779                                 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_FELDS, CleanUpOptions.TRUE);
780                                 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_LOCAL_VARIABLES, CleanUpOptions.TRUE);
781                                 break;
782                 }
783
784                 return result;
785         }
786
787         private static ImportDeclaration getImportDeclaration(IProblemLocation problem, CompilationUnit compilationUnit) {
788                 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
789                 if (selectedNode != null) {
790                         ASTNode node= ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
791                         if (node instanceof ImportDeclaration) {
792                                 return (ImportDeclaration)node;
793                         }
794                 }
795                 return null;
796         }
797
798         private static void replaceCast(CastExpression castExpression, Expression replacement, ASTRewrite rewrite, TextEditGroup group) {
799                 boolean castEnclosedInNecessaryParentheses= castExpression.getParent() instanceof ParenthesizedExpression
800                                 && NecessaryParenthesesChecker.needsParentheses(castExpression, castExpression.getParent().getParent(), castExpression.getParent().getLocationInParent());
801                 
802                 ASTNode toReplace= castEnclosedInNecessaryParentheses ? castExpression.getParent() : castExpression;
803                 ASTNode move;
804                 if (NecessaryParenthesesChecker.needsParentheses(replacement, toReplace.getParent(), toReplace.getLocationInParent())) {
805                         if (replacement.getParent() instanceof ParenthesizedExpression) {
806                                 move= rewrite.createMoveTarget(replacement.getParent());
807                         } else if (castEnclosedInNecessaryParentheses) {
808                                 toReplace= castExpression;
809                                 move= rewrite.createMoveTarget(replacement);
810                         } else {
811                                 ParenthesizedExpression parentheses= replacement.getAST().newParenthesizedExpression();
812                                 parentheses.setExpression((Expression) rewrite.createMoveTarget(replacement));
813                                 move= parentheses;
814                         }
815                 } else {
816                         move= rewrite.createMoveTarget(replacement);
817                 }
818                 rewrite.replace(toReplace, move, group);
819         }
820
821         private final Map<String, String> fCleanUpOptions;
822
823         private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
824                 this(name, compilationUnit, fixRewriteOperations, null);
825         }
826
827         private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations, Map<String, String> options) {
828                 super(name, compilationUnit, fixRewriteOperations);
829                 fCleanUpOptions= options;
830         }
831
832         public UnusedCodeCleanUp getCleanUp() {
833                 if (fCleanUpOptions == null)
834                         return null;
835
836                 return new UnusedCodeCleanUp(fCleanUpOptions);
837         }
838
839 }