001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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.FileContents; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 028 029/** 030 * <p> 031 * Checks for long methods and constructors. 032 * </p> 033 * <p> 034 * Rationale: If a method becomes very long it is hard to understand. 035 * Therefore long methods should usually be refactored into several 036 * individual methods that focus on a specific task. 037 * </p> 038 * <ul> 039 * <li> 040 * Property {@code max} - Specify the maximum number of lines allowed. 041 * Type is {@code int}. 042 * Default value is {@code 150}. 043 * </li> 044 * <li> 045 * Property {@code countEmpty} - Control whether to count empty lines and single 046 * line comments of the form {@code //}. 047 * Type is {@code boolean}. 048 * Default value is {@code true}. 049 * </li> 050 * <li> 051 * Property {@code tokens} - tokens to check 052 * Type is {@code java.lang.String[]}. 053 * Validation type is {@code tokenSet}. 054 * Default value is: 055 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 056 * METHOD_DEF</a>, 057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 058 * CTOR_DEF</a>, 059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 060 * COMPACT_CTOR_DEF</a>. 061 * </li> 062 * </ul> 063 * <p> 064 * To configure the check: 065 * </p> 066 * <pre> 067 * <module name="MethodLength"/> 068 * </pre> 069 * <p> 070 * To configure the check so that it accepts methods with at most 60 lines: 071 * </p> 072 * <pre> 073 * <module name="MethodLength"> 074 * <property name="tokens" value="METHOD_DEF"/> 075 * <property name="max" value="60"/> 076 * </module> 077 * </pre> 078 * <p> 079 * To configure the check so that it accepts methods with at most 60 lines, 080 * not counting empty lines and single line comments: 081 * </p> 082 * <pre> 083 * <module name="MethodLength"> 084 * <property name="tokens" value="METHOD_DEF"/> 085 * <property name="max" value="60"/> 086 * <property name="countEmpty" value="false"/> 087 * </module> 088 * </pre> 089 * <p> 090 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 091 * </p> 092 * <p> 093 * Violation Message Keys: 094 * </p> 095 * <ul> 096 * <li> 097 * {@code maxLen.method} 098 * </li> 099 * </ul> 100 * 101 * @since 3.0 102 */ 103@StatelessCheck 104public class MethodLengthCheck extends AbstractCheck { 105 106 /** 107 * A key is pointing to the warning message text in "messages.properties" 108 * file. 109 */ 110 public static final String MSG_KEY = "maxLen.method"; 111 112 /** Default maximum number of lines. */ 113 private static final int DEFAULT_MAX_LINES = 150; 114 115 /** Control whether to count empty lines and single line comments of the form {@code //}. */ 116 private boolean countEmpty = true; 117 118 /** Specify the maximum number of lines allowed. */ 119 private int max = DEFAULT_MAX_LINES; 120 121 @Override 122 public int[] getDefaultTokens() { 123 return getAcceptableTokens(); 124 } 125 126 @Override 127 public int[] getAcceptableTokens() { 128 return new int[] { 129 TokenTypes.METHOD_DEF, 130 TokenTypes.CTOR_DEF, 131 TokenTypes.COMPACT_CTOR_DEF, 132 }; 133 } 134 135 @Override 136 public int[] getRequiredTokens() { 137 return CommonUtil.EMPTY_INT_ARRAY; 138 } 139 140 @Override 141 public void visitToken(DetailAST ast) { 142 final DetailAST openingBrace = ast.findFirstToken(TokenTypes.SLIST); 143 if (openingBrace != null) { 144 final DetailAST closingBrace = 145 openingBrace.findFirstToken(TokenTypes.RCURLY); 146 final int length = getLengthOfBlock(openingBrace, closingBrace); 147 if (length > max) { 148 log(ast, MSG_KEY, length, max); 149 } 150 } 151 } 152 153 /** 154 * Returns length of code only without comments and blank lines. 155 * 156 * @param openingBrace block opening brace 157 * @param closingBrace block closing brace 158 * @return number of lines with code for current block 159 */ 160 private int getLengthOfBlock(DetailAST openingBrace, DetailAST closingBrace) { 161 int length = closingBrace.getLineNo() - openingBrace.getLineNo() + 1; 162 163 if (!countEmpty) { 164 final FileContents contents = getFileContents(); 165 final int lastLine = closingBrace.getLineNo(); 166 // lastLine - 1 is actual last line index. Last line is line with curly brace, 167 // which is always not empty. So, we make it lastLine - 2 to cover last line that 168 // actually may be empty. 169 for (int i = openingBrace.getLineNo() - 1; i <= lastLine - 2; i++) { 170 if (contents.lineIsBlank(i) || contents.lineIsComment(i)) { 171 length--; 172 } 173 } 174 } 175 return length; 176 } 177 178 /** 179 * Setter to specify the maximum number of lines allowed. 180 * 181 * @param length the maximum length of a method. 182 */ 183 public void setMax(int length) { 184 max = length; 185 } 186 187 /** 188 * Setter to control whether to count empty lines and single line comments 189 * of the form {@code //}. 190 * 191 * @param countEmpty whether to count empty and single line comments 192 * of the form //. 193 */ 194 public void setCountEmpty(boolean countEmpty) { 195 this.countEmpty = countEmpty; 196 } 197 198}