/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.rule.design; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; import net.sourceforge.pmd.lang.java.ast.ASTResultType; import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck; import net.sourceforge.pmd.lang.java.symboltable.ClassScope; import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.util.CollectionUtil; /** * Detect structures like "foo.size() == 0" and suggest replacing them with * foo.isEmpty(). Will also find != 0 (replaceable with !isEmpty()). * * @author Jason Bennett */ public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { public boolean appliesToClassName(String name) { return CollectionUtil.isCollectionType(name, true); } /** * Determine if we're dealing with .size method * * @param occ * The name occurrence * @return true if it's .size, else false */ public boolean isTargetMethod(JavaNameOccurrence occ) { if (occ.getNameForWhichThisIsAQualifier() != null) { if (occ.getLocation().getImage().endsWith(".size")) { return true; } } return false; } @Override public Map<String, List<String>> getComparisonTargets() { Map<String, List<String>> rules = new HashMap<>(); rules.put("<", Arrays.asList("0", "1")); rules.put(">", Arrays.asList("0")); rules.put("==", Arrays.asList("0")); rules.put("!=", Arrays.asList("0")); rules.put(">=", Arrays.asList("0", "1")); rules.put("<=", Arrays.asList("0")); return rules; } @Override public Object visit(ASTPrimarySuffix node, Object data) { if (node.getImage() != null && node.getImage().endsWith("size")) { ASTClassOrInterfaceType type = getTypeOfPrimaryPrefix(node); if (type == null) { type = getTypeOfMethodCall(node); } if (type != null && CollectionUtil.isCollectionType(type.getType(), true)) { Node expr = node.jjtGetParent().jjtGetParent(); checkNodeAndReport(data, node, expr); } } return data; } private ASTClassOrInterfaceType getTypeOfMethodCall(ASTPrimarySuffix node) { ASTClassOrInterfaceType type = null; ASTName methodName = node.jjtGetParent().getFirstChildOfType(ASTPrimaryPrefix.class) .getFirstChildOfType(ASTName.class); if (methodName != null) { ClassScope classScope = node.getScope().getEnclosingScope(ClassScope.class); Map<MethodNameDeclaration, List<NameOccurrence>> methods = classScope.getMethodDeclarations(); for (Map.Entry<MethodNameDeclaration, List<NameOccurrence>> e : methods.entrySet()) { if (e.getKey().getName().equals(methodName.getImage())) { type = e.getKey().getNode().getFirstParentOfType(ASTMethodDeclaration.class) .getFirstChildOfType(ASTResultType.class) .getFirstDescendantOfType(ASTClassOrInterfaceType.class); break; } } } return type; } private ASTClassOrInterfaceType getTypeOfPrimaryPrefix(ASTPrimarySuffix node) { return node.jjtGetParent().getFirstChildOfType(ASTPrimaryPrefix.class) .getFirstDescendantOfType(ASTClassOrInterfaceType.class); } }