我正在尝试为checkstyle创建一个规则,这将阻止编写内联注释用法,如下所示:
@Entity MyClass someEntity;
@Foo(a="B") public void bar(Baz baz) {
}
但不会阻止这样的想法:
public void bar(@Param Baz baz) {
}
有没有办法实现这个目标?
答案 0 :(得分:12)
这个答案的大部分都是受Checkstyle's "Writing Checks" article的启发。大多数工作都在AnnotationSameLineCheck
完成。
<强> AnnotationSameLineCheck.java 强>
这个Java文件的灵感来自“写作检查”文章的"Visitor In Action" section。
getDefaultTokens
定义了我们感兴趣的Java文件的哪些部分(a.k.a. tokens)。第一个可能认为我们对TokenTypes.ANNOTATION
感兴趣,但事实并非如此。我们对TokenTypes.ANNOTATION
不感兴趣,因为我们不想检查所有注释;我们实际上想忽视TokenTypes.PARAMETER_DEF
。
我们对此感兴趣的是什么?我们实际上对可以注释的Java文件的那些部分感兴趣(即,类定义TokenTypes.CLASS_DEF
,方法定义TokenTypes.METHOD_DEF
等)。方便的是,Checkstyle的API有一个方法可以告诉我们是否注释了令牌。该方法为AnnotationUtility.containsAnnotation
,用于visitToken
。
用于确定注释是否与Java文件的其他部分位于同一行的算法如下:
此算法位于visitToken
。
package example;
import com.puppycrawl.tools.checkstyle.api.AnnotationUtility;
import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
public class AnnotationSameLineCheck extends Check {
@Override
public int[] getDefaultTokens() {
// PACKAGE_DEF and PARAMETER_DEF were left out of the list
return new int[] { TokenTypes.ANNOTATION_DEF, //
TokenTypes.ANNOTATION_FIELD_DEF, //
TokenTypes.CLASS_DEF, //
TokenTypes.CTOR_DEF, //
TokenTypes.ENUM_DEF, //
TokenTypes.ENUM_CONSTANT_DEF, //
TokenTypes.INTERFACE_DEF, //
TokenTypes.METHOD_DEF, //
TokenTypes.VARIABLE_DEF };
}
@Override
public void visitToken(DetailAST ast) {
if (AnnotationUtility.containsAnnotation(ast)) {
final DetailAST holder = AnnotationUtility.getAnnotationHolder(ast);
final DetailAST annotation = getAnnotationAst(holder);
final DetailAST prev = getPreviousSibling(annotation, holder, ast);
final DetailAST next = getNextSibling(annotation, holder, ast);
if (isPreviousSiblingOnSameLine(prev, annotation) || //
isNextSiblingOnSameLine(annotation, next)) {
log(annotation.getLineNo(), //
annotation.getColumnNo(), //
"Annotations must exist on their own line");
}
}
}
private static boolean isPreviousSiblingOnSameLine(DetailAST prev, DetailAST annotation) {
if (prev == null) {
return false;
} else if (prev.getLastChild() == null) {
return prev.getLineNo() == annotation.getLineNo();
}
return prev.getLastChild().getLineNo() == annotation.getLineNo();
}
private static boolean isNextSiblingOnSameLine(DetailAST annotation, DetailAST next) {
if (next == null) {
return false;
}
return annotation.getLineNo() == next.getLineNo();
}
private static DetailAST getAnnotationAst(DetailAST aHolderAst) {
if (aHolderAst.getType() == TokenTypes.ANNOTATIONS) {
return aHolderAst;
} else if (aHolderAst.getType() == TokenTypes.MODIFIERS) {
return aHolderAst.findFirstToken(TokenTypes.ANNOTATION);
}
throw new AssertionError("aHolder must be one of TokenTypes.ANNOTATIONS or TokenTypes.MODIFIERS but was " + aHolderAst);
}
private static DetailAST getPreviousSibling(DetailAST annotation, DetailAST holder, DetailAST ast) {
if (annotation.getPreviousSibling() != null) {
return annotation.getPreviousSibling();
} else if (holder.getPreviousSibling() != null) {
return holder.getPreviousSibling();
}
return ast.getPreviousSibling();
}
private static DetailAST getNextSibling(DetailAST annotation, DetailAST holder, DetailAST ast) {
if (annotation.getNextSibling() != null) {
return annotation.getNextSibling();
} else if (holder.getNextSibling() != null) {
return holder.getNextSibling();
}
return ast.getNextSibling();
}
}
<强> checks.xml 强>
此XML文件的灵感来自“写作检查”文章的"Integrate Your Check" section。 Checkstyle使用它来指定对一组Java文件执行哪些检查。请注意,AnnotationSameLineCheck
是完全限定的(即,它的包在名称中指定)。 checks.xml
文件中提到了build.xml
。
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="TreeWalker">
<module name="example.AnnotationSameLineCheck"/>
</module>
</module>
<强>的build.xml 强>
此Ant构建文件的灵感来自Checkstyle's "Ant Task" article。使用Ant只是执行Checkstyle的一种方法。使用命令行是另一种选择。
<project default="example">
<taskdef resource="checkstyletask.properties" classpath="target/classes:lib/checkstyle-5.5-all.jar" />
<target name="example">
<checkstyle config="checks.xml">
<fileset dir="src/main/java" includes="**/AnnotatedClass.java" />
<formatter type="plain" />
</checkstyle>
</target>
</project>
<强> AnnotationClass.java 强>
可以使用以下类来测试AnnotationSameLineCheck
。它在build.xml
文件中提到。注意接口,类,枚举,成员变量,方法,参数和局部变量声明的测试。
package example;
@Deprecated
class CorrectClassDefA {}
@Deprecated class IncorrectClassDefA {}
abstract
@Deprecated
class CorrectClassDefB {}
abstract @SuppressWarnings(value = "unused") class IncorrectClassDefB0 {}
abstract
@Deprecated class IncorrectClassDefB1 {}
abstract@Deprecated
class IncorrectClassDefB2 {}
@Deprecated abstract class IncorrectClassDefB3 {}
@Deprecated
abstract class CorrectClassDefB4 {}
@SuppressWarnings(value = "unused")
interface CorrectInterfaceDefA {}
@Deprecated interface IncorrectInterfaceDefA {}
abstract
@Deprecated
interface CorrectInterfaceDefB {}
abstract @Deprecated interface IncorrectInterfaceDefB0 {}
abstract
@Deprecated interface IncorrectInterfaceDefB1 {}
abstract @SuppressWarnings(value = "unused")
interface IncorrectInterfaceDefB2 {}
@SuppressWarnings(value = "unused") abstract interface IncorrectInterfaceDefB3 {}
@SuppressWarnings(value = "unused")
abstract
interface CorrectInterfaceDefB4 {}
@Deprecated
enum CorrectEnumA {
@SuppressWarnings(value = "unused")
CORRECT,
@Deprecated INCORRECT }
@Deprecated enum
IncorrectEnumA {
@Deprecated
CORRECT,
@SuppressWarnings(value = "unused") INCORRECT }
public class AnnotatedClass { @Deprecated // incorrect
public AnnotatedClass() {}
@Deprecated
AnnotatedClass(int correct) {}
public
@SuppressWarnings(value = "unused")
AnnotatedClass(boolean correct, boolean correct0) {}
@SuppressWarnings(value = "unused")
AnnotatedClass(int correct, int correct0, int correct1) {}
public @SuppressWarnings(value = "unused")
AnnotatedClass(@Deprecated int bad, int bad0, int bad1, int bad2) {}
@SuppressWarnings(value = "unused") AnnotatedClass(@Deprecated int bad, int bad0, int bad1, int bad2, int bad3) {}
@Deprecated private int incorrectB;
transient @Deprecated
private int incorrectC;
transient
@Deprecated
private
int correctD;
private
@SuppressWarnings(value = "unused")
Object correctA; @SuppressWarnings(value = "dog")
public void incorrectA(final Object baz) {
}
public void correctB(@SuppressWarnings(value = "dog") final Object good) {
@Deprecated
int correctA;
final @Deprecated int incorrectB;
final
@Deprecated
Object
correctC;
}
@SuppressWarnings(value = "dog") public
void incorrectC(final Object bad) {
}
public
@SuppressWarnings(value = "dog") void incorrectD(final Object bad) {
}
}