View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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  
20  package com.puppycrawl.tools.checkstyle.checks.sizes;
21  
22  import java.util.ArrayDeque;
23  import java.util.Deque;
24  
25  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29  
30  /**
31   * Restricts the number of executable statements to a specified limit
32   * (default = 30).
33   */
34  @FileStatefulCheck
35  public final class ExecutableStatementCountCheck
36      extends AbstractCheck {
37  
38      /**
39       * A key is pointing to the warning message text in "messages.properties"
40       * file.
41       */
42      public static final String MSG_KEY = "executableStatementCount";
43  
44      /** Default threshold. */
45      private static final int DEFAULT_MAX = 30;
46  
47      /** Stack of method contexts. */
48      private final Deque<Context> contextStack = new ArrayDeque<>();
49  
50      /** Threshold to report error for. */
51      private int max;
52  
53      /** Current method context. */
54      private Context context;
55  
56      /** Constructs a {@code ExecutableStatementCountCheck}. */
57      public ExecutableStatementCountCheck() {
58          max = DEFAULT_MAX;
59      }
60  
61      @Override
62      public int[] getDefaultTokens() {
63          return new int[] {
64              TokenTypes.CTOR_DEF,
65              TokenTypes.METHOD_DEF,
66              TokenTypes.INSTANCE_INIT,
67              TokenTypes.STATIC_INIT,
68              TokenTypes.SLIST,
69          };
70      }
71  
72      @Override
73      public int[] getRequiredTokens() {
74          return new int[] {TokenTypes.SLIST};
75      }
76  
77      @Override
78      public int[] getAcceptableTokens() {
79          return new int[] {
80              TokenTypes.CTOR_DEF,
81              TokenTypes.METHOD_DEF,
82              TokenTypes.INSTANCE_INIT,
83              TokenTypes.STATIC_INIT,
84              TokenTypes.SLIST,
85          };
86      }
87  
88      /**
89       * Sets the maximum threshold.
90       * @param max the maximum threshold.
91       */
92      public void setMax(int max) {
93          this.max = max;
94      }
95  
96      @Override
97      public void beginTree(DetailAST rootAST) {
98          context = new Context(null);
99          contextStack.clear();
100     }
101 
102     @Override
103     public void visitToken(DetailAST ast) {
104         switch (ast.getType()) {
105             case TokenTypes.CTOR_DEF:
106             case TokenTypes.METHOD_DEF:
107             case TokenTypes.INSTANCE_INIT:
108             case TokenTypes.STATIC_INIT:
109                 visitMemberDef(ast);
110                 break;
111             case TokenTypes.SLIST:
112                 visitSlist(ast);
113                 break;
114             default:
115                 throw new IllegalStateException(ast.toString());
116         }
117     }
118 
119     @Override
120     public void leaveToken(DetailAST ast) {
121         switch (ast.getType()) {
122             case TokenTypes.CTOR_DEF:
123             case TokenTypes.METHOD_DEF:
124             case TokenTypes.INSTANCE_INIT:
125             case TokenTypes.STATIC_INIT:
126                 leaveMemberDef(ast);
127                 break;
128             case TokenTypes.SLIST:
129                 // Do nothing
130                 break;
131             default:
132                 throw new IllegalStateException(ast.toString());
133         }
134     }
135 
136     /**
137      * Process the start of the member definition.
138      * @param ast the token representing the member definition.
139      */
140     private void visitMemberDef(DetailAST ast) {
141         contextStack.push(context);
142         context = new Context(ast);
143     }
144 
145     /**
146      * Process the end of a member definition.
147      *
148      * @param ast the token representing the member definition.
149      */
150     private void leaveMemberDef(DetailAST ast) {
151         final int count = context.getCount();
152         if (count > max) {
153             log(ast.getLineNo(), ast.getColumnNo(),
154                     MSG_KEY, count, max);
155         }
156         context = contextStack.pop();
157     }
158 
159     /**
160      * Process the end of a statement list.
161      *
162      * @param ast the token representing the statement list.
163      */
164     private void visitSlist(DetailAST ast) {
165         if (context.getAST() != null) {
166             // find member AST for the statement list
167             final DetailAST contextAST = context.getAST();
168             DetailAST parent = ast.getParent();
169             int type = parent.getType();
170             while (type != TokenTypes.CTOR_DEF
171                 && type != TokenTypes.METHOD_DEF
172                 && type != TokenTypes.INSTANCE_INIT
173                 && type != TokenTypes.STATIC_INIT) {
174                 parent = parent.getParent();
175                 type = parent.getType();
176             }
177             if (parent == contextAST) {
178                 context.addCount(ast.getChildCount() / 2);
179             }
180         }
181     }
182 
183     /**
184      * Class to encapsulate counting information about one member.
185      */
186     private static class Context {
187 
188         /** Member AST node. */
189         private final DetailAST ast;
190 
191         /** Counter for context elements. */
192         private int count;
193 
194         /**
195          * Creates new member context.
196          * @param ast member AST node.
197          */
198         Context(DetailAST ast) {
199             this.ast = ast;
200             count = 0;
201         }
202 
203         /**
204          * Increase count.
205          * @param addition the count increment.
206          */
207         public void addCount(int addition) {
208             count += addition;
209         }
210 
211         /**
212          * Gets the member AST node.
213          * @return the member AST node.
214          */
215         public DetailAST getAST() {
216             return ast;
217         }
218 
219         /**
220          * Gets the count.
221          * @return the count.
222          */
223         public int getCount() {
224             return count;
225         }
226 
227     }
228 
229 }