UsageNodeVisitor.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.dcd;
005 
006 import java.lang.reflect.Method;
007 import java.lang.reflect.Modifier;
008 
009 import net.sourceforge.pmd.dcd.graph.ClassNode;
010 import net.sourceforge.pmd.dcd.graph.ConstructorNode;
011 import net.sourceforge.pmd.dcd.graph.FieldNode;
012 import net.sourceforge.pmd.dcd.graph.MemberNode;
013 import net.sourceforge.pmd.dcd.graph.MethodNode;
014 import net.sourceforge.pmd.dcd.graph.NodeVisitorAdapter;
015 import net.sourceforge.pmd.dcd.graph.UsageGraph;
016 
017 /**
018  * Perform a visitation a UsageGraph, looking for <em>dead code</em>, which
019  * is essential code which is not used by any other code.  There are various
020  * options for configuration how this determination is made.
021  */
022 public class UsageNodeVisitor extends NodeVisitorAdapter {
023 
024   /**
025    * Configuration options for usage analysis.
026    */
027   public static final class Options {
028     private boolean ignoreClassAnonymous = true;
029 
030     private boolean ignoreConstructorStaticInitializer = true;
031 
032     private boolean ignoreConstructorSinglePrivateNoArg = true;
033 
034     private boolean ignoreConstructorAllPrivate = false;
035 
036     private boolean ignoreMethodJavaLangObjectOverride = true;
037 
038     private boolean ignoreMethodAllOverride = false;
039 
040     private boolean ignoreMethodMain = true;
041 
042     private boolean ignoreFieldInlinable = true;
043 
044     public boolean isIgnoreClassAnonymous() {
045       return ignoreClassAnonymous;
046     }
047 
048     public void setIgnoreClassAnonymous(boolean ignoreClassAnonymous) {
049       this.ignoreClassAnonymous = ignoreClassAnonymous;
050     }
051 
052     public boolean isIgnoreConstructorStaticInitializer() {
053       return ignoreConstructorStaticInitializer;
054     }
055 
056     public void setIgnoreConstructorStaticInitializer(boolean ignoreConstructorStaticInitializer) {
057       this.ignoreConstructorStaticInitializer = ignoreConstructorStaticInitializer;
058     }
059 
060     public boolean isIgnoreConstructorSinglePrivateNoArg() {
061       return ignoreConstructorSinglePrivateNoArg;
062     }
063 
064     public void setIgnoreConstructorSinglePrivateNoArg(boolean ignoreConstructorSinglePrivateNoArg) {
065       this.ignoreConstructorSinglePrivateNoArg = ignoreConstructorSinglePrivateNoArg;
066     }
067 
068     public boolean isIgnoreConstructorAllPrivate() {
069       return ignoreConstructorAllPrivate;
070     }
071 
072     public void setIgnoreConstructorAllPrivate(boolean ignoreConstructorAllPrivate) {
073       this.ignoreConstructorAllPrivate = ignoreConstructorAllPrivate;
074     }
075 
076     public boolean isIgnoreMethodJavaLangObjectOverride() {
077       return ignoreMethodJavaLangObjectOverride;
078     }
079 
080     public void setIgnoreMethodJavaLangObjectOverride(boolean ignoreMethodJavaLangObjectOverride) {
081       this.ignoreMethodJavaLangObjectOverride = ignoreMethodJavaLangObjectOverride;
082     }
083 
084     public boolean isIgnoreMethodAllOverride() {
085       return ignoreMethodAllOverride;
086     }
087 
088     public void setIgnoreMethodAllOverride(boolean ignoreMethodAllOverride) {
089       this.ignoreMethodAllOverride = ignoreMethodAllOverride;
090     }
091 
092     public boolean isIgnoreMethodMain() {
093       return ignoreMethodMain;
094     }
095 
096     public void setIgnoreMethodMain(boolean ignoreMethodMain) {
097       this.ignoreMethodMain = ignoreMethodMain;
098     }
099 
100     public boolean isIgnoreFieldInlinable() {
101       return ignoreFieldInlinable;
102     }
103 
104     public void setIgnoreFieldInlinable(boolean ignoreFieldInlinable) {
105       this.ignoreFieldInlinable = ignoreFieldInlinable;
106     }
107 
108   }
109 
110   private final Options options = new Options();
111 
112   public Object visit(UsageGraph usageGraph, Object data) {
113     System.out.println("----------------------------------------");
114     super.visit(usageGraph, data);
115     System.out.println("----------------------------------------");
116     return data;
117   }
118 
119   public Object visit(ClassNode classNode, Object data) {
120     boolean log = true;
121     if (options.isIgnoreClassAnonymous() && classNode.getType().isAnonymousClass()) {
122       ignore("class anonymous", classNode);
123       log = false;
124     }
125     if (log) {
126       System.out.println("--- " + classNode.getName() " ---");
127       return super.visit(classNode, data);
128     else {
129       return data;
130     }
131   }
132 
133   public Object visit(FieldNode fieldNode, Object data) {
134     if (fieldNode.getUsers().isEmpty()) {
135       boolean log = true;
136       // A field is inlinable if:
137       // 1) It is final
138       // 2) It is a primitive, or a java.lang.String
139       if (options.isIgnoreFieldInlinable()) {
140         if (Modifier.isFinal(fieldNode.getMember().getModifiers())
141             && fieldNode.getMember().getType().isPrimitive()
142             || fieldNode.getMember().getType().getName().equals("java.lang.String")) {
143           ignore("field inlinable", fieldNode);
144           log = false;
145         }
146       }
147       if (log) {
148         System.out.println("\t" + fieldNode.toStringLong());
149       }
150     }
151     return super.visit(fieldNode, data);
152   }
153 
154   public Object visit(ConstructorNode constructorNode, Object data) {
155     if (constructorNode.getUsers().isEmpty()) {
156       boolean log = true;
157       if (constructorNode.isStaticInitializer()) {
158         if (options.isIgnoreConstructorStaticInitializer()) {
159           ignore("constructor static initializer", constructorNode);
160           log = false;
161         }
162       else if (constructorNode.isInstanceInitializer()) {
163         if (Modifier.isPrivate(constructorNode.getMember().getModifiers())) {
164           if (options.isIgnoreConstructorAllPrivate()) {
165             ignore("constructor all private", constructorNode);
166             log = false;
167           else if (options.isIgnoreConstructorSinglePrivateNoArg()
168               && constructorNode.getMember().getParameterTypes().length == 0
169               && constructorNode.getClassNode().getConstructorNodes().size() == 1) {
170             ignore("constructor single private no-arg", constructorNode);
171             log = false;
172           }
173         }
174       }
175       if (log) {
176         System.out.println("\t" + constructorNode.toStringLong());
177       }
178     }
179     return super.visit(constructorNode, data);
180   }
181 
182   private static boolean isMainMethod(MethodNode node) {
183     
184     final Method method = node.getMember();
185     
186     return method.getName().equals("main")
187       && Modifier.isPublic(method.getModifiers())
188       && Modifier.isStatic(method.getModifiers())
189       && method.getReturnType() == Void.TYPE
190       && method.getParameterTypes().length == 1
191       && method.getParameterTypes()[0].isArray()
192       && method.getParameterTypes()[0].getComponentType().equals(java.lang.String.class);
193   }
194   
195   
196   public Object visit(MethodNode methodNode, Object data) {
197     if (methodNode.getUsers().isEmpty()) {
198       boolean log = true;
199       if (options.isIgnoreMethodAllOverride()) {
200         if (ClassLoaderUtil.isOverridenMethod(methodNode.getClassNode().getClass(), methodNode.getMember(),
201             false)) {
202           ignore("method all override", methodNode);
203           log = false;
204         }
205       else if (options.isIgnoreMethodJavaLangObjectOverride()) {
206         if (ClassLoaderUtil.isOverridenMethod(java.lang.Object.class, methodNode.getMember()true)) {
207           ignore("method java.lang.Object override", methodNode);
208           log = false;
209         }
210       }
211       if (options.isIgnoreMethodMain()) {
212         if (isMainMethod(methodNode)) {
213           ignore("method public static void main(String[])", methodNode);
214           log = false;
215         }
216       }
217       if (log) {
218         System.out.println("\t" + methodNode.toStringLong());
219       }
220     }
221     return super.visit(methodNode, data);
222   }
223 
224   private void ignore(String description, ClassNode classNode) {
225     System.out.println("Ignoring " + description + ": " + classNode.getName());
226   }
227 
228   private void ignore(String description, MemberNode memberNode) {
229     System.out.println("Ignoring " + description + ": " + memberNode.toStringLong());
230   }
231 }