1 4 package net.sourceforge.pmd.rules; 5 6 import java.util.Stack ; 7 8 import net.sourceforge.pmd.AbstractRule; 9 import net.sourceforge.pmd.ast.ASTBlockStatement; 10 import net.sourceforge.pmd.ast.ASTCatchStatement; 11 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration; 12 import net.sourceforge.pmd.ast.ASTCompilationUnit; 13 import net.sourceforge.pmd.ast.ASTConditionalExpression; 14 import net.sourceforge.pmd.ast.ASTConstructorDeclaration; 15 import net.sourceforge.pmd.ast.ASTDoStatement; 16 import net.sourceforge.pmd.ast.ASTEnumDeclaration; 17 import net.sourceforge.pmd.ast.ASTExpression; 18 import net.sourceforge.pmd.ast.ASTForStatement; 19 import net.sourceforge.pmd.ast.ASTIfStatement; 20 import net.sourceforge.pmd.ast.ASTMethodDeclaration; 21 import net.sourceforge.pmd.ast.ASTMethodDeclarator; 22 import net.sourceforge.pmd.ast.ASTSwitchLabel; 23 import net.sourceforge.pmd.ast.ASTSwitchStatement; 24 import net.sourceforge.pmd.ast.ASTWhileStatement; 25 import net.sourceforge.pmd.ast.Node; 26 import net.sourceforge.pmd.ast.SimpleNode; 27 import net.sourceforge.pmd.rules.design.NpathComplexity; 28 29 34 public class CyclomaticComplexity extends AbstractRule { 35 36 private int reportLevel; 37 38 private static class Entry { 39 private SimpleNode node; 40 private int decisionPoints = 1; 41 public int highestDecisionPoints; 42 public int methodCount; 43 44 private Entry(SimpleNode node) { 45 this.node = node; 46 } 47 48 public void bumpDecisionPoints() { 49 decisionPoints++; 50 } 51 52 public void bumpDecisionPoints(int size) { 53 decisionPoints += size; 54 } 55 56 public int getComplexityAverage() { 57 return ( (double) methodCount == 0 ) ? 1 58 : (int) ( Math.rint( (double) decisionPoints / (double) methodCount ) ); 59 } 60 } 61 62 private Stack entryStack = new Stack (); 63 64 public Object visit(ASTCompilationUnit node, Object data) { 65 reportLevel = getIntProperty( "reportLevel" ); 66 super.visit( node, data ); 67 return data; 68 } 69 70 public Object visit(ASTIfStatement node, Object data) { 71 int boolCompIf = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) ); 72 boolCompIf++; 74 75 ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompIf ); 76 super.visit( node, data ); 77 return data; 78 } 79 80 public Object visit(ASTCatchStatement node, Object data) { 81 ( (Entry) entryStack.peek() ).bumpDecisionPoints(); 82 super.visit( node, data ); 83 return data; 84 } 85 86 public Object visit(ASTForStatement node, Object data) { 87 int boolCompFor = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) ); 88 boolCompFor++; 90 91 ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompFor ); 92 super.visit( node, data ); 93 return data; 94 } 95 96 public Object visit(ASTDoStatement node, Object data) { 97 int boolCompDo = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) ); 98 boolCompDo++; 100 101 ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompDo ); 102 super.visit( node, data ); 103 return data; 104 } 105 106 public Object visit(ASTSwitchStatement node, Object data) { 107 Entry entry = (Entry) entryStack.peek(); 108 109 int boolCompSwitch = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) ); 110 entry.bumpDecisionPoints( boolCompSwitch ); 111 112 int childCount = node.jjtGetNumChildren(); 113 int lastIndex = childCount - 1; 114 for ( int n = 0; n < lastIndex; n++ ) { 115 Node childNode = node.jjtGetChild( n ); 116 if ( childNode instanceof ASTSwitchLabel ) { 117 ASTSwitchLabel sl = (ASTSwitchLabel) childNode; 119 if ( !sl.isDefault() ) { 120 childNode = node.jjtGetChild( n + 1 ); 121 if ( childNode instanceof ASTBlockStatement ) { 122 entry.bumpDecisionPoints(); 123 } 124 } 125 } 126 } 127 super.visit( node, data ); 128 return data; 129 } 130 131 public Object visit(ASTWhileStatement node, Object data) { 132 int boolCompWhile = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) ); 133 boolCompWhile++; 135 136 ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompWhile ); 137 super.visit( node, data ); 138 return data; 139 } 140 141 public Object visit(ASTConditionalExpression node, Object data) { 142 if ( node.isTernary() ) { 143 int boolCompTern = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) ); 144 boolCompTern++; 146 147 ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompTern ); 148 super.visit( node, data ); 149 } 150 return data; 151 } 152 153 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { 154 if ( node.isInterface() ) { 155 return data; 156 } 157 158 entryStack.push( new Entry( node ) ); 159 super.visit( node, data ); 160 Entry classEntry = (Entry) entryStack.pop(); 161 if ( ( classEntry.getComplexityAverage() >= reportLevel ) 162 || ( classEntry.highestDecisionPoints >= reportLevel ) ) { 163 addViolation( data, node, new String [] { 164 "class", 165 node.getImage(), 166 classEntry.getComplexityAverage() + " (Highest = " 167 + classEntry.highestDecisionPoints + ')' } ); 168 } 169 return data; 170 } 171 172 public Object visit(ASTMethodDeclaration node, Object data) { 173 entryStack.push( new Entry( node ) ); 174 super.visit( node, data ); 175 Entry methodEntry = (Entry) entryStack.pop(); 176 int methodDecisionPoints = methodEntry.decisionPoints; 177 Entry classEntry = (Entry) entryStack.peek(); 178 classEntry.methodCount++; 179 classEntry.bumpDecisionPoints( methodDecisionPoints ); 180 181 if ( methodDecisionPoints > classEntry.highestDecisionPoints ) { 182 classEntry.highestDecisionPoints = methodDecisionPoints; 183 } 184 185 ASTMethodDeclarator methodDeclarator = null; 186 for ( int n = 0; n < node.jjtGetNumChildren(); n++ ) { 187 Node childNode = node.jjtGetChild( n ); 188 if ( childNode instanceof ASTMethodDeclarator ) { 189 methodDeclarator = (ASTMethodDeclarator) childNode; 190 break; 191 } 192 } 193 194 if ( methodEntry.decisionPoints >= reportLevel ) { 195 addViolation( data, node, new String [] { "method", 196 ( methodDeclarator == null ) ? "" : methodDeclarator.getImage(), 197 String.valueOf( methodEntry.decisionPoints ) } ); 198 } 199 200 return data; 201 } 202 203 public Object visit(ASTEnumDeclaration node, Object data) { 204 entryStack.push( new Entry( node ) ); 205 super.visit( node, data ); 206 Entry classEntry = (Entry) entryStack.pop(); 207 if ( ( classEntry.getComplexityAverage() >= reportLevel ) 208 || ( classEntry.highestDecisionPoints >= reportLevel ) ) { 209 addViolation( data, node, new String [] { 210 "class", 211 node.getImage(), 212 classEntry.getComplexityAverage() + "(Highest = " 213 + classEntry.highestDecisionPoints + ')' } ); 214 } 215 return data; 216 } 217 218 public Object visit(ASTConstructorDeclaration node, Object data) { 219 entryStack.push( new Entry( node ) ); 220 super.visit( node, data ); 221 Entry constructorEntry = (Entry) entryStack.pop(); 222 int constructorDecisionPointCount = constructorEntry.decisionPoints; 223 Entry classEntry = (Entry) entryStack.peek(); 224 classEntry.methodCount++; 225 classEntry.decisionPoints += constructorDecisionPointCount; 226 if ( constructorDecisionPointCount > classEntry.highestDecisionPoints ) { 227 classEntry.highestDecisionPoints = constructorDecisionPointCount; 228 } 229 if ( constructorEntry.decisionPoints >= reportLevel ) { 230 addViolation( data, node, new String [] { "constructor", 231 classEntry.node.getImage(), 232 String.valueOf( constructorDecisionPointCount ) } ); 233 } 234 return data; 235 } 236 237 } 238 | Popular Tags |