001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.indentation; 020 021import com.puppycrawl.tools.checkstyle.api.DetailAST; 022import com.puppycrawl.tools.checkstyle.api.TokenTypes; 023 024/** 025 * Handler for parents of blocks ('if', 'else', 'while', etc). 026 * <P> 027 * The "block" handler classes use a common superclass BlockParentHandler, 028 * employing the Template Method pattern. 029 * </P> 030 * 031 * <UL> 032 * <LI>template method to get the lcurly</LI> 033 * <LI>template method to get the rcurly</LI> 034 * <LI>if curlys aren't present, then template method to get expressions 035 * is called</LI> 036 * <LI>now all the repetitous code which checks for BOL, if curlys are on 037 * same line, etc. can be collapsed into the superclass</LI> 038 * </UL> 039 * 040 * 041 * @author jrichard 042 */ 043public class BlockParentHandler extends ExpressionHandler 044{ 045 /** 046 * Children checked by parent handlers. 047 */ 048 private static final int[] CHECKED_CHILDREN = new int[] { 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 * Returns array of token types which should be checked among childrens. 060 * @return array of token types to check. 061 */ 062 protected int[] getCheckedChildren() 063 { 064 return CHECKED_CHILDREN; 065 } 066 067 /** 068 * Construct an instance of this handler with the given indentation check, 069 * name, abstract syntax tree, and parent handler. 070 * 071 * @param indentCheck the indentation check 072 * @param name the name of the handler 073 * @param ast the abstract syntax tree 074 * @param parent the parent handler 075 */ 076 public BlockParentHandler(IndentationCheck indentCheck, 077 String name, DetailAST ast, ExpressionHandler parent) 078 { 079 super(indentCheck, name, ast, parent); 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 { 089 return getMainAst(); 090 } 091 092 /** 093 * Check the indent of the top level token. 094 */ 095 protected void checkToplevelToken() 096 { 097 final DetailAST toplevel = getToplevelAST(); 098 099 if ((toplevel == null) 100 || getLevel().accept(expandedTabsColumnNo(toplevel)) || hasLabelBefore()) 101 { 102 return; 103 } 104 if (!toplevelMustStartLine() && !startsLine(toplevel)) { 105 return; 106 } 107 logError(toplevel, "", expandedTabsColumnNo(toplevel)); 108 } 109 110 /** 111 * Check if the top level token has label before. 112 * @return true if the top level token has label before. 113 */ 114 protected boolean hasLabelBefore() 115 { 116 final DetailAST parent = getToplevelAST().getParent(); 117 return parent != null && parent.getType() == TokenTypes.LABELED_STAT 118 && parent.getLineNo() == getToplevelAST().getLineNo(); 119 } 120 121 /** 122 * Determines if the top level token must start the line. 123 * 124 * @return true 125 */ 126 protected boolean toplevelMustStartLine() 127 { 128 return true; 129 } 130 131 /** 132 * Determines if this block expression has curly braces. 133 * 134 * @return true if curly braces are present, false otherwise 135 */ 136 protected boolean hasCurlys() 137 { 138 return (getLCurly() != null) && (getRCurly() != null); 139 } 140 141 /** 142 * Get the left curly brace portion of the expression we are handling. 143 * 144 * @return the left curly brace expression 145 */ 146 protected DetailAST getLCurly() 147 { 148 return getMainAst().findFirstToken(TokenTypes.SLIST); 149 } 150 151 /** 152 * Get the right curly brace portion of the expression we are handling. 153 * 154 * @return the right curly brace expression 155 */ 156 protected DetailAST getRCurly() 157 { 158 final DetailAST slist = getMainAst().findFirstToken(TokenTypes.SLIST); 159 if (slist == null) { 160 return null; 161 } 162 163 return slist.findFirstToken(TokenTypes.RCURLY); 164 } 165 166 /** 167 * Check the indentation of the left curly brace. 168 */ 169 protected void checkLCurly() 170 { 171 // the lcurly can either be at the correct indentation, or nested 172 // with a previous expression 173 final DetailAST lcurly = getLCurly(); 174 final int lcurlyPos = expandedTabsColumnNo(lcurly); 175 176 if ((lcurly == null) 177 || curlyLevel().accept(lcurlyPos) 178 || !startsLine(lcurly)) 179 { 180 return; 181 } 182 183 logError(lcurly, "lcurly", lcurlyPos); 184 } 185 186 /** 187 * Get the expected indentation level for the curly braces. 188 * 189 * @return the curly brace indentation level 190 */ 191 protected IndentLevel curlyLevel() 192 { 193 return new IndentLevel(getLevel(), getBraceAdjustement()); 194 } 195 196 /** 197 * Determines if the right curly brace must be at the start of the line. 198 * 199 * @return true 200 */ 201 protected boolean rcurlyMustStart() 202 { 203 return true; 204 } 205 206 /** 207 * Determines if child elements within the expression may be nested. 208 * 209 * @return false 210 */ 211 protected boolean childrenMayNest() 212 { 213 return false; 214 } 215 216 /** 217 * Check the indentation of the right curly brace. 218 */ 219 protected void checkRCurly() 220 { 221 // the rcurly can either be at the correct indentation, or 222 // on the same line as the lcurly 223 final DetailAST lcurly = getLCurly(); 224 final DetailAST rcurly = getRCurly(); 225 final int rcurlyPos = expandedTabsColumnNo(rcurly); 226 227 if ((rcurly == null) 228 || curlyLevel().accept(rcurlyPos) 229 || (!rcurlyMustStart() && !startsLine(rcurly)) 230 || areOnSameLine(rcurly, lcurly)) 231 { 232 return; 233 } 234 logError(rcurly, "rcurly", rcurlyPos, curlyLevel()); 235 } 236 237 /** 238 * Get the child element that is not a list of statements. 239 * 240 * @return the non-list child element 241 */ 242 protected DetailAST getNonlistChild() 243 { 244 return getMainAst().findFirstToken(TokenTypes.RPAREN).getNextSibling(); 245 } 246 247 /** 248 * Check the indentation level of a child that is not a list of statements. 249 */ 250 private void checkNonlistChild() 251 { 252 final DetailAST nonlist = getNonlistChild(); 253 if (nonlist == null) { 254 return; 255 } 256 257 final IndentLevel expected = new IndentLevel(getLevel(), getBasicOffset()); 258 checkExpressionSubtree(nonlist, expected, false, false); 259 } 260 261 /** 262 * Get the child element representing the list of statements. 263 * 264 * @return the statement list child 265 */ 266 protected DetailAST getListChild() 267 { 268 return getMainAst().findFirstToken(TokenTypes.SLIST); 269 } 270 271 /** 272 * Get the right parenthesis portion of the expression we are handling. 273 * 274 * @return the right parenthis expression 275 */ 276 protected DetailAST getRParen() 277 { 278 return getMainAst().findFirstToken(TokenTypes.RPAREN); 279 } 280 281 /** 282 * Get the left parenthesis portion of the expression we are handling. 283 * 284 * @return the left parenthis expression 285 */ 286 protected DetailAST getLParen() 287 { 288 return getMainAst().findFirstToken(TokenTypes.LPAREN); 289 } 290 291 @Override 292 public void checkIndentation() 293 { 294 checkToplevelToken(); 295 // seperate to allow for eventual configuration 296 checkLParen(getLParen()); 297 checkRParen(getLParen(), getRParen()); 298 if (hasCurlys()) { 299 checkLCurly(); 300 checkRCurly(); 301 } 302 final DetailAST listChild = getListChild(); 303 if (listChild != null) { 304 // NOTE: switch statements usually don't have curlys 305 if (!hasCurlys() || !areOnSameLine(getLCurly(), getRCurly())) { 306 checkChildren(listChild, 307 getCheckedChildren(), 308 getChildrenExpectedLevel(), 309 true, 310 childrenMayNest()); 311 } 312 } 313 else { 314 checkNonlistChild(); 315 } 316 } 317 318 /** 319 * @return indentation level expected for children 320 */ 321 protected IndentLevel getChildrenExpectedLevel() 322 { 323 // if we have multileveled expected level then we should 324 // try to suggest single level to children using curlies' 325 // levels. 326 if (getLevel().isMultiLevel() && hasCurlys() 327 && !areOnSameLine(getLCurly(), getRCurly())) 328 { 329 if (startsLine(getLCurly())) { 330 return new IndentLevel(expandedTabsColumnNo(getLCurly()) + getBasicOffset()); 331 } 332 else if (startsLine(getRCurly())) { 333 final IndentLevel level = new IndentLevel(curlyLevel(), getBasicOffset()); 334 level.addAcceptedIndent(level.getFirstIndentLevel() + getLineWrappingIndent()); 335 return level; 336 } 337 } 338 return new IndentLevel(getLevel(), getBasicOffset()); 339 } 340 341 @Override 342 public IndentLevel suggestedChildLevel(ExpressionHandler child) 343 { 344 return getChildrenExpectedLevel(); 345 } 346 347 /** 348 * A shortcut for <code>IndentationCheck</code> property. 349 * @return value of lineWrappingIndentation property 350 * of <code>IndentationCheck</code> 351 */ 352 private int getLineWrappingIndent() 353 { 354 return getIndentCheck().getLineWrappingIndentation(); 355 } 356}