001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.metrics; 021 022import java.math.BigInteger; 023import java.util.ArrayDeque; 024import java.util.Deque; 025 026import com.puppycrawl.tools.checkstyle.api.Check; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029 030/** 031 * Base class for checks the calculate complexity based around methods. 032 * 033 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 034 * @author Oliver Burn 035 */ 036public abstract class AbstractComplexityCheck 037 extends Check { 038 /** The initial current value. */ 039 private static final BigInteger INITIAL_VALUE = BigInteger.ONE; 040 041 /** Stack of values - all but the current value. */ 042 private final Deque<BigInteger> valueStack = new ArrayDeque<>(); 043 044 /** The current value. */ 045 private BigInteger currentValue = BigInteger.ZERO; 046 047 /** Threshold to report error for. */ 048 private int max; 049 050 /** 051 * Creates an instance. 052 * @param max the threshold of when to report an error 053 */ 054 protected AbstractComplexityCheck(int max) { 055 this.max = max; 056 } 057 058 /** 059 * Gets the message ID to log violations with. 060 * @return the message ID to log violations with 061 */ 062 protected abstract String getMessageID(); 063 064 @Override 065 public final int[] getRequiredTokens() { 066 return new int[] { 067 TokenTypes.CTOR_DEF, 068 TokenTypes.METHOD_DEF, 069 TokenTypes.INSTANCE_INIT, 070 TokenTypes.STATIC_INIT, 071 }; 072 } 073 074 /** 075 * Set the maximum threshold allowed. 076 * 077 * @param max the maximum threshold 078 */ 079 public final void setMax(int max) { 080 this.max = max; 081 } 082 083 @Override 084 public void visitToken(DetailAST ast) { 085 switch (ast.getType()) { 086 case TokenTypes.CTOR_DEF: 087 case TokenTypes.METHOD_DEF: 088 case TokenTypes.INSTANCE_INIT: 089 case TokenTypes.STATIC_INIT: 090 visitMethodDef(); 091 break; 092 default: 093 visitTokenHook(ast); 094 } 095 } 096 097 @Override 098 public void leaveToken(DetailAST ast) { 099 switch (ast.getType()) { 100 case TokenTypes.CTOR_DEF: 101 case TokenTypes.METHOD_DEF: 102 case TokenTypes.INSTANCE_INIT: 103 case TokenTypes.STATIC_INIT: 104 leaveMethodDef(ast); 105 break; 106 default: 107 leaveTokenHook(ast); 108 } 109 } 110 111 /** 112 * Hook called when visiting a token. Will not be called the method 113 * definition tokens. 114 * 115 * @param ast the token being visited 116 */ 117 protected void visitTokenHook(DetailAST ast) { 118 // no code 119 } 120 121 /** 122 * Hook called when leaving a token. Will not be called the method 123 * definition tokens. 124 * 125 * @param ast the token being left 126 */ 127 protected void leaveTokenHook(DetailAST ast) { 128 // no code 129 } 130 131 /** 132 * Gets the current value. 133 * @return the current value 134 */ 135 protected final BigInteger getCurrentValue() { 136 return currentValue; 137 } 138 139 /** 140 * Set the current value. 141 * @param value the new value 142 */ 143 protected final void setCurrentValue(BigInteger value) { 144 currentValue = value; 145 } 146 147 /** 148 * Increments the current value by a specified amount. 149 * 150 * @param by the amount to increment by 151 */ 152 protected final void incrementCurrentValue(BigInteger by) { 153 currentValue = currentValue.add(by); 154 } 155 156 /** Push the current value on the stack. */ 157 protected final void pushValue() { 158 valueStack.push(currentValue); 159 currentValue = INITIAL_VALUE; 160 } 161 162 /** 163 * Pops a value off the stack and makes it the current value. 164 * @return pop a value off the stack and make it the current value 165 */ 166 protected final BigInteger popValue() { 167 currentValue = valueStack.pop(); 168 return currentValue; 169 } 170 171 /** Process the start of the method definition. */ 172 private void visitMethodDef() { 173 pushValue(); 174 } 175 176 /** 177 * Process the end of a method definition. 178 * 179 * @param ast the token representing the method definition 180 */ 181 private void leaveMethodDef(DetailAST ast) { 182 final BigInteger bigIntegerMax = BigInteger.valueOf(max); 183 if (currentValue.compareTo(bigIntegerMax) > 0) { 184 log(ast, getMessageID(), currentValue, bigIntegerMax); 185 } 186 popValue(); 187 } 188}