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