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