PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/pmd-4.2.5/src/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclaration.java

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