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