View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2015 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  package com.puppycrawl.tools.checkstyle.checks.metrics;
20  
21  import com.puppycrawl.tools.checkstyle.api.Check;
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.api.FastStack;
24  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25  import java.math.BigInteger;
26  
27  /**
28   * Base class for checks the calculate complexity based around methods.
29   *
30   * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
31   * @author Oliver Burn
32   */
33  public abstract class AbstractComplexityCheck
34      extends Check
35  {
36      /** the initial current value */
37      private static final BigInteger INITIAL_VALUE = BigInteger.ONE;
38  
39      /** stack of values - all but the current value */
40      private final FastStack<BigInteger> valueStack = FastStack.newInstance();
41  
42      /** the current value */
43      private BigInteger currentValue = BigInteger.ZERO;
44  
45      /** threshold to report error for */
46      private int max;
47  
48      /**
49       * Creates an instance.
50       * @param max the threshold of when to report an error
51       */
52      public AbstractComplexityCheck(int max)
53      {
54          this.max = max;
55      }
56  
57      /**
58       * @return the message ID to log violations with
59       */
60      protected abstract String getMessageID();
61  
62      /**
63       * Hook called when visiting a token. Will not be called the method
64       * definition tokens.
65       *
66       * @param ast the token being visited
67       */
68      protected void visitTokenHook(DetailAST ast)
69      {
70      }
71  
72      /**
73       * Hook called when leaving a token. Will not be called the method
74       * definition tokens.
75       *
76       * @param ast the token being left
77       */
78      protected void leaveTokenHook(DetailAST ast)
79      {
80      }
81  
82      @Override
83      public final int[] getRequiredTokens()
84      {
85          return new int[] {
86              TokenTypes.CTOR_DEF,
87              TokenTypes.METHOD_DEF,
88              TokenTypes.INSTANCE_INIT,
89              TokenTypes.STATIC_INIT,
90          };
91      }
92  
93      /** @return the maximum threshold allowed */
94      public final int getMax()
95      {
96          return max;
97      }
98  
99      /**
100      * Set the maximum threshold allowed.
101      *
102      * @param max the maximum threshold
103      */
104     public final void setMax(int max)
105     {
106         this.max = max;
107     }
108 
109     @Override
110     public void visitToken(DetailAST ast)
111     {
112         switch (ast.getType()) {
113             case TokenTypes.CTOR_DEF:
114             case TokenTypes.METHOD_DEF:
115             case TokenTypes.INSTANCE_INIT:
116             case TokenTypes.STATIC_INIT:
117                 visitMethodDef();
118                 break;
119             default:
120                 visitTokenHook(ast);
121         }
122     }
123 
124     @Override
125     public void leaveToken(DetailAST ast)
126     {
127         switch (ast.getType()) {
128             case TokenTypes.CTOR_DEF:
129             case TokenTypes.METHOD_DEF:
130             case TokenTypes.INSTANCE_INIT:
131             case TokenTypes.STATIC_INIT:
132                 leaveMethodDef(ast);
133                 break;
134             default:
135                 leaveTokenHook(ast);
136         }
137     }
138 
139     /**
140      * @return the current value
141      */
142     protected final BigInteger getCurrentValue()
143     {
144         return currentValue;
145     }
146 
147     /**
148      * Set the current value
149      * @param value the new value
150      */
151     protected final void setCurrentValue(BigInteger value)
152     {
153         currentValue = value;
154     }
155 
156     /**
157      * Increments the current value by a specified amount.
158      *
159      * @param by the amount to increment by
160      */
161     protected final void incrementCurrentValue(BigInteger by)
162     {
163         setCurrentValue(getCurrentValue().add(by));
164     }
165 
166     /** Push the current value on the stack */
167     protected final void pushValue()
168     {
169         valueStack.push(currentValue);
170         currentValue = INITIAL_VALUE;
171     }
172 
173     /**
174      * @return pop a value off the stack and make it the current value
175      */
176     protected final BigInteger popValue()
177     {
178         currentValue = valueStack.pop();
179         return currentValue;
180     }
181 
182     /** Process the start of the method definition */
183     private void visitMethodDef()
184     {
185         pushValue();
186     }
187 
188     /**
189      * Process the end of a method definition.
190      *
191      * @param ast the token representing the method definition
192      */
193     private void leaveMethodDef(DetailAST ast)
194     {
195         final BigInteger bigIntegerMax = BigInteger.valueOf(max);
196         if (currentValue.compareTo(bigIntegerMax) > 0) {
197             log(ast, getMessageID(), currentValue, bigIntegerMax);
198         }
199         popValue();
200     }
201 }