1 4 package net.sourceforge.pmd.rules.strings; 5 6 import net.sourceforge.pmd.AbstractRule; 7 import net.sourceforge.pmd.PropertyDescriptor; 8 import net.sourceforge.pmd.ast.ASTAdditiveExpression; 9 import net.sourceforge.pmd.ast.ASTArgumentList; 10 import net.sourceforge.pmd.ast.ASTDoStatement; 11 import net.sourceforge.pmd.ast.ASTForStatement; 12 import net.sourceforge.pmd.ast.ASTIfStatement; 13 import net.sourceforge.pmd.ast.ASTLiteral; 14 import net.sourceforge.pmd.ast.ASTMethodDeclaration; 15 import net.sourceforge.pmd.ast.ASTName; 16 import net.sourceforge.pmd.ast.ASTPrimaryExpression; 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.ASTWhileStatement; 22 import net.sourceforge.pmd.ast.Node; 23 import net.sourceforge.pmd.ast.SimpleNode; 24 import net.sourceforge.pmd.properties.IntegerProperty; 25 import net.sourceforge.pmd.symboltable.NameOccurrence; 26 27 import java.util.HashSet ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.Set ; 32 33 56 public class ConsecutiveLiteralAppends extends AbstractRule { 57 58 private final static Set blockParents; 59 60 static { 61 blockParents = new HashSet (); 62 blockParents.add(ASTForStatement.class); 63 blockParents.add(ASTWhileStatement.class); 64 blockParents.add(ASTDoStatement.class); 65 blockParents.add(ASTIfStatement.class); 66 blockParents.add(ASTSwitchStatement.class); 67 blockParents.add(ASTMethodDeclaration.class); 68 } 69 70 private static final PropertyDescriptor thresholdDescriptor = new IntegerProperty( 71 "threshold", 72 "?", 73 1, 74 1.0f 75 ); 76 77 private static final Map propertyDescriptorsByName = asFixedMap(thresholdDescriptor); 78 79 80 private int threshold = 1; 81 82 public Object visit(ASTVariableDeclaratorId node, Object data) { 83 84 if (!isStringBuffer(node)) { 85 return data; 86 } 87 threshold = getIntProperty(thresholdDescriptor); 88 89 int concurrentCount = checkConstructor(node, data); 90 Node lastBlock = getFirstParentBlock(node); 91 Node currentBlock = lastBlock; 92 Map decls = node.getScope().getVariableDeclarations(); 93 SimpleNode rootNode = null; 94 if (concurrentCount == 1) { 96 rootNode = node; 97 } 98 for (Iterator iter = decls.entrySet().iterator(); iter.hasNext();) { 99 Map.Entry entry = (Map.Entry ) iter.next(); 100 List decl = (List) entry.getValue(); 101 for (int ix = 0; ix < decl.size(); ix++) { 102 NameOccurrence no = (NameOccurrence) decl.get(ix); 103 SimpleNode n = no.getLocation(); 104 105 currentBlock = getFirstParentBlock(n); 106 107 if (!InefficientStringBuffering.isInStringBufferOperation(n, 3,"append")) { 108 if (!no.isPartOfQualifiedName()) { 109 checkForViolation(rootNode, data, concurrentCount); 110 concurrentCount = 0; 111 } 112 continue; 113 } 114 ASTPrimaryExpression s = (ASTPrimaryExpression) n 115 .getFirstParentOfType(ASTPrimaryExpression.class); 116 int numChildren = s.jjtGetNumChildren(); 117 for (int jx = 0; jx < numChildren; jx++) { 118 SimpleNode sn = (SimpleNode) s.jjtGetChild(jx); 119 if (!(sn instanceof ASTPrimarySuffix) 120 || sn.getImage() != null) { 121 continue; 122 } 123 124 if ((currentBlock != null && lastBlock != null && !currentBlock 126 .equals(lastBlock)) 127 || (currentBlock == null ^ lastBlock == null)) { 128 checkForViolation(rootNode, data, concurrentCount); 129 concurrentCount = 0; 130 } 131 132 if (concurrentCount == 0) { 135 rootNode = sn; 136 } 137 if (isAdditive(sn)) { 138 concurrentCount = processAdditive(data, 139 concurrentCount, sn, rootNode); 140 if (concurrentCount != 0) { 141 rootNode = sn; 142 } 143 } else if (!isAppendingStringLiteral(sn)) { 144 checkForViolation(rootNode, data, concurrentCount); 145 concurrentCount = 0; 146 } else { 147 concurrentCount++; 148 } 149 lastBlock = currentBlock; 150 } 151 } 152 } 153 checkForViolation(rootNode, data, concurrentCount); 154 return data; 155 } 156 157 163 private int checkConstructor(ASTVariableDeclaratorId node, Object data) { 164 Node parent = node.jjtGetParent(); 165 if (parent.jjtGetNumChildren() >= 2) { 166 ASTArgumentList list = (ASTArgumentList) ((SimpleNode) parent 167 .jjtGetChild(1)).getFirstChildOfType(ASTArgumentList.class); 168 if (list != null) { 169 ASTLiteral literal = (ASTLiteral) list 170 .getFirstChildOfType(ASTLiteral.class); 171 if (!isAdditive(list) && literal != null 172 && literal.isStringLiteral()) { 173 return 1; 174 } 175 return processAdditive(data, 0, list, node); 176 } 177 } 178 return 0; 179 } 180 181 private int processAdditive(Object data, int concurrentCount, 182 SimpleNode sn, SimpleNode rootNode) { 183 ASTAdditiveExpression additive = (ASTAdditiveExpression) sn 184 .getFirstChildOfType(ASTAdditiveExpression.class); 185 if (additive == null) { 186 return 0; 187 } 188 int count = concurrentCount; 189 boolean found = false; 190 for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) { 191 SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix); 192 if (childNode.jjtGetNumChildren() != 1 193 || childNode.findChildrenOfType(ASTName.class).size() != 0) { 194 if (!found) { 195 checkForViolation(rootNode, data, count); 196 found = true; 197 } 198 count = 0; 199 } else { 200 count++; 201 } 202 } 203 204 if (!found) { 207 count = 1; 208 } 209 210 return count; 211 } 212 213 224 private boolean isAdditive(SimpleNode n) { 225 List lstAdditive = n.findChildrenOfType(ASTAdditiveExpression.class); 226 if (lstAdditive.isEmpty()) { 227 return false; 228 } 229 for (int ix = 0; ix < lstAdditive.size(); ix++) { 233 ASTAdditiveExpression expr = (ASTAdditiveExpression) lstAdditive.get(ix); 234 if (expr.getParentsOfType(ASTArgumentList.class).size() != 1) { 235 return false; 236 } 237 } 238 return true; 239 } 240 241 249 private Node getFirstParentBlock(Node node) { 250 Node parentNode = node.jjtGetParent(); 251 252 Node lastNode = node; 253 while (parentNode != null 254 && !blockParents.contains(parentNode.getClass())) { 255 lastNode = parentNode; 256 parentNode = parentNode.jjtGetParent(); 257 } 258 if (parentNode != null 259 && parentNode.getClass().equals(ASTIfStatement.class)) { 260 parentNode = lastNode; 261 } else if (parentNode != null 262 && parentNode.getClass().equals(ASTSwitchStatement.class)) { 263 parentNode = getSwitchParent(parentNode, lastNode); 264 } 265 return parentNode; 266 } 267 268 275 private Node getSwitchParent(Node parentNode, Node lastNode) { 276 int allChildren = parentNode.jjtGetNumChildren(); 277 ASTSwitchLabel label = null; 278 for (int ix = 0; ix < allChildren; ix++) { 279 Node n = parentNode.jjtGetChild(ix); 280 if (n.getClass().equals(ASTSwitchLabel.class)) { 281 label = (ASTSwitchLabel) n; 282 } else if (n.equals(lastNode)) { 283 parentNode = label; 284 break; 285 } 286 } 287 return parentNode; 288 } 289 290 294 private void checkForViolation(SimpleNode node, Object data, 295 int concurrentCount) { 296 if (concurrentCount > threshold) { 297 String [] param = {String.valueOf(concurrentCount)}; 298 addViolation(data, node, param); 299 } 300 } 301 302 private boolean isAppendingStringLiteral(SimpleNode node) { 303 SimpleNode n = node; 304 while (n.jjtGetNumChildren() != 0 305 && !n.getClass().equals(ASTLiteral.class)) { 306 n = (SimpleNode) n.jjtGetChild(0); 307 } 308 return n.getClass().equals(ASTLiteral.class); 309 } 310 311 private static boolean isStringBuffer(ASTVariableDeclaratorId node) { 312 SimpleNode nn = node.getTypeNameNode(); 313 if (nn.jjtGetNumChildren() == 0) { 314 return false; 315 } 316 return "StringBuffer".equals(((SimpleNode) nn.jjtGetChild(0)).getImage()); 317 } 318 319 protected Map propertiesByName() { 320 return propertyDescriptorsByName; 321 } 322 } | Popular Tags |