KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > pmd > rules > CyclomaticComplexity


1 /**
2  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3  */

4 package net.sourceforge.pmd.rules;
5
6 import java.util.Stack JavaDoc;
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 /**
30  * @author Donald A. Leckie
31  * @version $Revision: 1.18 $, $Date: 2006/10/16 13:25:23 $
32  * @since January 14, 2003
33  */

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 JavaDoc entryStack = new Stack JavaDoc();
63
64   public Object JavaDoc visit(ASTCompilationUnit node, Object JavaDoc data) {
65     reportLevel = getIntProperty( "reportLevel" );
66     super.visit( node, data );
67     return data;
68   }
69
70   public Object JavaDoc visit(ASTIfStatement node, Object JavaDoc data) {
71     int boolCompIf = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
72     // If statement always has a complexity of at least 1
73
boolCompIf++;
74
75     ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompIf );
76     super.visit( node, data );
77     return data;
78   }
79
80   public Object JavaDoc visit(ASTCatchStatement node, Object JavaDoc data) {
81     ( (Entry) entryStack.peek() ).bumpDecisionPoints();
82     super.visit( node, data );
83     return data;
84   }
85
86   public Object JavaDoc visit(ASTForStatement node, Object JavaDoc data) {
87     int boolCompFor = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
88     // For statement always has a complexity of at least 1
89
boolCompFor++;
90
91     ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompFor );
92     super.visit( node, data );
93     return data;
94   }
95
96   public Object JavaDoc visit(ASTDoStatement node, Object JavaDoc data) {
97     int boolCompDo = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
98     // Do statement always has a complexity of at least 1
99
boolCompDo++;
100
101     ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompDo );
102     super.visit( node, data );
103     return data;
104   }
105
106   public Object JavaDoc visit(ASTSwitchStatement node, Object JavaDoc 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         // default is generally not considered a decision (same as "else")
118
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 JavaDoc visit(ASTWhileStatement node, Object JavaDoc data) {
132     int boolCompWhile = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
133     // While statement always has a complexity of at least 1
134
boolCompWhile++;
135
136     ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompWhile );
137     super.visit( node, data );
138     return data;
139   }
140
141   public Object JavaDoc visit(ASTConditionalExpression node, Object JavaDoc data) {
142     if ( node.isTernary() ) {
143       int boolCompTern = NpathComplexity.sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
144       // Ternary statement always has a complexity of at least 1
145
boolCompTern++;
146
147       ( (Entry) entryStack.peek() ).bumpDecisionPoints( boolCompTern );
148       super.visit( node, data );
149     }
150     return data;
151   }
152
153   public Object JavaDoc visit(ASTClassOrInterfaceDeclaration node, Object JavaDoc 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 JavaDoc[] {
164           "class",
165           node.getImage(),
166           classEntry.getComplexityAverage() + " (Highest = "
167               + classEntry.highestDecisionPoints + ')' } );
168     }
169     return data;
170   }
171
172   public Object JavaDoc visit(ASTMethodDeclaration node, Object JavaDoc 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 JavaDoc[] { "method",
196           ( methodDeclarator == null ) ? "" : methodDeclarator.getImage(),
197           String.valueOf( methodEntry.decisionPoints ) } );
198     }
199
200     return data;
201   }
202
203   public Object JavaDoc visit(ASTEnumDeclaration node, Object JavaDoc 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 JavaDoc[] {
210           "class",
211           node.getImage(),
212           classEntry.getComplexityAverage() + "(Highest = "
213               + classEntry.highestDecisionPoints + ')' } );
214     }
215     return data;
216   }
217
218   public Object JavaDoc visit(ASTConstructorDeclaration node, Object JavaDoc 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 JavaDoc[] { "constructor",
231           classEntry.node.getImage(),
232           String.valueOf( constructorDecisionPointCount ) } );
233     }
234     return data;
235   }
236
237 }
238
Popular Tags