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 lambda expressions.
027 *
028 */
029public class LambdaHandler extends AbstractExpressionHandler {
030    /**
031     * Checks whether the lambda is correctly indented, this variable get its value from checking
032     * the lambda handler's indentation, and it is being used in aligning the lambda's children.
033     * A true value depicts lambda is correctly aligned without giving any errors.
034     * This is updated to false where there is any Indentation error log.
035     */
036    private boolean isLambdaCorrectlyIndented = true;
037
038    /**
039     * Construct an instance of this handler with the given indentation check,
040     * abstract syntax tree, and parent handler.
041     *
042     * @param indentCheck the indentation check
043     * @param ast the abstract syntax tree
044     * @param parent the parent handler
045     */
046    public LambdaHandler(IndentationCheck indentCheck,
047                         DetailAST ast, AbstractExpressionHandler parent) {
048        super(indentCheck, "lambda", ast, parent);
049    }
050
051    @Override
052    public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
053        IndentLevel childIndent = getIndent();
054        if (isLambdaCorrectlyIndented) {
055            childIndent = IndentLevel.addAcceptable(childIndent, getLineStart(getMainAst()),
056                    getLineStart(getMainAst().getFirstChild()));
057        }
058
059        return childIndent;
060    }
061
062    /**
063     * {@inheritDoc}.
064     *
065     * @noinspection MethodWithMultipleReturnPoints
066     */
067    @Override
068    protected IndentLevel getIndentImpl() {
069        if (getParent() instanceof MethodCallHandler) {
070            return getParent().getSuggestedChildIndent(this);
071        }
072
073        DetailAST parent = getMainAst().getParent();
074        if (getParent() instanceof NewHandler) {
075            parent = parent.getParent();
076        }
077
078        // Use the start of the parent's line as the reference indentation level.
079        IndentLevel level = new IndentLevel(getLineStart(parent));
080
081        // If the start of the lambda is the first element on the line;
082        // assume line wrapping with respect to its parent.
083        final DetailAST firstChild = getMainAst().getFirstChild();
084        if (getLineStart(firstChild) == expandedTabsColumnNo(firstChild)) {
085            level = new IndentLevel(level, getIndentCheck().getLineWrappingIndentation());
086        }
087
088        return level;
089    }
090
091    @Override
092    public void checkIndentation() {
093        // If the argument list is the first element on the line
094        final DetailAST firstChild = getMainAst().getFirstChild();
095        final DetailAST parent = getMainAst().getParent();
096
097        if (parent.getType() != TokenTypes.SWITCH_RULE
098                && getLineStart(firstChild) == expandedTabsColumnNo(firstChild)) {
099            final int firstChildColumnNo = expandedTabsColumnNo(firstChild);
100            final IndentLevel level = getIndent();
101
102            if (isNonAcceptableIndent(firstChildColumnNo, level)) {
103                isLambdaCorrectlyIndented = false;
104                logError(firstChild, "arguments", firstChildColumnNo, level);
105            }
106        }
107
108        // If the "->" is the first element on the line, assume line wrapping.
109        final int mainAstColumnNo = expandedTabsColumnNo(getMainAst());
110        if (mainAstColumnNo == getLineStart(getMainAst())) {
111            final IndentLevel level =
112                new IndentLevel(getIndent(), getIndentCheck().getLineWrappingIndentation());
113
114            if (isNonAcceptableIndent(mainAstColumnNo, level)) {
115                isLambdaCorrectlyIndented = false;
116                logError(getMainAst(), "", mainAstColumnNo, level);
117            }
118        }
119    }
120
121    private boolean isNonAcceptableIndent(int astColumnNo, IndentLevel level) {
122        return astColumnNo < level.getFirstIndentLevel()
123            || getIndentCheck().isForceStrictCondition()
124               && !level.isAcceptable(astColumnNo);
125    }
126
127}