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 }
|