Clover coverage report - PMD - 3.9 Coverage timestamp: Tue Dec 19 2006 09:38:44 EST FRAMES NO FRAMES |
file stats: | LOC: | 294 |  | Methods: | 14
| NCLOC: | 168 |  | Classes: | 1
Source file | Conditionals | Statements | Methods | TOTAL
NpathComplexity.java | 62.5% | 70% | 85.7% | 69.9% |

1 |  | package net.sourceforge.pmd.rules.design;
2 |  |
3 |  | import java.util.ArrayList;
4 |  | import java.util.Iterator;
5 |  | import java.util.List;
6 |  | import java.util.Set;
7 |  |
8 |  | import net.sourceforge.pmd.RuleContext;
9 |  | import net.sourceforge.pmd.ast.ASTConditionalAndExpression;
10 |  | import net.sourceforge.pmd.ast.ASTConditionalExpression;
11 |  | import net.sourceforge.pmd.ast.ASTConditionalOrExpression;
12 |  | import net.sourceforge.pmd.ast.ASTDoStatement;
13 |  | import net.sourceforge.pmd.ast.ASTExpression;
14 |  | import net.sourceforge.pmd.ast.ASTForStatement;
15 |  | import net.sourceforge.pmd.ast.ASTIfStatement;
16 |  | import net.sourceforge.pmd.ast.ASTMethodDeclaration;
17 |  | import net.sourceforge.pmd.ast.ASTReturnStatement;
18 |  | import net.sourceforge.pmd.ast.ASTStatement;
19 |  | import net.sourceforge.pmd.ast.ASTSwitchLabel;
20 |  | import net.sourceforge.pmd.ast.ASTSwitchStatement;
21 |  | import net.sourceforge.pmd.ast.ASTTryStatement;
22 |  | import net.sourceforge.pmd.ast.ASTWhileStatement;
23 |  | import net.sourceforge.pmd.ast.SimpleJavaNode;
24 |  | import net.sourceforge.pmd.stat.DataPoint;
25 |  | import net.sourceforge.pmd.stat.StatisticalRule;
26 |  | import net.sourceforge.pmd.util.NumericConstants;
27 |  |
28 |  | /**
29 |  | * NPath complexity is a measurement of the acyclic execution paths through a
30 |  | * function. See Nejmeh, Communications of the ACM Feb 1988 pp 188-200.
31 |  | *
32 |  | * @author Jason Bennett
33 |  | */
34 |  | public class NpathComplexity extends StatisticalRule {
35 |  |
36 |  |
37 | 200 | private int complexityMultipleOf(SimpleJavaNode node, int npathStart, Object data) {
38 |  |
39 | 200 | int npath = npathStart;
40 | 200 | SimpleJavaNode simpleNode;
41 |  |
42 | 200 | for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
43 | 198 | simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
44 | 198 | npath *= ((Integer) simpleNode.jjtAccept( this, data )).intValue();
45 |  | }
46 |  |
47 | 200 | return npath;
48 |  | }
49 |  |
50 | 1 | private int complexitySumOf(SimpleJavaNode node, int npathStart, Object data) {
51 |  |
52 | 1 | int npath = npathStart;
53 | 1 | SimpleJavaNode simpleNode;
54 |  |
55 | 1 | for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
56 | 2 | simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
57 | 2 | npath += ((Integer) simpleNode.jjtAccept( this, data )).intValue();
58 |  | }
59 |  |
60 | 1 | return npath;
61 |  | }
62 |  |
63 | 2 | public Object visit(ASTMethodDeclaration node, Object data) {
64 |  |
65 |  | // int npath = 1;
66 |  | //
67 |  | // // Basic NPath functionality multiplies the complexity of peer nodes
68 |  | // for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
69 |  | // SimpleJavaNode simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
70 |  | // Integer complexity = (Integer) simpleNode.jjtAccept( this, data );
71 |  | // npath *= complexity.intValue();
72 |  | // }
73 |  |
74 | 2 | int npath = complexityMultipleOf(node, 1, data);
75 |  |
76 | 2 | DataPoint point = new DataPoint();
77 | 2 | point.setNode( node );
78 | 2 | point.setScore( 1.0 * npath );
79 | 2 | point.setMessage( getMessage() );
80 | 2 | addDataPoint( point );
81 |  |
82 | 2 | return new Integer( npath );
83 |  | }
84 |  |
85 | 198 | public Object visit(SimpleJavaNode node, Object data) {
86 |  | // int npath = 1;
87 |  | //
88 |  | // for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
89 |  | // SimpleJavaNode simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
90 |  | // Integer complexity = (Integer) simpleNode.jjtAccept( this, data );
91 |  | // npath *= complexity.intValue();
92 |  | // }
93 |  |
94 | 198 | int npath = complexityMultipleOf(node, 1, data);
95 |  |
96 | 198 | return new Integer( npath );
97 |  | }
98 |  |
99 | 7 | public Object visit(ASTIfStatement node, Object data) {
100 |  | // (npath of if + npath of else (or 1) + bool_comp of if) * npath of next
101 |  |
102 | 7 | int boolCompIf = sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
103 |  |
104 | 7 | int complexity = 0;
105 |  |
106 | 7 | List statementChildren = new ArrayList();
107 | 7 | for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
108 | 15 | if ( node.jjtGetChild( i ).getClass() == ASTStatement.class ) {
109 | 8 | statementChildren.add( node.jjtGetChild( i ) );
110 |  | }
111 |  | }
112 |  |
113 | 7 | if ( statementChildren.isEmpty()
114 |  | || ( statementChildren.size() == 1 && node.hasElse() )
115 |  | || ( statementChildren.size() != 1 && !node.hasElse() ) ) {
116 | 0 | throw new IllegalStateException( "If node has wrong number of children" );
117 |  | }
118 |  |
119 |  | // add path for not taking if
120 | 7 | if ( !node.hasElse() ) {
121 | 6 | complexity++;
122 |  | }
123 |  |
124 | 7 | for ( Iterator iter = statementChildren.iterator(); iter.hasNext(); ) {
125 | 8 | SimpleJavaNode element = (SimpleJavaNode) iter.next();
126 | 8 | complexity += ( (Integer) element.jjtAccept( this, data ) ).intValue();
127 |  | }
128 |  |
129 | 7 | return new Integer( boolCompIf + complexity );
130 |  | }
131 |  |
132 | 1 | public Object visit(ASTWhileStatement node, Object data) {
133 |  | // (npath of while + bool_comp of while + 1) * npath of next
134 |  |
135 | 1 | int boolCompWhile = sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
136 |  |
137 | 1 | Integer nPathWhile = (Integer) ( (SimpleJavaNode) node.getFirstChildOfType( ASTStatement.class ) ).jjtAccept(
138 |  | this, data );
139 |  |
140 | 1 | return new Integer( boolCompWhile + nPathWhile.intValue() + 1 );
141 |  | }
142 |  |
143 | 1 | public Object visit(ASTDoStatement node, Object data) {
144 |  | // (npath of do + bool_comp of do + 1) * npath of next
145 |  |
146 | 1 | int boolCompDo = sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
147 |  |
148 | 1 | Integer nPathDo = (Integer) ( (SimpleJavaNode) node.getFirstChildOfType( ASTStatement.class ) ).jjtAccept(
149 |  | this, data );
150 |  |
151 | 1 | return new Integer( boolCompDo + nPathDo.intValue() + 1 );
152 |  | }
153 |  |
154 | 1 | public Object visit(ASTForStatement node, Object data) {
155 |  | // (npath of for + bool_comp of for + 1) * npath of next
156 |  |
157 | 1 | int boolCompFor = sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
158 |  |
159 | 1 | Integer nPathFor = (Integer) ( (SimpleJavaNode) node.getFirstChildOfType( ASTStatement.class ) ).jjtAccept(
160 |  | this, data );
161 |  |
162 | 1 | return new Integer( boolCompFor + nPathFor.intValue() + 1 );
163 |  | }
164 |  |
165 | 2 | public Object visit(ASTReturnStatement node, Object data) {
166 |  | // return statements are valued at 1, or the value of the boolean expression
167 |  |
168 | 2 | ASTExpression expr = (ASTExpression) node.getFirstChildOfType( ASTExpression.class );
169 |  |
170 | 2 | if ( expr == null ) {
171 | 0 | return NumericConstants.ONE;
172 |  | }
173 |  |
174 | 2 | List andNodes = expr.findChildrenOfType( ASTConditionalAndExpression.class );
175 | 2 | List orNodes = expr.findChildrenOfType( ASTConditionalOrExpression.class );
176 | 2 | int boolCompReturn = andNodes.size() + orNodes.size();
177 |  |
178 | 2 | if ( boolCompReturn > 0 ) {
179 | 0 | return new Integer( boolCompReturn );
180 |  | }
181 | 2 | return NumericConstants.ONE;
182 |  | }
183 |  |
184 | 0 | public Object visit(ASTSwitchStatement node, Object data) {
185 |  | // bool_comp of switch + sum(npath(case_range))
186 |  |
187 | 0 | int boolCompSwitch = sumExpressionComplexity( (ASTExpression) node.getFirstChildOfType( ASTExpression.class ) );
188 |  |
189 | 0 | int npath = 0;
190 | 0 | int caseRange = 0;
191 | 0 | for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
192 | 0 | SimpleJavaNode simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
193 |  |
194 |  | // Fall-through labels count as 1 for complexity
195 | 0 | if ( simpleNode instanceof ASTSwitchLabel ) {
196 | 0 | npath += caseRange;
197 | 0 | caseRange = 1;
198 |  | } else {
199 | 0 | Integer complexity = (Integer) simpleNode.jjtAccept( this, data );
200 | 0 | caseRange *= complexity.intValue();
201 |  | }
202 |  | }
203 |  | // add in npath of last label
204 | 0 | npath += caseRange;
205 | 0 | return new Integer( boolCompSwitch + npath );
206 |  | }
207 |  |
208 | 1 | public Object visit(ASTTryStatement node, Object data) {
209 |  | /*
210 |  | * This scenario was not addressed by the original paper. Based on the
211 |  | * principles outlined in the paper, as well as the Checkstyle NPath
212 |  | * implementation, this code will add the complexity of the try to the
213 |  | * complexities of the catch and finally blocks.
214 |  | */
215 |  |
216 |  | // int npath = 0;
217 |  | //
218 |  | // for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
219 |  | // SimpleJavaNode simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
220 |  | // Integer complexity = (Integer) simpleNode.jjtAccept( this, data );
221 |  | // npath += complexity.intValue();
222 |  | // }
223 |  |
224 | 1 | int npath = complexitySumOf(node, 0, data);
225 |  |
226 | 1 | return new Integer( npath );
227 |  |
228 |  | }
229 |  |
230 | 0 | public Object visit(ASTConditionalExpression node, Object data) {
231 | 0 | if ( node.isTernary() ) {
232 |  | // int npath = 0;
233 |  | //
234 |  | // for ( int i = 0; i < node.jjtGetNumChildren(); i++ ) {
235 |  | // SimpleJavaNode simpleNode = (SimpleJavaNode) node.jjtGetChild( i );
236 |  | // Integer complexity = (Integer) simpleNode.jjtAccept( this, data );
237 |  | // npath += complexity.intValue();
238 |  | // }
239 | 0 | int npath = complexitySumOf(node, 0, data);
240 |  |
241 | 0 | npath += 2;
242 | 0 | return new Integer( npath );
243 |  | }
244 | 0 | return NumericConstants.ONE;
245 |  | }
246 |  |
247 |  | /**
248 |  | * Calculate the boolean complexity of the given expression. NPath boolean
249 |  | * complexity is the sum of && and || tokens. This is calculated by summing
250 |  | * the number of children of the &&'s (minus one) and the children of the ||'s
251 |  | * (minus one).
252 |  | * <p>
253 |  | * Note that this calculation applies to Cyclomatic Complexity as well.
254 |  | *
255 |  | * @param expr
256 |  | * control structure expression
257 |  | * @return complexity of the boolean expression
258 |  | */
259 | 18 | public static int sumExpressionComplexity(ASTExpression expr) {
260 | 18 | if (expr == null) {
261 | 0 | return 0;
262 |  | }
263 |  |
264 | 18 | List andNodes = expr.findChildrenOfType( ASTConditionalAndExpression.class );
265 | 18 | List orNodes = expr.findChildrenOfType( ASTConditionalOrExpression.class );
266 |  |
267 | 18 | int children = 0;
268 |  |
269 | 18 | for ( Iterator iter = orNodes.iterator(); iter.hasNext(); ) {
270 | 0 | ASTConditionalOrExpression element = (ASTConditionalOrExpression) iter.next();
271 | 0 | children += element.jjtGetNumChildren();
272 | 0 | children--;
273 |  | }
274 |  |
275 | 18 | for ( Iterator iter = andNodes.iterator(); iter.hasNext(); ) {
276 | 0 | ASTConditionalAndExpression element = (ASTConditionalAndExpression) iter.next();
277 | 0 | children += element.jjtGetNumChildren();
278 | 0 | children--;
279 |  | }
280 |  |
281 | 18 | return children;
282 |  | }
283 |  |
284 | 2 | protected void makeViolations(RuleContext ctx, Set p) {
285 | 2 | Iterator points = p.iterator();
286 | 2 | while ( points.hasNext() ) {
287 | 1 | DataPoint point = (DataPoint) points.next();
288 | 1 | addViolation( ctx, point.getNode(), new String[] {
289 |  | ( (ASTMethodDeclaration) point.getNode() ).getMethodName(),
290 |  | String.valueOf( (int) point.getScore() ) } );
291 |  | }
292 |  | }
293 |  |
294 |  | }