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.Modifier;
    7
    8   import net.sourceforge.pmd.dcd.graph.ClassNode;
    9   import net.sourceforge.pmd.dcd.graph.ConstructorNode;
    10  import net.sourceforge.pmd.dcd.graph.FieldNode;
    11  import net.sourceforge.pmd.dcd.graph.MemberNode;
    12  import net.sourceforge.pmd.dcd.graph.MethodNode;
    13  import net.sourceforge.pmd.dcd.graph.NodeVisitorAdapter;
    14  import net.sourceforge.pmd.dcd.graph.UsageGraph;
    15
    16  /**
    17   * Perform a visitation a UsageGraph, looking for <em>dead code</em>, which
    18   * is essential code which is not used by any other code.  There are various
    19   * options for configuration how this determination is made.
    20   */
    21  public class UsageNodeVisitor extends NodeVisitorAdapter {
    22
    23  	/**
    24  	 * Configuration options for usage analysus.
    25  	 */
    26  	public static final class Options {
    27  		private boolean ignoreClassAnonymous = true;
    28
    29  		private boolean ignoreConstructorStaticInitializer = true;
    30
    31  		private boolean ignoreConstructorSinglePrivateNoArg = true;
    32
    33  		private boolean ignoreConstructorAllPrivate = false;
    34
    35  		private boolean ignoreMethodJavaLangObjectOverride = true;
    36
    37  		private boolean ignoreMethodAllOverride = false;
    38
    39  		private boolean ignoreMethodMain = true;
    40
    41  		private boolean ignoreFieldInlinable = true;
    42
    43  		public boolean isIgnoreClassAnonymous() {
    44  			return ignoreClassAnonymous;
    45  		}
    46
    47  		public void setIgnoreClassAnonymous(boolean ignoreClassAnonymous) {
    48  			this.ignoreClassAnonymous = ignoreClassAnonymous;
    49  		}
    50
    51  		public boolean isIgnoreConstructorStaticInitializer() {
    52  			return ignoreConstructorStaticInitializer;
    53  		}
    54
    55  		public void setIgnoreConstructorStaticInitializer(boolean ignoreConstructorStaticInitializer) {
    56  			this.ignoreConstructorStaticInitializer = ignoreConstructorStaticInitializer;
    57  		}
    58
    59  		public boolean isIgnoreConstructorSinglePrivateNoArg() {
    60  			return ignoreConstructorSinglePrivateNoArg;
    61  		}
    62
    63  		public void setIgnoreConstructorSinglePrivateNoArg(boolean ignoreConstructorSinglePrivateNoArg) {
    64  			this.ignoreConstructorSinglePrivateNoArg = ignoreConstructorSinglePrivateNoArg;
    65  		}
    66
    67  		public boolean isIgnoreConstructorAllPrivate() {
    68  			return ignoreConstructorAllPrivate;
    69  		}
    70
    71  		public void setIgnoreConstructorAllPrivate(boolean ignoreConstructorAllPrivate) {
    72  			this.ignoreConstructorAllPrivate = ignoreConstructorAllPrivate;
    73  		}
    74
    75  		public boolean isIgnoreMethodJavaLangObjectOverride() {
    76  			return ignoreMethodJavaLangObjectOverride;
    77  		}
    78
    79  		public void setIgnoreMethodJavaLangObjectOverride(boolean ignoreMethodJavaLangObjectOverride) {
    80  			this.ignoreMethodJavaLangObjectOverride = ignoreMethodJavaLangObjectOverride;
    81  		}
    82
    83  		public boolean isIgnoreMethodAllOverride() {
    84  			return ignoreMethodAllOverride;
    85  		}
    86
    87  		public void setIgnoreMethodAllOverride(boolean ignoreMethodAllOverride) {
    88  			this.ignoreMethodAllOverride = ignoreMethodAllOverride;
    89  		}
    90
    91  		public boolean isIgnoreMethodMain() {
    92  			return ignoreMethodMain;
    93  		}
    94
    95  		public void setIgnoreMethodMain(boolean ignoreMethodMain) {
    96  			this.ignoreMethodMain = ignoreMethodMain;
    97  		}
    98
    99  		public boolean isIgnoreFieldInlinable() {
    100 			return ignoreFieldInlinable;
    101 		}
    102
    103 		public void setIgnoreFieldInlinable(boolean ignoreFieldInlinable) {
    104 			this.ignoreFieldInlinable = ignoreFieldInlinable;
    105 		}
    106
    107 	}
    108
    109 	private final Options options = new Options();
    110
    111 	public Object visit(UsageGraph usageGraph, Object data) {
    112 		System.out.println("----------------------------------------");
    113 		super.visit(usageGraph, data);
    114 		System.out.println("----------------------------------------");
    115 		return data;
    116 	}
    117
    118 	public Object visit(ClassNode classNode, Object data) {
    119 		boolean log = true;
    120 		if (options.isIgnoreClassAnonymous() && classNode.getType().isAnonymousClass()) {
    121 			ignore("class anonymous", classNode);
    122 			log = false;
    123 		}
    124 		if (log) {
    125 			System.out.println("--- " + classNode.getName() + " ---");
    126 			return super.visit(classNode, data);
    127 		} else {
    128 			return data;
    129 		}
    130 	}
    131
    132 	public Object visit(FieldNode fieldNode, Object data) {
    133 		if (fieldNode.getUsers().isEmpty()) {
    134 			boolean log = true;
    135 			// A field is inlinable if:
    136 			// 1) It is final
    137 			// 2) It is a primitive, or a java.lang.String
    138 			if (options.isIgnoreFieldInlinable()) {
    139 				if (Modifier.isFinal(fieldNode.getMember().getModifiers())
    140 						&& fieldNode.getMember().getType().isPrimitive()
    141 						|| fieldNode.getMember().getType().getName().equals("java.lang.String")) {
    142 					ignore("field inlinable", fieldNode);
    143 					log = false;
    144 				}
    145 			}
    146 			if (log) {
    147 				System.out.println("\t" + fieldNode.toStringLong());
    148 			}
    149 		}
    150 		return super.visit(fieldNode, data);
    151 	}
    152
    153 	public Object visit(ConstructorNode constructorNode, Object data) {
    154 		if (constructorNode.getUsers().isEmpty()) {
    155 			boolean log = true;
    156 			if (constructorNode.isStaticInitializer()) {
    157 				if (options.isIgnoreConstructorStaticInitializer()) {
    158 					ignore("constructor static initializer", constructorNode);
    159 					log = false;
    160 				}
    161 			} else if (constructorNode.isInstanceInitializer()) {
    162 				if (Modifier.isPrivate(constructorNode.getMember().getModifiers())) {
    163 					if (options.isIgnoreConstructorAllPrivate()) {
    164 						ignore("constructor all private", constructorNode);
    165 						log = false;
    166 					} else if (options.isIgnoreConstructorSinglePrivateNoArg()
    167 							&& constructorNode.getMember().getParameterTypes().length == 0
    168 							&& constructorNode.getClassNode().getConstructorNodes().size() == 1) {
    169 						ignore("constructor single private no-arg", constructorNode);
    170 						log = false;
    171 					}
    172 				}
    173 			}
    174 			if (log) {
    175 				System.out.println("\t" + constructorNode.toStringLong());
    176 			}
    177 		}
    178 		return super.visit(constructorNode, data);
    179 	}
    180
    181 	public Object visit(MethodNode methodNode, Object data) {
    182 		if (methodNode.getUsers().isEmpty()) {
    183 			boolean log = true;
    184 			if (options.isIgnoreMethodAllOverride()) {
    185 				if (ClassLoaderUtil.isOverridenMethod(methodNode.getClassNode().getClass(), methodNode.getMember(),
    186 						false)) {
    187 					ignore("method all override", methodNode);
    188 					log = false;
    189 				}
    190 			} else if (options.isIgnoreMethodJavaLangObjectOverride()) {
    191 				if (ClassLoaderUtil.isOverridenMethod(java.lang.Object.class, methodNode.getMember(), true)) {
    192 					ignore("method java.lang.Object override", methodNode);
    193 					log = false;
    194 				}
    195 			}
    196 			if (options.isIgnoreMethodMain()) {
    197 				if (methodNode.getMember().getName().equals("main")
    198 						&& Modifier.isPublic(methodNode.getMember().getModifiers())
    199 						&& Modifier.isStatic(methodNode.getMember().getModifiers())
    200 						&& methodNode.getMember().getReturnType() == Void.TYPE
    201 						&& methodNode.getMember().getParameterTypes().length == 1
    202 						&& methodNode.getMember().getParameterTypes()[0].isArray()
    203 						&& methodNode.getMember().getParameterTypes()[0].getComponentType().equals(
    204 								java.lang.String.class)) {
    205 					ignore("method public static void main(String[])", methodNode);
    206 					log = false;
    207 				}
    208 			}
    209 			if (log) {
    210 				System.out.println("\t" + methodNode.toStringLong());
    211 			}
    212 		}
    213 		return super.visit(methodNode, data);
    214 	}
    215
    216 	private void ignore(String description, ClassNode classNode) {
    217 		System.out.println("Ignoring " + description + ": " + classNode.getName());
    218 	}
    219
    220 	private void ignore(String description, MemberNode memberNode) {
    221 		System.out.println("Ignoring " + description + ": " + memberNode.toStringLong());
    222 	}
    223
    224 	private void printMember(MemberNode memberNode) {
    225 		if (memberNode.getUsers().size() == 0) {
    226 			System.out.println("\t" + memberNode.toStringLong());
    227 		}
    228 	}
    229 }