View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
25  
26  /**
27   * Handler for switch statements.
28   *
29   */
30  public class SwitchHandler extends BlockParentHandler {
31  
32      /**
33       * Token types that, when appearing as a parent or grandparent of the
34       * current switch expression, indicate that the expression is likely
35       * line-wrapped and should be indented.
36       */
37      private static final int[] LINE_WRAPPING_INDENT_TRIGGERS = {
38          TokenTypes.ASSIGN,
39          TokenTypes.SWITCH_RULE,
40          TokenTypes.LAMBDA,
41      };
42  
43      /**
44       * Construct an instance of this handler with the given indentation check,
45       * abstract syntax tree, and parent handler.
46       *
47       * @param indentCheck   the indentation check
48       * @param ast           the abstract syntax tree
49       * @param parent        the parent handler
50       */
51      public SwitchHandler(IndentationCheck indentCheck,
52          DetailAST ast, AbstractExpressionHandler parent) {
53          super(indentCheck, "switch", ast, parent);
54      }
55  
56      @Override
57      protected DetailAST getLeftCurly() {
58          return getMainAst().findFirstToken(TokenTypes.LCURLY);
59      }
60  
61      @Override
62      protected DetailAST getRightCurly() {
63          return getMainAst().findFirstToken(TokenTypes.RCURLY);
64      }
65  
66      @Override
67      protected DetailAST getListChild() {
68          // all children should be taken care of by case handler (plus
69          // there is no parent of just the cases, if checking is needed
70          // here in the future, an additional way beyond checkChildren()
71          // will have to be devised to get children)
72          return null;
73      }
74  
75      @Override
76      protected DetailAST getNonListChild() {
77          return null;
78      }
79  
80      /**
81       * Check the indentation of the switch expression.
82       */
83      private void checkSwitchExpr() {
84          checkExpressionSubtree(
85              getMainAst().findFirstToken(TokenTypes.LPAREN).getNextSibling(),
86              getIndent(),
87              false,
88              false);
89      }
90  
91      @Override
92      protected IndentLevel getIndentImpl() {
93          IndentLevel indentLevel = super.getIndentImpl();
94          // if switch is starting the line
95          if (isOnStartOfLine(getMainAst())) {
96              final DetailAST parent = getMainAst().getParent();
97              final DetailAST grandParent = parent.getParent();
98  
99              if (shouldIndentDueToWrapping(parent, grandParent)) {
100                 indentLevel = new IndentLevel(indentLevel,
101                     getIndentCheck().getLineWrappingIndentation());
102             }
103         }
104         return indentLevel;
105     }
106 
107     /**
108      * Determines if indentation is needed due to line wrapping caused by intermediate nodes
109      * between the current AST node and its enclosing handler node.
110      *
111      * @param directParent The immediate parent node of the current AST node
112      * @param grandParent The grandparent node of the current AST node
113      * @return true if either the direct parent or grandparent requires additional indentation,
114      *         but only when they are not the enclosing handler node itself
115      */
116     private boolean shouldIndentDueToWrapping(DetailAST directParent, DetailAST grandParent) {
117         // The enclosing handler node that already determines base indentation
118         // (e.g., method declaration containing our current node)
119         final DetailAST enclosingHandlerNode = getParent().getMainAst();
120         final boolean isDirectParentTheHandler = directParent.equals(enclosingHandlerNode);
121 
122         final boolean shouldIndentForDirectParent = !isDirectParentTheHandler
123             && isWrappingTrigger(directParent);
124 
125         // Check if grandparent requires extra indentation (when
126         // neither it nor direct parent is the handler)
127         final boolean shouldIndentForGrandParent = !isDirectParentTheHandler
128             && !grandParent.equals(enclosingHandlerNode)
129             && isWrappingTrigger(grandParent);
130 
131         return shouldIndentForDirectParent || shouldIndentForGrandParent;
132     }
133 
134     /**
135      * Checks if the given AST node represents a construct that typically causes line wrapping
136      * and therefore requires additional indentation level.
137      *
138      * @param astNode The AST node to check
139      * @return true if the node type matches one of the line-wrapping triggers
140      *         (e.g., assignments, switch rules, lambdas)
141      */
142     private static boolean isWrappingTrigger(DetailAST astNode) {
143         return TokenUtil.isOfType(astNode, LINE_WRAPPING_INDENT_TRIGGERS);
144     }
145 
146     @Override
147     public void checkIndentation() {
148         checkSwitchExpr();
149         super.checkIndentation();
150     }
151 
152 }