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