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