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 }