//////////////////////////////////////////////////////////////////////////////// // checkstyle: Checks Java source code for adherence to a set of rules. // Copyright (C) 2001-2017 the original author or authors. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// package com.puppycrawl.tools.checkstyle.checks.indentation; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.TokenTypes; /** * Handler for parents of blocks ('if', 'else', 'while', etc). * <P> * The "block" handler classes use a common superclass BlockParentHandler, * employing the Template Method pattern. * </P> * * <UL> * <LI>template method to get the lcurly</LI> * <LI>template method to get the rcurly</LI> * <LI>if curlies aren't present, then template method to get expressions * is called</LI> * <LI>now all the repetitious code which checks for BOL, if curlies are on * same line, etc. can be collapsed into the superclass</LI> * </UL> * * * @author jrichard */ public class BlockParentHandler extends AbstractExpressionHandler { /** * Children checked by parent handlers. */ private static final int[] CHECKED_CHILDREN = { TokenTypes.VARIABLE_DEF, TokenTypes.EXPR, TokenTypes.OBJBLOCK, TokenTypes.LITERAL_BREAK, TokenTypes.LITERAL_RETURN, TokenTypes.LITERAL_THROW, TokenTypes.LITERAL_CONTINUE, }; /** * Construct an instance of this handler with the given indentation check, * name, abstract syntax tree, and parent handler. * * @param indentCheck the indentation check * @param name the name of the handler * @param ast the abstract syntax tree * @param parent the parent handler */ public BlockParentHandler(IndentationCheck indentCheck, String name, DetailAST ast, AbstractExpressionHandler parent) { super(indentCheck, name, ast, parent); } /** * Returns array of token types which should be checked among children. * @return array of token types to check. */ protected int[] getCheckedChildren() { return CHECKED_CHILDREN.clone(); } /** * Get the top level expression being managed by this handler. * * @return the top level expression */ protected DetailAST getTopLevelAst() { return getMainAst(); } /** * Check the indent of the top level token. */ protected void checkTopLevelToken() { final DetailAST topLevel = getTopLevelAst(); if (topLevel != null && !getIndent().isAcceptable(expandedTabsColumnNo(topLevel)) && !hasLabelBefore() && (shouldTopLevelStartLine() || isOnStartOfLine(topLevel))) { logError(topLevel, "", expandedTabsColumnNo(topLevel)); } } /** * Check if the top level token has label before. * @return true if the top level token has label before. */ protected boolean hasLabelBefore() { final DetailAST parent = getTopLevelAst().getParent(); return parent.getType() == TokenTypes.LABELED_STAT && parent.getLineNo() == getTopLevelAst().getLineNo(); } /** * Determines if the top level token must start the line. * * @return true */ protected boolean shouldTopLevelStartLine() { return true; } /** * Determines if this block expression has curly braces. * * @return true if curly braces are present, false otherwise */ protected boolean hasCurlies() { return getLeftCurly() != null && getRightCurly() != null; } /** * Get the left curly brace portion of the expression we are handling. * * @return the left curly brace expression */ protected DetailAST getLeftCurly() { return getMainAst().findFirstToken(TokenTypes.SLIST); } /** * Get the right curly brace portion of the expression we are handling. * * @return the right curly brace expression */ protected DetailAST getRightCurly() { final DetailAST slist = getMainAst().findFirstToken(TokenTypes.SLIST); return slist.findFirstToken(TokenTypes.RCURLY); } /** * Check the indentation of the left curly brace. */ protected void checkLeftCurly() { // the lcurly can either be at the correct indentation, or nested // with a previous expression final DetailAST lcurly = getLeftCurly(); final int lcurlyPos = expandedTabsColumnNo(lcurly); if (!curlyIndent().isAcceptable(lcurlyPos) && isOnStartOfLine(lcurly)) { logError(lcurly, "lcurly", lcurlyPos, curlyIndent()); } } /** * Get the expected indentation level for the curly braces. * * @return the curly brace indentation level */ protected IndentLevel curlyIndent() { return new IndentLevel(getIndent(), getBraceAdjustment()); } /** * Determines if child elements within the expression may be nested. * * @return false */ protected boolean canChildrenBeNested() { return false; } /** * Check the indentation of the right curly brace. */ protected void checkRightCurly() { final DetailAST rcurly = getRightCurly(); final int rcurlyPos = expandedTabsColumnNo(rcurly); if (!curlyIndent().isAcceptable(rcurlyPos) && isOnStartOfLine(rcurly)) { logError(rcurly, "rcurly", rcurlyPos, curlyIndent()); } } /** * Get the child element that is not a list of statements. * * @return the non-list child element */ protected DetailAST getNonListChild() { return getMainAst().findFirstToken(TokenTypes.RPAREN).getNextSibling(); } /** * Check the indentation level of a child that is not a list of statements. */ private void checkNonListChild() { final DetailAST nonList = getNonListChild(); if (nonList != null) { final IndentLevel expected = new IndentLevel(getIndent(), getBasicOffset()); checkExpressionSubtree(nonList, expected, false, false); } } /** * Get the child element representing the list of statements. * * @return the statement list child */ protected DetailAST getListChild() { return getMainAst().findFirstToken(TokenTypes.SLIST); } /** * Get the right parenthesis portion of the expression we are handling. * * @return the right parenthesis expression */ protected DetailAST getRightParen() { return getMainAst().findFirstToken(TokenTypes.RPAREN); } /** * Get the left parenthesis portion of the expression we are handling. * * @return the left parenthesis expression */ protected DetailAST getLeftParen() { return getMainAst().findFirstToken(TokenTypes.LPAREN); } @Override public void checkIndentation() { checkTopLevelToken(); // separate to allow for eventual configuration checkLeftParen(getLeftParen()); checkRightParen(getLeftParen(), getRightParen()); if (hasCurlies()) { checkLeftCurly(); checkRightCurly(); } final DetailAST listChild = getListChild(); if (listChild == null) { checkNonListChild(); } else { // NOTE: switch statements usually don't have curlies if (!hasCurlies() || !areOnSameLine(getLeftCurly(), getRightCurly())) { checkChildren(listChild, getCheckedChildren(), getChildrenExpectedIndent(), true, canChildrenBeNested()); } } } /** * Gets indentation level expected for children. * @return indentation level expected for children */ protected IndentLevel getChildrenExpectedIndent() { IndentLevel indentLevel = new IndentLevel(getIndent(), getBasicOffset()); // if we have multileveled expected level then we should // try to suggest single level to children using curlies' // levels. if (getIndent().isMultiLevel() && hasCurlies()) { if (isOnStartOfLine(getLeftCurly())) { indentLevel = new IndentLevel(expandedTabsColumnNo(getLeftCurly()) + getBasicOffset()); } else if (isOnStartOfLine(getRightCurly())) { final IndentLevel level = new IndentLevel(curlyIndent(), getBasicOffset()); level.addAcceptedIndent(level.getFirstIndentLevel() + getLineWrappingIndent()); indentLevel = level; } } return indentLevel; } @Override public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) { return getChildrenExpectedIndent(); } /** * A shortcut for {@code IndentationCheck} property. * @return value of lineWrappingIndentation property * of {@code IndentationCheck} */ private int getLineWrappingIndent() { return getIndentCheck().getLineWrappingIndentation(); } }