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