001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks;
021
022import java.util.Arrays;
023import java.util.Set;
024
025import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
029import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
030
031/**
032 * <p>
033 * Checks for restricted tokens beneath other tokens.
034 * </p>
035 * <p>
036 * WARNING: This is a very powerful and flexible check, but, at the same time,
037 * it is low-level and very implementation-dependent because its results depend
038 * on the grammar we use to build abstract syntax trees. Thus we recommend using
039 * other checks when they provide the desired functionality. Essentially, this
040 * check just works on the level of an abstract syntax tree and knows nothing
041 * about language structures.
042 * </p>
043 * <ul>
044 * <li>
045 * Property {@code limitedTokens} - Specify set of tokens with limited occurrences as descendants.
046 * Default value is {@code {}}.
047 * </li>
048 * <li>
049 * Property {@code minimumDepth} - Specify the minimum depth for descendant counts.
050 * Default value is {@code 0}.
051 * </li>
052 * <li>
053 * Property {@code maximumDepth} - Specify the maximum depth for descendant counts.
054 * Default value is {@code java.lang.Integer.MAX_VALUE}.
055 * </li>
056 * <li>
057 * Property {@code minimumNumber} - Specify a minimum count for descendants.
058 * Default value is {@code 0}.
059 * </li>
060 * <li>
061 * Property {@code maximumNumber} - Specify a maximum count for descendants.
062 * Default value is {@code java.lang.Integer.MAX_VALUE}.
063 * </li>
064 * <li>
065 * Property {@code sumTokenCounts} - Control whether the number of tokens found
066 * should be calculated from the sum of the individual token counts.
067 * Default value is {@code false}.
068 * </li>
069 * <li>
070 * Property {@code minimumMessage} - Define the violation message
071 * when the minimum count is not reached.
072 * Default value is {@code null}.
073 * </li>
074 * <li>
075 * Property {@code maximumMessage} - Define the violation message
076 * when the maximum count is exceeded.
077 * Default value is {@code null}.
078 * </li>
079 * </ul>
080 * <p>
081 * To configure the check to produce a violation on a switch statement with no default case:
082 * </p>
083 * <pre>
084 * &lt;module name=&quot;DescendantToken&quot;&gt;
085 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_SWITCH&quot;/&gt;
086 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;2&quot;/&gt;
087 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_DEFAULT&quot;/&gt;
088 *   &lt;property name=&quot;minimumNumber&quot; value=&quot;1&quot;/&gt;
089 * &lt;/module&gt;
090 * </pre>
091 * <p>
092 * To configure the check to produce a violation on a condition in {@code for}
093 * which performs no check:
094 * </p>
095 * <pre>
096 * &lt;module name=&quot;DescendantToken&quot;&gt;
097 *   &lt;property name=&quot;tokens&quot; value=&quot;FOR_CONDITION&quot;/&gt;
098 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;EXPR&quot;/&gt;
099 *   &lt;property name=&quot;minimumNumber&quot; value=&quot;1&quot;/&gt;
100 * &lt;/module&gt;
101 * </pre>
102 * <p>
103 * To configure the check to produce a violation on comparing {@code this} with
104 * {@code null}(i.e. {@code this == null} and {@code this != null}):
105 * </p>
106 * <pre>
107 * &lt;module name=&quot;DescendantToken&quot;&gt;
108 *   &lt;property name=&quot;tokens&quot; value=&quot;EQUAL,NOT_EQUAL&quot;/&gt;
109 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_THIS,LITERAL_NULL&quot;/&gt;
110 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;1&quot;/&gt;
111 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;1&quot;/&gt;
112 *   &lt;property name=&quot;sumTokenCounts&quot; value=&quot;true&quot;/&gt;
113 * &lt;/module&gt;
114 * </pre>
115 * <p>
116 * To configure the check to produce a violation on a {@code String} literal equality check:
117 * </p>
118 * <pre>
119 * &lt;module name=&quot;DescendantToken&quot;&gt;
120 *   &lt;property name=&quot;tokens&quot; value=&quot;EQUAL,NOT_EQUAL&quot;/&gt;
121 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;STRING_LITERAL&quot;/&gt;
122 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
123 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;1&quot;/&gt;
124 * &lt;/module&gt;
125 * </pre>
126 * <p>
127 * To configure the check to produce a violation on an assert statement that may
128 * have side effects (formatted for browser display):
129 * </p>
130 * <pre>
131 * &lt;module name=&quot;DescendantToken&quot;&gt;
132 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_ASSERT&quot;/&gt;
133 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;ASSIGN,DEC,INC,POST_DEC,
134 *     POST_INC,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,DIV_ASSIGN,MOD_ASSIGN,
135 *     BSR_ASSIGN,SR_ASSIGN,SL_ASSIGN,BAND_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,
136 *     METHOD_CALL&quot;/&gt;
137 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
138 * &lt;/module&gt;
139 * </pre>
140 * <p>
141 * To configure the check to produce a violation on an initializer in {@code for}
142 * performs no setup (where a {@code while} statement could be used instead):
143 * </p>
144 * <pre>
145 * &lt;module name=&quot;DescendantToken&quot;&gt;
146 *   &lt;property name=&quot;tokens&quot; value=&quot;FOR_INIT&quot;/&gt;
147 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;EXPR&quot;/&gt;
148 *   &lt;property name=&quot;minimumNumber&quot; value=&quot;1&quot;/&gt;
149 * &lt;/module&gt;
150 * </pre>
151 * <p>
152 * To configure the check to produce a violation on a switch that is nested in another switch:
153 * </p>
154 * <pre>
155 * &lt;module name=&quot;DescendantToken&quot;&gt;
156 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_SWITCH&quot;/&gt;
157 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_SWITCH&quot;/&gt;
158 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
159 *   &lt;property name=&quot;minimumDepth&quot; value=&quot;1&quot;/&gt;
160 * &lt;/module&gt;
161 * </pre>
162 * <p>
163 * To configure the check to produce a violation on a return statement from
164 * within a catch or finally block:
165 * </p>
166 * <pre>
167 * &lt;module name=&quot;DescendantToken&quot;&gt;
168 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_FINALLY,LITERAL_CATCH&quot;/&gt;
169 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_RETURN&quot;/&gt;
170 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
171 * &lt;/module&gt;
172 * </pre>
173 * <p>
174 * To configure the check to produce a violation on a try statement within a catch or finally block:
175 * </p>
176 * <pre>
177 * &lt;module name=&quot;DescendantToken&quot;&gt;
178 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_CATCH,LITERAL_FINALLY&quot;/&gt;
179 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_TRY&quot;/&gt;
180 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
181 * &lt;/module&gt;
182 * </pre>
183 * <p>
184 * To configure the check to produce a violation on a switch with too many cases:
185 * </p>
186 * <pre>
187 * &lt;module name=&quot;DescendantToken&quot;&gt;
188 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_SWITCH&quot;/&gt;
189 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_CASE&quot;/&gt;
190 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;2&quot;/&gt;
191 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;10&quot;/&gt;
192 * &lt;/module&gt;
193 * </pre>
194 * <p>
195 * To configure the check to produce a violation on a method with too many local variables:
196 * </p>
197 * <pre>
198 * &lt;module name=&quot;DescendantToken&quot;&gt;
199 *   &lt;property name=&quot;tokens&quot; value=&quot;METHOD_DEF&quot;/&gt;
200 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;VARIABLE_DEF&quot;/&gt;
201 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;2&quot;/&gt;
202 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;10&quot;/&gt;
203 * &lt;/module&gt;
204 * </pre>
205 * <p>
206 * To configure the check to produce a violation on a method with too many returns:
207 * </p>
208 * <pre>
209 * &lt;module name=&quot;DescendantToken&quot;&gt;
210 *   &lt;property name=&quot;tokens&quot; value=&quot;METHOD_DEF&quot;/&gt;
211 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;LITERAL_RETURN&quot;/&gt;
212 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;3&quot;/&gt;
213 * &lt;/module&gt;
214 * </pre>
215 * <p>
216 * To configure the check to produce a violation on an interface with too many fields:
217 * </p>
218 * <pre>
219 * &lt;module name=&quot;DescendantToken&quot;&gt;
220 *   &lt;property name=&quot;tokens&quot; value=&quot;INTERFACE_DEF&quot;/&gt;
221 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;VARIABLE_DEF&quot;/&gt;
222 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;2&quot;/&gt;
223 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
224 * &lt;/module&gt;
225 * </pre>
226 * <p>
227 * To configure the check to produce a violation on a method which throws too many exceptions:
228 * </p>
229 * <pre>
230 * &lt;module name=&quot;DescendantToken&quot;&gt;
231 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_THROWS&quot;/&gt;
232 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;IDENT&quot;/&gt;
233 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;1&quot;/&gt;
234 * &lt;/module&gt;
235 * </pre>
236 * <p>
237 * To configure the check to produce a violation on a method with too many expressions:
238 * </p>
239 * <pre>
240 * &lt;module name=&quot;DescendantToken&quot;&gt;
241 *   &lt;property name=&quot;tokens&quot; value=&quot;METHOD_DEF&quot;/&gt;
242 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;EXPR&quot;/&gt;
243 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;200&quot;/&gt;
244 * &lt;/module&gt;
245 * </pre>
246 * <p>
247 * To configure the check to produce a violation on an empty statement:
248 * </p>
249 * <pre>
250 * &lt;module name=&quot;DescendantToken&quot;&gt;
251 *   &lt;property name=&quot;tokens&quot; value=&quot;EMPTY_STAT&quot;/&gt;
252 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;EMPTY_STAT&quot;/&gt;
253 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;0&quot;/&gt;
254 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;0&quot;/&gt;
255 *   &lt;property name=&quot;maximumMessage&quot;
256 *     value=&quot;Empty statement is not allowed.&quot;/&gt;
257 * &lt;/module&gt;
258 * </pre>
259 * <p>
260 * To configure the check to produce a violation on a class with too many fields:
261 * </p>
262 * <pre>
263 * &lt;module name=&quot;DescendantToken&quot;&gt;
264 *   &lt;property name=&quot;tokens&quot; value=&quot;CLASS_DEF&quot;/&gt;
265 *   &lt;property name=&quot;limitedTokens&quot; value=&quot;VARIABLE_DEF&quot;/&gt;
266 *   &lt;property name=&quot;maximumDepth&quot; value=&quot;2&quot;/&gt;
267 *   &lt;property name=&quot;maximumNumber&quot; value=&quot;10&quot;/&gt;
268 * &lt;/module&gt;
269 * </pre>
270 *
271 * @since 3.2
272 */
273@FileStatefulCheck
274public class DescendantTokenCheck extends AbstractCheck {
275
276    /**
277     * A key is pointing to the warning message text in "messages.properties"
278     * file.
279     */
280    public static final String MSG_KEY_MIN = "descendant.token.min";
281
282    /**
283     * A key is pointing to the warning message text in "messages.properties"
284     * file.
285     */
286    public static final String MSG_KEY_MAX = "descendant.token.max";
287
288    /**
289     * A key is pointing to the warning message text in "messages.properties"
290     * file.
291     */
292    public static final String MSG_KEY_SUM_MIN = "descendant.token.sum.min";
293
294    /**
295     * A key is pointing to the warning message text in "messages.properties"
296     * file.
297     */
298    public static final String MSG_KEY_SUM_MAX = "descendant.token.sum.max";
299
300    /** Specify the minimum depth for descendant counts. */
301    private int minimumDepth;
302    /** Specify the maximum depth for descendant counts. */
303    private int maximumDepth = Integer.MAX_VALUE;
304    /** Specify a minimum count for descendants. */
305    private int minimumNumber;
306    /** Specify a maximum count for descendants. */
307    private int maximumNumber = Integer.MAX_VALUE;
308    /**
309     * Control whether the number of tokens found should be calculated from
310     * the sum of the individual token counts.
311     */
312    private boolean sumTokenCounts;
313    /** Specify set of tokens with limited occurrences as descendants. */
314    private int[] limitedTokens = CommonUtil.EMPTY_INT_ARRAY;
315    /** Define the violation message when the minimum count is not reached. */
316    private String minimumMessage;
317    /** Define the violation message when the maximum count is exceeded. */
318    private String maximumMessage;
319
320    /**
321     * Counts of descendant tokens.
322     * Indexed by (token ID - 1) for performance.
323     */
324    private int[] counts = CommonUtil.EMPTY_INT_ARRAY;
325
326    @Override
327    public int[] getDefaultTokens() {
328        return getRequiredTokens();
329    }
330
331    @Override
332    public int[] getRequiredTokens() {
333        return CommonUtil.EMPTY_INT_ARRAY;
334    }
335
336    @Override
337    public void visitToken(DetailAST ast) {
338        // reset counts
339        Arrays.fill(counts, 0);
340        countTokens(ast, 0);
341
342        if (sumTokenCounts) {
343            logAsTotal(ast);
344        }
345        else {
346            logAsSeparated(ast);
347        }
348    }
349
350    /**
351     * Log violations for each Token.
352     *
353     * @param ast token
354     */
355    private void logAsSeparated(DetailAST ast) {
356        // name of this token
357        final String name = TokenUtil.getTokenName(ast.getType());
358
359        for (int element : limitedTokens) {
360            final int tokenCount = counts[element - 1];
361            if (tokenCount < minimumNumber) {
362                final String descendantName = TokenUtil.getTokenName(element);
363
364                if (minimumMessage == null) {
365                    minimumMessage = MSG_KEY_MIN;
366                }
367                log(ast,
368                        minimumMessage,
369                        String.valueOf(tokenCount),
370                        String.valueOf(minimumNumber),
371                        name,
372                        descendantName);
373            }
374            if (tokenCount > maximumNumber) {
375                final String descendantName = TokenUtil.getTokenName(element);
376
377                if (maximumMessage == null) {
378                    maximumMessage = MSG_KEY_MAX;
379                }
380                log(ast,
381                        maximumMessage,
382                        String.valueOf(tokenCount),
383                        String.valueOf(maximumNumber),
384                        name,
385                        descendantName);
386            }
387        }
388    }
389
390    /**
391     * Log validation as one violation.
392     *
393     * @param ast current token
394     */
395    private void logAsTotal(DetailAST ast) {
396        // name of this token
397        final String name = TokenUtil.getTokenName(ast.getType());
398
399        int total = 0;
400        for (int element : limitedTokens) {
401            total += counts[element - 1];
402        }
403        if (total < minimumNumber) {
404            if (minimumMessage == null) {
405                minimumMessage = MSG_KEY_SUM_MIN;
406            }
407            log(ast,
408                    minimumMessage,
409                    String.valueOf(total),
410                    String.valueOf(minimumNumber), name);
411        }
412        if (total > maximumNumber) {
413            if (maximumMessage == null) {
414                maximumMessage = MSG_KEY_SUM_MAX;
415            }
416            log(ast,
417                    maximumMessage,
418                    String.valueOf(total),
419                    String.valueOf(maximumNumber), name);
420        }
421    }
422
423    /**
424     * Counts the number of occurrences of descendant tokens.
425     *
426     * @param ast the root token for descendants.
427     * @param depth the maximum depth of the counted descendants.
428     */
429    private void countTokens(DetailAST ast, int depth) {
430        if (depth <= maximumDepth) {
431            // update count
432            if (depth >= minimumDepth) {
433                final int type = ast.getType();
434                if (type <= counts.length) {
435                    counts[type - 1]++;
436                }
437            }
438            DetailAST child = ast.getFirstChild();
439            final int nextDepth = depth + 1;
440            while (child != null) {
441                countTokens(child, nextDepth);
442                child = child.getNextSibling();
443            }
444        }
445    }
446
447    @Override
448    public int[] getAcceptableTokens() {
449        // Any tokens set by property 'tokens' are acceptable
450        final Set<String> tokenNames = getTokenNames();
451        final int[] result = new int[tokenNames.size()];
452        int index = 0;
453        for (String name : tokenNames) {
454            result[index] = TokenUtil.getTokenId(name);
455            index++;
456        }
457        return result;
458    }
459
460    /**
461     * Setter to specify set of tokens with limited occurrences as descendants.
462     *
463     * @param limitedTokensParam - list of tokens to ignore.
464     */
465    public void setLimitedTokens(String... limitedTokensParam) {
466        limitedTokens = new int[limitedTokensParam.length];
467
468        int maxToken = 0;
469        for (int i = 0; i < limitedTokensParam.length; i++) {
470            limitedTokens[i] = TokenUtil.getTokenId(limitedTokensParam[i]);
471            if (limitedTokens[i] >= maxToken + 1) {
472                maxToken = limitedTokens[i];
473            }
474        }
475        counts = new int[maxToken];
476    }
477
478    /**
479     * Setter to specify the minimum depth for descendant counts.
480     *
481     * @param minimumDepth the minimum depth for descendant counts.
482     */
483    public void setMinimumDepth(int minimumDepth) {
484        this.minimumDepth = minimumDepth;
485    }
486
487    /**
488     * Setter to specify the maximum depth for descendant counts.
489     *
490     * @param maximumDepth the maximum depth for descendant counts.
491     */
492    public void setMaximumDepth(int maximumDepth) {
493        this.maximumDepth = maximumDepth;
494    }
495
496    /**
497     * Setter to specify a minimum count for descendants.
498     *
499     * @param minimumNumber the minimum count for descendants.
500     */
501    public void setMinimumNumber(int minimumNumber) {
502        this.minimumNumber = minimumNumber;
503    }
504
505    /**
506      * Setter to specify a maximum count for descendants.
507      *
508      * @param maximumNumber the maximum count for descendants.
509      */
510    public void setMaximumNumber(int maximumNumber) {
511        this.maximumNumber = maximumNumber;
512    }
513
514    /**
515     * Setter to define the violation message when the minimum count is not reached.
516     *
517     * @param message the violation message for minimum count not reached.
518     *     Used as a {@code MessageFormat} pattern with arguments
519     *     <ul>
520     *     <li>{0} - token count</li>
521     *     <li>{1} - minimum number</li>
522     *     <li>{2} - name of token</li>
523     *     <li>{3} - name of limited token</li>
524     *     </ul>
525     */
526    public void setMinimumMessage(String message) {
527        minimumMessage = message;
528    }
529
530    /**
531     * Setter to define the violation message when the maximum count is exceeded.
532     *
533     * @param message the violation message for maximum count exceeded.
534     *     Used as a {@code MessageFormat} pattern with arguments
535     * <ul>
536     * <li>{0} - token count</li>
537     * <li>{1} - maximum number</li>
538     * <li>{2} - name of token</li>
539     * <li>{3} - name of limited token</li>
540     * </ul>
541     */
542
543    public void setMaximumMessage(String message) {
544        maximumMessage = message;
545    }
546
547    /**
548     * Setter to control whether the number of tokens found should be calculated
549     * from the sum of the individual token counts.
550     *
551     * @param sum whether to use the sum.
552     */
553    public void setSumTokenCounts(boolean sum) {
554        sumTokenCounts = sum;
555    }
556
557}