View Javadoc
1 /*** 2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html 3 */ 4 package net.sourceforge.pmd.rules; 5 6 import net.sourceforge.pmd.AbstractRule; 7 import net.sourceforge.pmd.RuleContext; 8 import net.sourceforge.pmd.ast.ASTArguments; 9 import net.sourceforge.pmd.ast.ASTClassBody; 10 import net.sourceforge.pmd.ast.ASTCompilationUnit; 11 import net.sourceforge.pmd.ast.ASTInterfaceDeclaration; 12 import net.sourceforge.pmd.ast.ASTMethodDeclarator; 13 import net.sourceforge.pmd.ast.ASTName; 14 import net.sourceforge.pmd.ast.ASTPrimaryExpression; 15 import net.sourceforge.pmd.ast.ASTPrimaryPrefix; 16 import net.sourceforge.pmd.ast.ASTPrimarySuffix; 17 import net.sourceforge.pmd.ast.AccessNode; 18 import net.sourceforge.pmd.ast.SimpleNode; 19 20 import java.text.MessageFormat; 21 import java.util.HashSet; 22 import java.util.Iterator; 23 import java.util.Set; 24 25 public class UnusedPrivateMethodRule extends AbstractRule { 26 27 private Set privateMethodNodes = new HashSet(); 28 29 // TODO - What I need is a Visitor that does a breadth first search 30 private boolean trollingForDeclarations; 31 private int depth; 32 33 // Skip interfaces because they have no implementation 34 public Object visit(ASTInterfaceDeclaration node, Object data) { 35 return data; 36 } 37 38 // Reset state when we leave an ASTCompilationUnit 39 public Object visit(ASTCompilationUnit node, Object data) { 40 depth = 0; 41 super.visit(node, data); 42 privateMethodNodes.clear(); 43 depth = 0; 44 trollingForDeclarations = false; 45 return data; 46 } 47 48 public Object visit(ASTClassBody node, Object data) { 49 depth++; 50 51 // first troll for declarations, but only in the top level class 52 if (depth == 1) { 53 trollingForDeclarations = true; 54 super.visit(node, null); 55 trollingForDeclarations = false; 56 } else { 57 trollingForDeclarations = false; 58 } 59 60 // troll for usages, regardless of depth 61 super.visit(node, null); 62 63 // if we're back at the top level class, harvest 64 if (depth == 1) { 65 RuleContext ctx = (RuleContext) data; 66 harvestUnused(ctx); 67 } 68 69 depth--; 70 return data; 71 } 72 73 //ASTMethodDeclarator 74 // FormalParameters 75 // FormalParameter 76 // FormalParameter 77 public Object visit(ASTMethodDeclarator node, Object data) { 78 if (!trollingForDeclarations) { 79 return super.visit(node, data); 80 } 81 82 AccessNode parent = (AccessNode) node.jjtGetParent(); 83 if (!parent.isPrivate()) { 84 return super.visit(node, data); 85 } 86 // exclude these serializable things 87 if (node.getImage().equals("readObject") || node.getImage().equals("writeObject") || node.getImage().equals("readResolve") || node.getImage().equals("writeReplace")) { 88 return super.visit(node, data); 89 } 90 privateMethodNodes.add(node); 91 return super.visit(node, data); 92 } 93 94 //PrimarySuffix 95 // Arguments 96 // ArgumentList 97 // Expression 98 // Expression 99 public Object visit(ASTPrimarySuffix node, Object data) { 100 if (!trollingForDeclarations && (node.jjtGetParent() instanceof ASTPrimaryExpression) && (node.getImage() != null)) { 101 if (node.jjtGetNumChildren() > 0) { 102 ASTArguments args = (ASTArguments) node.jjtGetChild(0); 103 removeIfUsed(node.getImage(), args.getArgumentCount()); 104 return super.visit(node, data); 105 } 106 // to handle this.foo() 107 //PrimaryExpression 108 // PrimaryPrefix 109 // PrimarySuffix <-- this node has "foo" 110 // PrimarySuffix <-- this node has null 111 // Arguments 112 ASTPrimaryExpression parent = (ASTPrimaryExpression) node.jjtGetParent(); 113 int pointer = 0; 114 while (true) { 115 if (parent.jjtGetChild(pointer).equals(node)) { 116 break; 117 } 118 pointer++; 119 } 120 // now move to the next PrimarySuffix and get the number of arguments 121 pointer++; 122 // this.foo = foo; 123 // yields this: 124 // PrimaryExpression 125 // PrimaryPrefix 126 // PrimarySuffix 127 // so we check for that 128 if (parent.jjtGetNumChildren() <= pointer) { 129 return super.visit(node, data); 130 } 131 if (!(parent.jjtGetChild(pointer) instanceof ASTPrimarySuffix)) { 132 return super.visit(node, data); 133 } 134 ASTPrimarySuffix actualMethodNode = (ASTPrimarySuffix) parent.jjtGetChild(pointer); 135 // when does this happen? 136 if (actualMethodNode.jjtGetNumChildren() == 0 || !(actualMethodNode.jjtGetChild(0) instanceof ASTArguments)) { 137 return super.visit(node, data); 138 } 139 ASTArguments args = (ASTArguments) actualMethodNode.jjtGetChild(0); 140 removeIfUsed(node.getImage(), args.getArgumentCount()); 141 // what about Outer.this.foo()? 142 } 143 return super.visit(node, data); 144 } 145 146 //PrimaryExpression 147 // PrimaryPrefix 148 // Name 149 // PrimarySuffix 150 // Arguments 151 public Object visit(ASTName node, Object data) { 152 if (!trollingForDeclarations && (node.jjtGetParent() instanceof ASTPrimaryPrefix)) { 153 ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node.jjtGetParent().jjtGetParent(); 154 if (primaryExpression.jjtGetNumChildren() > 1) { 155 ASTPrimarySuffix primarySuffix = (ASTPrimarySuffix) primaryExpression.jjtGetChild(1); 156 if (primarySuffix.jjtGetNumChildren() > 0 && (primarySuffix.jjtGetChild(0) instanceof ASTArguments)) { 157 ASTArguments arguments = (ASTArguments) primarySuffix.jjtGetChild(0); 158 removeIfUsed(node.getImage(), arguments.getArgumentCount()); 159 } 160 } 161 } 162 return super.visit(node, data); 163 } 164 165 private void removeIfUsed(String nodeImage, int args) { 166 String img = (nodeImage.indexOf('.') == -1) ? nodeImage : nodeImage.substring(nodeImage.indexOf('.') + 1, nodeImage.length()); 167 for (Iterator i = privateMethodNodes.iterator(); i.hasNext();) { 168 ASTMethodDeclarator methodNode = (ASTMethodDeclarator) i.next(); 169 // are name and number of parameters the same? 170 if (methodNode.getImage().equals(img) && methodNode.getParameterCount() == args) { 171 // should check parameter types here, this misses some unused methods 172 i.remove(); 173 } 174 } 175 } 176 177 private void harvestUnused(RuleContext ctx) { 178 for (Iterator i = privateMethodNodes.iterator(); i.hasNext();) { 179 SimpleNode node = (SimpleNode) i.next(); 180 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine(), MessageFormat.format(getMessage(), new Object[]{node.getImage()}))); 181 } 182 } 183 184 /* 185 TODO this uses the symbol table 186 public Object visit(ASTUnmodifiedClassDeclaration node, Object data) { 187 for (Iterator i = node.getScope().getUnusedMethodDeclarations();i.hasNext();) { 188 VariableNameDeclaration decl = (VariableNameDeclaration)i.next(); 189 190 // exclude non-private methods and serializable methods 191 if (!decl.getAccessNodeParent().isPrivate() || decl.getImage().equals("readObject") || decl.getImage().equals("writeObject")|| decl.getImage().equals("readResolve")) { 192 continue; 193 } 194 195 RuleContext ctx = (RuleContext)data; 196 ctx.getReport().addRuleViolation(createRuleViolation(ctx, decl.getNode().getBeginLine(), MessageFormat.format(getMessage(), new Object[] {decl.getNode().getImage()}))); 197 } 198 return super.visit(node, data); 199 } 200 201 */ 202 }

This page was automatically generated by Maven