Source code: com/puppycrawl/tools/checkstyle/checks/DescendantTokenCheck.java
1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2003 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;
20
21 import java.util.Arrays;
22 import java.util.Iterator;
23 import java.util.Set;
24
25 import antlr.collections.AST;
26
27 import com.puppycrawl.tools.checkstyle.api.Check;
28 import com.puppycrawl.tools.checkstyle.api.DetailAST;
29 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30
31 /**
32 * <p>
33 * Checks for restricted tokens beneath other tokens.
34 * <p>
35 * Examples of how to configure the check:
36 * </p>
37 * <pre>
38 * <!-- String literal equality check -->
39 * <module name="DescendantToken">
40 * <property name="tokens" value="EQUAL,NOT_EQUAL"/>
41 * <property name="limitedTokens" value="STRING_LITERAL"/>
42 * <property name="maximumNumber" value="0"/>
43 * <property name="maximumDepth" value="1"/&dt;
44 * </module>
45 *
46 * <!-- Switch with no default -->
47 * <module name="DescendantToken">
48 * <property name="tokens" value="LITERAL_SWITCH"/>
49 * <property name="maximumDepth" value="2"/>
50 * <property name="limitedTokens" value="LITERAL_DEFAULT"/>
51 * <property name="minimumNumber" value="1"/>
52 * </module>
53 *
54 * <!-- Assert statement may have side effects -->
55 * <module name="DescendantToken">
56 * <property name="tokens" value="LITERAL_ASSERT"/>
57 * <property name="limitedTokens" value="ASSIGN,DEC,INC,POST_DEC,
58 * POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN,
59 * BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,
60 * METHOD_CALL"/>
61 * <property name="maximumNumber" value="0"/>
62 * </module>
63 *
64 * <!-- Initialiser in for performs no setup - use while instead? -->
65 * <module name="DescendantToken">
66 * <property name="tokens" value="FOR_INIT"/>
67 * <property name="limitedTokens" value="EXPR"/>
68 * <property name="minimumNumber" value="1"/>
69 * </module>
70 *
71 * <!-- Condition in for performs no check -->
72 * <module name="DescendantToken">
73 * <property name="tokens" value="FOR_CONDITION"/>
74 * <property name="limitedTokens" value="EXPR"/>
75 * <property name="minimumNumber" value="1"/>
76 * </module>
77 *
78 * <!-- Switch within switch -->
79 * <module name="DescendantToken">
80 * <property name="tokens" value="LITERAL_SWITCH"/>
81 * <property name="limitedTokens" value="LITERAL_SWITCH"/>
82 * <property name="maximumNumber" value="0"/>
83 * <property name="minimumDepth" value="1"/>
84 * </module>
85 *
86 * <!-- Return from within a catch or finally block -->
87 * <module name="DescendantToken">
88 * <property name="tokens" value="LITERAL_FINALLY,LITERAL_CATCH"/>
89 * <property name="limitedTokens" value="LITERAL_RETURN"/>
90 * <property name="maximumNumber" value="0"/>
91 * </module>
92 *
93 * <!-- Try within catch or finally block -->
94 * <module name="DescendantToken">
95 * <property name="tokens" value="LITERAL_CATCH,LITERAL_FINALLY"/>
96 * <property name="limitedTokens" value="LITERAL_TRY"/>
97 * <property name="maximumNumber" value="0"/>
98 * </module>
99 *
100 * <!-- Too many cases within a switch -->
101 * <module name="DescendantToken">
102 * <property name="tokens" value="LITERAL_SWITCH"/>
103 * <property name="limitedTokens" value="LITERAL_CASE"/>
104 * <property name="maximumDepth" value="2"/>
105 * <property name="maximumNumber" value="10"/>
106 * </module>
107 *
108 * <!-- Too many local variables within a method -->
109 * <module name="DescendantToken">
110 * <property name="tokens" value="METHOD_DEF"/>
111 * <property name="limitedTokens" value="VARIABLE_DEF"/>
112 * <property name="maximumDepth" value="2"/>
113 * <property name="maximumNumber" value="10"/>
114 * </module>
115 *
116 * <!-- Too many returns from within a method -->
117 * <module name="DescendantToken">
118 * <property name="tokens" value="METHOD_DEF"/>
119 * <property name="limitedTokens" value="LITERAL_RETURN"/>
120 * <property name="maximumNumber" value="3"/>
121 * </module>
122 *
123 * <!-- Too many fields within an interface -->
124 * <module name="DescendantToken">
125 * <property name="tokens" value="INTERFACE_DEF"/>
126 * <property name="limitedTokens" value="VARIABLE_DEF"/>
127 * <property name="maximumDepth" value="2"/>
128 * <property name="maximumNumber" value="0"/>
129 * </module>
130 *
131 * <!-- Limit the number of exceptions a method can throw -->
132 * <module name="DescendantToken">
133 * <property name="tokens" value="LITERAL_THROWS"/>
134 * <property name="limitedTokens" value="IDENT"/>
135 * <property name="maximumNumber" value="1"/>
136 * </module>
137 *
138 * <!-- Limit the number of expressions in a method -->
139 * <module name="DescendantToken">
140 * <property name="tokens" value="METHOD_DEF"/>
141 * <property name="limitedTokens" value="EXPR"/>
142 * <property name="maximumNumber" value="200"/>
143 * </module>
144 *
145 * <!-- Disallow empty statements -->
146 * <module name="DescendantToken">
147 * <property name="tokens" value="EMPTY_STAT"/>
148 * <property name="limitedTokens" value="EMPTY_STAT"/>
149 * <property name="maximumNumber" value="0"/>
150 * <property name="maximumDepth" value="0"/>
151 * <property name="maximumMessage"
152 * value="Empty statement is not allowed."/>
153 * </module>
154 *
155 * <!-- Too many fields within a class -->
156 * <module name="DescendantToken">
157 * <property name="tokens" value="CLASS_DEF"/>
158 * <property name="limitedTokens" value="VARIABLE_DEF"/>
159 * <property name="maximumDepth" value="2"/>
160 * <property name="maximumNumber" value="10"/>
161 * </module>
162 * </pre>
163 * <p>
164 * @author Tim Tyler <tim@tt1.org>
165 * @author Rick Giles
166 */
167 public class DescendantTokenCheck extends Check
168 {
169 /** minimum depth */
170 private int mMinimumDepth;
171
172 /** maximum depth */
173 private int mMaximumDepth = Integer.MAX_VALUE;
174
175 /** minimum number */
176 private int mMinimumNumber;
177
178 /** maximum number */
179 private int mMaximumNumber = Integer.MAX_VALUE;
180
181 /** limited tokens */
182 private int[] mLimitedTokens = new int[0];
183
184 /** error message when minimum count not reached */
185 private String mMinimumMessage = "descendant.token.min";
186
187 /** error message when maximum count exceeded */
188 private String mMaximumMessage = "descendant.token.max";
189
190 /**
191 * Counts of descendant tokens.
192 * Indexed by (token ID - 1) for performance.
193 */
194 private int[] mCounts = new int[0];
195
196 /** @see com.puppycrawl.tools.checkstyle.api.Check#getDefaultTokens() */
197 public int[] getDefaultTokens()
198 {
199 return new int[0];
200 }
201
202 /** @see com.puppycrawl.tools.checkstyle.api.Check */
203 public void visitToken(DetailAST aAST)
204 {
205 //reset counts
206 Arrays.fill(mCounts, 0);
207
208 countTokens(aAST, 0);
209
210 // name of this token
211 final String name = TokenTypes.getTokenName(aAST.getType());
212
213 for (int i = 0; i < mLimitedTokens.length; i++) {
214 final int tokenCount = mCounts[mLimitedTokens[i] - 1];
215 if (tokenCount < mMinimumNumber) {
216 final String descendantName =
217 TokenTypes.getTokenName(mLimitedTokens[i]);
218 log(
219 aAST.getLineNo(),
220 aAST.getColumnNo(),
221 mMinimumMessage,
222 new String[] {
223 "" + tokenCount,
224 "" + mMinimumNumber,
225 name,
226 descendantName,
227 });
228 }
229 if (tokenCount > mMaximumNumber) {
230 final String descendantName =
231 TokenTypes.getTokenName(mLimitedTokens[i]);
232 log(
233 aAST.getLineNo(),
234 aAST.getColumnNo(),
235 mMaximumMessage,
236 new String[] {
237 "" + tokenCount,
238 "" + mMaximumNumber,
239 name,
240 descendantName,
241 });
242 }
243 }
244 }
245
246 /**
247 * Counts the number of occurrences of descendant tokens.
248 * @param aAST the root token for descendants.
249 * @param aDepth the maximum depth of the counted descendants.
250 */
251 private void countTokens(AST aAST, int aDepth)
252 {
253 if (aDepth <= mMaximumDepth) {
254 //update count
255 if (aDepth >= mMinimumDepth) {
256 final int type = aAST.getType();
257 if (type <= mCounts.length) {
258 mCounts[type - 1]++;
259 }
260 }
261 AST child = aAST.getFirstChild();
262 final int nextDepth = aDepth + 1;
263 while (child != null) {
264 countTokens(child, nextDepth);
265 child = child.getNextSibling();
266 }
267 }
268 }
269
270 /** @see com.puppycrawl.tools.checkstyle.api.Check */
271 public int[] getAcceptableTokens()
272 {
273 // Any tokens set by property 'tokens' are acceptable
274 final Set tokenNames = getTokenNames();
275 final int[] result = new int[tokenNames.size()];
276 int i = 0;
277 final Iterator it = tokenNames.iterator();
278 while (it.hasNext()) {
279 final String name = (String) it.next();
280 result[i] = TokenTypes.getTokenId(name);
281 i++;
282 }
283 return result;
284 }
285
286 /**
287 * Sets the tokens which occurance as descendant is limited.
288 * @param aLimitedTokens - list of tokens to ignore.
289 */
290 public void setLimitedTokens(String[] aLimitedTokens)
291 {
292 mLimitedTokens = new int[aLimitedTokens.length];
293
294 int maxToken = 0;
295 for (int i = 0; i < aLimitedTokens.length; i++) {
296 mLimitedTokens[i] = TokenTypes.getTokenId(aLimitedTokens[i]);
297 if (mLimitedTokens[i] > maxToken) {
298 maxToken = mLimitedTokens[i];
299 }
300 }
301 mCounts = new int[maxToken];
302 }
303
304 /**
305 * Sets the mimimum depth for descendant counts.
306 * @param aMinimumDepth the mimimum depth for descendant counts.
307 */
308 public void setMinimumDepth(int aMinimumDepth)
309 {
310 mMinimumDepth = aMinimumDepth;
311 }
312
313 /**
314 * Sets the maximum depth for descendant counts.
315 * @param aMaximumDepth the maximum depth for descendant counts.
316 */
317 public void setMaximumDepth(int aMaximumDepth)
318 {
319 mMaximumDepth = aMaximumDepth;
320 }
321
322 /**
323 * Sets a minimum count for descendants.
324 * @param aMinimumNumber the minimum count for descendants.
325 */
326 public void setMinimumNumber(int aMinimumNumber)
327 {
328 mMinimumNumber = aMinimumNumber;
329 }
330
331 /**
332 * Sets a maximum count for descendants.
333 * @param aMaximumNumber the maximum count for descendants.
334 */
335 public void setMaximumNumber(int aMaximumNumber)
336 {
337 mMaximumNumber = aMaximumNumber;
338 }
339
340 /**
341 * Sets the error message for minimum count not reached.
342 * @param aMessage the error message for minimum count not reached.
343 * Used as a <code>MessageFormat</code> pattern with arguments
344 * <ul>
345 * <li>{0} - token count</li>
346 * <li>{1} - minimum number</li>
347 * <li>{2} - name of token</li>
348 * <li>{3} - name of limited token</li>
349 * </ul>
350 */
351 public void setMinimumMessage(String aMessage)
352 {
353 mMinimumMessage = aMessage;
354 }
355
356 /**
357 * Sets the error message for maximum count exceeded.
358 * @param aMessage the error message for maximum count exceeded.
359 * Used as a <code>MessageFormat</code> pattern with arguments
360 * <ul>
361 * <li>{0} - token count</li>
362 * <li>{1} - maximum number</li>
363 * <li>{2} - name of token</li>
364 * <li>{3} - name of limited token</li>
365 * </ul>
366 */
367
368 public void setMaximumMessage(String aMessage)
369 {
370 mMaximumMessage = aMessage;
371 }
372 }