Java “的检查样式规则”@注释必须在单独的行“中”;
我正在尝试为checkstyle创建一个规则,该规则将防止在使用时编写内联注释,如下所示:Java “的检查样式规则”@注释必须在单独的行“中”;,java,annotations,checkstyle,Java,Annotations,Checkstyle,我正在尝试为checkstyle创建一个规则,该规则将防止在使用时编写内联注释,如下所示: @Entity MyClass someEntity; @Foo(a="B") public void bar(Baz baz) { } 但不会阻止这样的想法: public void bar(@Param Baz baz) { } 有什么办法可以做到这一点吗?这个答案的大部分灵感来自于。大部分工作是在注释SamelineCheck中完成的 注释SamelineCheck.java 这个Java文件的
@Entity MyClass someEntity;
@Foo(a="B") public void bar(Baz baz) {
}
但不会阻止这样的想法:
public void bar(@Param Baz baz) {
}
有什么办法可以做到这一点吗?这个答案的大部分灵感来自于。大部分工作是在
注释SamelineCheck
中完成的
注释SamelineCheck.java
这个Java文件的灵感来自于“编写检查”文章的主题
getDefaultTokens
定义我们感兴趣的Java文件的哪些部分(也称为令牌)。首先,人们可能会认为我们对TokenTypes.ANNOTATION
感兴趣,但事实并非如此。我们对TokenTypes.ANNOTATION
不感兴趣,因为我们不想检查所有注释;我们实际上想要忽略标记类型。参数_DEF
那么我们对什么感兴趣呢?实际上,我们对Java文件中可以注释的部分感兴趣(即类定义TokenTypes.class_DEF
、方法定义TokenTypes.method_DEF
),等等)。方便的是,Checkstyle的API有一个方法可以告诉我们是否对令牌进行了注释。该方法是AnnotationUtility.containsAnotation
,用于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();
}
}
用于确定注释是否与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文件的灵感来自于“编写支票”一文的主题。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>
Checkstyle似乎有一个相当灵活的API,所以我认为这是可能的。你试过什么?我试过创建regexp,但写得不对。你能在你的问题中写下你的regexp吗?这样我们就可以找出问题所在了?我觉得奇怪的是,你想阻止方法参数的内联注释。对我来说,这似乎是最干净的方式。我不确定使用正则表达式是否可行。它们可能不足以验证诸如
@Foo(a=“\”)公共空白条(Baz-Baz)之类的内容{“”
。您可能需要AST访问者。我也同意此规则不是很好的主意,但如果您真的必须这样做,请尝试做一些较弱的操作。只需假设注释参数中没有括号。那么它看起来很容易让人印象深刻!感谢您提供详细的答案!我很乐意在我们的网站上托管您的代码,请提供帮助
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) {
}
}