次のように、インライン注釈の使用を禁止する checkstyle のルールを作成しようとしています。
@Entity MyClass someEntity;
@Foo(a="B") public void bar(Baz baz) {
}
しかし、そのような考えを妨げません:
public void bar(@Param Baz baz) {
}
それを達成する方法はありますか?
次のように、インライン注釈の使用を禁止する checkstyle のルールを作成しようとしています。
@Entity MyClass someEntity;
@Foo(a="B") public void bar(Baz baz) {
}
しかし、そのような考えを妨げません:
public void bar(@Param Baz baz) {
}
それを達成する方法はありますか?
この回答のほとんどは、 Checkstyle の「Writing Checks」記事に触発されたものです。ほとんどの作業は で行われAnnotationSameLineCheck
ます。
AnnotationSameLineCheck.java
この Java ファイルは、「Writing Checks」記事の「Visitor In Action」セクションに触発されました。
getDefaultTokens
Java ファイルのどの部分 (別名トークン) に関心があるかを定義します。最初は に関心があると思うかもしれませんが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();
}
}
チェック.xml
この XML ファイルは、記事「チェックの書き込み」の「チェックの統合」セクションに触発されたものです。これは、一連の Java ファイルに対して実行するチェックを指定するために Checkstyle によって使用されます。は完全に修飾されていることに注意してください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 の記事「Ant タスク」に触発されたものです。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) {
}
}