1 package net.sourceforge.pmd.lang.java.rule;
2
3 import java.util.List;
4
5 import net.sourceforge.pmd.lang.ast.Node;
6 import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
7 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
8 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
9 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
10 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
11
12 /**
13 * Detects and flags the occurrences of specific method calls against an instance of
14 * a designated class. I.e. String.indexOf. The goal is to be able to suggest more
15 * efficient/modern ways of implementing the same function.
16 *
17 * Concrete subclasses are expected to provide the name of the target class and an
18 * array of method names that we are looking for. We then pass judgment on any literal
19 * arguments we find in the subclass as well.
20 *
21 * @author Brian Remedios
22 * @version $Revision$
23 */
24 public abstract class AbstractPoorMethodCall extends AbstractJavaRule {
25 //FIXME not sure the abstraction is generic enough to be reused as is.
26
27 /**
28 * The name of the type the method will be invoked against.
29 * @return String
30 */
31 protected abstract String targetTypename();
32
33 /**
34 * Return the names of all the methods we are scanning for, no brackets or
35 * argument types.
36 *
37 * @return String[]
38 */
39 protected abstract String[] methodNames();
40
41 /**
42 * Returns whether the node being sent to the method is OK or not. Return
43 * true if you want to record the method call as a violation.
44 *
45 * @param arg the node to inspect
46 * @return boolean
47 */
48 protected abstract boolean isViolationArgument(Node arg);
49
50 /**
51 * Returns whether the name occurrence is one of the method calls
52 * we are interested in.
53 *
54 * @param occurrence NameOccurrence
55 * @return boolean
56 */
57 private boolean isNotedMethod(NameOccurrence occurrence) {
58
59 if (occurrence == null) {
60 return false;
61 }
62
63 String methodCall = occurrence.getImage();
64 String[] methodNames = methodNames();
65
66 for (String element : methodNames) {
67 if (methodCall.indexOf(element) != -1) {
68 return true;
69 }
70 }
71 return false;
72 }
73
74 /**
75 * Method visit.
76 * @param node ASTVariableDeclaratorId
77 * @param data Object
78 * @return Object
79 * @see net.sourceforge.pmd.lang.java.ast.JavaParserVisitor#visit(ASTVariableDeclaratorId, Object)
80 */
81 @Override
82 public Object visit(ASTVariableDeclaratorId node, Object data) {
83 if (!node.getNameDeclaration().getTypeImage().equals(targetTypename())) {
84 return data;
85 }
86
87 for (NameOccurrence occ : node.getUsages()) {
88 if (isNotedMethod(occ.getNameForWhichThisIsAQualifier())) {
89 Node parent = occ.getLocation().jjtGetParent().jjtGetParent();
90 if (parent instanceof ASTPrimaryExpression) {
91 // bail out if it's something like indexOf("a" + "b")
92 if (parent.hasDescendantOfType(ASTAdditiveExpression.class)) {
93 return data;
94 }
95 List<ASTLiteral> literals = parent.findDescendantsOfType(ASTLiteral.class);
96 for (int l = 0; l < literals.size(); l++) {
97 ASTLiteral literal = literals.get(l);
98 if (isViolationArgument(literal)) {
99 addViolation(data, occ.getLocation());
100 }
101 }
102 }
103 }
104 }
105 return data;
106 }
107 }