1 package net.sourceforge.pmd.rules.strings; 2 3 import net.sourceforge.pmd.AbstractRule; 4 import net.sourceforge.pmd.ast.ASTEqualityExpression; 5 import net.sourceforge.pmd.ast.ASTLiteral; 6 import net.sourceforge.pmd.ast.ASTPrimitiveType; 7 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId; 8 import net.sourceforge.pmd.ast.Node; 9 import net.sourceforge.pmd.ast.SimpleNode; 10 import net.sourceforge.pmd.symboltable.NameOccurrence; 11 12 import java.util.Iterator; 13 import java.util.List; 14 15 /*** 16 * This rule finds code which inefficiently determines empty strings. This code 17 * <p/> 18 * <pre> 19 * if(str.trim().length()==0){.... 20 * </pre> 21 * <p/> 22 * is quite inefficient as trim() causes a new String to be created. Smarter 23 * code to check for an empty string would be: 24 * <p/> 25 * <pre> 26 * Character.isWhitespace(str.charAt(i)); 27 * </pre> 28 * 29 * @author acaplan 30 */ 31 public class InefficientEmptyStringCheck extends AbstractRule { 32 33 public Object visit(ASTVariableDeclaratorId node, Object data) { 34 SimpleNode nameNode = node.getTypeNameNode(); 35 if (nameNode instanceof ASTPrimitiveType) { 36 return data; 37 } 38 39 if (!"String".equals(node.getNameDeclaration().getTypeImage())) { 40 return data; 41 } 42 43 List declars = node.getUsages(); 44 for (Iterator i = declars.iterator(); i.hasNext();) { 45 NameOccurrence occ = (NameOccurrence) i.next(); 46 if (!isStringLength(occ)) { 47 continue; 48 } 49 ASTEqualityExpression equality = (ASTEqualityExpression) occ 50 .getLocation().getFirstParentOfType(ASTEqualityExpression.class); 51 if (equality != null && isCompareZero(equality)) { 52 addViolation(data, occ.getLocation()); 53 } 54 } 55 return data; 56 } 57 58 /*** 59 * We only need to report if this is comparing against 0 60 * 61 * @param equality 62 * @return true if this is comparing to 0 else false 63 */ 64 private boolean isCompareZero(ASTEqualityExpression equality) { 65 return (checkComparison(equality, 0) || checkComparison(equality, 1)); 66 67 } 68 69 /*** 70 * Determine if we're dealing with String.length method 71 * 72 * @param occ The name occurance 73 * @return true if it's String.length, else false 74 */ 75 private boolean isStringLength(NameOccurrence occ) { 76 if (occ.getNameForWhichThisIsAQualifier() != null 77 && occ.getNameForWhichThisIsAQualifier().getImage().indexOf("trim") != -1) { 78 Node pExpression = occ.getLocation().jjtGetParent().jjtGetParent(); 79 if (pExpression.jjtGetNumChildren() >= 3 80 && "length" 81 .equals(((SimpleNode) pExpression.jjtGetChild(2)) 82 .getImage())) { 83 return true; 84 } 85 } 86 return false; 87 } 88 89 /*** 90 * Checks if the equality expression passed in is of comparing against the 91 * value passed in as i 92 * 93 * @param equality 94 * @param i The ordinal in the equality expression to check 95 * @return true if the value in position i is 0, else false 96 */ 97 private boolean checkComparison(ASTEqualityExpression equality, int i) { 98 return (equality.jjtGetChild(i).jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral && "0" 99 .equals(((SimpleNode) equality.jjtGetChild(i).jjtGetChild(0) 100 .jjtGetChild(0)).getImage())); 101 } 102 103 }