diff --git a/pmd/etc/changelog.txt b/pmd/etc/changelog.txt index d64f7d05bf..b3d9971eb6 100644 --- a/pmd/etc/changelog.txt +++ b/pmd/etc/changelog.txt @@ -1,7 +1,7 @@ ????, 2006 - 3.6: New rules: Design ruleset: UnsynchronizedStaticDateFormatter - Strings ruleset: InefficientEmptyStringCheck + Strings ruleset: InefficientEmptyStringCheck, InsufficientStringBufferDeclaration Fixed bug 1414985 - ConsecutiveLiteralAppends now checks for intervening references between appends. Fixed bug 1418424 - ConsecutiveLiteralAppends no longer flags appends in separate methods. Fixed bug 1416167 - AppendCharacterWithChar now catches cases involving escaped characters. diff --git a/pmd/regress/test/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclarationTest.java b/pmd/regress/test/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclarationTest.java new file mode 100644 index 0000000000..06e091f26f --- /dev/null +++ b/pmd/regress/test/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclarationTest.java @@ -0,0 +1,386 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html +*/ +package test.net.sourceforge.pmd.rules.strings; + +import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.Rule; +import test.net.sourceforge.pmd.testframework.SimpleAggregatorTst; +import test.net.sourceforge.pmd.testframework.TestDescriptor; + +public class InsufficientStringBufferDeclarationTest extends SimpleAggregatorTst { + + private Rule rule; + + public void setUp() throws Exception { + rule = findRule("strings", "InsufficientStringBufferDeclaration"); + } + + public void testAll() { + + // first run the legal tests + runTests(new TestDescriptor[] { + new TestDescriptor(TEST1, "1, StringBuffer allocated with enough space", 0, rule), + new TestDescriptor(TEST3, "3, StringBuffer allocated with space", 0, rule), + new TestDescriptor(TEST4, "4, StringBuffer allocated from variable", 0, rule), + new TestDescriptor(TEST5, "5, creating a new StringBuffer", 0, rule), + new TestDescriptor(TEST6, "6, Initialize with a specific String", 1, rule), + new TestDescriptor(TEST7, "7, appends inside if statements", 0, rule), + new TestDescriptor(TEST8, "8, Field level variable", 0, rule), + new TestDescriptor(TEST10, "10, Appending non-literals", 0, rule), + new TestDescriptor(TEST11, "11, Initialized to null", 0, rule), + new TestDescriptor(TEST12, "12, Passed in as parameter", 0, rule), + new TestDescriptor(TEST14, "14, Compound append, presized just fine", 0, rule), + new TestDescriptor(TEST16, "16, Append int, properly presized", 0, rule), + new TestDescriptor(TEST18, "18, Append char, properly presized", 0, rule), + new TestDescriptor(TEST22, "22, appends inside if/else if/else statements", 0, rule), + new TestDescriptor(TEST23, "23, appends inside if/else if/else statements", 0, rule), + new TestDescriptor(TEST24, "24, appends inside if/else if/else statements", 1, rule), + new TestDescriptor(TEST25, "25, Compound ifs", 0, rule), + new TestDescriptor(TEST27, "27, Switch statement doesn't exceed 16 characters", 0, rule), + }); + + // Then run the failure tests + runTests(new TestDescriptor[] { + new TestDescriptor(TEST2_FAIL, "2, StringBuffer not allocated with enough space", 1, rule), + new TestDescriptor(TEST9_FAIL, "9, Field level variable", 1, rule), + new TestDescriptor(TEST13_FAIL, "13, compound append", 1, rule), + new TestDescriptor(TEST15_FAIL, "15, Append int, incorrect presize", 1, rule), + new TestDescriptor(TEST17_FAIL, "17, Append char, incorrect presize", 1, rule), + new TestDescriptor(TEST19_FAIL, "19, String concatenation, incorrect presize", 1, rule), + new TestDescriptor(TEST20_FAIL, "20, String concatenation with non-literal, incorrect presize", 1, rule), + new TestDescriptor(TEST21_FAIL, "21, Incorrectly presized twice", 2, rule), + new TestDescriptor(TEST26_FAIL, "26, Compound if, pushed over the edge", 1, rule), + new TestDescriptor(TEST28_FAIL, "28, Compound if, pushed over the edge", 1, rule), + }); + } + + private static final String TEST1 = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(16);" + PMD.EOL + + " sb.append(\"foo\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST2_FAIL = + "public class Foo {" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb.append(\"World\");" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST3 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(l.size());" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb.append(\"World\");" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST4 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " int x = 3;" + PMD.EOL + + " StringBuffer sb = new StringBuffer(x);" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb.append(\"World\");" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST5 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " int x = 3;" + PMD.EOL + + " StringBuffer sb = new StringBuffer(5);" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb = new StringBuffer(23);" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST6 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " int x = 3;" + PMD.EOL + + " StringBuffer sb = new StringBuffer(\"Initialize With A String\");" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST7 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"1234567890\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"123456789\");" + PMD.EOL + + " }" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST8 = + "public class Foo {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(200);" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST9_FAIL = + "public class Foo {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST10 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(0);" + PMD.EOL + + " sb.append(l.get(2));" + PMD.EOL + + " sb.append(l.toString());" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST11 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = null;" + PMD.EOL + + " sb = new StringBuffer(20);" + PMD.EOL + + " sb.append(l.toString());" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST12 = + "public class Foo {" + PMD.EOL + + " public void bar(StringBuffer param) {" + PMD.EOL + + " param.append(\"Append something\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST13_FAIL = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " sb.append(\"foo\").append(\"this will make it long\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST14 = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(30);" + PMD.EOL + + " sb.append(\"foo\").append(\"this is presized just right\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST15_FAIL = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " sb.append(12345678901234567890);" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST16 = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " sb.append(12345);" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST17_FAIL = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(2);" + PMD.EOL + + " sb.append('a');" + PMD.EOL + + " sb.append('a');" + PMD.EOL + + " sb.append('a');" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST18 = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer(3);" + PMD.EOL + + " sb.append('a');" + PMD.EOL + + " sb.append('a');" + PMD.EOL + + " sb.append('a');" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST19_FAIL = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar() {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " sb.append(\"This string\" + \" \" + \"isn't nice, but valid\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST20_FAIL = + "public class Foo {" + PMD.EOL + + " private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);" + PMD.EOL + + " public void bar(String x) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " sb.append(\"This string\" + x + \"isn't nice, but valid\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST21_FAIL = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " int x = 3;" + PMD.EOL + + " StringBuffer sb = new StringBuffer(2);" + PMD.EOL + + " sb.append(\"Hello\");" + PMD.EOL + + " sb = new StringBuffer(5);" + PMD.EOL + + " sb.append(\"How are you today world\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST22 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"1234567890\");" + PMD.EOL + + " } else if( l.size() == 5){" + PMD.EOL + + " sb.append(\"1234567890\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"1234567890\");" + PMD.EOL + + " }" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST23 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"12345\");" + PMD.EOL + + " } else if( l.size() == 5){" + PMD.EOL + + " sb.append(\"12345\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"12345\");" + PMD.EOL + + " }" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"12345\");" + PMD.EOL + + " } else if( l.size() == 5){" + PMD.EOL + + " sb.append(\"12345\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"12345\");" + PMD.EOL + + " }" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + + private static final String TEST24 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"This should use\");" + PMD.EOL + + " } else if( l.size() == 5){" + PMD.EOL + + " sb.append(\"The longest if\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"statement for its violation, which is this one\");" + PMD.EOL + + " }" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST25 = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " if(true){" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"More\");" + PMD.EOL + + " } else if( l.size() == 5){" + PMD.EOL + + " sb.append(\"Compound\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"If\");" + PMD.EOL + + " }" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"A compound if\");" + PMD.EOL + + " } " + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST26_FAIL = + "public class Foo {" + PMD.EOL + + " public void bar(List l) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " if(true){" + PMD.EOL + + " if(true){" + PMD.EOL + + " sb.append(\"More\");" + PMD.EOL + + " } else if( l.size() == 5){" + PMD.EOL + + " sb.append(\"Compound\");" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"If\");" + PMD.EOL + + " }" + PMD.EOL + + " } else {" + PMD.EOL + + " sb.append(\"A compound if\");" + PMD.EOL + + " } " + PMD.EOL + + " sb.append(\"Push\");" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST27 = + "public class Foo {" + PMD.EOL + + " public void bar(String str) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " switch(str.charAt(0)){" + PMD.EOL + + " case 'a':" + PMD.EOL + + " sb.append(\"Switch block\");" + PMD.EOL + + " break;" + PMD.EOL + + " case 'b':" + PMD.EOL + + " sb.append(\"Doesn't exceed\");" + PMD.EOL + + " break;" + PMD.EOL + + " default:" + PMD.EOL + + " sb.append(\"16 chars\");" + PMD.EOL + + " }" + PMD.EOL + + " }" + PMD.EOL + + "}"; + + private static final String TEST28_FAIL = + "public class Foo {" + PMD.EOL + + " public void bar(String str) {" + PMD.EOL + + " StringBuffer sb = new StringBuffer();" + PMD.EOL + + " switch(str.charAt(0)){" + PMD.EOL + + " case 'a':" + PMD.EOL + + " sb.append(\"Switch block\");" + PMD.EOL + + " break;" + PMD.EOL + + " default:" + PMD.EOL + + " sb.append(\"The default block exceeds 16 characters and will fail\");" + PMD.EOL + + " }" + PMD.EOL + + " }" + PMD.EOL + + "}"; + +} \ No newline at end of file diff --git a/pmd/rulesets/releases/36.xml b/pmd/rulesets/releases/36.xml index 4f26c2abab..ccf99853d2 100644 --- a/pmd/rulesets/releases/36.xml +++ b/pmd/rulesets/releases/36.xml @@ -8,6 +8,7 @@ This ruleset contains links to rules that are new in PMD v3.6 + diff --git a/pmd/rulesets/strings.xml b/pmd/rulesets/strings.xml index b9e891b43b..bc3590f415 100644 --- a/pmd/rulesets/strings.xml +++ b/pmd/rulesets/strings.xml @@ -235,6 +235,31 @@ public class Foo { } } } +]]> + + + + + +Failing to pre-size a StringBuffer properly could cause it to re-size many times +during runtime. This rule checks the characters that are actually passed into +StringBuffer.append(), but represents a best guess "worst case" scenario. An +empty StringBuffer constructor initializes the object to 16 characters. This default +is assumed if the length of the constructor can not be determined. + + 3 + + diff --git a/pmd/src/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclaration.java b/pmd/src/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclaration.java new file mode 100644 index 0000000000..e373d804a6 --- /dev/null +++ b/pmd/src/net/sourceforge/pmd/rules/strings/InsufficientStringBufferDeclaration.java @@ -0,0 +1,274 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.rules.strings; + +import net.sourceforge.pmd.AbstractRule; +import net.sourceforge.pmd.ast.ASTAdditiveExpression; +import net.sourceforge.pmd.ast.ASTBlockStatement; +import net.sourceforge.pmd.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.ast.ASTFormalParameter; +import net.sourceforge.pmd.ast.ASTIfStatement; +import net.sourceforge.pmd.ast.ASTLiteral; +import net.sourceforge.pmd.ast.ASTName; +import net.sourceforge.pmd.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.ast.ASTSwitchLabel; +import net.sourceforge.pmd.ast.ASTSwitchStatement; +import net.sourceforge.pmd.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.ast.Node; +import net.sourceforge.pmd.ast.SimpleNode; +import net.sourceforge.pmd.symboltable.NameOccurrence; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.oro.text.perl.Perl5Util; + +/** + * This rule finds StringBuffers which may have been pre-sized incorrectly + * + * @see http://sourceforge.net/forum/forum.php?thread_id=1438119&forum_id=188194 + * @author Allan Caplan + */ +public class InsufficientStringBufferDeclaration extends AbstractRule { + + private final static Set blockParents; + + static { + blockParents = new HashSet(); + blockParents.add(ASTIfStatement.class); + blockParents.add(ASTSwitchStatement.class); + } + + private final Perl5Util regexp = new Perl5Util(); + + public Object visit(ASTVariableDeclaratorId node, Object data) { + + if (!"StringBuffer".equals(node.getNameDeclaration().getTypeImage())) { + return data; + } + Node rootNode = node; + int anticipatedLength = 0; + int constructorLength = 16; + + constructorLength = getConstructorLength(node, constructorLength); + List usage = node.getUsages(); + Map blocks = new HashMap(); + for (int ix = 0; ix < usage.size(); ix++) { + NameOccurrence no = (NameOccurrence) usage.get(ix); + SimpleNode n = no.getLocation(); + if (!InefficientStringBuffering.isInStringBufferAppend(n, 3)) { + if (!no.isOnLeftHandSide()) { + continue; + } + if (constructorLength != -1 && anticipatedLength > constructorLength) { + anticipatedLength += processBlocks(blocks); + String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) }; + addViolation(data, rootNode, param); + } + constructorLength = getConstructorLength(n, constructorLength); + rootNode = n; + anticipatedLength = 0; + } + ASTPrimaryExpression s = (ASTPrimaryExpression) n.getFirstParentOfType(ASTPrimaryExpression.class); + int numChildren = s.jjtGetNumChildren(); + for (int jx = 0; jx < numChildren; jx++) { + SimpleNode sn = (SimpleNode) s.jjtGetChild(jx); + if (!(sn instanceof ASTPrimarySuffix) || sn.getImage() != null) { + continue; + } + int thisSize = 0; + Node block = getFirstParentBlock(sn); + if (isAdditive(sn)) { + thisSize = processAdditive(sn); + } else { + thisSize = processNode(sn); + } + if (block != null) { + storeBlockStatistics(blocks, thisSize, block); + } else { + anticipatedLength += thisSize; + } + } + } + anticipatedLength += processBlocks(blocks); + if (constructorLength != -1 && anticipatedLength > constructorLength) { + String[] param = { String.valueOf(constructorLength), String.valueOf(anticipatedLength) }; + addViolation(data, rootNode, param); + } + return data; + } + + /** + * This rule is concerned with IF and Switch blocks. Process the block into + * a local Map, from which we can later determine which is the longest block + * inside + * + * @param blocks + * The map of blocks in the method being investigated + * @param thisSize + * The size of the current block + * @param block + * The block in question + */ + private void storeBlockStatistics(Map blocks, int thisSize, Node block) { + Node statement = block.jjtGetParent(); + if (ASTIfStatement.class.equals(block.jjtGetParent().getClass())) { + // Else Ifs are their own subnode in AST. So we have to + // look a little farther up the tree to find the IF statement + Node possibleStatement = ((SimpleNode) statement).getFirstParentOfType(ASTIfStatement.class); + if (possibleStatement != null && possibleStatement.getClass().equals(ASTIfStatement.class)) { + statement = possibleStatement; + } + } + Map thisBranch = (Map) blocks.get(statement); + if (thisBranch == null) { + thisBranch = new HashMap(); + blocks.put(statement, thisBranch); + } + Integer x = (Integer) thisBranch.get(block); + if (x != null) { + thisSize += x.intValue(); + } + thisBranch.put(statement, new Integer(thisSize)); + } + + private int processBlocks(Map blocks) { + int anticipatedLength = 0; + int ifLength = 0; + for (Iterator iter = blocks.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + ifLength = 0; + for (Iterator iter2 = ((Map) entry.getValue()).entrySet().iterator(); iter2.hasNext();) { + Map.Entry entry2 = (Map.Entry) iter2.next(); + Integer value = (Integer) entry2.getValue(); + ifLength = Math.max(ifLength, value.intValue()); + } + anticipatedLength += ifLength; + } + return anticipatedLength; + } + + private int processAdditive(SimpleNode sn) { + ASTAdditiveExpression additive = (ASTAdditiveExpression) sn.getFirstChildOfType(ASTAdditiveExpression.class); + if (additive == null) { + return 0; + } + int anticipatedLength = 0; + for (int ix = 0; ix < additive.jjtGetNumChildren(); ix++) { + SimpleNode childNode = (SimpleNode) additive.jjtGetChild(ix); + ASTLiteral literal = (ASTLiteral) childNode.getFirstChildOfType(ASTLiteral.class); + if (literal != null && literal.getImage() != null) { + anticipatedLength += literal.getImage().length() - 2; + } + } + + return anticipatedLength; + } + + private int processNode(SimpleNode sn) { + int anticipatedLength = 0; + ASTPrimaryPrefix xn = (ASTPrimaryPrefix) sn.getFirstChildOfType(ASTPrimaryPrefix.class); + if (xn.jjtGetNumChildren() != 0 && xn.jjtGetChild(0).getClass().equals(ASTLiteral.class)) { + + String str = ((SimpleNode) xn.jjtGetChild(0)).getImage(); + int i = (regexp.match("/^[\"']/", str)) ? 2 : 0; + anticipatedLength += str.length() - i; + } + return anticipatedLength; + } + + private int getConstructorLength(SimpleNode node, int constructorLength) { + SimpleNode block = (SimpleNode) node.getFirstParentOfType(ASTBlockStatement.class); + List literal; + + if (block == null) { + block = (ASTFieldDeclaration) node.getFirstParentOfType(ASTFieldDeclaration.class); + } + if (block == null) { + block = (ASTFormalParameter) node.getFirstParentOfType(ASTFormalParameter.class); + if (block != null) { + constructorLength = -1; + } + } + literal = (block.findChildrenOfType(ASTLiteral.class)); + if (literal.size() == 0) { + List name = (block.findChildrenOfType(ASTName.class)); + if (name.size() != 0) { + constructorLength = -1; + } + } else if (literal.size() == 1) { + String str = ((SimpleNode) literal.get(0)).getImage(); + if (str == null) { + constructorLength = 0; + } else if (regexp.match("/^['\"]/", str)) { + // since it's not taken into account + // anywhere. only count the extra 16 + // characters + constructorLength = 16; // don't add the constructor's length, + } else { + constructorLength = Integer.parseInt(str); + } + } + return constructorLength; + } + + private boolean isAdditive(SimpleNode n) { + return n.findChildrenOfType(ASTAdditiveExpression.class).size() >= 1; + } + + /** + * Locate the block that the given node is in, if any + * + * @param node + * The node we're looking for a parent of + * @return Node - The node that corresponds to any block that may be a + * parent of this object + */ + private Node getFirstParentBlock(Node node) { + Node parentNode = node.jjtGetParent(); + + Node lastNode = node; + while (parentNode != null && !blockParents.contains(parentNode.getClass())) { + lastNode = parentNode; + parentNode = parentNode.jjtGetParent(); + } + if (parentNode != null && ASTIfStatement.class.equals(parentNode.getClass())) { + parentNode = lastNode; + } else if (parentNode != null && parentNode.getClass().equals(ASTSwitchStatement.class)) { + parentNode = getSwitchParent(parentNode, lastNode); + } + return parentNode; + } + + /** + * Determine which SwitchLabel we belong to inside a switch + * + * @param parentNode + * The parent node we're looking at + * @param lastNode + * The last node processed + * @return The parent node for the switch statement + */ + private static Node getSwitchParent(Node parentNode, Node lastNode) { + int allChildren = parentNode.jjtGetNumChildren(); + ASTSwitchLabel label = null; + for (int ix = 0; ix < allChildren; ix++) { + Node n = parentNode.jjtGetChild(ix); + if (n.getClass().equals(ASTSwitchLabel.class)) { + label = (ASTSwitchLabel) n; + } else if (n.equals(lastNode)) { + parentNode = label; + break; + } + } + return parentNode; + } + +} \ No newline at end of file