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