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 java.math.BigInteger;
22  
23  import com.puppycrawl.tools.checkstyle.api.DetailAST;
24  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25  
26  /**
27   * Checks the npath complexity against a specified limit (default = 200).
28   * The npath metric computes the number of possible execution paths
29   * through a function. Similar to the cyclomatic complexity but also
30   * takes into account the nesting of conditional statements and
31   * multi-part boolean expressions.
32   *
33   * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
34   * @author o_sukhodolsky
35   * TODO: For every or: _value += (_orCount * (nestedValue - 1));
36   * TODO: For every and: ???
37   */
38  public final class NPathComplexityCheck extends AbstractComplexityCheck
39  {
40  
41      /**
42       * A key is pointing to the warning message text in "messages.properties"
43       * file.
44       */
45      public static final String MSG_KEY = "npathComplexity";
46  
47      /** Default allowed complexity. */
48      private static final int DEFAULT_MAX = 200;
49  
50      /** Creates new instance of the check. */
51      public NPathComplexityCheck()
52      {
53          super(DEFAULT_MAX);
54      }
55  
56      @Override
57      public int[] getDefaultTokens()
58      {
59          return new int[] {
60              TokenTypes.CTOR_DEF,
61              TokenTypes.METHOD_DEF,
62              TokenTypes.STATIC_INIT,
63              TokenTypes.INSTANCE_INIT,
64              TokenTypes.LITERAL_WHILE,
65              TokenTypes.LITERAL_DO,
66              TokenTypes.LITERAL_FOR,
67              TokenTypes.LITERAL_IF,
68              TokenTypes.LITERAL_ELSE,
69              TokenTypes.LITERAL_SWITCH,
70              TokenTypes.LITERAL_CASE,
71              TokenTypes.LITERAL_TRY,
72              TokenTypes.LITERAL_CATCH,
73              TokenTypes.QUESTION,
74          };
75      }
76  
77      @Override
78      public int[] getAcceptableTokens()
79      {
80          return new int[] {
81              TokenTypes.CTOR_DEF,
82              TokenTypes.METHOD_DEF,
83              TokenTypes.STATIC_INIT,
84              TokenTypes.INSTANCE_INIT,
85              TokenTypes.LITERAL_WHILE,
86              TokenTypes.LITERAL_DO,
87              TokenTypes.LITERAL_FOR,
88              TokenTypes.LITERAL_IF,
89              TokenTypes.LITERAL_ELSE,
90              TokenTypes.LITERAL_SWITCH,
91              TokenTypes.LITERAL_CASE,
92              TokenTypes.LITERAL_TRY,
93              TokenTypes.LITERAL_CATCH,
94              TokenTypes.QUESTION,
95          };
96      }
97  
98      @Override
99      public void visitToken(DetailAST ast)
100     {
101         switch (ast.getType()) {
102             case TokenTypes.LITERAL_WHILE:
103             case TokenTypes.LITERAL_DO:
104             case TokenTypes.LITERAL_FOR:
105             case TokenTypes.LITERAL_IF:
106             case TokenTypes.QUESTION:
107             case TokenTypes.LITERAL_TRY:
108             case TokenTypes.LITERAL_SWITCH:
109                 visitMultiplyingConditional();
110                 break;
111             case TokenTypes.LITERAL_ELSE:
112             case TokenTypes.LITERAL_CATCH:
113             case TokenTypes.LITERAL_CASE:
114                 visitAddingConditional();
115                 break;
116             default:
117                 super.visitToken(ast);
118         }
119     }
120 
121     @Override
122     public void leaveToken(DetailAST ast)
123     {
124         switch (ast.getType()) {
125             case TokenTypes.LITERAL_WHILE:
126             case TokenTypes.LITERAL_DO:
127             case TokenTypes.LITERAL_FOR:
128             case TokenTypes.LITERAL_IF:
129             case TokenTypes.QUESTION:
130             case TokenTypes.LITERAL_TRY:
131             case TokenTypes.LITERAL_SWITCH:
132                 leaveMultiplyingConditional();
133                 break;
134             case TokenTypes.LITERAL_ELSE:
135             case TokenTypes.LITERAL_CATCH:
136             case TokenTypes.LITERAL_CASE:
137                 leaveAddingConditional();
138                 break;
139             default:
140                 super.leaveToken(ast);
141         }
142     }
143 
144     @Override
145     protected String getMessageID()
146     {
147         return MSG_KEY;
148     }
149 
150     /** Visits else, catch or case. */
151     private void visitAddingConditional()
152     {
153         pushValue();
154     }
155 
156     /** Leaves else, catch or case. */
157     private void leaveAddingConditional()
158     {
159         setCurrentValue(
160                 getCurrentValue().subtract(BigInteger.ONE).add(popValue()));
161     }
162 
163     /** Visits while, do, for, if, try, ? (in ?::) or switch. */
164     private void visitMultiplyingConditional()
165     {
166         pushValue();
167     }
168 
169     /** Leaves while, do, for, if, try, ? (in ?::) or switch. */
170     private void leaveMultiplyingConditional()
171     {
172         setCurrentValue(
173                 getCurrentValue().add(BigInteger.ONE).multiply(popValue()));
174     }
175 }