AbstractPoorMethodCall.java
001 package net.sourceforge.pmd.lang.java.rule;
002 
003 import java.util.List;
004 
005 import net.sourceforge.pmd.lang.ast.Node;
006 import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
007 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
008 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
009 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
010 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
011 
012 /**
013  * Detects and flags the occurrences of specific method calls against an instance of
014  * a designated class. I.e. String.indexOf. The goal is to be able to suggest more
015  * efficient/modern ways of implementing the same function.
016  *
017  * Concrete subclasses are expected to provide the name of the target class and an
018  * array of method names that we are looking for. We then pass judgment on any literal
019  * arguments we find in the subclass as well.
020  *
021  @author Brian Remedios
022  @version $Revision: 6421 $
023  */
024 public abstract class AbstractPoorMethodCall extends AbstractJavaRule {
025     //FIXME not sure the abstraction is generic enough to be reused as is.
026 
027     /**
028      * The name of the type the method will be invoked against.
029      @return String
030      */
031     protected abstract String targetTypename();
032 
033     /**
034      * Return the names of all the methods we are scanning for, no brackets or
035      * argument types.
036      *
037      @return String[]
038      */
039     protected abstract String[] methodNames();
040 
041     /**
042      * Returns whether the node being sent to the method is OK or not. Return
043      * true if you want to record the method call as a violation.
044      *
045      @param arg the node to inspect
046      @return boolean
047      */
048     protected abstract boolean isViolationArgument(Node arg);
049 
050     /**
051      * Returns whether the name occurrence is one of the method calls
052      * we are interested in.
053      *
054      @param occurrence NameOccurrence
055      @return boolean
056      */
057     private boolean isNotedMethod(NameOccurrence occurrence) {
058 
059         if (occurrence == null) {
060             return false;
061         }
062 
063         String methodCall = occurrence.getImage();
064         String[] methodNames = methodNames();
065 
066         for (String element : methodNames) {
067             if (methodCall.indexOf(element!= -1) {
068                 return true;
069             }
070         }
071         return false;
072     }
073 
074     /**
075      * Method visit.
076      @param node ASTVariableDeclaratorId
077      @param data Object
078      @return Object
079      @see net.sourceforge.pmd.lang.java.ast.JavaParserVisitor#visit(ASTVariableDeclaratorId, Object)
080      */
081     @Override
082     public Object visit(ASTVariableDeclaratorId node, Object data) {
083         if (!node.getNameDeclaration().getTypeImage().equals(targetTypename())) {
084             return data;
085         }
086 
087         for (NameOccurrence occ : node.getUsages()) {
088             if (isNotedMethod(occ.getNameForWhichThisIsAQualifier())) {
089                 Node parent = occ.getLocation().jjtGetParent().jjtGetParent();
090                 if (parent instanceof ASTPrimaryExpression) {
091                     // bail out if it's something like indexOf("a" + "b")
092                     if (parent.hasDescendantOfType(ASTAdditiveExpression.class)) {
093                         return data;
094                     }
095                     List<ASTLiteral> literals = parent.findDescendantsOfType(ASTLiteral.class);
096                     for (int l = 0; l < literals.size(); l++) {
097                         ASTLiteral literal = literals.get(l);
098                         if (isViolationArgument(literal)) {
099                             addViolation(data, occ.getLocation());
100                         }
101                     }
102                 }
103             }
104         }
105         return data;
106     }
107 }