001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2026 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.coding;
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025import java.util.regex.Pattern;
026
027import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
029import com.puppycrawl.tools.checkstyle.api.DetailAST;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
032import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
033
034/**
035 * <div>
036 * Checks if unnecessary parentheses are used in a statement or expression.
037 * The check will flag the following with warnings:
038 * </div>
039 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
040 * return (x);          // parens around identifier
041 * return (x + 1);      // parens around return value
042 * int x = (y / 2 + 1); // parens around assignment rhs
043 * for (int i = (0); i &lt; 10; i++) {  // parens around literal
044 * t -= (z + 1);                     // parens around assignment rhs
045 * boolean a = (x &gt; 7 &amp;&amp; y &gt; 5)      // parens around expression
046 *             || z &lt; 9;
047 * boolean b = (~a) &gt; -27            // parens around ~a
048 *             &amp;&amp; (a-- &lt; 30);        // parens around expression
049 * </code></pre></div>
050 *
051 * <p>
052 * Notes:
053 * The check is not "type aware", that is to say, it can't tell if parentheses
054 * are unnecessary based on the types in an expression. The check is partially aware about
055 * operator precedence but unaware about operator associativity.
056 * It won't catch cases such as:
057 * </p>
058 * {@snippet lang="text" :
059 * int x = (a + b) + c; // 1st Case
060 * boolean p = true; // 2nd Case
061 * int q = 4;
062 * int r = 3;
063 * if (p == (q <= r)) {}
064 * }
065 *
066 * <p>
067 * In the first case, given that <em>a</em>, <em>b</em>, and <em>c</em> are
068 * all {@code int} variables, the parentheses around {@code a + b}
069 * are not needed.
070 * In the second case, parentheses are required as <em>q</em>, <em>r</em> are
071 * of type {@code int} and <em>p</em> is of type {@code boolean}
072 * and removing parentheses will give a compile-time error. Even if <em>q</em>
073 * and <em>r</em> were {@code boolean} still there will be no violation
074 * raised as check is not "type aware".
075 * </p>
076 *
077 * <p>
078 * The partial support for operator precedence includes cases of the following type:
079 * </p>
080 * {@snippet lang="text" :
081 * boolean a = true, b = true;
082 * boolean c = false, d = false;
083 * if ((a && b) || c) { // violation, unnecessary paren
084 * }
085 * if (a && (b || c)) { // ok
086 * }
087 * if ((a == b) && c) { // violation, unnecessary paren
088 * }
089 * String e = "e";
090 * if ((e instanceof String) && a || b) { // violation, unnecessary paren
091 * }
092 * int f = 0;
093 * int g = 0;
094 * if (!(f >= g) // ok
095 *         && (g > f)) { // violation, unnecessary paren
096 * }
097 * if ((++f) > g && a) { // violation, unnecessary paren
098 * }
099 * }
100 *
101 * @since 3.4
102 */
103@FileStatefulCheck
104public class UnnecessaryParenthesesCheck extends AbstractCheck {
105
106    /**
107     * A key is pointing to the warning message text in "messages.properties"
108     * file.
109     */
110    public static final String MSG_IDENT = "unnecessary.paren.ident";
111
112    /**
113     * A key is pointing to the warning message text in "messages.properties"
114     * file.
115     */
116    public static final String MSG_ASSIGN = "unnecessary.paren.assign";
117
118    /**
119     * A key is pointing to the warning message text in "messages.properties"
120     * file.
121     */
122    public static final String MSG_EXPR = "unnecessary.paren.expr";
123
124    /**
125     * A key is pointing to the warning message text in "messages.properties"
126     * file.
127     */
128    public static final String MSG_LITERAL = "unnecessary.paren.literal";
129
130    /**
131     * A key is pointing to the warning message text in "messages.properties"
132     * file.
133     */
134    public static final String MSG_STRING = "unnecessary.paren.string";
135
136    /**
137     * A key is pointing to the warning message text in "messages.properties"
138     * file.
139     */
140    public static final String MSG_RETURN = "unnecessary.paren.return";
141
142    /**
143     * A key is pointing to the warning message text in "messages.properties"
144     * file.
145     */
146    public static final String MSG_LAMBDA = "unnecessary.paren.lambda";
147
148    /**
149     * Compiled pattern used to match newline control characters, for replacement.
150     */
151    private static final Pattern NEWLINE = Pattern.compile("\\R");
152
153    /**
154     * String used to amend TEXT_BLOCK_CONTENT so that it matches STRING_LITERAL.
155     */
156    private static final String QUOTE = "\"";
157
158    /** The maximum string length before we chop the string. */
159    private static final int MAX_QUOTED_LENGTH = 25;
160
161    /** Token types for literals. */
162    private static final int[] LITERALS = {
163        TokenTypes.NUM_DOUBLE,
164        TokenTypes.NUM_FLOAT,
165        TokenTypes.NUM_INT,
166        TokenTypes.NUM_LONG,
167        TokenTypes.STRING_LITERAL,
168        TokenTypes.LITERAL_NULL,
169        TokenTypes.LITERAL_FALSE,
170        TokenTypes.LITERAL_TRUE,
171        TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
172    };
173
174    /** Token types for assignment operations. */
175    private static final int[] ASSIGNMENTS = {
176        TokenTypes.ASSIGN,
177        TokenTypes.BAND_ASSIGN,
178        TokenTypes.BOR_ASSIGN,
179        TokenTypes.BSR_ASSIGN,
180        TokenTypes.BXOR_ASSIGN,
181        TokenTypes.DIV_ASSIGN,
182        TokenTypes.MINUS_ASSIGN,
183        TokenTypes.MOD_ASSIGN,
184        TokenTypes.PLUS_ASSIGN,
185        TokenTypes.SL_ASSIGN,
186        TokenTypes.SR_ASSIGN,
187        TokenTypes.STAR_ASSIGN,
188    };
189
190    /** Token types for conditional operators. */
191    private static final int[] CONDITIONAL_OPERATOR = {
192        TokenTypes.LOR,
193        TokenTypes.LAND,
194    };
195
196    /** Token types for relation operator. */
197    private static final int[] RELATIONAL_OPERATOR = {
198        TokenTypes.LITERAL_INSTANCEOF,
199        TokenTypes.GT,
200        TokenTypes.LT,
201        TokenTypes.GE,
202        TokenTypes.LE,
203        TokenTypes.EQUAL,
204        TokenTypes.NOT_EQUAL,
205    };
206
207    /** Token types for unary and postfix operators. */
208    private static final int[] UNARY_AND_POSTFIX = {
209        TokenTypes.UNARY_MINUS,
210        TokenTypes.UNARY_PLUS,
211        TokenTypes.INC,
212        TokenTypes.DEC,
213        TokenTypes.LNOT,
214        TokenTypes.BNOT,
215        TokenTypes.POST_INC,
216        TokenTypes.POST_DEC,
217    };
218
219    /** Types of tokens with higher priority than unary operators. */
220    private static final int[] ARRAY_AND_FIELD_ACCESS = {
221        TokenTypes.INDEX_OP,
222        TokenTypes.DOT,
223        TokenTypes.LITERAL_NEW,
224    };
225
226    /** Token types for bitwise binary operator. */
227    private static final int[] BITWISE_BINARY_OPERATORS = {
228        TokenTypes.BXOR,
229        TokenTypes.BOR,
230        TokenTypes.BAND,
231    };
232
233    /**
234     * Used to test if logging a warning in a parent node may be skipped
235     * because a warning was already logged on an immediate child node.
236     */
237    private DetailAST parentToSkip;
238    /** Depth of nested assignments.  Normally this will be 0 or 1. */
239    private int assignDepth;
240
241    /**
242     * Creates a new {@code UnnecessaryParenthesesCheck} instance.
243     */
244    public UnnecessaryParenthesesCheck() {
245        // no code by default
246    }
247
248    @Override
249    public int[] getDefaultTokens() {
250        return new int[] {
251            TokenTypes.EXPR,
252            TokenTypes.IDENT,
253            TokenTypes.NUM_DOUBLE,
254            TokenTypes.NUM_FLOAT,
255            TokenTypes.NUM_INT,
256            TokenTypes.NUM_LONG,
257            TokenTypes.STRING_LITERAL,
258            TokenTypes.LITERAL_NULL,
259            TokenTypes.LITERAL_FALSE,
260            TokenTypes.LITERAL_TRUE,
261            TokenTypes.ASSIGN,
262            TokenTypes.BAND_ASSIGN,
263            TokenTypes.BOR_ASSIGN,
264            TokenTypes.BSR_ASSIGN,
265            TokenTypes.BXOR_ASSIGN,
266            TokenTypes.DIV_ASSIGN,
267            TokenTypes.MINUS_ASSIGN,
268            TokenTypes.MOD_ASSIGN,
269            TokenTypes.PLUS_ASSIGN,
270            TokenTypes.SL_ASSIGN,
271            TokenTypes.SR_ASSIGN,
272            TokenTypes.STAR_ASSIGN,
273            TokenTypes.LAMBDA,
274            TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
275            TokenTypes.LAND,
276            TokenTypes.LOR,
277            TokenTypes.LITERAL_INSTANCEOF,
278            TokenTypes.GT,
279            TokenTypes.LT,
280            TokenTypes.GE,
281            TokenTypes.LE,
282            TokenTypes.EQUAL,
283            TokenTypes.NOT_EQUAL,
284            TokenTypes.UNARY_MINUS,
285            TokenTypes.UNARY_PLUS,
286            TokenTypes.INC,
287            TokenTypes.DEC,
288            TokenTypes.LNOT,
289            TokenTypes.BNOT,
290            TokenTypes.POST_INC,
291            TokenTypes.POST_DEC,
292            TokenTypes.INDEX_OP,
293            TokenTypes.DOT,
294            TokenTypes.TYPECAST,
295        };
296    }
297
298    @Override
299    public int[] getAcceptableTokens() {
300        return new int[] {
301            TokenTypes.EXPR,
302            TokenTypes.IDENT,
303            TokenTypes.NUM_DOUBLE,
304            TokenTypes.NUM_FLOAT,
305            TokenTypes.NUM_INT,
306            TokenTypes.NUM_LONG,
307            TokenTypes.STRING_LITERAL,
308            TokenTypes.LITERAL_NULL,
309            TokenTypes.LITERAL_FALSE,
310            TokenTypes.LITERAL_TRUE,
311            TokenTypes.ASSIGN,
312            TokenTypes.BAND_ASSIGN,
313            TokenTypes.BOR_ASSIGN,
314            TokenTypes.BSR_ASSIGN,
315            TokenTypes.BXOR_ASSIGN,
316            TokenTypes.DIV_ASSIGN,
317            TokenTypes.MINUS_ASSIGN,
318            TokenTypes.MOD_ASSIGN,
319            TokenTypes.PLUS_ASSIGN,
320            TokenTypes.SL_ASSIGN,
321            TokenTypes.SR_ASSIGN,
322            TokenTypes.STAR_ASSIGN,
323            TokenTypes.LAMBDA,
324            TokenTypes.TEXT_BLOCK_LITERAL_BEGIN,
325            TokenTypes.LAND,
326            TokenTypes.LOR,
327            TokenTypes.LITERAL_INSTANCEOF,
328            TokenTypes.GT,
329            TokenTypes.LT,
330            TokenTypes.GE,
331            TokenTypes.LE,
332            TokenTypes.EQUAL,
333            TokenTypes.NOT_EQUAL,
334            TokenTypes.UNARY_MINUS,
335            TokenTypes.UNARY_PLUS,
336            TokenTypes.INC,
337            TokenTypes.DEC,
338            TokenTypes.LNOT,
339            TokenTypes.BNOT,
340            TokenTypes.POST_INC,
341            TokenTypes.POST_DEC,
342            TokenTypes.BXOR,
343            TokenTypes.BOR,
344            TokenTypes.BAND,
345            TokenTypes.QUESTION,
346            TokenTypes.INDEX_OP,
347            TokenTypes.DOT,
348            TokenTypes.LITERAL_NEW,
349            TokenTypes.TYPECAST,
350        };
351    }
352
353    @Override
354    public int[] getRequiredTokens() {
355        // Check can work with any of acceptable tokens
356        return CommonUtil.EMPTY_INT_ARRAY;
357    }
358
359    // -@cs[CyclomaticComplexity] All logs should be in visit token.
360    @Override
361    public void visitToken(DetailAST ast) {
362        final DetailAST parent = ast.getParent();
363
364        if (isLambdaSingleParameterSurrounded(ast)) {
365            log(ast, MSG_LAMBDA);
366        }
367        else if (ast.getType() == TokenTypes.QUESTION) {
368            getParenthesesChildrenAroundQuestion(ast)
369                .forEach(unnecessaryChild -> log(unnecessaryChild, MSG_EXPR));
370        }
371        else if (parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
372            final int type = ast.getType();
373            final boolean surrounded = isSurrounded(getSelfOrParentMethodCall(ast));
374            // An identifier surrounded by parentheses.
375            if (surrounded && type == TokenTypes.IDENT) {
376                parentToSkip = ast.getParent();
377                log(ast, MSG_IDENT, ast.getText());
378            }
379            // A literal (numeric or string) surrounded by parentheses.
380            else if (surrounded && TokenUtil.isOfType(type, LITERALS)) {
381                parentToSkip = ast.getParent();
382                logLiteral(ast, type);
383            }
384            // The rhs of an assignment surrounded by parentheses.
385            else if (TokenUtil.isOfType(type, ASSIGNMENTS)) {
386                assignDepth++;
387                final DetailAST last = ast.getLastChild();
388                if (last.getType() == TokenTypes.RPAREN) {
389                    log(ast, MSG_ASSIGN);
390                }
391            }
392            // A type cast surrounded by parentheses.
393            else if (surrounded && type == TokenTypes.TYPECAST) {
394                logUnnecessaryTypeCast(ast);
395            }
396        }
397    }
398
399    /**
400     * Logs the appropriate message for a parenthesized literal.
401     *
402     * @param ast the literal token
403     * @param type the token type
404     */
405    private void logLiteral(DetailAST ast, int type) {
406        if (type == TokenTypes.STRING_LITERAL) {
407            log(ast, MSG_STRING,
408                chopString(ast.getText()));
409        }
410        else if (type == TokenTypes.TEXT_BLOCK_LITERAL_BEGIN) {
411            // Strip newline control characters to keep message as single-line, add
412            // quotes to make string consistent with STRING_LITERAL
413            final String logString = QUOTE
414                + NEWLINE.matcher(
415                    ast.getFirstChild().getText()).replaceAll("\\\\n")
416                + QUOTE;
417            log(ast, MSG_STRING, chopString(logString));
418        }
419        else {
420            log(ast, MSG_LITERAL, ast.getText());
421        }
422    }
423
424    /**
425     * Logs a warning for a surrounded TYPECAST when the outer parentheses are
426     * not required by member access, method reference, or another rule's report.
427     *
428     * @param ast the TYPECAST node
429     */
430    private void logUnnecessaryTypeCast(DetailAST ast) {
431        final DetailAST parent = ast.getParent();
432        final int parentType = parent.getType();
433        final boolean isWrappedByOtherRule =
434                parentType == TokenTypes.EXPR
435                || TokenUtil.isOfType(parentType, ASSIGNMENTS);
436        final boolean isReceiverOfMemberAccess =
437                parentType == TokenTypes.DOT
438                || parentType == TokenTypes.INDEX_OP
439                || parentType == TokenTypes.METHOD_REF;
440        if (!isWrappedByOtherRule && !isReceiverOfMemberAccess) {
441            log(ast.getPreviousSibling(), MSG_EXPR);
442        }
443    }
444
445    @Override
446    public void leaveToken(DetailAST ast) {
447        final int type = ast.getType();
448        final DetailAST parent = ast.getParent();
449
450        // shouldn't process assign in annotation pairs
451        if (type != TokenTypes.ASSIGN
452            || parent.getType() != TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR) {
453            final DetailAST selfOrParentMethodCall = getSelfOrParentMethodCall(ast);
454            if (type == TokenTypes.EXPR) {
455                checkExpression(ast);
456            }
457            else if (TokenUtil.isOfType(type, ASSIGNMENTS)) {
458                assignDepth--;
459            }
460            else if (isSurrounded(selfOrParentMethodCall) && unnecessaryParenAroundOperators(ast)) {
461                log(selfOrParentMethodCall.getPreviousSibling(), MSG_EXPR);
462            }
463        }
464    }
465
466    /**
467     * Get the node itself ot its parent, if it's a method call.
468     *
469     * @param ast AST node
470     * @return node or its parent
471     */
472    private static DetailAST getSelfOrParentMethodCall(DetailAST ast) {
473        DetailAST selfOrParent = ast;
474        if (ast.getParent().getType() == TokenTypes.METHOD_CALL) {
475            selfOrParent = ast.getParent();
476        }
477        return selfOrParent;
478    }
479
480    /**
481     * Tests if the given {@code DetailAST} is surrounded by parentheses.
482     *
483     * @param ast the {@code DetailAST} to check if it is surrounded by
484     *        parentheses.
485     * @return {@code true} if {@code ast} is surrounded by
486     *         parentheses.
487     */
488    private static boolean isSurrounded(DetailAST ast) {
489        final DetailAST prev = ast.getPreviousSibling();
490        return prev != null && prev.getType() == TokenTypes.LPAREN;
491    }
492
493    /**
494     * Tests if the given expression node is surrounded by parentheses.
495     *
496     * @param ast a {@code DetailAST} whose type is
497     *        {@code TokenTypes.EXPR}.
498     * @return {@code true} if the expression is surrounded by
499     *         parentheses.
500     */
501    private static boolean isExprSurrounded(DetailAST ast) {
502        return ast.getFirstChild().getType() == TokenTypes.LPAREN;
503    }
504
505    /**
506     * Checks whether an expression is surrounded by parentheses.
507     *
508     * @param ast the {@code DetailAST} to check if it is surrounded by
509     *        parentheses.
510     */
511    private void checkExpression(DetailAST ast) {
512        // If 'parentToSkip' == 'ast', then we've already logged a
513        // warning about an immediate child node in visitToken, so we don't
514        // need to log another one here.
515        if (parentToSkip != ast && isExprSurrounded(ast)) {
516            if (ast.getParent().getType() == TokenTypes.LITERAL_RETURN) {
517                log(ast, MSG_RETURN);
518            }
519            else if (assignDepth >= 1) {
520                log(ast, MSG_ASSIGN);
521            }
522            else {
523                log(ast, MSG_EXPR);
524            }
525        }
526    }
527
528    /**
529     * Checks if conditional, relational, bitwise binary operator, unary and postfix operators
530     * in expressions are surrounded by unnecessary parentheses.
531     *
532     * @param ast the {@code DetailAST} to check if it is surrounded by
533     *        unnecessary parentheses.
534     * @return {@code true} if the expression is surrounded by
535     *         unnecessary parentheses.
536     */
537    private static boolean unnecessaryParenAroundOperators(DetailAST ast) {
538        final int type = ast.getType();
539        final boolean isConditionalOrRelational = TokenUtil.isOfType(type, CONDITIONAL_OPERATOR)
540                        || TokenUtil.isOfType(type, RELATIONAL_OPERATOR);
541        final boolean isBitwise = TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS);
542        final boolean hasUnnecessaryParentheses;
543        if (isConditionalOrRelational) {
544            hasUnnecessaryParentheses = checkConditionalOrRelationalOperator(ast);
545        }
546        else if (isBitwise) {
547            hasUnnecessaryParentheses = checkBitwiseBinaryOperator(ast);
548        }
549        else if (TokenUtil.isOfType(type, ARRAY_AND_FIELD_ACCESS)) {
550            hasUnnecessaryParentheses = isNotFirstArgOfTernary(getSelfOrParentMethodCall(ast));
551        }
552        else {
553            hasUnnecessaryParentheses = TokenUtil.isOfType(type, UNARY_AND_POSTFIX)
554                    && isBitWiseBinaryOrConditionalOrRelationalOperator(ast.getParent().getType());
555        }
556        return hasUnnecessaryParentheses;
557    }
558
559    /**
560     * Check that an expression is not the first argument of a conditional ternary operator.
561     *
562     * @param ast expression
563     * @return whether the expression is not the first argument of a ternary operator
564     */
565    private static boolean isNotFirstArgOfTernary(DetailAST ast) {
566        return ast.getParent().getType() != TokenTypes.QUESTION
567                || !ast.equals(ast.getParent().getFirstChild().getNextSibling());
568    }
569
570    /**
571     * Check if conditional or relational operator has unnecessary parentheses.
572     *
573     * @param ast to check if surrounded by unnecessary parentheses
574     * @return true if unnecessary parenthesis
575     */
576    private static boolean checkConditionalOrRelationalOperator(DetailAST ast) {
577        final int type = ast.getType();
578        final int parentType = ast.getParent().getType();
579        final boolean isParentEqualityOperator =
580                TokenUtil.isOfType(parentType, TokenTypes.EQUAL, TokenTypes.NOT_EQUAL);
581        final boolean result;
582        if (type == TokenTypes.LOR) {
583            result = !TokenUtil.isOfType(parentType, TokenTypes.LAND)
584                    && !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS);
585        }
586        else if (type == TokenTypes.LAND) {
587            result = !TokenUtil.isOfType(parentType, BITWISE_BINARY_OPERATORS);
588        }
589        else {
590            result = true;
591        }
592        return result && !isParentEqualityOperator
593                && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType);
594    }
595
596    /**
597     * Check if bitwise binary operator has unnecessary parentheses.
598     *
599     * @param ast to check if surrounded by unnecessary parentheses
600     * @return true if unnecessary parenthesis
601     */
602    private static boolean checkBitwiseBinaryOperator(DetailAST ast) {
603        final int type = ast.getType();
604        final int parentType = ast.getParent().getType();
605        final boolean result;
606        if (type == TokenTypes.BOR) {
607            result = !TokenUtil.isOfType(parentType, TokenTypes.BAND, TokenTypes.BXOR)
608                    && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
609        }
610        else if (type == TokenTypes.BXOR) {
611            result = !TokenUtil.isOfType(parentType, TokenTypes.BAND)
612                    && !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
613        }
614        // we deal with bitwise AND here.
615        else {
616            result = !TokenUtil.isOfType(parentType, RELATIONAL_OPERATOR);
617        }
618        return result && isBitWiseBinaryOrConditionalOrRelationalOperator(parentType);
619    }
620
621    /**
622     * Check if token type is bitwise binary or conditional or relational operator.
623     *
624     * @param type Token type to check
625     * @return true if it is bitwise binary or conditional operator
626     */
627    private static boolean isBitWiseBinaryOrConditionalOrRelationalOperator(int type) {
628        return TokenUtil.isOfType(type, CONDITIONAL_OPERATOR)
629                || TokenUtil.isOfType(type, RELATIONAL_OPERATOR)
630                || TokenUtil.isOfType(type, BITWISE_BINARY_OPERATORS);
631    }
632
633    /**
634     * Tests if the given node has a single parameter, no defined type, and is surrounded
635     * by parentheses. This condition can only be true for lambdas.
636     *
637     * @param ast a {@code DetailAST} node
638     * @return {@code true} if the lambda has a single parameter, no defined type, and is
639     *         surrounded by parentheses.
640     */
641    private static boolean isLambdaSingleParameterSurrounded(DetailAST ast) {
642        final DetailAST firstChild = ast.getFirstChild();
643        boolean result = false;
644        if (TokenUtil.isOfType(firstChild, TokenTypes.LPAREN)) {
645            final DetailAST parameters = firstChild.getNextSibling();
646            if (parameters.getChildCount(TokenTypes.PARAMETER_DEF) == 1
647                    && !parameters.getFirstChild().findFirstToken(TokenTypes.TYPE).hasChildren()) {
648                result = true;
649            }
650        }
651        return result;
652    }
653
654    /**
655     *  Returns the direct LPAREN tokens children to a given QUESTION token which
656     *  contain an expression not a literal variable.
657     *
658     *  @param questionToken {@code DetailAST} question token to be checked
659     *  @return the direct children to the given question token which their types are LPAREN
660     *          tokens and not contain a literal inside the parentheses
661     */
662    private static List<DetailAST> getParenthesesChildrenAroundQuestion(DetailAST questionToken) {
663        final List<DetailAST> surroundedChildren = new ArrayList<>();
664        DetailAST directChild = questionToken.getFirstChild();
665        while (directChild != null) {
666            if (directChild.getType() == TokenTypes.LPAREN
667                    && !TokenUtil.isOfType(directChild.getNextSibling(), LITERALS)) {
668                surroundedChildren.add(directChild);
669            }
670            directChild = directChild.getNextSibling();
671        }
672        return Collections.unmodifiableList(surroundedChildren);
673    }
674
675    /**
676     * Returns the specified string chopped to {@code MAX_QUOTED_LENGTH}
677     * plus an ellipsis (...) if the length of the string exceeds {@code
678     * MAX_QUOTED_LENGTH}.
679     *
680     * @param value the string to potentially chop.
681     * @return the chopped string if {@code string} is longer than
682     *         {@code MAX_QUOTED_LENGTH}; otherwise {@code string}.
683     */
684    private static String chopString(String value) {
685        String result = value;
686        if (value.length() > MAX_QUOTED_LENGTH) {
687            result = value.substring(0, MAX_QUOTED_LENGTH) + "...\"";
688        }
689        return result;
690    }
691
692}