2012-05-17 8 views
11

Sto cercando di creare una regola per checkstyle, che impedirà la scrittura utilizzo annotazioni in linea, in questo modo:Checkstyle regola per "@annotations deve essere sulla linea separata"

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

ma non impedirà pensa così :

public void bar(@Param Baz baz) { 
} 

esiste un modo per raggiungere questo obiettivo?

+0

Checkstyle sembra avere un'API abbastanza flessibile, quindi immagino che sarebbe possibile. Che cosa hai provato? –

+0

Ho provato a creare l'espressione regolare, ma non riesco a scriverlo correttamente –

+2

puoi scrivere la tua espressione regolare nella tua domanda, quindi possiamo individuare cosa c'è che non va? – zmo

risposta

12

La maggior parte di questa risposta è stata ispirata da Checkstyle's "Writing Checks" article. La maggior parte del lavoro viene eseguita in AnnotationSameLineCheck.

AnnotationSameLineCheck.java

Questo file Java è stato ispirato dal "Visitor In Action" section dell'articolo "Controlli di scrittura".

getDefaultTokens definisce quali parti (note anche come gettoni) di un file Java che ci interessa. Fuori prima si potrebbe pensare che saremmo interessati a TokenTypes.ANNOTATION, ma questo non è il caso. Non siamo interessati a TokenTypes.ANNOTATION perché non vogliamo ispezionare tutte le annotazioni; in realtà vogliamo ignorare TokenTypes.PARAMETER_DEF.

A cosa ci interessa allora? In realtà siamo interessati a quelle parti di un file Java che possono essere annotate (ad esempio, la definizione di classe TokenTypes.CLASS_DEF, le definizioni di metodo TokenTypes.METHOD_DEF, ecc.). Convenientemente, l'API di Checkstyle ha un metodo che può dirci se un token è annotato. Questo metodo è AnnotationUtility.containsAnnotation, che viene utilizzato in visitToken.

L'algoritmo utilizzato per determinare se un annotazione è sulla stessa linea di altre parti di un file Java è la seguente:

  1. determinare se un particolare token contiene un'annotazione. In caso contrario, non fare nulla.
  2. Trova il token di annotazione.
  3. Trova il token prima del token di annotazione.
  4. Trova il token dopo il token di annotazione.
  5. Se il token di annotazione si trova sulla stessa riga del token precedente, registrare un errore.
  6. Se il token di annotazione si trova sulla stessa riga del token successivo, registrare un errore.

Questo algoritmo è trovato in 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 file di

Questo XML è stato ispirato dal "Integrate Your Check" section dell'articolo "Controlli di scrittura". Viene utilizzato da Checkstyle per specificare quali controlli eseguire su un set di file Java. Si noti che AnnotationSameLineCheck è completamente qualificato (vale a dire, il suo pacchetto è specificato nel nome). checks.xml è menzionato nel file 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

Questo file di Ant è stato ispirato da Checkstyle's "Ant Task" article. Usare Ant è solo un modo per eseguire Checkstyle. L'uso della riga di comando è un'altra opzione.

<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

si può usare la seguente classe di testare AnnotationSameLineCheck. È menzionato nel file build.xml. Notare il test di dichiarazioni di interfaccia, classe, enum, variabile membro, metodo, parametro e variabile locale.

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

Molto impressionante! Grazie per la risposta dettagliata! –

+0

Sarei felice di ospitare il tuo codice nella nostra estensione [Checkstyle] (http://sevntu-checkstyle.github.com/sevntu.checkstyle/), per favore contribuisci –

Problemi correlati