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