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