com.github.sevntu.checkstyle.checks.coding.SingleBreakOrContinueCheck Maven / Gradle / Ivy
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2018 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.github.sevntu.checkstyle.checks.coding;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
/**
*
* This check restricts the number of break and continue statements inside cycle body (only one is
* allowed).
*
*
* Restricting the number of break and continue statements in a loop is done in the interest of good
* structured programming.
*
*
* One break and continue statement is acceptable in a loop, since it facilitates optimal coding. If
* there is more than one, the code should be refactored to increase readability.
*
*
* For example: (http://nemo.sonarqube.org/coding_rules#languages=java|q=one%20break)
*
*
*
* for (int i = 1; i <= 10; i++)
* { // violation - 2 continue - one might be tempted to add some logic in between
* if (i % 2 == 0)
* {
* continue;
* }
*
* if (i % 3 == 0)
* {
* continue;
* }
*
* System.out.println("i = " + i);
* }
*
*
* Please note that Switch statements and inner loops are ignored in this check. This Rule
* only validate loop structure with depth 0.
*
*
* For example:
*
*
*
* for (int i = 1; i <= 10; i++)// OK - Outer loop
* {
* while (true) // violation - Inner loop: 1 continue and 1 break
* {
* if (true)
* {
* continue;
* }
*
* if (true)
* {
* break;
* }
*
* System.out.println("violation - 1 continue and 1 break");
* }
* }
*
*
*
* while (true) // OK - Switch block
* {
* final char chr = value.charAt(i);
* switch (chr) {
* case '<':
* sb.append("<");
* break;
* case '>':
* sb.append(">");
* break;
* case '\"':
* sb.append(""");
* break;
* case '&':
* sb.append(chr);
* break;
* default:
* sb.append(chr);
* break;
* }
* }
*
*
* @author Yasser Aziza
* @since 1.18.0
*/
public class SingleBreakOrContinueCheck extends AbstractCheck {
/**
* Warning message key.
*/
public static final String MSG_KEY = "single.break.or.continue.in.loops";
@Override
public int[] getDefaultTokens() {
return new int[] {
TokenTypes.LITERAL_FOR,
TokenTypes.LITERAL_WHILE,
TokenTypes.LITERAL_DO, };
}
@Override
public int[] getAcceptableTokens() {
return getDefaultTokens();
}
@Override
public int[] getRequiredTokens() {
return getDefaultTokens();
}
@Override
public void visitToken(DetailAST ast) {
if (getNumberOfContinueAndBreaks(ast.getFirstChild()) > 1) {
log(ast, MSG_KEY);
}
}
/**
* Gets the number of "continue" and "break" statements inside a loop.
*
* @param node current parent node.
* @return number of break and continue statements inside a loop
*/
private int getNumberOfContinueAndBreaks(DetailAST node) {
int numberOfBreakOrContinue = 0;
if (node != null) {
if (TokenTypes.LITERAL_CONTINUE == node.getType()
|| TokenTypes.LITERAL_BREAK == node.getType()) {
numberOfBreakOrContinue++;
}
else if (shouldIgnore(node)) {
numberOfBreakOrContinue += getNumberOfContinueAndBreaks(node.getNextSibling());
}
else {
numberOfBreakOrContinue += getNumberOfContinueAndBreaks(node.getFirstChild());
numberOfBreakOrContinue += getNumberOfContinueAndBreaks(node.getNextSibling());
}
}
return numberOfBreakOrContinue;
}
/**
* Either a node should be ignored while counting the number of "break" and "continue"
* statements. This check is needed in order to e.g. ignore Switch statements and inner loops.
*
* @param node current node.
* @return true
if the node should be ignored, otherwise false
*/
private static boolean shouldIgnore(DetailAST node) {
return TokenTypes.LITERAL_SWITCH == node.getType()
|| TokenTypes.LITERAL_FOR == node.getType()
|| TokenTypes.LITERAL_WHILE == node.getType()
|| TokenTypes.LITERAL_DO == node.getType();
}
}