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 net.sourceforge.pmd.AbstractRule; 7 import net.sourceforge.pmd.ast.ASTBlockStatement; 8 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration; 9 import net.sourceforge.pmd.ast.ASTConstructorDeclaration; 10 import net.sourceforge.pmd.ast.ASTEnumDeclaration; 11 import net.sourceforge.pmd.ast.ASTForStatement; 12 import net.sourceforge.pmd.ast.ASTIfStatement; 13 import net.sourceforge.pmd.ast.ASTMethodDeclaration; 14 import net.sourceforge.pmd.ast.ASTMethodDeclarator; 15 import net.sourceforge.pmd.ast.ASTSwitchLabel; 16 import net.sourceforge.pmd.ast.ASTSwitchStatement; 17 import net.sourceforge.pmd.ast.ASTWhileStatement; 18 import net.sourceforge.pmd.ast.Node; 19 import net.sourceforge.pmd.ast.SimpleNode; 20 21 import java.util.Stack; 22 23 /*** 24 * @author Donald A. Leckie 25 * @version $Revision: 1.11 $, $Date: 2006/03/29 13:58:50 $ 26 * @since January 14, 2003 27 */ 28 public class CyclomaticComplexity extends AbstractRule { 29 30 private static class Entry { 31 private SimpleNode node; 32 private int decisionPoints = 1; 33 public int highestDecisionPoints; 34 public int methodCount; 35 36 private Entry(SimpleNode node) { 37 this.node = node; 38 } 39 40 public void bumpDecisionPoints() { 41 decisionPoints++; 42 } 43 44 public void bumpDecisionPoints(int size) { 45 decisionPoints += size; 46 } 47 48 public int getComplexityAverage() { 49 return ((double) methodCount == 0) ? 1 : (int) (Math.rint((double) decisionPoints / (double) methodCount)); 50 } 51 } 52 53 private Stack entryStack = new Stack(); 54 55 public Object visit(ASTIfStatement node, Object data) { 56 ((Entry) entryStack.peek()).bumpDecisionPoints(); 57 super.visit(node, data); 58 return data; 59 } 60 61 public Object visit(ASTForStatement node, Object data) { 62 ((Entry) entryStack.peek()).bumpDecisionPoints(); 63 super.visit(node, data); 64 return data; 65 } 66 67 public Object visit(ASTSwitchStatement node, Object data) { 68 Entry entry = (Entry) entryStack.peek(); 69 int childCount = node.jjtGetNumChildren(); 70 int lastIndex = childCount - 1; 71 for (int n = 0; n < lastIndex; n++) { 72 Node childNode = node.jjtGetChild(n); 73 if (childNode instanceof ASTSwitchLabel) { 74 childNode = node.jjtGetChild(n + 1); 75 if (childNode instanceof ASTBlockStatement) { 76 entry.bumpDecisionPoints(); 77 } 78 } 79 } 80 super.visit(node, data); 81 return data; 82 } 83 84 public Object visit(ASTWhileStatement node, Object data) { 85 ((Entry) entryStack.peek()).bumpDecisionPoints(); 86 super.visit(node, data); 87 return data; 88 } 89 90 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { 91 if (node.isInterface()) { 92 return data; 93 } 94 95 entryStack.push(new Entry(node)); 96 super.visit(node, data); 97 Entry classEntry = (Entry) entryStack.pop(); 98 if ((classEntry.getComplexityAverage() >= getIntProperty("reportLevel")) || (classEntry.highestDecisionPoints >= getIntProperty("reportLevel"))) { 99 addViolation(data, node, new String[]{"class", node.getImage(), String.valueOf(classEntry.getComplexityAverage()) + " (Highest = " + String.valueOf(classEntry.highestDecisionPoints) + ")"}); 100 } 101 return data; 102 } 103 104 public Object visit(ASTMethodDeclaration node, Object data) { 105 entryStack.push(new Entry(node)); 106 super.visit(node, data); 107 Entry methodEntry = (Entry) entryStack.pop(); 108 int methodDecisionPoints = methodEntry.decisionPoints; 109 Entry classEntry = (Entry) entryStack.peek(); 110 classEntry.methodCount++; 111 classEntry.bumpDecisionPoints(methodDecisionPoints); 112 113 if (methodDecisionPoints > classEntry.highestDecisionPoints) { 114 classEntry.highestDecisionPoints = methodDecisionPoints; 115 } 116 117 ASTMethodDeclarator methodDeclarator = null; 118 for (int n = 0; n < node.jjtGetNumChildren(); n++) { 119 Node childNode = node.jjtGetChild(n); 120 if (childNode instanceof ASTMethodDeclarator) { 121 methodDeclarator = (ASTMethodDeclarator) childNode; 122 break; 123 } 124 } 125 126 if (methodEntry.decisionPoints >= getIntProperty("reportLevel")) { 127 addViolation(data, node, new String[]{"method", (methodDeclarator == null) ? "" : methodDeclarator.getImage(), String.valueOf(methodEntry.decisionPoints)}); 128 } 129 130 return data; 131 } 132 133 public Object visit(ASTEnumDeclaration node, Object data) { 134 entryStack.push(new Entry(node)); 135 super.visit(node, data); 136 Entry classEntry = (Entry) entryStack.pop(); 137 if ((classEntry.getComplexityAverage() >= getIntProperty("reportLevel")) || (classEntry.highestDecisionPoints >= getIntProperty("reportLevel"))) { 138 addViolation(data, node, new String[]{"class", node.getImage(), String.valueOf(classEntry.getComplexityAverage()) + "(Highest = " + String.valueOf(classEntry.highestDecisionPoints) + ")"}); 139 } 140 return data; 141 } 142 143 public Object visit(ASTConstructorDeclaration node, Object data) { 144 entryStack.push(new Entry(node)); 145 super.visit(node, data); 146 Entry constructorEntry = (Entry) entryStack.pop(); 147 int constructorDecisionPointCount = constructorEntry.decisionPoints; 148 Entry classEntry = (Entry) entryStack.peek(); 149 classEntry.methodCount++; 150 classEntry.decisionPoints += constructorDecisionPointCount; 151 if (constructorDecisionPointCount > classEntry.highestDecisionPoints) { 152 classEntry.highestDecisionPoints = constructorDecisionPointCount; 153 } 154 if (constructorEntry.decisionPoints >= getIntProperty("reportLevel")) { 155 addViolation(data, node, new String[]{"constructor", classEntry.node.getImage(), String.valueOf(constructorDecisionPointCount)}); 156 } 157 return data; 158 } 159 160 }