001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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 || getLevel().isAcceptable(expandedTabsColumnNo(topLevel)) || hasLabelBefore()) { 097 return; 098 } 099 if (!shouldTopLevelStartLine() && !startsLine(topLevel)) { 100 return; 101 } 102 logError(topLevel, "", expandedTabsColumnNo(topLevel)); 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 protected 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 protected boolean hasCurlies() { 130 return getLCurly() != null && getRCurly() != 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 getLCurly() { 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 getRCurly() { 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 protected void checkLCurly() { 156 // the lcurly can either be at the correct indentation, or nested 157 // with a previous expression 158 final DetailAST lcurly = getLCurly(); 159 final int lcurlyPos = expandedTabsColumnNo(lcurly); 160 161 if (curlyLevel().isAcceptable(lcurlyPos) || !startsLine(lcurly)) { 162 return; 163 } 164 165 logError(lcurly, "lcurly", lcurlyPos); 166 } 167 168 /** 169 * Get the expected indentation level for the curly braces. 170 * 171 * @return the curly brace indentation level 172 */ 173 protected IndentLevel curlyLevel() { 174 return new IndentLevel(getLevel(), getBraceAdjustment()); 175 } 176 177 /** 178 * Determines if the right curly brace must be at the start of the line. 179 * 180 * @return true 181 */ 182 protected boolean shouldStartWithRCurly() { 183 return true; 184 } 185 186 /** 187 * Determines if child elements within the expression may be nested. 188 * 189 * @return false 190 */ 191 protected boolean canChildrenBeNested() { 192 return false; 193 } 194 195 /** 196 * Check the indentation of the right curly brace. 197 */ 198 protected void checkRCurly() { 199 // the rcurly can either be at the correct indentation, or 200 // on the same line as the lcurly 201 final DetailAST lcurly = getLCurly(); 202 final DetailAST rcurly = getRCurly(); 203 final int rcurlyPos = expandedTabsColumnNo(rcurly); 204 205 if (curlyLevel().isAcceptable(rcurlyPos) 206 || !shouldStartWithRCurly() && !startsLine(rcurly) 207 || areOnSameLine(rcurly, lcurly)) { 208 return; 209 } 210 logError(rcurly, "rcurly", rcurlyPos, curlyLevel()); 211 } 212 213 /** 214 * Get the child element that is not a list of statements. 215 * 216 * @return the non-list child element 217 */ 218 protected DetailAST getNonListChild() { 219 return getMainAst().findFirstToken(TokenTypes.RPAREN).getNextSibling(); 220 } 221 222 /** 223 * Check the indentation level of a child that is not a list of statements. 224 */ 225 private void checkNonListChild() { 226 final DetailAST nonList = getNonListChild(); 227 if (nonList == null) { 228 return; 229 } 230 231 final IndentLevel expected = new IndentLevel(getLevel(), getBasicOffset()); 232 checkExpressionSubtree(nonList, expected, false, false); 233 } 234 235 /** 236 * Get the child element representing the list of statements. 237 * 238 * @return the statement list child 239 */ 240 protected DetailAST getListChild() { 241 return getMainAst().findFirstToken(TokenTypes.SLIST); 242 } 243 244 /** 245 * Get the right parenthesis portion of the expression we are handling. 246 * 247 * @return the right parenthesis expression 248 */ 249 protected DetailAST getRParen() { 250 return getMainAst().findFirstToken(TokenTypes.RPAREN); 251 } 252 253 /** 254 * Get the left parenthesis portion of the expression we are handling. 255 * 256 * @return the left parenthesis expression 257 */ 258 protected DetailAST getLParen() { 259 return getMainAst().findFirstToken(TokenTypes.LPAREN); 260 } 261 262 @Override 263 public void checkIndentation() { 264 checkTopLevelToken(); 265 // separate to allow for eventual configuration 266 checkLParen(getLParen()); 267 checkRParen(getLParen(), getRParen()); 268 if (hasCurlies()) { 269 checkLCurly(); 270 checkRCurly(); 271 } 272 final DetailAST listChild = getListChild(); 273 if (listChild == null) { 274 checkNonListChild(); 275 } 276 else { 277 // NOTE: switch statements usually don't have curlies 278 if (!hasCurlies() || !areOnSameLine(getLCurly(), getRCurly())) { 279 checkChildren(listChild, 280 getCheckedChildren(), 281 getChildrenExpectedLevel(), 282 true, 283 canChildrenBeNested()); 284 } 285 } 286 } 287 288 /** 289 * Gets indentation level expected for children. 290 * @return indentation level expected for children 291 */ 292 protected IndentLevel getChildrenExpectedLevel() { 293 IndentLevel indentLevel = new IndentLevel(getLevel(), getBasicOffset()); 294 // if we have multileveled expected level then we should 295 // try to suggest single level to children using curlies' 296 // levels. 297 if (getLevel().isMultiLevel() && hasCurlies()) { 298 if (startsLine(getLCurly())) { 299 indentLevel = new IndentLevel(expandedTabsColumnNo(getLCurly()) + getBasicOffset()); 300 } 301 else if (startsLine(getRCurly())) { 302 final IndentLevel level = new IndentLevel(curlyLevel(), getBasicOffset()); 303 level.addAcceptedIndent(level.getFirstIndentLevel() + getLineWrappingIndent()); 304 indentLevel = level; 305 } 306 } 307 return indentLevel; 308 } 309 310 @Override 311 public IndentLevel suggestedChildLevel(AbstractExpressionHandler child) { 312 return getChildrenExpectedLevel(); 313 } 314 315 /** 316 * A shortcut for {@code IndentationCheck} property. 317 * @return value of lineWrappingIndentation property 318 * of {@code IndentationCheck} 319 */ 320 private int getLineWrappingIndent() { 321 return getIndentCheck().getLineWrappingIndentation(); 322 } 323}