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