/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule; import java.util.List; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; /** * Detects and flags the occurrences of specific method calls against an * instance of a designated class. I.e. String.indexOf. The goal is to be able * to suggest more efficient/modern ways of implementing the same function. * * Concrete subclasses are expected to provide the name of the target class and * an array of method names that we are looking for. We then pass judgment on * any literal arguments we find in the subclass as well. * * @author Brian Remedios * @version $Revision$ */ public abstract class AbstractPoorMethodCall extends AbstractJavaRule { // FIXME not sure the abstraction is generic enough to be reused as is. /** * The name of the type the method will be invoked against. * * @return String */ protected abstract String targetTypename(); /** * Return the names of all the methods we are scanning for, no brackets or * argument types. * * @return String[] */ protected abstract String[] methodNames(); /** * Returns whether the node being sent to the method is OK or not. Return * true if you want to record the method call as a violation. * * @param arg * the node to inspect * @return boolean */ protected abstract boolean isViolationArgument(Node arg); /** * Returns whether the name occurrence is one of the method calls we are * interested in. * * @param occurrence * NameOccurrence * @return boolean */ private boolean isNotedMethod(NameOccurrence occurrence) { if (occurrence == null) { return false; } String methodCall = occurrence.getImage(); String[] methodNames = methodNames(); for (String element : methodNames) { if (methodCall.indexOf(element) != -1) { return true; } } return false; } /** * Method visit. * * @param node * ASTVariableDeclaratorId * @param data * Object * @return Object * @see net.sourceforge.pmd.lang.java.ast.JavaParserVisitor#visit(ASTVariableDeclaratorId, * Object) */ @Override public Object visit(ASTVariableDeclaratorId node, Object data) { if (!targetTypename().equals(node.getNameDeclaration().getTypeImage())) { return data; } for (NameOccurrence occ : node.getUsages()) { JavaNameOccurrence jocc = (JavaNameOccurrence) occ; if (isNotedMethod(jocc.getNameForWhichThisIsAQualifier())) { Node parent = jocc.getLocation().jjtGetParent().jjtGetParent(); if (parent instanceof ASTPrimaryExpression) { // bail out if it's something like indexOf("a" + "b") if (parent.hasDescendantOfType(ASTAdditiveExpression.class)) { return data; } List<ASTLiteral> literals = parent.findDescendantsOfType(ASTLiteral.class); for (int l = 0; l < literals.size(); l++) { ASTLiteral literal = literals.get(l); if (isViolationArgument(literal)) { addViolation(data, jocc.getLocation()); } } } } } return data; } }