View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.dcd;
    5
    6   import java.lang.reflect.Method;
    7   import java.lang.reflect.Modifier;
    8
    9   import net.sourceforge.pmd.dcd.graph.ClassNode;
    10  import net.sourceforge.pmd.dcd.graph.ConstructorNode;
    11  import net.sourceforge.pmd.dcd.graph.FieldNode;
    12  import net.sourceforge.pmd.dcd.graph.MemberNode;
    13  import net.sourceforge.pmd.dcd.graph.MethodNode;
    14  import net.sourceforge.pmd.dcd.graph.NodeVisitorAdapter;
    15  import net.sourceforge.pmd.dcd.graph.UsageGraph;
    16
    17  /**
    18   * Perform a visitation a UsageGraph, looking for <em>dead code</em>, which
    19   * is essential code which is not used by any other code.  There are various
    20   * options for configuration how this determination is made.
    21   */
    22  public class UsageNodeVisitor extends NodeVisitorAdapter {
    23
    24  	/**
    25  	 * Configuration options for usage analysis.
    26  	 */
    27  	public static final class Options {
    28  		private boolean ignoreClassAnonymous = true;
    29
    30  		private boolean ignoreConstructorStaticInitializer = true;
    31
    32  		private boolean ignoreConstructorSinglePrivateNoArg = true;
    33
    34  		private boolean ignoreConstructorAllPrivate = false;
    35
    36  		private boolean ignoreMethodJavaLangObjectOverride = true;
    37
    38  		private boolean ignoreMethodAllOverride = false;
    39
    40  		private boolean ignoreMethodMain = true;
    41
    42  		private boolean ignoreFieldInlinable = true;
    43
    44  		public boolean isIgnoreClassAnonymous() {
    45  			return ignoreClassAnonymous;
    46  		}
    47
    48  		public void setIgnoreClassAnonymous(boolean ignoreClassAnonymous) {
    49  			this.ignoreClassAnonymous = ignoreClassAnonymous;
    50  		}
    51
    52  		public boolean isIgnoreConstructorStaticInitializer() {
    53  			return ignoreConstructorStaticInitializer;
    54  		}
    55
    56  		public void setIgnoreConstructorStaticInitializer(boolean ignoreConstructorStaticInitializer) {
    57  			this.ignoreConstructorStaticInitializer = ignoreConstructorStaticInitializer;
    58  		}
    59
    60  		public boolean isIgnoreConstructorSinglePrivateNoArg() {
    61  			return ignoreConstructorSinglePrivateNoArg;
    62  		}
    63
    64  		public void setIgnoreConstructorSinglePrivateNoArg(boolean ignoreConstructorSinglePrivateNoArg) {
    65  			this.ignoreConstructorSinglePrivateNoArg = ignoreConstructorSinglePrivateNoArg;
    66  		}
    67
    68  		public boolean isIgnoreConstructorAllPrivate() {
    69  			return ignoreConstructorAllPrivate;
    70  		}
    71
    72  		public void setIgnoreConstructorAllPrivate(boolean ignoreConstructorAllPrivate) {
    73  			this.ignoreConstructorAllPrivate = ignoreConstructorAllPrivate;
    74  		}
    75
    76  		public boolean isIgnoreMethodJavaLangObjectOverride() {
    77  			return ignoreMethodJavaLangObjectOverride;
    78  		}
    79
    80  		public void setIgnoreMethodJavaLangObjectOverride(boolean ignoreMethodJavaLangObjectOverride) {
    81  			this.ignoreMethodJavaLangObjectOverride = ignoreMethodJavaLangObjectOverride;
    82  		}
    83
    84  		public boolean isIgnoreMethodAllOverride() {
    85  			return ignoreMethodAllOverride;
    86  		}
    87
    88  		public void setIgnoreMethodAllOverride(boolean ignoreMethodAllOverride) {
    89  			this.ignoreMethodAllOverride = ignoreMethodAllOverride;
    90  		}
    91
    92  		public boolean isIgnoreMethodMain() {
    93  			return ignoreMethodMain;
    94  		}
    95
    96  		public void setIgnoreMethodMain(boolean ignoreMethodMain) {
    97  			this.ignoreMethodMain = ignoreMethodMain;
    98  		}
    99
    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 }