001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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 java.util.Optional;
023
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026
027/**
028 * Handler for member definitions.
029 *
030 */
031public class MemberDefHandler extends AbstractExpressionHandler {
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 MemberDefHandler(IndentationCheck indentCheck,
042        DetailAST ast, AbstractExpressionHandler parent) {
043        super(indentCheck, "member def", ast, parent);
044    }
045
046    @Override
047    public void checkIndentation() {
048        final DetailAST modifiersNode = getMainAst().findFirstToken(TokenTypes.MODIFIERS);
049        if (modifiersNode.hasChildren()) {
050            checkModifiers();
051        }
052        else {
053            checkType();
054        }
055        final DetailAST firstNode = getMainAst();
056        final DetailAST lastNode = getArrayInitNode(firstNode)
057            .orElseGet(() -> getVarDefStatementSemicolon(firstNode));
058
059        if (lastNode != null) {
060            checkWrappingIndentation(firstNode, lastNode);
061        }
062    }
063
064    /**
065     * Finds the array init node.
066     *
067     * @param firstNode Node to begin searching
068     * @return array init node
069     */
070    private static Optional<DetailAST> getArrayInitNode(DetailAST firstNode) {
071        return Optional.ofNullable(firstNode.findFirstToken(TokenTypes.ASSIGN))
072            .map(assign -> {
073                return Optional.ofNullable(assign.findFirstToken(TokenTypes.EXPR))
074                    .map(expr -> expr.findFirstToken(TokenTypes.LITERAL_NEW))
075                    .orElse(assign);
076            })
077            .map(node -> node.findFirstToken(TokenTypes.ARRAY_INIT));
078    }
079
080    @Override
081    public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
082        return getIndent();
083    }
084
085    @Override
086    protected void checkModifiers() {
087        final DetailAST modifier = getMainAst().findFirstToken(TokenTypes.MODIFIERS);
088        if (isOnStartOfLine(modifier)
089            && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) {
090            logError(modifier, "modifier", expandedTabsColumnNo(modifier));
091        }
092    }
093
094    /**
095     * Check the indentation of the method type.
096     */
097    private void checkType() {
098        final DetailAST type = getMainAst().findFirstToken(TokenTypes.TYPE);
099        final DetailAST ident = AbstractExpressionHandler.getFirstToken(type);
100        final int columnNo = expandedTabsColumnNo(ident);
101        if (isOnStartOfLine(ident) && !getIndent().isAcceptable(columnNo)) {
102            logError(ident, "type", columnNo);
103        }
104    }
105
106    /**
107     * Returns semicolon for variable definition statement.
108     *
109     * @param variableDef
110     *          ast node of type TokenTypes.VARIABLE_DEF
111     * @return ast node of type TokenTypes.SEMI
112     */
113    private static DetailAST getVarDefStatementSemicolon(DetailAST variableDef) {
114        DetailAST lastNode = variableDef.getLastChild();
115        if (lastNode.getType() != TokenTypes.SEMI) {
116            lastNode = variableDef.getNextSibling();
117        }
118        return lastNode;
119    }
120
121}