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