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