2012-05-17 16 views
11

Estoy tratando de crear una regla para Checkstyle, que evitará el uso de escribir anotaciones integradas, así:regla Checkstyle para "@annotations debe estar en línea separada"

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

pero no impedirá que piensa como :

public void bar(@Param Baz baz) { 
} 

¿Hay alguna manera de lograr eso?

+0

Checkstyle parece tener una API bastante flexible, por lo que me imagino que sería posible. ¿Qué has intentado? –

+0

Intenté crear la expresión regular, pero no puedo escribirla correctamente –

+2

¿Puedes escribir tu expresión regular en tu pregunta, para que podamos identificar qué es lo que está mal? – zmo

Respuesta

12

La mayor parte de esta respuesta se inspiró en Checkstyle's "Writing Checks" article. La mayor parte del trabajo se realiza en AnnotationSameLineCheck.

AnnotationSameLineCheck.java

Este archivo Java se inspiró en la "Visitor In Action" section del artículo "Los cheques de la escritura".

getDefaultTokens define qué partes (ak.a. tokens) de un archivo Java que nos interesan. Al principio uno podría pensar que estaríamos interesados ​​en TokenTypes.ANNOTATION, pero este no es el caso. No estamos interesados ​​en TokenTypes.ANNOTATION porque no queremos inspeccionar todas las anotaciones; de hecho, queremos ignorar TokenTypes.PARAMETER_DEF.

Lo que nos interesa, entonces? En realidad, nos interesan las partes de un archivo Java que pueden anotarse (es decir, la definición de clase TokenTypes.CLASS_DEF, las definiciones de método TokenTypes.METHOD_DEF, etc.). Convenientemente, la API de Checkstyle tiene un método que nos puede decir si un token está anotado. Ese método es AnnotationUtility.containsAnnotation, que se usa en visitToken.

El algoritmo utilizado para determinar si una anotación está en la misma línea que otras partes de un archivo Java es el siguiente:

  1. determinar si un contador particular contiene una anotación. Si no, no hagas nada.
  2. Encuentra el token de anotación.
  3. Encuentra el token antes del token de anotación.
  4. Encuentra el token después del token de anotación.
  5. Si el token de anotación está en la misma línea que el testigo anterior, registrar un error.
  6. Si el token de anotación está en la misma línea que el siguiente token, registre un error.

Este algoritmo se encuentra en 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 archivo

Este XML se inspiró en la "Integrate Your Check" section del artículo "Los cheques de la escritura". Es utilizado por Checkstyle para especificar qué verificaciones realizar en un conjunto de archivos Java. Tenga en cuenta que AnnotationSameLineCheck está totalmente calificado (es decir, su paquete está especificado en el nombre). checks.xml se menciona en el archivo 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

Este archivo de compilación Ant se inspiró en Checkstyle's "Ant Task" article. Usar Ant es solo una forma de ejecutar Checkstyle. Usar la línea de comando es otra opción.

<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

se puede utilizar la siguiente clase para probar AnnotationSameLineCheck. Se menciona en el archivo build.xml. Tenga en cuenta la prueba de la interfaz, clase, enumeración, variable de miembro, método, parámetro y declaraciones de variables locales.

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

¡Muy impresionante! ¡Gracias por la respuesta detallada! –

+0

Estaría encantado de alojar su código en nuestra [Extensión de Checkstyle] (http://sevntu-checkstyle.github.com/sevntu.checkstyle/), por favor contribuya –

Cuestiones relacionadas