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     * @param ast token
353     */
354    private void logAsSeparated(DetailAST ast) {
355        // name of this token
356        final String name = TokenUtil.getTokenName(ast.getType());
357
358        for (int element : limitedTokens) {
359            final int tokenCount = counts[element - 1];
360            if (tokenCount < minimumNumber) {
361                final String descendantName = TokenUtil.getTokenName(element);
362
363                if (minimumMessage == null) {
364                    minimumMessage = MSG_KEY_MIN;
365                }
366                log(ast,
367                        minimumMessage,
368                        String.valueOf(tokenCount),
369                        String.valueOf(minimumNumber),
370                        name,
371                        descendantName);
372            }
373            if (tokenCount > maximumNumber) {
374                final String descendantName = TokenUtil.getTokenName(element);
375
376                if (maximumMessage == null) {
377                    maximumMessage = MSG_KEY_MAX;
378                }
379                log(ast,
380                        maximumMessage,
381                        String.valueOf(tokenCount),
382                        String.valueOf(maximumNumber),
383                        name,
384                        descendantName);
385            }
386        }
387    }
388
389    /**
390     * Log validation as one violation.
391     * @param ast current token
392     */
393    private void logAsTotal(DetailAST ast) {
394        // name of this token
395        final String name = TokenUtil.getTokenName(ast.getType());
396
397        int total = 0;
398        for (int element : limitedTokens) {
399            total += counts[element - 1];
400        }
401        if (total < minimumNumber) {
402            if (minimumMessage == null) {
403                minimumMessage = MSG_KEY_SUM_MIN;
404            }
405            log(ast,
406                    minimumMessage,
407                    String.valueOf(total),
408                    String.valueOf(minimumNumber), name);
409        }
410        if (total > maximumNumber) {
411            if (maximumMessage == null) {
412                maximumMessage = MSG_KEY_SUM_MAX;
413            }
414            log(ast,
415                    maximumMessage,
416                    String.valueOf(total),
417                    String.valueOf(maximumNumber), name);
418        }
419    }
420
421    /**
422     * Counts the number of occurrences of descendant tokens.
423     * @param ast the root token for descendants.
424     * @param depth the maximum depth of the counted descendants.
425     */
426    private void countTokens(DetailAST ast, int depth) {
427        if (depth <= maximumDepth) {
428            //update count
429            if (depth >= minimumDepth) {
430                final int type = ast.getType();
431                if (type <= counts.length) {
432                    counts[type - 1]++;
433                }
434            }
435            DetailAST child = ast.getFirstChild();
436            final int nextDepth = depth + 1;
437            while (child != null) {
438                countTokens(child, nextDepth);
439                child = child.getNextSibling();
440            }
441        }
442    }
443
444    @Override
445    public int[] getAcceptableTokens() {
446        // Any tokens set by property 'tokens' are acceptable
447        final Set<String> tokenNames = getTokenNames();
448        final int[] result = new int[tokenNames.size()];
449        int index = 0;
450        for (String name : tokenNames) {
451            result[index] = TokenUtil.getTokenId(name);
452            index++;
453        }
454        return result;
455    }
456
457    /**
458     * Setter to specify set of tokens with limited occurrences as descendants.
459     *
460     * @param limitedTokensParam - list of tokens to ignore.
461     */
462    public void setLimitedTokens(String... limitedTokensParam) {
463        limitedTokens = new int[limitedTokensParam.length];
464
465        int maxToken = 0;
466        for (int i = 0; i < limitedTokensParam.length; i++) {
467            limitedTokens[i] = TokenUtil.getTokenId(limitedTokensParam[i]);
468            if (limitedTokens[i] >= maxToken + 1) {
469                maxToken = limitedTokens[i];
470            }
471        }
472        counts = new int[maxToken];
473    }
474
475    /**
476     * Setter to specify the minimum depth for descendant counts.
477     *
478     * @param minimumDepth the minimum depth for descendant counts.
479     */
480    public void setMinimumDepth(int minimumDepth) {
481        this.minimumDepth = minimumDepth;
482    }
483
484    /**
485     * Setter to specify the maximum depth for descendant counts.
486     *
487     * @param maximumDepth the maximum depth for descendant counts.
488     */
489    public void setMaximumDepth(int maximumDepth) {
490        this.maximumDepth = maximumDepth;
491    }
492
493    /**
494     * Setter to specify a minimum count for descendants.
495     *
496     * @param minimumNumber the minimum count for descendants.
497     */
498    public void setMinimumNumber(int minimumNumber) {
499        this.minimumNumber = minimumNumber;
500    }
501
502    /**
503      * Setter to specify a maximum count for descendants.
504      *
505      * @param maximumNumber the maximum count for descendants.
506      */
507    public void setMaximumNumber(int maximumNumber) {
508        this.maximumNumber = maximumNumber;
509    }
510
511    /**
512     * Setter to define the violation message when the minimum count is not reached.
513     *
514     * @param message the violation message for minimum count not reached.
515     *     Used as a {@code MessageFormat} pattern with arguments
516     *     <ul>
517     *     <li>{0} - token count</li>
518     *     <li>{1} - minimum number</li>
519     *     <li>{2} - name of token</li>
520     *     <li>{3} - name of limited token</li>
521     *     </ul>
522     */
523    public void setMinimumMessage(String message) {
524        minimumMessage = message;
525    }
526
527    /**
528     * Setter to define the violation message when the maximum count is exceeded.
529     *
530     * @param message the violation message for maximum count exceeded.
531     *     Used as a {@code MessageFormat} pattern with arguments
532     * <ul>
533     * <li>{0} - token count</li>
534     * <li>{1} - maximum number</li>
535     * <li>{2} - name of token</li>
536     * <li>{3} - name of limited token</li>
537     * </ul>
538     */
539
540    public void setMaximumMessage(String message) {
541        maximumMessage = message;
542    }
543
544    /**
545     * Setter to control whether the number of tokens found should be calculated
546     * from the sum of the individual token counts.
547     *
548     * @param sum whether to use the sum.
549     */
550    public void setSumTokenCounts(boolean sum) {
551        sumTokenCounts = sum;
552    }
553
554}