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

This page was automatically generated by Maven