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