View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.regex.Pattern;
26  
27  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
28  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29  import com.puppycrawl.tools.checkstyle.api.DetailAST;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
33  
34  /**
35   * <div>
36   * Checks if unnecessary parentheses are used in a statement or expression.
37   * The check will flag the following with warnings:
38   * </div>
39   * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
40   * return (x);          // parens around identifier
41   * return (x + 1);      // parens around return value
42   * int x = (y / 2 + 1); // parens around assignment rhs
43   * for (int i = (0); i &lt; 10; i++) {  // parens around literal
44   * t -= (z + 1);                     // parens around assignment rhs
45   * boolean a = (x &gt; 7 &amp;&amp; y &gt; 5)      // parens around expression
46   *             || z &lt; 9;
47   * boolean b = (~a) &gt; -27            // parens around ~a
48   *             &amp;&amp; (a-- &lt; 30);        // parens around expression
49   * </code></pre></div>
50   *
51   * <p>
52   * Notes:
53   * The check is not "type aware", that is to say, it can't tell if parentheses
54   * are unnecessary based on the types in an expression. The check is partially aware about
55   * operator precedence but unaware about operator associativity.
56   * It won't catch cases such as:
57   * </p>
58   * {@snippet lang="text" :
59   * int x = (a + b) + c; // 1st Case
60   * boolean p = true; // 2nd Case
61   * int q = 4;
62   * int r = 3;
63   * if (p == (q <= r)) {}
64   * }
65   *
66   * <p>
67   * In the first case, given that <em>a</em>, <em>b</em>, and <em>c</em> are
68   * all {@code int} variables, the parentheses around {@code a + b}
69   * are not needed.
70   * In the second case, parentheses are required as <em>q</em>, <em>r</em> are
71   * of type {@code int} and <em>p</em> is of type {@code boolean}
72   * and removing parentheses will give a compile-time error. Even if <em>q</em>
73   * and <em>r</em> were {@code boolean} still there will be no violation
74   * raised as check is not "type aware".
75   * </p>
76   *
77   * <p>
78   * The partial support for operator precedence includes cases of the following type:
79   * </p>
80   * {@snippet lang="text" :
81   * boolean a = true, b = true;
82   * boolean c = false, d = false;
83   * if ((a && b) || c) { // violation, unnecessary paren
84   * }
85   * if (a && (b || c)) { // ok
86   * }
87   * if ((a == b) && c) { // violation, unnecessary paren
88   * }
89   * String e = "e";
90   * if ((e instanceof String) && a || b) { // violation, unnecessary paren
91   * }
92   * int f = 0;
93   * int g = 0;
94   * if (!(f >= g) // ok
95   *         && (g > f)) { // violation, unnecessary paren
96   * }
97   * if ((++f) > g && a) { // violation, unnecessary paren
98   * }
99   * }
100  *
101  * @since 3.4
102  */
103 @FileStatefulCheck
104 public 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 }