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.sizes;
21
22 import com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26
27 /**
28 * <div>
29 * Checks lambda body length.
30 * </div>
31 *
32 * <p>
33 * Rationale: Similar to anonymous inner classes, if lambda body becomes very long
34 * it is hard to understand and to see the flow of the method
35 * where the lambda is defined. Therefore, long lambda body
36 * should usually be extracted to method.
37 * </p>
38 *
39 * @since 8.37
40 */
41 @StatelessCheck
42 public class LambdaBodyLengthCheck extends AbstractCheck {
43
44 /**
45 * A key is pointing to the warning message text in "messages.properties"
46 * file.
47 */
48 public static final String MSG_KEY = "maxLen.lambdaBody";
49
50 /** Default maximum number of lines. */
51 private static final int DEFAULT_MAX = 10;
52
53 /** Specify the maximum number of lines allowed. */
54 private int max = DEFAULT_MAX;
55
56 /**
57 * Creates a new {@code LambdaBodyLengthCheck} instance.
58 */
59 public LambdaBodyLengthCheck() {
60 // no code by default
61 }
62
63 /**
64 * Setter to specify the maximum number of lines allowed.
65 *
66 * @param length the maximum length of lambda body.
67 * @since 8.37
68 */
69 public void setMax(int length) {
70 max = length;
71 }
72
73 @Override
74 public int[] getDefaultTokens() {
75 return getRequiredTokens();
76 }
77
78 @Override
79 public int[] getAcceptableTokens() {
80 return getRequiredTokens();
81 }
82
83 @Override
84 public int[] getRequiredTokens() {
85 return new int[] {TokenTypes.LAMBDA};
86 }
87
88 @Override
89 public void visitToken(DetailAST ast) {
90 if (ast.getParent().getType() != TokenTypes.SWITCH_RULE) {
91 final int length = getLength(ast);
92 if (length > max) {
93 log(ast, MSG_KEY, length, max);
94 }
95 }
96 }
97
98 /**
99 * Get length of lambda body.
100 *
101 * @param ast lambda body node.
102 * @return length of lambda body.
103 */
104 private static int getLength(DetailAST ast) {
105 final DetailAST lambdaBody = ast.getLastChild();
106 final int length;
107 if (lambdaBody.getType() == TokenTypes.SLIST) {
108 length = lambdaBody.getLastChild().getLineNo() - lambdaBody.getLineNo();
109 }
110 else {
111 length = getLastNodeLineNumber(lambdaBody) - getFirstNodeLineNumber(lambdaBody);
112 }
113 return length + 1;
114 }
115
116 /**
117 * Get last child node in the tree line number.
118 *
119 * @param lambdaBody lambda body node.
120 * @return last child node in the tree line number.
121 */
122 private static int getLastNodeLineNumber(DetailAST lambdaBody) {
123 DetailAST node = lambdaBody;
124 int result;
125 do {
126 result = node.getLineNo();
127 node = node.getLastChild();
128 } while (node != null);
129 return result;
130 }
131
132 /**
133 * Get first child node in the tree line number.
134 *
135 * @param lambdaBody lambda body node.
136 * @return first child node in the tree line number.
137 */
138 private static int getFirstNodeLineNumber(DetailAST lambdaBody) {
139 DetailAST node = lambdaBody;
140 int result;
141 do {
142 result = node.getLineNo();
143 node = node.getFirstChild();
144 } while (node != null);
145 return result;
146 }
147
148 }