View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.lang.java.rule.strings;
    5
    6   import java.util.Iterator;
    7   import java.util.List;
    8
    9   import net.sourceforge.pmd.lang.ast.Node;
    10  import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
    11  import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
    12  import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
    13  import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
    14  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
    15  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
    16  import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
    17  import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
    18  import net.sourceforge.pmd.lang.java.ast.ASTName;
    19  import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
    20  import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
    21  import net.sourceforge.pmd.lang.java.ast.ASTType;
    22  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
    23  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
    24  import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
    25
    26  /**
    27   * How this rule works:
    28   * find additive expressions: +
    29   * check that the addition is between anything other than two literals
    30   * if true and also the parent is StringBuffer constructor or append,
    31   * report a violation.
    32   *
    33   * @author mgriffa
    34   */
    35  public class InefficientStringBufferingRule extends AbstractJavaRule {
    36
    37      @Override
    38      public Object visit(ASTAdditiveExpression node, Object data) {
    39          ASTBlockStatement bs = node.getFirstParentOfType(ASTBlockStatement.class);
    40          if (bs == null) {
    41              return data;
    42          }
    43
    44          int immediateLiterals = 0;
    45          int immediateStringLiterals = 0;
    46          List<ASTLiteral> nodes = node.findDescendantsOfType(ASTLiteral.class);
    47          for (ASTLiteral literal: nodes) {
    48              if (literal.getNthParent(3) instanceof ASTAdditiveExpression) {
    49                  immediateLiterals++;
    50                  if (literal.isStringLiteral()) {
    51                  	immediateStringLiterals++;
    52                  }
    53              }
    54              if (literal.isIntLiteral() || literal.isFloatLiteral()) {
    55          	return data;
    56              }
    57          }
    58
    59          if (immediateLiterals > 1) {
    60              return data;
    61          }
    62
    63          // if literal + public static final, return
    64          List<ASTName> nameNodes = node.findDescendantsOfType(ASTName.class);
    65          for (ASTName name: nameNodes) {
    66              if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
    67                  VariableNameDeclaration vnd = (VariableNameDeclaration)name.getNameDeclaration();
    68                  if (vnd.getAccessNodeParent().isFinal() && vnd.getAccessNodeParent().isStatic()) {
    69                      return data;
    70                  }
    71              }
    72          }
    73
    74          // if literal primitive type and not strings variables, then return
    75          boolean stringFound = false;
    76          for (ASTName name: nameNodes) {
    77          	if (!isPrimitiveType(name) && isStringType(name)) {
    78          		stringFound = true;
    79          		break;
    80          	}
    81          }
    82          if (!stringFound && immediateStringLiterals == 0) {
    83          	return data;
    84          }
    85
    86          if (bs.isAllocation()) {
    87              for (Iterator<ASTName> iterator = nameNodes.iterator(); iterator.hasNext();) {
    88              	ASTName name = iterator.next();
    89      			if (!name.getImage().endsWith("length")) {
    90      				break;
    91      			} else if (!iterator.hasNext()) {
    92      				return data;	//All names end with length
    93      			}
    94      		}
    95
    96              if (isAllocatedStringBuffer(node)) {
    97                  addViolation(data, node);
    98              }
    99          } else if (isInStringBufferOperation(node, 6, "append")) {
    100             addViolation(data, node);
    101         }
    102         return data;
    103     }
    104
    105     private boolean isStringType(ASTName name) {
    106     	ASTType type = getTypeNode(name);
    107     	if (type != null) {
    108     		List<ASTClassOrInterfaceType> types = type.findDescendantsOfType(ASTClassOrInterfaceType.class);
    109     		if (!types.isEmpty()) {
    110     			ASTClassOrInterfaceType typeDeclaration = types.get(0);
    111     			if (String.class == typeDeclaration.getType() || "String".equals(typeDeclaration.getImage())) {
    112     				return true;
    113     			}
    114     		}
    115     	}
    116 		return false;
    117 	}
    118
    119 	private boolean isPrimitiveType(ASTName name) {
    120 		ASTType type = getTypeNode(name);
    121 		if (type != null && !type.findChildrenOfType(ASTPrimitiveType.class).isEmpty()) {
    122 			return true;
    123 		}
    124 		return false;
    125 	}
    126
    127 	private ASTType getTypeNode(ASTName name) {
    128     	if (name.getNameDeclaration() instanceof VariableNameDeclaration) {
    129     		VariableNameDeclaration vnd = (VariableNameDeclaration) name.getNameDeclaration();
    130     		if (vnd.getAccessNodeParent() instanceof ASTLocalVariableDeclaration) {
    131     			ASTLocalVariableDeclaration l = (ASTLocalVariableDeclaration)vnd.getAccessNodeParent();
    132     			return l.getTypeNode();
    133     		} else if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
    134     			ASTFormalParameter p = (ASTFormalParameter) vnd.getAccessNodeParent();
    135     			return p.getTypeNode();
    136     		}
    137     	}
    138 		return null;
    139 	}
    140
    141 	protected static boolean isInStringBufferOperation(Node node, int length, String methodName) {
    142         if (!(node.getNthParent(length) instanceof ASTStatementExpression)) {
    143             return false;
    144         }
    145         ASTStatementExpression s = node.getFirstParentOfType(ASTStatementExpression.class);
    146         if (s == null) {
    147             return false;
    148         }
    149         ASTName n = s.getFirstDescendantOfType(ASTName.class);
    150         if (n == null || n.getImage().indexOf(methodName) == -1 || !(n.getNameDeclaration() instanceof VariableNameDeclaration)) {
    151             return false;
    152         }
    153
    154         // TODO having to hand-code this kind of dredging around is ridiculous
    155         // we need something to support this in the framework
    156         // but, "for now" (tm):
    157         // if more than one arg to append(), skip it
    158         ASTArgumentList argList = s.getFirstDescendantOfType(ASTArgumentList.class);
    159         if (argList == null || argList.jjtGetNumChildren() > 1) {
    160             return false;
    161         }
    162         return TypeHelper.isEither((VariableNameDeclaration)n.getNameDeclaration(), StringBuffer.class, StringBuilder.class);
    163     }
    164
    165     private boolean isAllocatedStringBuffer(ASTAdditiveExpression node) {
    166         ASTAllocationExpression ao = node.getFirstParentOfType(ASTAllocationExpression.class);
    167         if (ao == null) {
    168             return false;
    169         }
    170         // note that the child can be an ArrayDimsAndInits, for example, from java.lang.FloatingDecimal:  t = new int[ nWords+wordcount+1 ];
    171         ASTClassOrInterfaceType an = ao.getFirstChildOfType(ASTClassOrInterfaceType.class);
    172         return an != null && TypeHelper.isEither(an, StringBuffer.class, StringBuilder.class);
    173     }
    174 }
    175