001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules;
004:
005: import net.sourceforge.pmd.AbstractRule;
006: import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
007: import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
008: import net.sourceforge.pmd.ast.ASTInitializer;
009: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
010: import net.sourceforge.pmd.ast.ASTMethodDeclarator;
011: import net.sourceforge.pmd.ast.AccessNode;
012: import net.sourceforge.pmd.ast.SimpleNode;
013: import net.sourceforge.pmd.symboltable.ClassScope;
014: import net.sourceforge.pmd.symboltable.MethodNameDeclaration;
015: import net.sourceforge.pmd.symboltable.NameOccurrence;
016:
017: import java.util.HashSet;
018: import java.util.List;
019: import java.util.Map;
020: import java.util.Set;
021:
022: public class UnusedPrivateMethodRule extends AbstractRule {
023:
024: public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
025: if (node.isInterface()) {
026: return data;
027: }
028:
029: Map<MethodNameDeclaration, List<NameOccurrence>> methods = ((ClassScope) node
030: .getScope()).getMethodDeclarations();
031: for (MethodNameDeclaration mnd : findUnique(methods)) {
032: List<NameOccurrence> occs = methods.get(mnd);
033: if (!privateAndNotExcluded(mnd)) {
034: continue;
035: }
036: if (occs.isEmpty()) {
037: addViolation(data, mnd.getNode(), mnd.getImage()
038: + mnd.getParameterDisplaySignature());
039: } else {
040: if (calledFromOutsideItself(occs, mnd)) {
041: addViolation(data, mnd.getNode(), mnd.getImage()
042: + mnd.getParameterDisplaySignature());
043: }
044:
045: }
046: }
047: return data;
048: }
049:
050: private Set<MethodNameDeclaration> findUnique(
051: Map<MethodNameDeclaration, List<NameOccurrence>> methods) {
052: // some rather hideous hackery here
053: // to work around the fact that PMD does not yet do full type analysis
054: // when it does, delete this
055: Set<MethodNameDeclaration> unique = new HashSet<MethodNameDeclaration>();
056: Set<String> sigs = new HashSet<String>();
057: for (MethodNameDeclaration mnd : methods.keySet()) {
058: String sig = mnd.getImage() + mnd.getParameterCount();
059: if (!sigs.contains(sig)) {
060: unique.add(mnd);
061: }
062: sigs.add(sig);
063: }
064: return unique;
065: }
066:
067: private boolean calledFromOutsideItself(List<NameOccurrence> occs,
068: MethodNameDeclaration mnd) {
069: int callsFromOutsideMethod = 0;
070: for (NameOccurrence occ : occs) {
071: SimpleNode occNode = occ.getLocation();
072: ASTConstructorDeclaration enclosingConstructor = occNode
073: .getFirstParentOfType(ASTConstructorDeclaration.class);
074: if (enclosingConstructor != null) {
075: callsFromOutsideMethod++;
076: break; // Do we miss unused private constructors here?
077: }
078: ASTInitializer enclosingInitializer = occNode
079: .getFirstParentOfType(ASTInitializer.class);
080: if (enclosingInitializer != null) {
081: callsFromOutsideMethod++;
082: break;
083: }
084:
085: ASTMethodDeclaration enclosingMethod = occNode
086: .getFirstParentOfType(ASTMethodDeclaration.class);
087: if (enclosingMethod == null
088: || !mnd.getNode().jjtGetParent().equals(
089: enclosingMethod)) {
090: callsFromOutsideMethod++;
091: }
092: }
093: return callsFromOutsideMethod == 0;
094: }
095:
096: private boolean privateAndNotExcluded(MethodNameDeclaration mnd) {
097: ASTMethodDeclarator node = (ASTMethodDeclarator) mnd.getNode();
098: return ((AccessNode) node.jjtGetParent()).isPrivate()
099: && !node.hasImageEqualTo("readObject")
100: && !node.hasImageEqualTo("writeObject")
101: && !node.hasImageEqualTo("readResolve")
102: && !node.hasImageEqualTo("writeReplace");
103: }
104: }
|