View Javadoc
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 }