KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > refactoring > experimental > plugins > CleanUpRefactoringPlugin


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Leon Chiver. All Rights Reserved.
17  */

18 package org.netbeans.modules.refactoring.experimental.plugins;
19
20 import java.lang.reflect.Modifier JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Collection JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.ListIterator JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
30 import javax.jmi.reflect.RefObject;
31 import org.netbeans.jmi.javamodel.*;
32 import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
33 import org.netbeans.modules.refactoring.api.AbstractRefactoring;
34 import org.netbeans.modules.refactoring.api.Problem;
35 import org.netbeans.modules.refactoring.experimental.CleanUpRefactoring;
36 import org.netbeans.modules.refactoring.plugins.JavaRefactoringPlugin;
37 import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
38 import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImpl;
39 import org.openide.filesystems.FileObject;
40 import org.openide.text.PositionBounds;
41 import org.openide.util.NbBundle;
42
43 /**
44  * @author leon
45  */

46 public class CleanUpRefactoringPlugin extends JavaRefactoringPlugin {
47     
48     private CleanUpRefactoring refactoring;
49
50     public CleanUpRefactoringPlugin(CleanUpRefactoring refactoring) {
51         this.refactoring = refactoring;
52     }
53     
54     public Problem preCheck() {
55         return null;
56     }
57     
58     public Problem checkParameters() {
59         return null;
60     }
61     
62     public Problem fastCheckParameters() {
63         if (refactoring.getResources().size() == 0) {
64             return new Problem(true,
65                     NbBundle.getMessage(CleanUpRefactoringPlugin.class, "ERR_NoFiles")); // NOI18N
66
}
67         if (!isRemoveUnusedCode() && !refactoring.isRemoveUnusedImports()) {
68             return new Problem(true,
69                     NbBundle.getMessage(CleanUpRefactoringPlugin.class, "ERR_NoCleanUpActions")); // NOI18N
70
}
71         if (refactoring.getResources().size() > 1) {
72             return new Problem(false,
73                     NbBundle.getMessage(CleanUpRefactoringPlugin.class, "WARN_SingleFileRefactoring")); // NOI18N
74
} else {
75             return new Problem(false,
76                     NbBundle.getMessage(CleanUpRefactoringPlugin.class, "WARN_ReviewChanges")); // NOI18N
77
}
78     }
79     
80     public Problem prepare(RefactoringElementsBag refactoringElements) {
81         List JavaDoc/*<Resource>*/ resources = refactoring.getResources();
82         boolean comment = refactoring.isCommentInsteadOfRemoving();
83         boolean removeUnusedCode = isRemoveUnusedCode();
84         boolean removeUnusedImports = refactoring.isRemoveUnusedImports();
85         int steps = 0;
86         int sz = resources.size();
87         if (removeUnusedCode) {
88             // Take 10 steps to process each resource
89
steps = sz * 10;
90         }
91         if (removeUnusedImports) {
92             // One step to remove unused imports
93
steps += sz;
94         }
95         fireProgressListenerStart(AbstractRefactoring.PREPARE, steps);
96         try {
97             for (Iterator JavaDoc/*<Resource>*/ it = resources.iterator(); it.hasNext();) {
98                 Resource res = (Resource) it.next();
99                 if (removeUnusedCode) {
100                     FileObject fo = JavaMetamodel.getManager().getFileObject(res);
101                     Set JavaDoc/*<Resource>*/ candidates = new HashSet JavaDoc/*<Resource>*/();
102                     Map JavaDoc/*<Element, List<Element>>*/ element2UsageList = new HashMap JavaDoc/*<Element, List<Element>>*/();
103                     collectElements(res, candidates, element2UsageList, true);
104                     // Go through all elements and remove those without usages
105
for (Iterator JavaDoc candidateIt = candidates.iterator(); candidateIt.hasNext();) {
106                         Element el = (Element) candidateIt.next();
107                         if (el instanceof TypeCast) {
108                             refactoringElements.add(refactoring, new RemoveUnusedElement(fo, el, null, comment));
109                             candidateIt.remove();
110                         } else {
111                             List JavaDoc usages = (List JavaDoc) element2UsageList.get(el);
112                             if (usages.isEmpty()) {
113                                 refactoringElements.add(refactoring, new RemoveUnusedElement(fo, el, null, comment));
114                                 candidateIt.remove();
115                             }
116                         }
117                     }
118                     // For the elements with usages check if they may have usages between them.
119
// If yes (a uses b and b uses a, but both are not needed, as noone else uses them)
120
// remove them in the right order (construct a dependency graph)
121
// TODO - implement
122
}
123                 if (removeUnusedImports) {
124                     refactoringElements.add(refactoring, new RemoveUnusedImportsElement(res));
125                     fireProgressListenerStep();
126                 }
127             }
128         } finally {
129             fireProgressListenerStop();
130         }
131         return null;
132     }
133     
134
135     private boolean isRemoveUnusedCode() {
136         boolean removeUnusedCode =
137               refactoring.isRemoveUnusedFields() ||
138               refactoring.isRemoveUnusedCallableFeatures() ||
139               refactoring.isRemoveUnusedClasses() ||
140               refactoring.isRemoveUnusedLocalVars() ||
141               refactoring.isRemoveRedundantCasts();
142         return removeUnusedCode;
143     }
144     
145     private static class RemoveUnusedImportsElement extends SimpleRefactoringElementImpl {
146         
147         private Resource resource;
148         
149         private String JavaDoc text;
150         
151         public RemoveUnusedImportsElement(Resource resource) {
152             this.resource = resource;
153             this.text = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_RemoveUnusedImports");
154         }
155         
156         public String JavaDoc getText() {
157             return text;
158         }
159         
160         public String JavaDoc getDisplayText() {
161             return text;
162         }
163         
164         public void performChange() {
165             // TODO - remove imports from the same package
166
Set JavaDoc/*<Type>*/ types = new HashSet JavaDoc/*<Type>*/();
167             collectTypes(resource, types);
168             Set JavaDoc importedNames = new HashSet JavaDoc();
169             Set JavaDoc neededImportedNames = new HashSet JavaDoc();
170             Import[] imports = (Import[]) resource.getImports().toArray(new Import[0]);
171             
172             for (int i = 0; i < imports.length; i++) {
173                 Import imp = imports[i];
174                 NamedElement importedNs = imp.getImportedNamespace();
175                 importedNames.add(importedNs.getName());
176             }
177             
178             for (Iterator JavaDoc typesIt = types.iterator(); typesIt.hasNext();) {
179                 Type type = (Type) typesIt.next();
180                 String JavaDoc typeName = type.getName();
181                 String JavaDoc resourcePkgName = resource.getPackageName();
182                 if (resourcePkgName == null) {
183                     resourcePkgName = "";
184                 }
185                 String JavaDoc typePkgName = type.getResource().getPackageName();
186                 if (typePkgName == null) {
187                     typePkgName = "";
188                 }
189                 if (resourcePkgName.equals(typePkgName)) {
190                     continue;
191                 }
192                 if (importedNames.contains(typeName)) {
193                     neededImportedNames.add(typeName);
194                 } else if (importedNames.contains(typePkgName)) {
195                     neededImportedNames.add(typePkgName);
196                 }
197             }
198             
199             for (ListIterator JavaDoc it = resource.getImports().listIterator(); it.hasNext();) {
200                 Import i = (Import) it.next();
201                 NamedElement importedNs = i.getImportedNamespace();
202                 if (importedNs instanceof ClassDefinition && "java.lang".equals(importedNs.getResource().getPackageName())) {
203                     it.remove();
204                 } else if (!neededImportedNames.contains(importedNs.getName())) {
205                     it.remove();
206                 }
207             }
208         }
209         
210         
211         private void collectTypes(Element el, Set JavaDoc/*<Type>*/ types) {
212             Iterator JavaDoc it;
213             if (el instanceof ArrayReference) {
214                 el = ((ArrayReference) el).getParent();
215             }
216             if (el instanceof MultipartId) {
217                 MultipartId id = (MultipartId) el;
218                 List JavaDoc typeArgs = new ArrayList JavaDoc();
219                 while (id.getParent() != null) {
220                     id = id.getParent();
221                     typeArgs.addAll(id.getTypeArguments());
222                 }
223                 typeArgs.addAll(id.getTypeArguments());
224                 NamedElement ne = id.getElement();
225                 if (ne instanceof JavaClass) {
226                     types.add(ne);
227                 }
228                 it = typeArgs.iterator();
229             } else {
230                 it = el.getChildren().iterator();
231             }
232             while (it.hasNext()) {
233                 collectTypes((Element) it.next(), types);
234             }
235         }
236         
237         public Element getJavaElement() {
238             return resource;
239         }
240         
241         public FileObject getParentFile() {
242             return JavaMetamodel.getManager().getFileObject(resource);
243         }
244         
245         public PositionBounds getPosition() {
246             return null;
247         }
248         
249     }
250     
251     private static class RemoveUnusedElement extends SimpleRefactoringElementImpl {
252         
253         private Element element;
254         
255         private boolean commentOut;
256
257         private FileObject fo;
258         
259         private String JavaDoc text;
260         
261         private Collection JavaDoc dependencies;
262         
263         public RemoveUnusedElement(FileObject fo, Element element, Collection JavaDoc dependencies, boolean commentOut) {
264             this.element = element;
265             this.fo = fo;
266             this.commentOut = commentOut;
267             this.dependencies = dependencies;
268             if (element instanceof TypeCast) {
269                 initCastText();
270             } else {
271                 initElementText();
272             }
273         }
274         
275         public String JavaDoc getText() {
276             return text;
277         }
278         
279         private void initCastText() {
280             String JavaDoc action = commentOut ?
281                 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Comment") :
282                 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Remove");
283             TypeCast tc = (TypeCast) element;
284             String JavaDoc from = tc.getExpression().getType().getName();
285             String JavaDoc to = tc.getType().getName();
286             String JavaDoc code = new String JavaDoc(tc.getResource().getSourceText().substring(
287                     tc.getStartOffset(), tc.getExpression().getEndOffset()));
288             // Set the member variable
289
text = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_RemoveRedundantCast",
290                     new Object JavaDoc[] { action, from, to, code});
291         }
292         
293         private void initElementText() {
294             String JavaDoc action = commentOut ?
295                 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Comment") :
296                 NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Remove");
297             String JavaDoc type = null;
298             if (element instanceof LocalVariable) {
299                 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_LocalVariable");
300             } else if (element instanceof Field) {
301                 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Field");
302             } else if (element instanceof Method) {
303                 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Method");
304             } else if (element instanceof Constructor) {
305                 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Constructor");
306             } else if (element instanceof JavaClass) {
307                 type = NbBundle.getMessage(CleanUpRefactoringPlugin.class, "TXT_Class");
308             }
309             String JavaDoc name = getDisplayName((NamedElement) element);
310             // Update the member variable
311
text = NbBundle.getMessage(CleanUpRefactoringPlugin.class,
312                     "TXT_RemoveUnusedElement", // NOI18N
313
new Object JavaDoc[] { action, type, name, });
314         }
315         
316         public String JavaDoc getDisplayText() {
317             return text;
318         }
319         
320         private String JavaDoc getDisplayName(NamedElement ne) {
321             if (ne instanceof CallableFeature) {
322                 String JavaDoc name;
323                 if (ne instanceof Constructor) {
324                     name = ((JavaClass) ((Constructor) ne).getDeclaringClass()).getSimpleName();
325                 } else {
326                     name = ne.getName();
327                 }
328                 StringBuffer JavaDoc buff = new StringBuffer JavaDoc(name).append('(');
329                 Parameter[] p = (Parameter[]) ((CallableFeature) ne).getParameters().toArray(new Parameter[0]);
330                 for (int i = 0; i < p.length; i++) {
331                     if (i > 0) {
332                         buff.append(", "); // NOI18N
333
}
334                     buff.append(p[i].getType().getName());
335                 }
336                 buff.append(')');
337                 return buff.toString();
338             } else if (ne instanceof JavaClass) {
339                 return ((JavaClass) ne).getSimpleName();
340             } else {
341                 return ne.getName();
342             }
343         }
344         
345         public void performChange() {
346             if (dependencies != null) {
347                 for (Iterator JavaDoc it = dependencies.iterator(); it.hasNext();) {
348                     Element el = (Element) it.next();
349                     if (el.isValid()) {
350                         return;
351                     }
352                 }
353             }
354             if (element instanceof ClassMember) {
355                 if (commentOut) {
356                     // TODO - implement
357
} else {
358                     ClassMember cm = (ClassMember) element;
359                     cm.getDeclaringClass().replaceChild(cm, null);
360                 }
361             } else if (element instanceof LocalVariable) {
362                 if (commentOut) {
363                     // TODO - implement
364
} else {
365                     LocalVariable lv = (LocalVariable) element;
366                     LocalVarDeclaration lvd = (LocalVarDeclaration) lv.refImmediateComposite();
367                     List JavaDoc vars = lvd.getVariables();
368                     if (vars.size() > 1) {
369                         vars.remove(lv);
370                     } else {
371                         StatementBlock sb = (StatementBlock) lvd.refImmediateComposite();
372                         sb.getStatements().remove(lvd);
373                     }
374                 }
375             } else if (element instanceof TypeCast) {
376                 TypeCast tc = (TypeCast) element;
377                 Object JavaDoc parent = tc.refImmediateComposite();
378                 while (parent != null && !(parent instanceof Element)) {
379                     if (!(parent instanceof RefObject)) {
380                         continue;
381                     }
382                     parent = ((RefObject) parent).refImmediateComposite();
383                 }
384                 if (parent != null) {
385                     Element parentEl = (Element) parent;
386                     Expression clone = (Expression) tc.getExpression().duplicate();
387                     parentEl.replaceChild(tc, clone);
388                 }
389             }
390         }
391         
392         public Element getJavaElement() {
393             if (element instanceof ClassMember) {
394                 return ((ClassMember) element).getDeclaringClass();
395             } else if (element instanceof LocalVariable) {
396                 Object JavaDoc parent = element;
397                 while (parent != null && !(parent instanceof CallableFeature) && (parent instanceof RefObject)) {
398                     parent = ((RefObject) parent).refImmediateComposite();
399                 }
400                 return parent != null ? (Element) parent : element.getResource();
401             } else if (element instanceof TypeCast) {
402                 Object JavaDoc parent = element;
403                 while (parent != null && !(parent instanceof ClassMember) && (parent instanceof RefObject)) {
404                     parent = ((RefObject) parent).refImmediateComposite();
405                 }
406                 return parent != null ? (Element) parent : element.getResource();
407             } else {
408                 return element.getResource();
409             }
410         }
411         
412         public FileObject getParentFile() {
413             return fo;
414         }
415         
416         public PositionBounds getPosition() {
417             return null;
418         }
419         
420     }
421         
422     
423     private void collectElements(
424             Element el, Set JavaDoc/*<Resource>*/ removalCandidates, Map JavaDoc/*<Element, List<Element>>*/ element2UsageList, boolean fireProgress) {
425         if (isRemovalCandidate(el, refactoring)) {
426             addRemovalCandidate(el, null, removalCandidates, element2UsageList);
427         } else if (el instanceof ElementReference) {
428             Element actualElement = ((ElementReference) el).getElement();
429             if (isRemovalCandidate(actualElement, refactoring)) {
430                 addRemovalCandidate(actualElement, el, removalCandidates, element2UsageList);
431             }
432         }
433         Element[] children = (Element[]) el.getChildren().toArray(new Element[0]);
434         double currentStep = 0;
435         int lastStep = 0;
436         try {
437             // All iterations have to fire 10 progress steps. It doesn't matter
438
// how many elements there are.
439
double increment = 10d / children.length;
440             for (int i = 0; i < children.length; i++) {
441                 if (fireProgress) {
442                     currentStep += increment;
443                     if ((int) currentStep != lastStep) {
444                         fireProgressListenerStep((int) currentStep - lastStep);
445                         lastStep = (int) currentStep;
446                     }
447                 }
448                 collectElements(children[i], removalCandidates, element2UsageList, false);
449             }
450         } finally {
451             // Here we make sure that we didn't miss firing a progress step
452
if (fireProgress && lastStep < 10) {
453                 fireProgressListenerStep(10 - lastStep);
454             }
455         }
456     }
457     
458     private void addRemovalCandidate(Element candidate, Element user,
459             Set JavaDoc/*<Resource>*/ removalCandidates, Map JavaDoc/*<Element, List<Element>>*/ element2UsageList) {
460         removalCandidates.add(candidate);
461         addElementUser(candidate, user, element2UsageList);
462     }
463
464     private void addElementUser(Element element, Element user, Map JavaDoc element2UsageList) {
465         List JavaDoc users = (List JavaDoc) element2UsageList.get(element);
466         if (users == null) {
467             users = new ArrayList JavaDoc();
468             element2UsageList.put(element, users);
469         }
470         if (user != null) {
471             users.add(user);
472         }
473     }
474     
475     private boolean isRemovalCandidate(Element element, CleanUpRefactoring refactoring) {
476         if (element instanceof Field) {
477             return isRemovableField((Field) element);
478         } else if (element instanceof Method) {
479             // Method
480
boolean b = refactoring.isRemoveUnusedCallableFeatures() && (((Method) element).getModifiers() & Modifier.PRIVATE) != 0;
481             if (!b) {
482                 return false;
483             }
484             // TODO - check return types && params
485
return !isSerializationMethod((Method) element);
486         } else if (element instanceof Constructor) {
487             // Constructor
488
Constructor constr = (Constructor) element;
489             // constructor with 0 args could mean singleton
490
return refactoring.isRemoveUnusedCallableFeatures() && ((constr.getModifiers() & Modifier.PRIVATE) != 0) && !constr.getParameters().isEmpty();
491         } else if (element instanceof JavaClass) {
492             // Class
493
return refactoring.isRemoveUnusedClasses() && (((JavaClass) element).getModifiers() & Modifier.PRIVATE) != 0;
494         } else if (element instanceof LocalVariable) {
495             return isRemovableLocalVariable((LocalVariable) element);
496         } else if (element instanceof TypeCast) {
497             // Cast
498
TypeCast tc = (TypeCast) element;
499             return refactoring.isRemoveRedundantCasts() && isCastRedundant(tc.getExpression().getType(), tc.getType());
500         }
501         return false;
502     }
503     
504     private boolean isSerializationMethod(Method m) {
505         String JavaDoc name = m.getName();
506         boolean ro = "readObject".equals(name); // NOI18N
507
boolean wo = "writeObject".equals(name); // NOI18N
508
if (!(ro || wo)) {
509             return false;
510         }
511         Parameter[] p = (Parameter[]) m.getParameters().toArray(new Parameter[0]);
512         if (p.length != 1) {
513             return false;
514         }
515         if (ro) {
516             return "java.io.ObjectInputStream".equals(p[0].getType().getName()); // NOI18N
517
} else {
518             return "java.io.ObjectOutputStream".equals(p[0].getType().getName()); // NOI18N
519
}
520     }
521
522     /**
523      * Checks if a field may be removed
524      */

525     private boolean isRemovableField(Field f) {
526         if (!refactoring.isRemoveUnusedFields()) {
527             return false;
528         }
529         if ((f.getModifiers() & Modifier.PRIVATE) == 0) {
530             return false;
531         }
532         if ("serialVersionUID".equals(f.getName())) { // NOI18N
533
return false;
534         }
535         return isElementWithInitialValueRemovable(f.getInitialValue());
536     }
537     
538     /**
539      * Local variables or fields with a initial value may not be removed
540      * For example, one could have <code>boolean initialized = initialize();</code>
541      * and do some initialization work in that method
542      */

543     private boolean isElementWithInitialValueRemovable(InitialValue val) {
544         if (val == null) {
545             return true;
546         }
547         if (val instanceof Literal) {
548             return true;
549         }
550         if (val instanceof VariableAccess) {
551             return true;
552         }
553         return false;
554     }
555     
556     /**
557      * Checks if a local variable may be removed
558      */

559     private boolean isRemovableLocalVariable(LocalVariable var) {
560         if (!refactoring.isRemoveUnusedLocalVars()) {
561             return false;
562         }
563         return isElementWithInitialValueRemovable(var.getInitialValue());
564     }
565     
566     private boolean isCastRedundant(Type fromType, Type toType) {
567         if (fromType instanceof PrimitiveType && toType instanceof PrimitiveType) {
568             // TODO - implement
569
} else if (fromType instanceof ParameterizedType && toType instanceof ClassDefinition) {
570             ClassDefinition fromClass = ((ParameterizedType) fromType).getDefinition();
571             ClassDefinition toClass = (ClassDefinition) toType;
572             if (fromClass.isSubTypeOf(toClass)) {
573                 return true;
574             }
575         } else if (fromType instanceof ClassDefinition && toType instanceof ClassDefinition) {
576             ClassDefinition fromClass = (ClassDefinition) fromType;
577             ClassDefinition toClass = (ClassDefinition) toType;
578             if (fromClass.isSubTypeOf(toClass)) {
579                 return true;
580             }
581         }
582         return false;
583     }
584     
585 }
586
Popular Tags