View Javadoc

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                     // bail out if it's something like indexOf("a" + "b")
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