2012-05-17 10 views
11

tôi đang cố gắng để tạo ra một quy tắc cho checkstyle, mà sẽ ngăn chặn việc sử dụng văn bản chú thích nội tuyến, như thế này:Checkstyle quy tắc cho "@annotations phải nằm trên dòng riêng biệt"

@Entity MyClass someEntity; 
@Foo(a="B") public void bar(Baz baz) { 
} 

nhưng sẽ không ngăn cản suy nghĩ như thế :

public void bar(@Param Baz baz) { 
} 

Có cách nào để đạt được điều đó không?

+0

Chế độ kiểm tra dường như có API khá linh hoạt, vì vậy tôi tưởng tượng điều đó là có thể. Bạn đã thử cái gì? –

+0

Tôi đã thử tạo regexp, nhưng không thể viết đúng –

+2

bạn có thể viết regexp của mình trong câu hỏi của mình không, vì vậy chúng tôi có thể xác định chính xác điều gì? – zmo

Trả lời

12

Hầu hết câu trả lời này được lấy cảm hứng từ Checkstyle's "Writing Checks" article. Hầu hết công việc được thực hiện trong AnnotationSameLineCheck.

AnnotationSameLineCheck.java

tập Java này được lấy cảm hứng từ "Visitor In Action" section của "Kiểm tra Viết" bài viết.

getDefaultTokens xác định phần nào (a.k.a. mã thông báo) của tệp Java mà chúng tôi quan tâm. Đầu tiên, chúng tôi có thể nghĩ rằng chúng tôi muốn quan tâm đến TokenTypes.ANNOTATION, nhưng điều này không đúng. Chúng tôi không quan tâm đến TokenTypes.ANNOTATION vì chúng tôi không muốn kiểm tra tất cả các chú thích; chúng tôi thực sự muốn bỏ qua TokenTypes.PARAMETER_DEF.

Chúng tôi quan tâm đến điều gì sau đó? Chúng tôi thực sự quan tâm đến những phần của tệp Java có thể được chú thích (tức là định nghĩa lớp TokenTypes.CLASS_DEF, định nghĩa phương thức TokenTypes.METHOD_DEF, v.v.). Thuận tiện, API của Checkstyle có một phương thức có thể cho chúng tôi biết mã thông báo có được chú thích hay không. Phương pháp đó là AnnotationUtility.containsAnnotation, được sử dụng trong visitToken.

Các thuật toán sử dụng để xác định xem chú thích là trên cùng một dòng như các bộ phận khác của một file Java là như sau:

  1. Xác định xem một thẻ đặc biệt có chứa một chú thích. Nếu không, đừng làm gì cả.
  2. Tìm mã thông báo chú thích.
  3. Tìm mã thông báo trước mã thông báo chú thích.
  4. Tìm mã thông báo sau mã thông báo chú thích.
  5. Nếu mã thông báo chú thích nằm trên cùng một dòng với mã thông báo trước đó, hãy đăng nhập lỗi.
  6. Nếu mã thông báo chú thích nằm trên cùng một dòng với mã thông báo tiếp theo, hãy đăng nhập lỗi.

Thuật toán này được tìm thấy trong 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 tập tin

XML này được lấy cảm hứng từ "Integrate Your Check" section của "Kiểm tra Viết" bài viết. Nó được sử dụng bởi Checkstyle để chỉ định kiểm tra nào để thực hiện trên một tập hợp các tệp Java. Lưu ý rằng AnnotationSameLineCheck hoàn toàn đủ điều kiện (nghĩa là, gói của nó được chỉ định trong tên). checks.xml được đề cập trong tệp 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> 

xây dựng.xml

Tệp xây dựng Ant này được lấy cảm hứng từ Checkstyle's "Ant Task" article. Sử dụng Ant chỉ là một cách để thực thi Checkstyle. Sử dụng dòng lệnh là một tùy chọn khác.

<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

Người ta có thể sử dụng lớp sau đây để kiểm tra AnnotationSameLineCheck. Nó được đề cập trong tập tin build.xml. Lưu ý việc kiểm tra giao diện, lớp, enum, biến thành viên, phương thức, tham số và khai báo biến cục bộ.

package example; 
    @Deprecated 
class CorrectClassDefA {} 

@Deprecated class IncorrectClassDefA {} 

abstract 
@Deprecated 
class CorrectClassDefB {} 

abstract @SuppressWarnings(value = "unused") class IncorrectClassDefB0 {} 

abstract 
    @Deprecated class IncorrectClassDefB1 {} 

[email protected] 
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) { 
    } 
} 
+1

Rất ấn tượng! Cảm ơn bạn đã trả lời chi tiết! –

+0

Tôi rất sẵn lòng lưu trữ mã của bạn trong [Tiện ích mở rộng kiểu kiểm tra] của chúng tôi (http://sevntu-checkstyle.github.com/sevntu.checkstyle/), vui lòng đóng góp –