Skip to content

Commit

Permalink
OneTopLevelClass #21
Browse files Browse the repository at this point in the history
  • Loading branch information
maxvetrenko committed Jun 29, 2014
1 parent c3e98e5 commit c4043e4
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 1 deletion.
@@ -0,0 +1,150 @@
////////////////////////////////////////////////////////////////////////////////
// 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.checks.design;

import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

/**
*
* Checks that each top-level class, interface
* or enum resides in a source file of its own.
* <p>
* Official description of a 'top-level' term:<a
* href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.6">
* 7.6. Top Level Type Declarations</a>.
* </p>
* <p>
* An example of check's configuration:
* </p>
* <pre>
* &lt;module name="OneTopLevelClass"/&gt;
* </pre>
* <p>
* An example of check's configuration applied only to classes:
* </p>
* <pre>
* &lt;module name="OneTopLevelClass"&gt;
* &lt;property name="tokens" value="CLASS_DEF"&gt;
* &lt;/module&gt;
* </pre>
* <p>
* An example of code with violations:
* </p>
* <pre><code>
* public class Foo{
* //methods
* }
*
* class Foo2{
* //methods
* }
* </code></pre>
*
* <p>
* An example of code without violations:
* </p>
* <pre><code>
* public class Foo{
* //methods
* }
* </code></pre>
*
* @author Max Vetrenko
*/
public class OneTopLevelClassCheck extends Check
{
/** true if a java source file contains a type with a public access level modifier.*/
private boolean mPublicTypeFound;
/** Mapping between type names and line numbers of the type declarations.*/
private Map<Integer, String> mLineNumberTypeMap =
new TreeMap<Integer, String>();

@Override
public int[] getDefaultTokens()
{
return new int[] {};
}
@Override
public void beginTree(DetailAST aRootAST)
{
DetailAST currentNode = aRootAST;
while (currentNode != null) {
if (currentNode.getType() == TokenTypes.CLASS_DEF
|| currentNode.getType() == TokenTypes.ENUM_DEF
|| currentNode.getType() == TokenTypes.INTERFACE_DEF)
{
if (isPublic(currentNode.
findFirstToken(TokenTypes.MODIFIERS)))
{
mPublicTypeFound = true;
}

else {
final String typeName = currentNode.
findFirstToken(TokenTypes.IDENT).getText();
mLineNumberTypeMap.put(currentNode.getLineNo(), typeName);
}
}
currentNode = currentNode.getNextSibling();
}
}

@Override
public void finishTree(DetailAST aRootAST)
{
if (mPublicTypeFound) {
for (Map.Entry<Integer, String> entry
: mLineNumberTypeMap.entrySet())
{
log(entry.getKey(), "one.top.level.class", entry.getValue());
}
}

else {
final Iterator<Map.Entry<Integer, String>> iterator =
mLineNumberTypeMap.entrySet().iterator();
if (iterator.hasNext()) {
iterator.next(); // skip first top-level type
}

while (iterator.hasNext()) {
final Map.Entry<Integer, String> entry = iterator.next();
log(entry.getKey(), "one.top.level.class", entry.getValue());
}
}
mLineNumberTypeMap.clear();
mPublicTypeFound = false;
}

/**
* Checks if a type is public.
* @param aTypeNode modifiers type node.
* @return true if a type has a public access level modifier.
*/
private boolean isPublic(DetailAST aTypeNode)
{
return aTypeNode.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
}
}
Expand Up @@ -6,3 +6,4 @@ variable.notPrivate=Variable ''{0}'' must be private and have accessor methods.
mutable.exception=The field ''{0}'' must be declared final.
throws.count=Throws count is {0,number,integer} (max allowed is {1,number,integer}).
hide.utility.class=Utility classes should not have a public or default constructor.
one.top.level.class=Top-level class {0} has to reside in its own source file.
@@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////////////////////
// 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.checks.design;

import java.io.File;

import org.junit.Test;

import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;

public class OneTopLevelClassCheckTest extends BaseCheckTestSupport
{

@Test
public void testFileWithOneTopLevelClass() throws Exception
{
final DefaultConfiguration checkConfig =
createCheckConfig(OneTopLevelClassCheck.class);
final String[] expected = {};
verify(checkConfig, getPath("design" + File.separator + "InputOneTopLevelClass.java"), expected);
}
@Test
public void testFileWithFewTopLevelClasses() throws Exception
{
final DefaultConfiguration checkConfig =
createCheckConfig(OneTopLevelClassCheck.class);
final String[] expected = {
"25: Top-level class NoSuperClone has to reside in its own source file.",
"33: Top-level class InnerClone has to reside in its own source file.",
"50: Top-level class CloneWithTypeArguments has to reside in its own source file.",
"58: Top-level class CloneWithTypeArgumentsAndNoSuper has to reside in its own source file.",
"67: Top-level class MyClassWithGenericSuperMethod has to reside in its own source file.",
"84: Top-level class AnotherClass has to reside in its own source file.",
};
verify(checkConfig, getPath("coding" + File.separator + "InputClone.java"), expected);
}
@Test
public void testFileWithSecondEnumTopLevelClass() throws Exception
{
final DefaultConfiguration checkConfig =
createCheckConfig(OneTopLevelClassCheck.class);
final String[] expected = {
"83: Top-level class InputDeclarationOrderEnum has to reside in its own source file.",
};
verify(checkConfig, getPath("coding" + File.separator + "InputDeclarationOrder.java"), expected);
}
}
Expand Up @@ -24,4 +24,4 @@ public void methodWithLiterals()
final String ref = "<a href=\"";
final String refCase = "<A hReF=\"";
}
}
}
@@ -0,0 +1,81 @@
package net.ubiquity.tools.checkstyle.tests;

public class InputOneTopLevelClass
{
static final int FOO2 = 3;

// error public before package
public static final int FOO = 3;

private static final int FOO3 = 3;

// eror public before package and private
public static final int FOO4 = 3;

private static final String ERROR = "error";

// error protected before private
protected static final String ERROR1 = "error";

// error public before private
public static final String WARNING = "warning";

private int mMaxInitVars = 3;

// error statics should be before instance members
// error publics before private
public static final int MAX_ITER_VARS = 3;

private class InnerClass
{
private static final int INNER_FOO = 2;

// error public before private
public static final int INNER_FOO2 = 2;

public InnerClass()
{
int foo = INNER_FOO;
foo += INNER_FOO2;
foo += INNER_FOO3;
}

// error member variables should be before methods or ctors
// error public before private
public static final int INNER_FOO3 = 2;
}

public int getFoo1()
{
return mFoo;
}

// error ctors before methods
public InputDeclarationOrder()
{
String foo = ERROR;
foo += ERROR1;
foo += WARNING;
int fooInt = mMaxInitVars;
fooInt += MAX_ITER_VARS;
fooInt += mFoo;
}

public static int getFoo2()
{
return 13;
}

public int getFoo()
{
return mFoo;
}

private static int getFoo21()
{
return 14;
}

// error member variables should be before methods or ctors
private int mFoo = 0;
}
4 changes: 4 additions & 0 deletions src/xdocs/availablechecks.xml
Expand Up @@ -475,6 +475,10 @@
<td><a href="config_coding.html#OneStatementPerLine">OneStatementPerLine</a></td>
<td>Checks there is only one statement per line.</td>
</tr>
<tr>
<td><a href="config_design.html#OneTopLevelClass">OneTopLevelClass</a></td>
<td>Checks that each top-level class, interfaces or enum resides in a source file of its own.</td>
</tr>
<tr>
<td><a href="config_whitespace.html#OperatorWrap">OperatorWrap</a></td>
<td>
Expand Down
60 changes: 60 additions & 0 deletions src/xdocs/config_design.xml
Expand Up @@ -461,5 +461,65 @@ public class StringUtils // not final to allow subclassing
</p>
</subsection>
</section>

<section name="OneTopLevelClass">
<subsection name="Description">
<p>
Checks that each top-level class, interface or
enum resides in a source file of its own.
Official description of a 'top-level' term:<a
href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-7.html#jls-7.6">7.6. Top Level Type Declarations</a>.
</p>
</subsection>

<subsection name="Examples">
<p>
An example of check's configuration:
</p>
<source>
&lt;module name="OneTopLevelClass"/&gt;
</source>
<p>
An example of check's configuration applied only to classes:
</p>
<source>
&lt;module name="OneTopLevelClass"&gt;
&lt;property name="tokens" value="CLASS_DEF"&gt;
&lt;/module&gt;
</source>
<p>
An example of code with violations:
</p>
<source>
public class Foo{
//methods
}

class Foo2{
//methods
}
</source>
<p>
An example of code without violations:
</p>
<source>
public class Foo{
//methods
}
</source>
</subsection>

<subsection name="Package">
<p>
com.puppycrawl.tools.checkstyle.checks.design
</p>
</subsection>

<subsection name="Parent Module">
<p>
<a href="config.html#TreeWalker">TreeWalker</a>
</p>
</subsection>
</section>
</body>
</document>

0 comments on commit c4043e4

Please sign in to comment.