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