1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2014 Oliver Burn
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.coding;
20
21 import com.puppycrawl.tools.checkstyle.api.DetailAST;
22 import com.puppycrawl.tools.checkstyle.api.FastStack;
23 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24 import com.puppycrawl.tools.checkstyle.checks.AbstractFormatCheck;
25
26 /**
27 * <p>
28 * Restricts return statements to a specified count (default = 2).
29 * Ignores specified methods (<code>equals()</code> by default).
30 * </p>
31 * <p>
32 * Rationale: Too many return points can be indication that code is
33 * attempting to do too much or may be difficult to understand.
34 * </p>
35 *
36 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
37 * TODO: Test for inside a static block
38 */
39 public final class ReturnCountCheck extends AbstractFormatCheck
40 {
41 /** Default allowed number of return statements. */
42 private static final int DEFAULT_MAX = 2;
43
44 /** Stack of method contexts. */
45 private final FastStack<Context> contextStack = FastStack.newInstance();
46 /** Maximum allowed number of return stmts. */
47 private int max;
48 /** Current method context. */
49 private Context context;
50
51 /** Creates new instance of the checks. */
52 public ReturnCountCheck()
53 {
54 super("^equals$");
55 setMax(DEFAULT_MAX);
56 }
57
58 @Override
59 public int[] getDefaultTokens()
60 {
61 return new int[] {
62 TokenTypes.CTOR_DEF,
63 TokenTypes.METHOD_DEF,
64 TokenTypes.LITERAL_RETURN,
65 };
66 }
67
68 @Override
69 public int[] getRequiredTokens()
70 {
71 return new int[]{
72 TokenTypes.CTOR_DEF,
73 TokenTypes.METHOD_DEF,
74 };
75 }
76
77 /**
78 * Getter for max property.
79 * @return maximum allowed number of return statements.
80 */
81 public int getMax()
82 {
83 return max;
84 }
85
86 /**
87 * Setter for max property.
88 * @param max maximum allowed number of return statements.
89 */
90 public void setMax(int max)
91 {
92 this.max = max;
93 }
94
95 @Override
96 public void beginTree(DetailAST rootAST)
97 {
98 context = null;
99 contextStack.clear();
100 }
101
102 @Override
103 public void visitToken(DetailAST ast)
104 {
105 switch (ast.getType()) {
106 case TokenTypes.CTOR_DEF:
107 case TokenTypes.METHOD_DEF:
108 visitMethodDef(ast);
109 break;
110 case TokenTypes.LITERAL_RETURN:
111 context.visitLiteralReturn();
112 break;
113 default:
114 throw new IllegalStateException(ast.toString());
115 }
116 }
117
118 @Override
119 public void leaveToken(DetailAST ast)
120 {
121 switch (ast.getType()) {
122 case TokenTypes.CTOR_DEF:
123 case TokenTypes.METHOD_DEF:
124 leaveMethodDef(ast);
125 break;
126 case TokenTypes.LITERAL_RETURN:
127 // Do nothing
128 break;
129 default:
130 throw new IllegalStateException(ast.toString());
131 }
132 }
133
134 /**
135 * Creates new method context and places old one on the stack.
136 * @param ast method definition for check.
137 */
138 private void visitMethodDef(DetailAST ast)
139 {
140 contextStack.push(context);
141 final DetailAST methodNameAST = ast.findFirstToken(TokenTypes.IDENT);
142 context =
143 new Context(!getRegexp().matcher(methodNameAST.getText()).find());
144 }
145
146 /**
147 * Checks number of return statements and restore
148 * previous method context.
149 * @param ast method def node.
150 */
151 private void leaveMethodDef(DetailAST ast)
152 {
153 context.checkCount(ast);
154 context = contextStack.pop();
155 }
156
157 /**
158 * Class to encapsulate information about one method.
159 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
160 */
161 private class Context
162 {
163 /** Whether we should check this method or not. */
164 private final boolean checking;
165 /** Counter for return statements. */
166 private int count;
167
168 /**
169 * Creates new method context.
170 * @param checking should we check this method or not.
171 */
172 public Context(boolean checking)
173 {
174 this.checking = checking;
175 count = 0;
176 }
177
178 /** Increase number of return statements. */
179 public void visitLiteralReturn()
180 {
181 ++count;
182 }
183
184 /**
185 * Checks if number of return statements in method more
186 * than allowed.
187 * @param ast method def associated with this context.
188 */
189 public void checkCount(DetailAST ast)
190 {
191 if (checking && (count > getMax())) {
192 log(ast.getLineNo(), ast.getColumnNo(), "return.count",
193 count, getMax());
194 }
195 }
196 }
197 }