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