View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.indentation;
21  
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  
24  /**
25   * Handler for lambda expressions.
26   *
27   */
28  public class LambdaHandler extends AbstractExpressionHandler {
29      /**
30       * Checks whether the lambda is correctly indented, this variable get its value from checking
31       * the lambda handler's indentation, and it is being used in aligning the lambda's children.
32       * A true value depicts lambda is correctly aligned without giving any errors.
33       * This is updated to false where there is any Indentation error log.
34       */
35      private boolean isLambdaCorrectlyIndented = true;
36  
37      /**
38       * Construct an instance of this handler with the given indentation check,
39       * abstract syntax tree, and parent handler.
40       *
41       * @param indentCheck the indentation check
42       * @param ast the abstract syntax tree
43       * @param parent the parent handler
44       */
45      public LambdaHandler(IndentationCheck indentCheck,
46                           DetailAST ast, AbstractExpressionHandler parent) {
47          super(indentCheck, "lambda", ast, parent);
48      }
49  
50      @Override
51      public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
52          IndentLevel childIndent = getIndent();
53          if (isLambdaCorrectlyIndented) {
54              childIndent = IndentLevel.addAcceptable(childIndent, getLineStart(getMainAst()),
55                      getLineStart(getMainAst().getFirstChild()));
56          }
57  
58          return childIndent;
59      }
60  
61      /**
62       * {@inheritDoc}.
63       *
64       * @noinspection MethodWithMultipleReturnPoints
65       * @noinspectionreason MethodWithMultipleReturnPoints - indentation is complex and
66       *      tightly coupled, thus making this method difficult to refactor
67       */
68      @Override
69      protected IndentLevel getIndentImpl() {
70          if (getParent() instanceof MethodCallHandler) {
71              return getParent().getSuggestedChildIndent(this);
72          }
73  
74          DetailAST parent = getMainAst().getParent();
75          if (getParent() instanceof NewHandler) {
76              parent = parent.getParent();
77          }
78  
79          // Use the start of the parent's line as the reference indentation level.
80          IndentLevel level = new IndentLevel(getLineStart(parent));
81  
82          // If the start of the lambda is the first element on the line;
83          // assume line wrapping with respect to its parent.
84          final DetailAST firstChild = getMainAst().getFirstChild();
85          if (getLineStart(firstChild) == expandedTabsColumnNo(firstChild)) {
86              level = new IndentLevel(level, getIndentCheck().getLineWrappingIndentation());
87          }
88  
89          return level;
90      }
91  
92      @Override
93      public void checkIndentation() {
94          final DetailAST mainAst = getMainAst();
95          final DetailAST firstChild = mainAst.getFirstChild();
96  
97          // If the "->" has no children, it is a switch
98          // rule lambda (i.e. 'case ONE -> 1;')
99          final boolean isSwitchRuleLambda = firstChild == null;
100 
101         if (!isSwitchRuleLambda
102             && getLineStart(firstChild) == expandedTabsColumnNo(firstChild)) {
103             final int firstChildColumnNo = expandedTabsColumnNo(firstChild);
104             final IndentLevel level = getIndent();
105 
106             if (isNonAcceptableIndent(firstChildColumnNo, level)) {
107                 isLambdaCorrectlyIndented = false;
108                 logError(firstChild, "arguments", firstChildColumnNo, level);
109             }
110         }
111 
112         // If the "->" is the first element on the line, assume line wrapping.
113         final int mainAstColumnNo = expandedTabsColumnNo(mainAst);
114         final boolean isLineWrappedLambda = mainAstColumnNo == getLineStart(mainAst);
115         if (isLineWrappedLambda) {
116             checkLineWrappedLambda(isSwitchRuleLambda, mainAstColumnNo);
117         }
118     }
119 
120     /**
121      * Checks that given indent is acceptable or not.
122      *
123      * @param astColumnNo indent value to check
124      * @param level indent level
125      * @return true if indent is not acceptable
126      */
127     private boolean isNonAcceptableIndent(int astColumnNo, IndentLevel level) {
128         return astColumnNo < level.getFirstIndentLevel()
129             || getIndentCheck().isForceStrictCondition()
130                && !level.isAcceptable(astColumnNo);
131     }
132 
133     /**
134      * This method checks a line wrapped lambda, whether it is a lambda
135      * expression or switch rule lambda.
136      *
137      * @param isSwitchRuleLambda if mainAst is a switch rule lambda
138      * @param mainAstColumnNo the column number of the lambda we are checking
139      */
140     private void checkLineWrappedLambda(final boolean isSwitchRuleLambda,
141                                         final int mainAstColumnNo) {
142         final IndentLevel level;
143         final DetailAST mainAst = getMainAst();
144 
145         if (isSwitchRuleLambda) {
146             // We check the indentation of the case literal or default literal
147             // on the previous line and use that to determine the correct
148             // indentation for the line wrapped "->"
149             final DetailAST previousSibling = mainAst.getPreviousSibling();
150             final int previousLineStart = getLineStart(previousSibling);
151 
152             level = new IndentLevel(new IndentLevel(previousLineStart),
153                     getIndentCheck().getLineWrappingIndentation());
154         }
155         else {
156             level = new IndentLevel(getIndent(),
157                 getIndentCheck().getLineWrappingIndentation());
158         }
159 
160         if (isNonAcceptableIndent(mainAstColumnNo, level)) {
161             isLambdaCorrectlyIndented = false;
162             logError(mainAst, "", mainAstColumnNo, level);
163         }
164     }
165 }