001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.sizes; 021 022import com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026 027/** 028 * <div> 029 * Checks lambda body length. 030 * </div> 031 * 032 * <p> 033 * Rationale: Similar to anonymous inner classes, if lambda body becomes very long 034 * it is hard to understand and to see the flow of the method 035 * where the lambda is defined. Therefore, long lambda body 036 * should usually be extracted to method. 037 * </p> 038 * 039 * @since 8.37 040 */ 041@StatelessCheck 042public class LambdaBodyLengthCheck extends AbstractCheck { 043 044 /** 045 * A key is pointing to the warning message text in "messages.properties" 046 * file. 047 */ 048 public static final String MSG_KEY = "maxLen.lambdaBody"; 049 050 /** Default maximum number of lines. */ 051 private static final int DEFAULT_MAX = 10; 052 053 /** Specify the maximum number of lines allowed. */ 054 private int max = DEFAULT_MAX; 055 056 /** 057 * Setter to specify the maximum number of lines allowed. 058 * 059 * @param length the maximum length of lambda body. 060 * @since 8.37 061 */ 062 public void setMax(int length) { 063 max = length; 064 } 065 066 @Override 067 public int[] getDefaultTokens() { 068 return getRequiredTokens(); 069 } 070 071 @Override 072 public int[] getAcceptableTokens() { 073 return getRequiredTokens(); 074 } 075 076 @Override 077 public int[] getRequiredTokens() { 078 return new int[] {TokenTypes.LAMBDA}; 079 } 080 081 @Override 082 public void visitToken(DetailAST ast) { 083 if (ast.getParent().getType() != TokenTypes.SWITCH_RULE) { 084 final int length = getLength(ast); 085 if (length > max) { 086 log(ast, MSG_KEY, length, max); 087 } 088 } 089 } 090 091 /** 092 * Get length of lambda body. 093 * 094 * @param ast lambda body node. 095 * @return length of lambda body. 096 */ 097 private static int getLength(DetailAST ast) { 098 final DetailAST lambdaBody = ast.getLastChild(); 099 final int length; 100 if (lambdaBody.getType() == TokenTypes.SLIST) { 101 length = lambdaBody.getLastChild().getLineNo() - lambdaBody.getLineNo(); 102 } 103 else { 104 length = getLastNodeLineNumber(lambdaBody) - getFirstNodeLineNumber(lambdaBody); 105 } 106 return length + 1; 107 } 108 109 /** 110 * Get last child node in the tree line number. 111 * 112 * @param lambdaBody lambda body node. 113 * @return last child node in the tree line number. 114 */ 115 private static int getLastNodeLineNumber(DetailAST lambdaBody) { 116 DetailAST node = lambdaBody; 117 int result; 118 do { 119 result = node.getLineNo(); 120 node = node.getLastChild(); 121 } while (node != null); 122 return result; 123 } 124 125 /** 126 * Get first child node in the tree line number. 127 * 128 * @param lambdaBody lambda body node. 129 * @return first child node in the tree line number. 130 */ 131 private static int getFirstNodeLineNumber(DetailAST lambdaBody) { 132 DetailAST node = lambdaBody; 133 int result; 134 do { 135 result = node.getLineNo(); 136 node = node.getFirstChild(); 137 } while (node != null); 138 return result; 139 } 140 141}