Clover coverage report - PMD - 3.9
Coverage timestamp: Tue Dec 19 2006 09:38:44 EST
file stats: LOC: 315   Methods: 11
NCLOC: 243   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
InsufficientStringBufferDeclaration.java 94% 98% 100% 96.7%
coverage coverage
 1    /**
 2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 3    */
 4    package net.sourceforge.pmd.rules.strings;
 5   
 6    import net.sourceforge.pmd.AbstractRule;
 7    import net.sourceforge.pmd.ast.ASTAdditiveExpression;
 8    import net.sourceforge.pmd.ast.ASTBlockStatement;
 9    import net.sourceforge.pmd.ast.ASTFieldDeclaration;
 10    import net.sourceforge.pmd.ast.ASTFormalParameter;
 11    import net.sourceforge.pmd.ast.ASTIfStatement;
 12    import net.sourceforge.pmd.ast.ASTLiteral;
 13    import net.sourceforge.pmd.ast.ASTName;
 14    import net.sourceforge.pmd.ast.ASTPrimaryExpression;
 15    import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
 16    import net.sourceforge.pmd.ast.ASTPrimarySuffix;
 17    import net.sourceforge.pmd.ast.ASTSwitchLabel;
 18    import net.sourceforge.pmd.ast.ASTSwitchStatement;
 19    import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
 20    import net.sourceforge.pmd.ast.Node;
 21    import net.sourceforge.pmd.ast.SimpleNode;
 22    import net.sourceforge.pmd.symboltable.NameOccurrence;
 23   
 24    import java.util.HashMap;
 25    import java.util.HashSet;
 26    import java.util.Iterator;
 27    import java.util.List;
 28    import java.util.Map;
 29    import java.util.Set;
 30   
 31    /**
 32    * This rule finds StringBuffers which may have been pre-sized incorrectly
 33    *
 34    * See http://sourceforge.net/forum/forum.php?thread_id=1438119&forum_id=188194
 35    * @author Allan Caplan
 36    */
 37    public class InsufficientStringBufferDeclaration extends AbstractRule {
 38   
 39    private final static Set blockParents;
 40   
 41    static {
 42  14 blockParents = new HashSet();
 43  14 blockParents.add(ASTIfStatement.class);
 44  14 blockParents.add(ASTSwitchStatement.class);
 45    }
 46   
 47  69 public Object visit(ASTVariableDeclaratorId node, Object data) {
 48   
 49  69 if (!"StringBuffer".equals(node.getNameDeclaration().getTypeImage())) {
 50  34 return data;
 51    }
 52  35 Node rootNode = node;
 53  35 int anticipatedLength = 0;
 54  35 int constructorLength = 16;
 55   
 56  35 constructorLength = getConstructorLength(node, constructorLength);
 57  35 anticipatedLength = getInitialLength(node);
 58  35 List usage = node.getUsages();
 59  35 Map blocks = new HashMap();
 60  35 for (int ix = 0; ix < usage.size(); ix++) {
 61  83 NameOccurrence no = (NameOccurrence) usage.get(ix);
 62  83 SimpleNode n = no.getLocation();
 63  83 if (!InefficientStringBuffering.isInStringBufferOperation(n, 3, "append")) {
 64   
 65  7 if (!no.isOnLeftHandSide() && !InefficientStringBuffering.isInStringBufferOperation(n, 3, "setLength")) {
 66  2 continue;
 67    }
 68  5 if (constructorLength != -1 && anticipatedLength > constructorLength) {
 69  1 anticipatedLength += processBlocks(blocks);
 70  1 String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
 71  1 addViolation(data, rootNode, param);
 72    }
 73  5 constructorLength = getConstructorLength(n, constructorLength);
 74  5 rootNode = n;
 75  5 anticipatedLength = getInitialLength(node);
 76    }
 77  81 ASTPrimaryExpression s = (ASTPrimaryExpression) n.getFirstParentOfType(ASTPrimaryExpression.class);
 78  81 int numChildren = s.jjtGetNumChildren();
 79  81 for (int jx = 0; jx < numChildren; jx++) {
 80  163 SimpleNode sn = (SimpleNode) s.jjtGetChild(jx);
 81  163 if (!(sn instanceof ASTPrimarySuffix) || sn.getImage() != null) {
 82  83 continue;
 83    }
 84  80 int thisSize = 0;
 85  80 Node block = getFirstParentBlock(sn);
 86  80 if (isAdditive(sn)) {
 87  2 thisSize = processAdditive(sn);
 88    } else {
 89  78 thisSize = processNode(sn);
 90    }
 91  80 if (block != null) {
 92  31 storeBlockStatistics(blocks, thisSize, block);
 93    } else {
 94  49 anticipatedLength += thisSize;
 95    }
 96    }
 97    }
 98  35 anticipatedLength += processBlocks(blocks);
 99  35 if (constructorLength != -1 && anticipatedLength > constructorLength) {
 100  13 String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) };
 101  13 addViolation(data, rootNode, param);
 102    }
 103  35 return data;
 104    }
 105   
 106    /**
 107    * This rule is concerned with IF and Switch blocks. Process the block into
 108    * a local Map, from which we can later determine which is the longest block
 109    * inside
 110    *
 111    * @param blocks
 112    * The map of blocks in the method being investigated
 113    * @param thisSize
 114    * The size of the current block
 115    * @param block
 116    * The block in question
 117    */
 118  31 private void storeBlockStatistics(Map blocks, int thisSize, Node block) {
 119  31 Node statement = block.jjtGetParent();
 120  31 if (ASTIfStatement.class.equals(block.jjtGetParent().getClass())) {
 121    // Else Ifs are their own subnode in AST. So we have to
 122    // look a little farther up the tree to find the IF statement
 123  26 Node possibleStatement = ((SimpleNode) statement).getFirstParentOfType(ASTIfStatement.class);
 124  26 while(possibleStatement != null && possibleStatement.getClass().equals(ASTIfStatement.class)) {
 125  23 statement = possibleStatement;
 126  23 possibleStatement = ((SimpleNode) possibleStatement).getFirstParentOfType(ASTIfStatement.class);
 127    }
 128    }
 129  31 Map thisBranch = (Map) blocks.get(statement);
 130  31 if (thisBranch == null) {
 131  10 thisBranch = new HashMap();
 132  10 blocks.put(statement, thisBranch);
 133    }
 134  31 Integer x = (Integer) thisBranch.get(block);
 135  31 if (x != null) {
 136  0 thisSize += x.intValue();
 137    }
 138  31 thisBranch.put(statement, new Integer(thisSize));
 139    }
 140   
 141  36 private int processBlocks(Map blocks) {
 142  36 int anticipatedLength = 0;
 143  36 int ifLength = 0;
 144  36 for (Iterator iter = blocks.entrySet().iterator(); iter.hasNext();) {
 145  10 Map.Entry entry = (Map.Entry) iter.next();
 146  10 ifLength = 0;
 147  10 for (Iterator iter2 = ((Map) entry.getValue()).entrySet().iterator(); iter2.hasNext();) {
 148  10 Map.Entry entry2 = (Map.Entry) iter2.next();
 149  10 Integer value = (Integer) entry2.getValue();
 150  10 ifLength = Math.max(ifLength, value.intValue());
 151    }
 152  10 anticipatedLength += ifLength;
 153    }
 154  36 return anticipatedLength;
 155    }
 156   
 157  2 private int processAdditive(SimpleNode sn) {
 158  2 ASTAdditiveExpression additive = (ASTAdditiveExpression) sn.getFirstChildOfType(ASTAdditiveExpression.class);
 159  2 if (additive == null) {
 160  0 return 0;
 161    }
 162  2 int anticipatedLength = 0;
 163  2 for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) {
 164  6 SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix);
 165  6 ASTLiteral literal = (ASTLiteral) childNode.getFirstChildOfType(ASTLiteral.class);
 166  6 if (literal != null && literal.getImage() != null) {
 167  5 anticipatedLength += literal.getImage().length() - 2;
 168    }
 169    }
 170   
 171  2 return anticipatedLength;
 172    }
 173   
 174  105 private static final boolean isLiteral(String str) {
 175  105 if (str.length() == 0) {
 176  0 return false;
 177    }
 178  105 char c = str.charAt(0);
 179  105 return (c == '"' || c == '\'');
 180    }
 181   
 182  78 private int processNode(SimpleNode sn) {
 183  78 int anticipatedLength = 0;
 184  78 ASTPrimaryPrefix xn = (ASTPrimaryPrefix) sn.getFirstChildOfType(ASTPrimaryPrefix.class);
 185  78 if (xn.jjtGetNumChildren() != 0 && xn.jjtGetChild(0).getClass().equals(ASTLiteral.class)) {
 186  74 String str = ((SimpleNode) xn.jjtGetChild(0)).getImage();
 187  74 if(isLiteral(str)){
 188  65 anticipatedLength += str.length() - 2;
 189  9 } else if(str.startsWith("0x")){
 190  5 anticipatedLength += 1;
 191    } else {
 192  4 anticipatedLength += str.length();
 193    }
 194    }
 195  78 return anticipatedLength;
 196    }
 197   
 198  40 private int getConstructorLength(SimpleNode node, int constructorLength) {
 199  40 int iConstructorLength = constructorLength;
 200  40 SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class);
 201  40 List literal;
 202   
 203  40 if (block == null) {
 204  3 block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class);
 205    }
 206  40 if (block == null) {
 207  1 block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class);
 208  1 if (block != null) {
 209  1 iConstructorLength = -1;
 210    }
 211    }
 212  40 literal = (block.findChildrenOfType(ASTLiteral.class));
 213  40 if (literal.isEmpty()) {
 214  20 List name = (block.findChildrenOfType(ASTName.class));
 215  20 if (!name.isEmpty()) {
 216  2 iConstructorLength = -1;
 217    }
 218  20 } else if (literal.size() == 1) {
 219  18 String str = ((SimpleNode) literal.get(0)).getImage();
 220  18 if (str == null) {
 221  1 iConstructorLength = 0;
 222  17 } else if (isLiteral(str)) {
 223    // since it's not taken into account
 224    // anywhere. only count the extra 16
 225    // characters
 226  1 iConstructorLength = 14 + str.length(); // don't add the constructor's length,
 227    } else {
 228  16 iConstructorLength = Integer.parseInt(str);
 229    }
 230    } else {
 231  2 iConstructorLength = -1;
 232    }
 233   
 234  40 if(iConstructorLength == 0){
 235  3 iConstructorLength = 16;
 236    }
 237   
 238  40 return iConstructorLength;
 239    }
 240   
 241   
 242  40 private int getInitialLength(SimpleNode node) {
 243  40 SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class);
 244  40 List literal;
 245   
 246  40 if (block == null) {
 247  3 block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class);
 248  3 if (block == null) {
 249  1 block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class);
 250    }
 251    }
 252  40 literal = (block.findChildrenOfType(ASTLiteral.class));
 253  40 if (literal.size() == 1) {
 254  16 String str = ((SimpleNode) literal.get(0)).getImage();
 255  16 if (str != null && isLiteral(str)) {
 256  1 return str.length() - 2; // take off the quotes
 257    }
 258    }
 259   
 260  39 return 0;
 261    }
 262   
 263  80 private boolean isAdditive(SimpleNode n) {
 264  80 return n.findChildrenOfType(ASTAdditiveExpression.class).size() >= 1;
 265    }
 266   
 267    /**
 268    * Locate the block that the given node is in, if any
 269    *
 270    * @param node
 271    * The node we're looking for a parent of
 272    * @return Node - The node that corresponds to any block that may be a
 273    * parent of this object
 274    */
 275  80 private Node getFirstParentBlock(Node node) {
 276  80 Node parentNode = node.jjtGetParent();
 277   
 278  80 Node lastNode = node;
 279  80 while (parentNode != null && !blockParents.contains(parentNode.getClass())) {
 280  715 lastNode = parentNode;
 281  715 parentNode = parentNode.jjtGetParent();
 282    }
 283  80 if (parentNode != null && ASTIfStatement.class.equals(parentNode.getClass())) {
 284  26 parentNode = lastNode;
 285  54 } else if (parentNode != null && parentNode.getClass().equals(ASTSwitchStatement.class)) {
 286  5 parentNode = getSwitchParent(parentNode, lastNode);
 287    }
 288  80 return parentNode;
 289    }
 290   
 291    /**
 292    * Determine which SwitchLabel we belong to inside a switch
 293    *
 294    * @param parentNode
 295    * The parent node we're looking at
 296    * @param lastNode
 297    * The last node processed
 298    * @return The parent node for the switch statement
 299    */
 300  5 private static Node getSwitchParent(Node parentNode, Node lastNode) {
 301  5 int allChildren = parentNode.jjtGetNumChildren();
 302  5 ASTSwitchLabel label = null;
 303  27 for (int ix = 0; ix < allChildren; ix++) {
 304  27 Node n = parentNode.jjtGetChild(ix);
 305  27 if (n.getClass().equals(ASTSwitchLabel.class)) {
 306  9 label = (ASTSwitchLabel) n;
 307  18 } else if (n.equals(lastNode)) {
 308  5 parentNode = label;
 309  5 break;
 310    }
 311    }
 312  5 return parentNode;
 313    }
 314   
 315    }