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