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