1 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 37 public class InsufficientStringBufferDeclaration extends AbstractRule { 38 39 private final static Set blockParents; 40 41 static { 42 blockParents = new HashSet (); 43 blockParents.add(ASTIfStatement.class); 44 blockParents.add(ASTSwitchStatement.class); 45 } 46 47 public Object visit(ASTVariableDeclaratorId node, Object data) { 48 49 if (!"StringBuffer".equals(node.getNameDeclaration().getTypeImage())) { 50 return data; 51 } 52 Node rootNode = node; 53 int anticipatedLength = 0; 54 int constructorLength = 16; 55 56 constructorLength = getConstructorLength(node, constructorLength); 57 anticipatedLength = getInitialLength(node); 58 List usage = node.getUsages(); 59 Map blocks = new HashMap (); 60 for (int ix = 0; ix < usage.size(); ix++) { 61 NameOccurrence no = (NameOccurrence) usage.get(ix); 62 SimpleNode n = no.getLocation(); 63 if (!InefficientStringBuffering.isInStringBufferOperation(n, 3, "append")) { 64 65 if (!no.isOnLeftHandSide() && !InefficientStringBuffering.isInStringBufferOperation(n, 3, "setLength")) { 66 continue; 67 } 68 if (constructorLength != -1 && anticipatedLength > constructorLength) { 69 anticipatedLength += processBlocks(blocks); 70 String [] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) }; 71 addViolation(data, rootNode, param); 72 } 73 constructorLength = getConstructorLength(n, constructorLength); 74 rootNode = n; 75 anticipatedLength = getInitialLength(node); 76 } 77 ASTPrimaryExpression s = (ASTPrimaryExpression) n.getFirstParentOfType(ASTPrimaryExpression.class); 78 int numChildren = s.jjtGetNumChildren(); 79 for (int jx = 0; jx < numChildren; jx++) { 80 SimpleNode sn = (SimpleNode) s.jjtGetChild(jx); 81 if (!(sn instanceof ASTPrimarySuffix) || sn.getImage() != null) { 82 continue; 83 } 84 int thisSize = 0; 85 Node block = getFirstParentBlock(sn); 86 if (isAdditive(sn)) { 87 thisSize = processAdditive(sn); 88 } else { 89 thisSize = processNode(sn); 90 } 91 if (block != null) { 92 storeBlockStatistics(blocks, thisSize, block); 93 } else { 94 anticipatedLength += thisSize; 95 } 96 } 97 } 98 anticipatedLength += processBlocks(blocks); 99 if (constructorLength != -1 && anticipatedLength > constructorLength) { 100 String [] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) }; 101 addViolation(data, rootNode, param); 102 } 103 return data; 104 } 105 106 118 private void storeBlockStatistics(Map blocks, int thisSize, Node block) { 119 Node statement = block.jjtGetParent(); 120 if (ASTIfStatement.class.equals(block.jjtGetParent().getClass())) { 121 Node possibleStatement = ((SimpleNode) statement).getFirstParentOfType(ASTIfStatement.class); 124 while(possibleStatement != null && possibleStatement.getClass().equals(ASTIfStatement.class)) { 125 statement = possibleStatement; 126 possibleStatement = ((SimpleNode) possibleStatement).getFirstParentOfType(ASTIfStatement.class); 127 } 128 } 129 Map thisBranch = (Map ) blocks.get(statement); 130 if (thisBranch == null) { 131 thisBranch = new HashMap (); 132 blocks.put(statement, thisBranch); 133 } 134 Integer x = (Integer ) thisBranch.get(block); 135 if (x != null) { 136 thisSize += x.intValue(); 137 } 138 thisBranch.put(statement, new Integer (thisSize)); 139 } 140 141 private int processBlocks(Map blocks) { 142 int anticipatedLength = 0; 143 int ifLength = 0; 144 for (Iterator iter = blocks.entrySet().iterator(); iter.hasNext();) { 145 Map.Entry entry = (Map.Entry ) iter.next(); 146 ifLength = 0; 147 for (Iterator iter2 = ((Map ) entry.getValue()).entrySet().iterator(); iter2.hasNext();) { 148 Map.Entry entry2 = (Map.Entry ) iter2.next(); 149 Integer value = (Integer ) entry2.getValue(); 150 ifLength = Math.max(ifLength, value.intValue()); 151 } 152 anticipatedLength += ifLength; 153 } 154 return anticipatedLength; 155 } 156 157 private int processAdditive(SimpleNode sn) { 158 ASTAdditiveExpression additive = (ASTAdditiveExpression) sn.getFirstChildOfType(ASTAdditiveExpression.class); 159 if (additive == null) { 160 return 0; 161 } 162 int anticipatedLength = 0; 163 for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) { 164 SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix); 165 ASTLiteral literal = (ASTLiteral) childNode.getFirstChildOfType(ASTLiteral.class); 166 if (literal != null && literal.getImage() != null) { 167 anticipatedLength += literal.getImage().length() - 2; 168 } 169 } 170 171 return anticipatedLength; 172 } 173 174 private static final boolean isLiteral(String str) { 175 if (str.length() == 0) { 176 return false; 177 } 178 char c = str.charAt(0); 179 return (c == '"' || c == '\''); 180 } 181 182 private int processNode(SimpleNode sn) { 183 int anticipatedLength = 0; 184 ASTPrimaryPrefix xn = (ASTPrimaryPrefix) sn.getFirstChildOfType(ASTPrimaryPrefix.class); 185 if (xn.jjtGetNumChildren() != 0 && xn.jjtGetChild(0).getClass().equals(ASTLiteral.class)) { 186 String str = ((SimpleNode) xn.jjtGetChild(0)).getImage(); 187 if(isLiteral(str)){ 188 anticipatedLength += str.length() - 2; 189 } else if(str.startsWith("0x")){ 190 anticipatedLength += 1; 191 } else { 192 anticipatedLength += str.length(); 193 } 194 } 195 return anticipatedLength; 196 } 197 198 private int getConstructorLength(SimpleNode node, int constructorLength) { 199 int iConstructorLength = constructorLength; 200 SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class); 201 List literal; 202 203 if (block == null) { 204 block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class); 205 } 206 if (block == null) { 207 block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class); 208 if (block != null) { 209 iConstructorLength = -1; 210 } 211 } 212 literal = (block.findChildrenOfType(ASTLiteral.class)); 213 if (literal.isEmpty()) { 214 List name = (block.findChildrenOfType(ASTName.class)); 215 if (!name.isEmpty()) { 216 iConstructorLength = -1; 217 } 218 } else if (literal.size() == 1) { 219 String str = ((SimpleNode) literal.get(0)).getImage(); 220 if (str == null) { 221 iConstructorLength = 0; 222 } else if (isLiteral(str)) { 223 iConstructorLength = 14 + str.length(); } else { 228 iConstructorLength = Integer.parseInt(str); 229 } 230 } else { 231 iConstructorLength = -1; 232 } 233 234 if(iConstructorLength == 0){ 235 iConstructorLength = 16; 236 } 237 238 return iConstructorLength; 239 } 240 241 242 private int getInitialLength(SimpleNode node) { 243 SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class); 244 List literal; 245 246 if (block == null) { 247 block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class); 248 if (block == null) { 249 block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class); 250 } 251 } 252 literal = (block.findChildrenOfType(ASTLiteral.class)); 253 if (literal.size() == 1) { 254 String str = ((SimpleNode) literal.get(0)).getImage(); 255 if (str != null && isLiteral(str)) { 256 return str.length() - 2; } 258 } 259 260 return 0; 261 } 262 263 private boolean isAdditive(SimpleNode n) { 264 return n.findChildrenOfType(ASTAdditiveExpression.class).size() >= 1; 265 } 266 267 275 private Node getFirstParentBlock(Node node) { 276 Node parentNode = node.jjtGetParent(); 277 278 Node lastNode = node; 279 while (parentNode != null && !blockParents.contains(parentNode.getClass())) { 280 lastNode = parentNode; 281 parentNode = parentNode.jjtGetParent(); 282 } 283 if (parentNode != null && ASTIfStatement.class.equals(parentNode.getClass())) { 284 parentNode = lastNode; 285 } else if (parentNode != null && parentNode.getClass().equals(ASTSwitchStatement.class)) { 286 parentNode = getSwitchParent(parentNode, lastNode); 287 } 288 return parentNode; 289 } 290 291 300 private static Node getSwitchParent(Node parentNode, Node lastNode) { 301 int allChildren = parentNode.jjtGetNumChildren(); 302 ASTSwitchLabel label = null; 303 for (int ix = 0; ix < allChildren; ix++) { 304 Node n = parentNode.jjtGetChild(ix); 305 if (n.getClass().equals(ASTSwitchLabel.class)) { 306 label = (ASTSwitchLabel) n; 307 } else if (n.equals(lastNode)) { 308 parentNode = label; 309 break; 310 } 311 } 312 return parentNode; 313 } 314 315 } | Popular Tags |