001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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 * <P> 029 * The "block" handler classes use a common superclass BlockParentHandler, 030 * employing the Template Method pattern. 031 * </P> 032 * 033 * <UL> 034 * <LI>template method to get the lcurly</LI> 035 * <LI>template method to get the rcurly</LI> 036 * <LI>if curlies aren't present, then template method to get expressions 037 * is called</LI> 038 * <LI>now all the repetitious code which checks for BOL, if curlies are on 039 * same line, etc. can be collapsed into the superclass</LI> 040 * </UL> 041 * 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 */ 072 public BlockParentHandler(IndentationCheck indentCheck, 073 String name, DetailAST ast, AbstractExpressionHandler parent) { 074 super(indentCheck, name, ast, parent); 075 } 076 077 /** 078 * Returns array of token types which should be checked among children. 079 * 080 * @return array of token types to check. 081 */ 082 protected int[] getCheckedChildren() { 083 return CHECKED_CHILDREN.clone(); 084 } 085 086 /** 087 * Get the top level expression being managed by this handler. 088 * 089 * @return the top level expression 090 */ 091 protected DetailAST getTopLevelAst() { 092 return getMainAst(); 093 } 094 095 /** 096 * Check the indent of the top level token. 097 */ 098 protected void checkTopLevelToken() { 099 final DetailAST topLevel = getTopLevelAst(); 100 101 if (topLevel != null 102 && !getIndent().isAcceptable(expandedTabsColumnNo(topLevel)) 103 && isOnStartOfLine(topLevel)) { 104 logError(topLevel, "", expandedTabsColumnNo(topLevel)); 105 } 106 } 107 108 /** 109 * Determines if this block expression has curly braces. 110 * 111 * @return true if curly braces are present, false otherwise 112 */ 113 private boolean hasCurlies() { 114 return getLeftCurly() != null && getRightCurly() != null; 115 } 116 117 /** 118 * Get the left curly brace portion of the expression we are handling. 119 * 120 * @return the left curly brace expression 121 */ 122 protected DetailAST getLeftCurly() { 123 return getMainAst().findFirstToken(TokenTypes.SLIST); 124 } 125 126 /** 127 * Get the right curly brace portion of the expression we are handling. 128 * 129 * @return the right curly brace expression 130 */ 131 protected DetailAST getRightCurly() { 132 final DetailAST slist = getMainAst().findFirstToken(TokenTypes.SLIST); 133 return slist.findFirstToken(TokenTypes.RCURLY); 134 } 135 136 /** 137 * Check the indentation of the left curly brace. 138 */ 139 private void checkLeftCurly() { 140 // the lcurly can either be at the correct indentation, or nested 141 // with a previous expression 142 final DetailAST lcurly = getLeftCurly(); 143 final int lcurlyPos = expandedTabsColumnNo(lcurly); 144 145 if (!curlyIndent().isAcceptable(lcurlyPos) && isOnStartOfLine(lcurly)) { 146 logError(lcurly, "lcurly", lcurlyPos, curlyIndent()); 147 } 148 } 149 150 /** 151 * Get the expected indentation level for the curly braces. 152 * 153 * @return the curly brace indentation level 154 */ 155 protected IndentLevel curlyIndent() { 156 final DetailAST lcurly = getLeftCurly(); 157 IndentLevel expIndentLevel = new IndentLevel(getIndent(), getBraceAdjustment()); 158 if (!isOnStartOfLine(lcurly) 159 || lcurly.getParent().getType() == TokenTypes.INSTANCE_INIT) { 160 expIndentLevel = new IndentLevel(getIndent(), 0); 161 } 162 163 return expIndentLevel; 164 } 165 166 /** 167 * Determines if child elements within the expression may be nested. 168 * 169 * @return false 170 */ 171 protected boolean canChildrenBeNested() { 172 return false; 173 } 174 175 /** 176 * Check the indentation of the right curly brace. 177 */ 178 private void checkRightCurly() { 179 final DetailAST rcurly = getRightCurly(); 180 final int rcurlyPos = expandedTabsColumnNo(rcurly); 181 182 if (!curlyIndent().isAcceptable(rcurlyPos) 183 && isOnStartOfLine(rcurly)) { 184 logError(rcurly, "rcurly", rcurlyPos, curlyIndent()); 185 } 186 } 187 188 /** 189 * Get the child element that is not a list of statements. 190 * 191 * @return the non-list child element 192 */ 193 protected DetailAST getNonListChild() { 194 return getMainAst().findFirstToken(TokenTypes.RPAREN).getNextSibling(); 195 } 196 197 /** 198 * Check the indentation level of a child that is not a list of statements. 199 */ 200 private void checkNonListChild() { 201 final DetailAST nonList = getNonListChild(); 202 if (nonList != null) { 203 final IndentLevel expected = new IndentLevel(getIndent(), getBasicOffset()); 204 checkExpressionSubtree(nonList, expected, false, false); 205 206 final DetailAST nonListStartAst = getFirstAstNode(nonList); 207 if (nonList != nonListStartAst) { 208 checkExpressionSubtree(nonListStartAst, expected, false, false); 209 } 210 } 211 } 212 213 /** 214 * Get the child element representing the list of statements. 215 * 216 * @return the statement list child 217 */ 218 protected DetailAST getListChild() { 219 return getMainAst().findFirstToken(TokenTypes.SLIST); 220 } 221 222 /** 223 * Get the right parenthesis portion of the expression we are handling. 224 * 225 * @return the right parenthesis expression 226 */ 227 private DetailAST getRightParen() { 228 return getMainAst().findFirstToken(TokenTypes.RPAREN); 229 } 230 231 /** 232 * Get the left parenthesis portion of the expression we are handling. 233 * 234 * @return the left parenthesis expression 235 */ 236 private DetailAST getLeftParen() { 237 return getMainAst().findFirstToken(TokenTypes.LPAREN); 238 } 239 240 @Override 241 public void checkIndentation() { 242 checkTopLevelToken(); 243 // separate to allow for eventual configuration 244 checkLeftParen(getLeftParen()); 245 checkRightParen(getLeftParen(), getRightParen()); 246 if (hasCurlies()) { 247 checkLeftCurly(); 248 checkRightCurly(); 249 } 250 final DetailAST listChild = getListChild(); 251 if (listChild == null) { 252 checkNonListChild(); 253 } 254 else { 255 // NOTE: switch statements usually don't have curlies 256 if (!hasCurlies() || !TokenUtil.areOnSameLine(getLeftCurly(), getRightCurly())) { 257 // Note: For Annotation Array Init only: 258 // If its a annotation array init block with strict cond being false then, 259 // we want flexible child indents with any indentLevel above minimum requirement 260 // All the other block elements will follow strict discrete indentation levels. 261 final boolean doesAnnotationArrayInitFollowsStrictCond = 262 !TokenUtil.isOfType(listChild, TokenTypes.ANNOTATION_ARRAY_INIT) 263 || getIndentCheck().isForceStrictCondition(); 264 265 checkChildren(listChild, 266 getCheckedChildren(), 267 getChildrenExpectedIndent(), 268 doesAnnotationArrayInitFollowsStrictCond, 269 canChildrenBeNested()); 270 } 271 } 272 } 273 274 /** 275 * Gets indentation level expected for children. 276 * 277 * @return indentation level expected for children 278 */ 279 protected IndentLevel getChildrenExpectedIndent() { 280 IndentLevel indentLevel = new IndentLevel(getIndent(), getBasicOffset()); 281 // if we have multileveled expected level then we should 282 // try to suggest single level to children using curlies' 283 // levels. 284 if (getIndent().isMultiLevel() && hasCurlies()) { 285 if (isOnStartOfLine(getLeftCurly())) { 286 indentLevel = new IndentLevel(expandedTabsColumnNo(getLeftCurly()) 287 + getBasicOffset()); 288 } 289 else if (isOnStartOfLine(getRightCurly())) { 290 final IndentLevel level = new IndentLevel(curlyIndent(), getBasicOffset()); 291 indentLevel = IndentLevel.addAcceptable(level, level.getFirstIndentLevel() 292 + getLineWrappingIndent()); 293 } 294 } 295 if (hasCurlies() && isOnStartOfLine(getLeftCurly())) { 296 indentLevel = IndentLevel.addAcceptable(indentLevel, 297 curlyIndent().getFirstIndentLevel() + getBasicOffset()); 298 } 299 return indentLevel; 300 } 301 302 @Override 303 public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) { 304 return getChildrenExpectedIndent(); 305 } 306 307 /** 308 * A shortcut for {@code IndentationCheck} property. 309 * 310 * @return value of lineWrappingIndentation property 311 * of {@code IndentationCheck} 312 */ 313 private int getLineWrappingIndent() { 314 return getIndentCheck().getLineWrappingIndentation(); 315 } 316 317}