001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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; 024import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 025 026/** 027 * Handler for parents of blocks ('if', 'else', 'while', etc). 028 * 029 * <P> 030 * The "block" handler classes use a common superclass BlockParentHandler, 031 * employing the Template Method pattern. 032 * </P> 033 * 034 * <UL> 035 * <LI>template method to get the lcurly</LI> 036 * <LI>template method to get the rcurly</LI> 037 * <LI>if curlies aren't present, then template method to get expressions 038 * is called</LI> 039 * <LI>now all the repetitious code which checks for BOL, if curlies are on 040 * same line, etc. can be collapsed into the superclass</LI> 041 * </UL> 042 * 043 */ 044public class BlockParentHandler extends AbstractExpressionHandler { 045 046 /** 047 * Children checked by parent handlers. 048 */ 049 private static final int[] CHECKED_CHILDREN = { 050 TokenTypes.VARIABLE_DEF, 051 TokenTypes.EXPR, 052 TokenTypes.ANNOTATION, 053 TokenTypes.OBJBLOCK, 054 TokenTypes.LITERAL_BREAK, 055 TokenTypes.LITERAL_RETURN, 056 TokenTypes.LITERAL_THROW, 057 TokenTypes.LITERAL_CONTINUE, 058 TokenTypes.CTOR_CALL, 059 TokenTypes.SUPER_CTOR_CALL, 060 }; 061 062 /** 063 * Construct an instance of this handler with the given indentation check, 064 * name, abstract syntax tree, and parent handler. 065 * 066 * @param indentCheck the indentation check 067 * @param name the name of the handler 068 * @param ast the abstract syntax tree 069 * @param parent the parent handler 070 * @noinspection WeakerAccess 071 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 072 */ 073 public BlockParentHandler(IndentationCheck indentCheck, 074 String name, DetailAST ast, AbstractExpressionHandler parent) { 075 super(indentCheck, name, ast, parent); 076 } 077 078 /** 079 * Returns array of token types which should be checked among children. 080 * 081 * @return array of token types to check. 082 */ 083 protected int[] getCheckedChildren() { 084 return CHECKED_CHILDREN.clone(); 085 } 086 087 /** 088 * Get the top level expression being managed by this handler. 089 * 090 * @return the top level expression 091 */ 092 protected DetailAST getTopLevelAst() { 093 return getMainAst(); 094 } 095 096 /** 097 * Check the indent of the top level token. 098 */ 099 protected void checkTopLevelToken() { 100 final DetailAST topLevel = getTopLevelAst(); 101 102 if (topLevel != null 103 && !getIndent().isAcceptable(expandedTabsColumnNo(topLevel)) 104 && isOnStartOfLine(topLevel)) { 105 logError(topLevel, "", expandedTabsColumnNo(topLevel)); 106 } 107 } 108 109 /** 110 * Determines if this block expression has curly braces. 111 * 112 * @return true if curly braces are present, false otherwise 113 */ 114 private boolean hasCurlies() { 115 return getLeftCurly() != null && getRightCurly() != null; 116 } 117 118 /** 119 * Get the left curly brace portion of the expression we are handling. 120 * 121 * @return the left curly brace expression 122 */ 123 protected DetailAST getLeftCurly() { 124 return getMainAst().findFirstToken(TokenTypes.SLIST); 125 } 126 127 /** 128 * Get the right curly brace portion of the expression we are handling. 129 * 130 * @return the right curly brace expression 131 */ 132 protected DetailAST getRightCurly() { 133 final DetailAST slist = getMainAst().findFirstToken(TokenTypes.SLIST); 134 return slist.findFirstToken(TokenTypes.RCURLY); 135 } 136 137 /** 138 * Check the indentation of the left curly brace. 139 */ 140 private void checkLeftCurly() { 141 // the lcurly can either be at the correct indentation, or nested 142 // with a previous expression 143 final DetailAST lcurly = getLeftCurly(); 144 final int lcurlyPos = expandedTabsColumnNo(lcurly); 145 146 if (!curlyIndent().isAcceptable(lcurlyPos) && isOnStartOfLine(lcurly)) { 147 logError(lcurly, "lcurly", lcurlyPos, curlyIndent()); 148 } 149 } 150 151 /** 152 * Get the expected indentation level for the curly braces. 153 * 154 * @return the curly brace indentation level 155 */ 156 protected IndentLevel curlyIndent() { 157 final DetailAST lcurly = getLeftCurly(); 158 IndentLevel expIndentLevel = new IndentLevel(getIndent(), getBraceAdjustment()); 159 if (!isOnStartOfLine(lcurly) || checkIfCodeBlock()) { 160 expIndentLevel = new IndentLevel(getIndent(), 0); 161 } 162 return expIndentLevel; 163 } 164 165 /** 166 * Checks if lcurly is a Code block. 167 * 168 * @return true if lcurly is a code block 169 */ 170 private boolean checkIfCodeBlock() { 171 final AbstractExpressionHandler parent = getParent(); 172 return getMainAst().getType() == TokenTypes.SLIST 173 && parent instanceof BlockParentHandler 174 && parent.getParent() instanceof BlockParentHandler; 175 } 176 177 /** 178 * Determines if child elements within the expression may be nested. 179 * 180 * @return false 181 */ 182 protected boolean canChildrenBeNested() { 183 return false; 184 } 185 186 /** 187 * Check the indentation of the right curly brace. 188 */ 189 private void checkRightCurly() { 190 final DetailAST rcurly = getRightCurly(); 191 final int rcurlyPos = expandedTabsColumnNo(rcurly); 192 193 if (!curlyIndent().isAcceptable(rcurlyPos) 194 && isOnStartOfLine(rcurly)) { 195 logError(rcurly, "rcurly", rcurlyPos, curlyIndent()); 196 } 197 } 198 199 /** 200 * Get the child element that is not a list of statements. 201 * 202 * @return the non-list child element 203 */ 204 protected DetailAST getNonListChild() { 205 return getMainAst().findFirstToken(TokenTypes.RPAREN).getNextSibling(); 206 } 207 208 /** 209 * Check the indentation level of a child that is not a list of statements. 210 */ 211 private void checkNonListChild() { 212 final DetailAST nonList = getNonListChild(); 213 if (nonList != null) { 214 final IndentLevel expected = new IndentLevel(getIndent(), getBasicOffset()); 215 checkExpressionSubtree(nonList, expected, false, false); 216 217 final DetailAST nonListStartAst = getFirstAstNode(nonList); 218 if (nonList != nonListStartAst) { 219 checkExpressionSubtree(nonListStartAst, expected, false, false); 220 } 221 } 222 } 223 224 /** 225 * Get the child element representing the list of statements. 226 * 227 * @return the statement list child 228 */ 229 protected DetailAST getListChild() { 230 return getMainAst().findFirstToken(TokenTypes.SLIST); 231 } 232 233 /** 234 * Determines whether left parenthesis should be checked. 235 * 236 * @param leftParen left parenthesis token 237 * @return true if left parenthesis should be checked 238 */ 239 protected boolean shouldCheckLeftParen(DetailAST leftParen) { 240 return true; 241 } 242 243 /** 244 * Determines whether right parenthesis should be checked. 245 * 246 * @param leftParen left parenthesis token 247 * @param rightParen right parenthesis token 248 * @return true if right parenthesis should be checked 249 */ 250 protected boolean shouldCheckRightParen(DetailAST leftParen, DetailAST rightParen) { 251 return true; 252 } 253 254 /** 255 * Get the right parenthesis portion of the expression we are handling. 256 * 257 * @return the right parenthesis expression 258 */ 259 private DetailAST getRightParen() { 260 return getMainAst().findFirstToken(TokenTypes.RPAREN); 261 } 262 263 /** 264 * Get the left parenthesis portion of the expression we are handling. 265 * 266 * @return the left parenthesis expression 267 */ 268 private DetailAST getLeftParen() { 269 return getMainAst().findFirstToken(TokenTypes.LPAREN); 270 } 271 272 @Override 273 public void checkIndentation() { 274 checkTopLevelToken(); 275 // separate to allow for eventual configuration 276 final DetailAST leftParen = getLeftParen(); 277 if (shouldCheckLeftParen(leftParen)) { 278 checkLeftParen(leftParen); 279 } 280 final DetailAST rightParen = getRightParen(); 281 if (shouldCheckRightParen(leftParen, rightParen)) { 282 checkRightParen(leftParen, rightParen); 283 } 284 if (hasCurlies()) { 285 checkLeftCurly(); 286 checkRightCurly(); 287 } 288 final DetailAST listChild = getListChild(); 289 if (listChild == null) { 290 checkNonListChild(); 291 } 292 else { 293 // NOTE: switch statements usually don't have curlies 294 if (!hasCurlies() || !TokenUtil.areOnSameLine(getLeftCurly(), getRightCurly())) { 295 checkChildren(listChild, 296 getCheckedChildren(), 297 getChildrenExpectedIndent(), 298 true, 299 canChildrenBeNested()); 300 } 301 } 302 } 303 304 /** 305 * Gets indentation level expected for children. 306 * 307 * @return indentation level expected for children 308 */ 309 protected IndentLevel getChildrenExpectedIndent() { 310 IndentLevel indentLevel = new IndentLevel(getIndent(), getBasicOffset()); 311 // if we have multileveled expected level then we should 312 // try to suggest single level to children using curlies' 313 // levels. 314 if (getIndent().isMultiLevel() && hasCurlies()) { 315 final DetailAST leftCurly = getLeftCurly(); 316 if (isOnStartOfLine(leftCurly)) { 317 indentLevel = new IndentLevel(expandedTabsColumnNo(leftCurly) 318 + getBasicOffset()); 319 } 320 else if (isOnStartOfLine(getRightCurly())) { 321 final IndentLevel level = new IndentLevel(curlyIndent(), getBasicOffset()); 322 indentLevel = IndentLevel.addAcceptable(level, level.getFirstIndentLevel() 323 + getLineWrappingIndent()); 324 } 325 } 326 if (hasCurlies() && isOnStartOfLine(getLeftCurly())) { 327 indentLevel = IndentLevel.addAcceptable(indentLevel, 328 curlyIndent().getFirstIndentLevel() + getBasicOffset()); 329 } 330 return indentLevel; 331 } 332 333 @Override 334 public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) { 335 return getChildrenExpectedIndent(); 336 } 337 338 /** 339 * A shortcut for {@code IndentationCheck} property. 340 * 341 * @return value of lineWrappingIndentation property 342 * of {@code IndentationCheck} 343 */ 344 private int getLineWrappingIndent() { 345 return getIndentCheck().getLineWrappingIndentation(); 346 } 347 348}