Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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   * &lt;!-- String literal equality check --&gt;
39   * &lt;module name="DescendantToken"&gt;
40   *     &lt;property name="tokens" value="EQUAL,NOT_EQUAL"/&gt;
41   *     &lt;property name="limitedTokens" value="STRING_LITERAL"/&gt;
42   *     &lt;property name="maximumNumber" value="0"/&gt;
43   *     &lt;property name="maximumDepth" value="1"/&dt;
44   * &lt;/module&gt;
45   *
46   * &lt;!-- Switch with no default --&gt;
47   * &lt;module name="DescendantToken"&gt;
48   *     &lt;property name="tokens" value="LITERAL_SWITCH"/&gt;
49   *     &lt;property name="maximumDepth" value="2"/&gt;
50   *     &lt;property name="limitedTokens" value="LITERAL_DEFAULT"/&gt;
51   *     &lt;property name="minimumNumber" value="1"/&gt;
52   * &lt;/module&gt;
53   *
54   * &lt;!-- Assert statement may have side effects --&gt;
55   * &lt;module name="DescendantToken"&gt;
56   *     &lt;property name="tokens" value="LITERAL_ASSERT"/&gt;
57   *     &lt;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"/&gt;
61   *     &lt;property name="maximumNumber" value="0"/&gt;
62   * &lt;/module&gt;
63   *
64   * &lt;!-- Initialiser in for performs no setup - use while instead? --&gt;
65   * &lt;module name="DescendantToken"&gt;
66   *     &lt;property name="tokens" value="FOR_INIT"/&gt;
67   *     &lt;property name="limitedTokens" value="EXPR"/&gt;
68   *     &lt;property name="minimumNumber" value="1"/&gt;
69   * &lt;/module&gt;
70   *
71   * &lt;!-- Condition in for performs no check --&gt;
72   * &lt;module name="DescendantToken"&gt;
73   *     &lt;property name="tokens" value="FOR_CONDITION"/&gt;
74   *     &lt;property name="limitedTokens" value="EXPR"/&gt;
75   *     &lt;property name="minimumNumber" value="1"/&gt;
76   * &lt;/module&gt;
77   *
78   * &lt;!-- Switch within switch --&gt;
79   * &lt;module name="DescendantToken"&gt;
80   *     &lt;property name="tokens" value="LITERAL_SWITCH"/&gt;
81   *     &lt;property name="limitedTokens" value="LITERAL_SWITCH"/&gt;
82   *     &lt;property name="maximumNumber" value="0"/&gt;
83   *     &lt;property name="minimumDepth" value="1"/&gt;
84   * &lt;/module&gt;
85   *
86   * &lt;!-- Return from within a catch or finally block --&gt;
87   * &lt;module name="DescendantToken"&gt;
88   *     &lt;property name="tokens" value="LITERAL_FINALLY,LITERAL_CATCH"/&gt;
89   *     &lt;property name="limitedTokens" value="LITERAL_RETURN"/&gt;
90   *     &lt;property name="maximumNumber" value="0"/&gt;
91   * &lt;/module&gt;
92   *
93   * &lt;!-- Try within catch or finally block --&gt;
94   * &lt;module name="DescendantToken"&gt;
95   *     &lt;property name="tokens" value="LITERAL_CATCH,LITERAL_FINALLY"/&gt;
96   *     &lt;property name="limitedTokens" value="LITERAL_TRY"/&gt;
97   *     &lt;property name="maximumNumber" value="0"/&gt;
98   * &lt;/module&gt;
99   *
100  * &lt;!-- Too many cases within a switch --&gt;
101  * &lt;module name="DescendantToken"&gt;
102  *     &lt;property name="tokens" value="LITERAL_SWITCH"/&gt;
103  *     &lt;property name="limitedTokens" value="LITERAL_CASE"/&gt;
104  *     &lt;property name="maximumDepth" value="2"/&gt;
105  *     &lt;property name="maximumNumber" value="10"/&gt;
106  * &lt;/module&gt;
107  *
108  * &lt;!-- Too many local variables within a method --&gt;
109  * &lt;module name="DescendantToken"&gt;
110  *     &lt;property name="tokens" value="METHOD_DEF"/&gt;
111  *     &lt;property name="limitedTokens" value="VARIABLE_DEF"/&gt;
112  *     &lt;property name="maximumDepth" value="2"/&gt;
113  *     &lt;property name="maximumNumber" value="10"/&gt;
114  * &lt;/module&gt;
115  *
116  * &lt;!-- Too many returns from within a method --&gt;
117  * &lt;module name="DescendantToken"&gt;
118  *     &lt;property name="tokens" value="METHOD_DEF"/&gt;
119  *     &lt;property name="limitedTokens" value="LITERAL_RETURN"/&gt;
120  *     &lt;property name="maximumNumber" value="3"/&gt;
121  * &lt;/module&gt;
122  *
123  * &lt;!-- Too many fields within an interface --&gt;
124  * &lt;module name="DescendantToken"&gt;
125  *     &lt;property name="tokens" value="INTERFACE_DEF"/&gt;
126  *     &lt;property name="limitedTokens" value="VARIABLE_DEF"/&gt;
127  *     &lt;property name="maximumDepth" value="2"/&gt;
128  *     &lt;property name="maximumNumber" value="0"/&gt;
129  * &lt;/module&gt;
130  *
131  * &lt;!-- Limit the number of exceptions a method can throw --&gt;
132  * &lt;module name="DescendantToken"&gt;
133  *     &lt;property name="tokens" value="LITERAL_THROWS"/&gt;
134  *     &lt;property name="limitedTokens" value="IDENT"/&gt;
135  *     &lt;property name="maximumNumber" value="1"/&gt;
136  * &lt;/module&gt;
137  *
138  * &lt;!-- Limit the number of expressions in a method --&gt;
139  * &lt;module name="DescendantToken"&gt;
140  *     &lt;property name="tokens" value="METHOD_DEF"/&gt;
141  *     &lt;property name="limitedTokens" value="EXPR"/&gt;
142  *     &lt;property name="maximumNumber" value="200"/&gt;
143  * &lt;/module&gt;
144  *
145  * &lt;!-- Disallow empty statements --&gt;
146  * &lt;module name="DescendantToken"&gt;
147  *     &lt;property name="tokens" value="EMPTY_STAT"/&gt;
148  *     &lt;property name="limitedTokens" value="EMPTY_STAT"/&gt;
149  *     &lt;property name="maximumNumber" value="0"/&gt;
150  *     &lt;property name="maximumDepth" value="0"/&gt;
151  *     &lt;property name="maximumMessage"
152  *         value="Empty statement is not allowed."/&gt;
153  * &lt;/module&gt;
154  *
155  * &lt;!-- Too many fields within a class --&gt;
156  * &lt;module name="DescendantToken"&gt;
157  *     &lt;property name="tokens" value="CLASS_DEF"/&gt;
158  *     &lt;property name="limitedTokens" value="VARIABLE_DEF"/&gt;
159  *     &lt;property name="maximumDepth" value="2"/&gt;
160  *     &lt;property name="maximumNumber" value="10"/&gt;
161  * &lt;/module&gt;
162  * </pre>
163  * <p>
164  * @author Tim Tyler &lt;tim@tt1.org&gt;
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 }