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