001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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.indentation; 021 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024 025/** 026 * Handler for method definitions. 027 * 028 * @author jrichard 029 * @author Maikel Steneker 030 */ 031public class MethodDefHandler extends BlockParentHandler { 032 /** 033 * Construct an instance of this handler with the given indentation check, 034 * abstract syntax tree, and parent handler. 035 * 036 * @param indentCheck the indentation check 037 * @param ast the abstract syntax tree 038 * @param parent the parent handler 039 */ 040 public MethodDefHandler(IndentationCheck indentCheck, 041 DetailAST ast, AbstractExpressionHandler parent) { 042 super(indentCheck, getHandlerName(ast), ast, parent); 043 } 044 045 @Override 046 protected DetailAST getTopLevelAst() { 047 // we check this stuff ourselves below 048 return null; 049 } 050 051 @Override 052 protected void checkModifiers() { 053 final DetailAST modifier = getMainAst().findFirstToken(TokenTypes.MODIFIERS); 054 if (isOnStartOfLine(modifier) 055 && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) { 056 logError(modifier, "modifier", expandedTabsColumnNo(modifier)); 057 } 058 } 059 060 /** 061 * Check the indentation level of the throws clause. 062 */ 063 private void checkThrows() { 064 final DetailAST throwsAst = getMainAst().findFirstToken(TokenTypes.LITERAL_THROWS); 065 066 if (throwsAst != null) { 067 checkWrappingIndentation(throwsAst, throwsAst.getNextSibling(), getIndentCheck() 068 .getThrowsIndent(), getLineStart(getMethodDefLineStart(getMainAst())), 069 !isOnStartOfLine(throwsAst)); 070 } 071 } 072 073 /** 074 * Gets the start line of the method, excluding any annotations. This is required because the 075 * current {@link TokenTypes#METHOD_DEF} may not always be the start as seen in 076 * https://github.com/checkstyle/checkstyle/issues/3145. 077 * 078 * @param mainAst 079 * The method definition ast. 080 * @return The start column position of the method. 081 */ 082 private int getMethodDefLineStart(DetailAST mainAst) { 083 // get first type position 084 int lineStart = mainAst.findFirstToken(TokenTypes.IDENT).getLineNo(); 085 086 // check if there is a type before the indent 087 final DetailAST typeNode = mainAst.findFirstToken(TokenTypes.TYPE); 088 if (typeNode != null) { 089 lineStart = getFirstLine(lineStart, typeNode); 090 } 091 092 // check if there is a modifier before the type 093 for (DetailAST node = mainAst.findFirstToken(TokenTypes.MODIFIERS).getFirstChild(); 094 node != null; 095 node = node.getNextSibling()) { 096 // skip annotations as we check them else where as outside the method 097 if (node.getType() == TokenTypes.ANNOTATION) { 098 continue; 099 } 100 101 if (node.getLineNo() < lineStart) { 102 lineStart = node.getLineNo(); 103 } 104 } 105 106 return lineStart; 107 } 108 109 @Override 110 public void checkIndentation() { 111 checkModifiers(); 112 checkThrows(); 113 114 checkWrappingIndentation(getMainAst(), getMethodDefParamRightParen(getMainAst())); 115 // abstract method def -- no body 116 if (getLeftCurly() != null) { 117 super.checkIndentation(); 118 } 119 } 120 121 /** 122 * Returns right parenthesis of method definition parameter list. 123 * @param methodDefAst 124 * method definition ast node(TokenTypes.LITERAL_IF) 125 * @return right parenthesis of method definition parameter list. 126 */ 127 private static DetailAST getMethodDefParamRightParen(DetailAST methodDefAst) { 128 return methodDefAst.findFirstToken(TokenTypes.RPAREN); 129 } 130 131 /** 132 * Creates a handler name for this class according to ast type. 133 * 134 * @param ast the abstract syntax tree. 135 * @return handler name for this class. 136 */ 137 private static String getHandlerName(DetailAST ast) { 138 final String name; 139 140 if (ast.getType() == TokenTypes.CTOR_DEF) { 141 name = "ctor def"; 142 } 143 else if (ast.getType() == TokenTypes.ANNOTATION_FIELD_DEF) { 144 name = "annotation field def"; 145 } 146 else { 147 name = "method def"; 148 } 149 return name; 150 } 151}