View Javadoc

    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 }