Skip to content

Commit

Permalink
Empty Line Separator Check, added option for managing empty lines bet…
Browse files Browse the repository at this point in the history
…ween class members, issue checkstyle#530
  • Loading branch information
alexkravin committed Jan 25, 2015
1 parent bac672f commit 6095c2c
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 9 deletions.
Expand Up @@ -106,14 +106,66 @@
* </module>
* </pre>
*
* @author maxvetrenko
* <p>
* Example of declarations with multiple empty lines between class members (allowed by default):
* </p>
*
* <pre>
* ///////////////////////////////////////////////////
* //HEADER
* ///////////////////////////////////////////////////
*
*
* package com.puppycrawl.tools.checkstyle.whitespace;
*
*
*
* import java.io.Serializable;
*
*
* class Foo
* {
* public static final int FOO_CONST = 1;
*
*
*
* public void foo() {}
* }
* </pre>
* <p>
* An example how to disallow multiple empty lines between class members:
* </p>
* <pre>
* &lt;module name="EmptyLineSeparator"&gt;
* &lt;property name="allowMultipleEmptyLines" value="false"/&gt;
* &lt;/module&gt;
* </pre>
*
* @author maxvetrenko
* @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a>
*/
public class EmptyLineSeparatorCheck extends Check
{

/**
* A key is pointing to the warning message empty.line.separator in "messages.properties"
* file.
*/
public static final String MSG_SHOULD_BE_SEPARATED = "empty.line.separator";

/**
* A key is pointing to the warning message empty.line.separator.multiple.lines
* in "messages.properties"
* file.
*/
public static final String MSG_MULTIPLE_LINES = "empty.line.separator.multiple.lines";

/** */
private boolean allowNoEmptyLineBetweenFields;

/** Allows multiple empty lines between class members. */
private boolean allowMultipleEmptyLines = true;

/**
* Allow no empty line between fields.
* @param allow
Expand All @@ -124,6 +176,15 @@ public final void setAllowNoEmptyLineBetweenFields(boolean allow)
allowNoEmptyLineBetweenFields = allow;
}

/**
* Allow multiple empty lines between class members.
* @param allow User's value.
*/
public void setAllowMultipleEmptyLines(boolean allow)
{
allowMultipleEmptyLines = allow;
}

@Override
public int[] getDefaultTokens()
{
Expand All @@ -146,43 +207,69 @@ public void visitToken(DetailAST ast)
{
final DetailAST nextToken = ast.getNextSibling();

if (nextToken != null && nextToken.getType() != TokenTypes.RCURLY) {
if (nextToken != null) {
final int astType = ast.getType();
switch (astType) {
case TokenTypes.VARIABLE_DEF:
if (isTypeField(ast) && !hasEmptyLineAfter(ast)) {
if (allowNoEmptyLineBetweenFields
&& nextToken.getType() != TokenTypes.VARIABLE_DEF)
{
log(nextToken.getLineNo(), "empty.line.separator",
log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED,
nextToken.getText());
}
else if (!allowNoEmptyLineBetweenFields) {
log(nextToken.getLineNo(), "empty.line.separator",
else if (!allowNoEmptyLineBetweenFields || !allowMultipleEmptyLines) {
log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED,
nextToken.getText());
}
}
if (!allowMultipleEmptyLines && isTypeField(ast) && hasEmptyLinesBefore(ast)) {
log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText());
}
break;
case TokenTypes.IMPORT:
if (astType != nextToken.getType() && !hasEmptyLineAfter(ast)
|| (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)
&& ast.getPreviousSibling() == null))
{
log(nextToken.getLineNo(), "empty.line.separator", nextToken.getText());
log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText());
}
if (!allowMultipleEmptyLines && hasEmptyLinesBefore(ast)) {
log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText());
}
break;
case TokenTypes.PACKAGE_DEF:
if (ast.getLineNo() > 1 && !hasEmptyLineBefore(ast)) {
log(ast.getLineNo(), "empty.line.separator", ast.getText());
log(ast.getLineNo(), MSG_SHOULD_BE_SEPARATED, ast.getText());
}
if (!allowMultipleEmptyLines && hasEmptyLinesBefore(ast)) {
log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText());
}
default:
if (!hasEmptyLineAfter(ast)) {
log(nextToken.getLineNo(), "empty.line.separator", nextToken.getText());
if (nextToken.getType() != TokenTypes.RCURLY && !hasEmptyLineAfter(ast)) {
log(nextToken.getLineNo(), MSG_SHOULD_BE_SEPARATED, nextToken.getText());
}
if (!allowMultipleEmptyLines && hasEmptyLinesBefore(ast)) {
log(ast.getLineNo(), MSG_MULTIPLE_LINES, ast.getText());
}
}
}
}

/**
* Checks if a token has more than 1 empty lines before.
* @param token DetailAST token.
* @return true, if token have empty lines before.
*/
private boolean hasEmptyLinesBefore(DetailAST token)
{
final int lineNo = token.getLineNo();
// 3 is the number of the pre-previous line because the numbering starts from zero.
final int number = 3;
final String lineBefore = getLines()[lineNo - number];
return lineBefore.trim().isEmpty();
}

/**
* Checks if token have empty line after.
* @param token token.
Expand Down
@@ -1,4 +1,5 @@
empty.line.separator=''{0}'' should be separated from previous statement.
empty.line.separator.multiple.lines=''{0}'' has more than 1 empty lines before.

containsTab=Line contains a tab character.
file.containsTab=File contains tab characters (this is the first instance).
Expand Down
Expand Up @@ -66,4 +66,19 @@ public void testHeader() throws Exception
};
verify(checkConfig, getPath("whitespace/InputEmptyLineSeparatorCheckHeader.java"), expected);
}

@Test
public void testMultipleEmptyLinesBetweenClassMembers() throws Exception
{
DefaultConfiguration checkConfig = createCheckConfig(EmptyLineSeparatorCheck.class);
checkConfig.addAttribute("allowMultipleEmptyLines", "false");
final String[] expected = {
"21: 'package' has more than 1 empty lines before.",
"24: 'import' has more than 1 empty lines before.",
"33: 'VARIABLE_DEF' has more than 1 empty lines before.",
"38: 'VARIABLE_DEF' has more than 1 empty lines before.",
"43: 'METHOD_DEF' has more than 1 empty lines before.",
};
verify(checkConfig, getPath("whitespace/InputEmptyLineSeparatorCheckMultipleEmptyLines.java"), expected);
}
}
@@ -0,0 +1,47 @@
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2014 Oliver Burn
//
// 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.whitespace;


import java.util.*;

import java.io.*;


public class InputEmptyLineSeparatorCheckMultipleEmptyLines
{


private int counter;




private Object obj = null;

private int k;


private static void foo() {}

private static void foo1() {}

}
44 changes: 44 additions & 0 deletions src/xdocs/config_whitespace.xml
Expand Up @@ -1351,6 +1351,12 @@ import com.puppycrawl.tools.checkstyle.api.Check;
<th><a href="property_types.html#boolean">boolean</a></th>
<th>false</th>
</tr>
<tr>
<th>allowMultipleEmptyLines</th>
<th>Allow multiple empty lines between class members</th>
<th><a href="property_types.html#boolean">boolean</a></th>
<th><code>true</code></th>
</tr>
</table>
</subsection>

Expand Down Expand Up @@ -1411,6 +1417,44 @@ class Foo
<source>
&lt;module name="EmptyLineSeparator"&gt;
&lt;property name="allowNoEmptyLineBetweenFields" value="true"/&gt;
&lt;/module&gt;
</source>
<p>
Example of declarations with multiple empty lines between class members
(allowed by default):
</p>
<source>
///////////////////////////////////////////////////
//HEADER
///////////////////////////////////////////////////



package com.puppycrawl.tools.checkstyle.whitespace;



import java.io.Serializable;



class Foo
{


public static final int FOO_CONST = 1;




public void foo() {} //should be separated from previous statement.
}
<p>
An example how to disallow multiple empty lines between class members:
</p>
<source>
&lt;module name=&quot;EmptyLineSeparator&quot;&gt;
&lt;property name=&quot;allowMultipleEmptyLines&quot; value=&quot;true&quot;/&gt;
&lt;/module&gt;
</source>
</subsection>
Expand Down

0 comments on commit 6095c2c

Please sign in to comment.