2017-06-09 3 views
1

Ich habe folgende Bedingung, die Java-Annotation mit einem bestimmten Feldwert benötigt, um genau einmal in jedem Feld der Klasse zu erscheinen. Ist es mit Java 8 möglich?Java-Annotation - Kardinalität (Vorkommen) der Annotation mit demselben Feldelementwert einschränken

Meine Anmerkung

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) //can use with fields only. 
public @interface TestAnnotation{ 

    public String id(); 
} 

die Klasse der Anmerkung unter Verwendung ist wie

@TestAnnotation(id="test") 
private String testString; 

@TestAnnotation(id="test1") 
private String test1String; 

@TestAnnotation(id="test2") 
private String test2String; 

Alles, was ich, dass mit dem Programmierer will, wie etwas verhindern

@TestAnnotation(id="test2") 
private String test2String; 

@TestAnnotation(id="test2") 
private String test3String; 

dh die gleiche Anmerkung mit spezifische ID@TestAnnotation(id="test2") kann nicht zweimal für Felder verwendet werden. Zumindest die id="..." sollte innerhalb der @TestAnnotaion angelegten Felder innerhalb einer Klasse eindeutig sein.

+0

Ok, wahrscheinlich kann ich dies mit Java Annotation Processor erreichen, würde es gepostet. – gyan

Antwort

3

Wie Sie anscheinend herausgefunden haben, ist dies mit einem Annotationsprozessor möglich.

Hier ist ein Beispiel:

package mcve.proc; 

import java.lang.annotation.*; 

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) 
public @interface IDExample { 
    String id(); 
} 
package mcve.proc; 

import javax.annotation.processing.*; 
import javax.lang.model.*; 
import javax.lang.model.element.*; 
import javax.lang.model.type.*; 
import javax.lang.model.util.*; 
import javax.tools.*; 
import java.util.*; 

@SupportedAnnotationTypes("mcve.proc.IDExample") 
@SupportedSourceVersion(SourceVersion.RELEASE_8) 
public class UniqueIDProcessor extends AbstractProcessor { 
    @Override 
    public boolean process(Set<? extends TypeElement> annotations, 
          RoundEnvironment roundEnv) { 
     Elements elements = processingEnv.getElementUtils(); 
     Types types = processingEnv.getTypeUtils(); 
     Map<TypeElement, Set<VariableElement>> map = new HashMap<>(); 
     // Find each of the fields annotated with @IDExample. 
     for (Element elem : roundEnv.getElementsAnnotatedWith(IDExample.class)) { 
      if (elem.getKind() == ElementKind.FIELD) { 
       VariableElement var = (VariableElement) elem; 
       TypeElement  decl = (TypeElement) var.getEnclosingElement(); 
       // Group them by declaring class. 
       map.computeIfAbsent(decl, key -> new HashSet<>()).add(var); 
      } 
     } 
     // Now for each set of fields annotated with @IDExample... 
     for (Set<VariableElement> fields : map.values()) { 
      Map<String, Set<VariableElement>> fieldsByID = new HashMap<>(); 
      // Group them by ID. 
      for (VariableElement field : fields) { 
       String id = field.getAnnotation(IDExample.class).id(); 
       fieldsByID.computeIfAbsent(id, key -> new HashSet<>()).add(field); 
      } 
      fieldsByID.forEach((String id, Set<VariableElement> fieldsWithID) -> { 
       // For each set of fields which have duplicate IDs, 
       // cause a compilation error on each annotation. 
       if (fieldsWithID.size() > 1) { 
        for (VariableElement field : fieldsWithID) { 
         // This is all just finding the annotation mirror so 
         // the compilation error appears in the right place. 
         TypeMirror idExampleMirror = 
          elements.getTypeElement(IDExample.class.getName()).asType(); 
         AnnotationMirror annotation = 
          field.getAnnotationMirrors().stream() 
           .filter(mirror -> types.isSameType(idExampleMirror, mirror.getAnnotationType())) 
           .findFirst().get(); 
         AnnotationValue value = 
          annotation.getElementValues().entrySet().stream() 
           .filter(e -> e.getKey().getSimpleName().contentEquals("id")) 
           .map(e -> e.getValue()) 
           .findFirst().get(); 
         // Actually cause the compilation error. 
         String errorMessage = String.format("\"%s\" is a duplicate ID.", id); 
         processingEnv.getMessager() 
            .printMessage(Diagnostic.Kind.ERROR, 
                errorMessage, 
                field, 
                annotation, 
                value); 
        } 
       } 
      }); 
     } 
     return false; 
    } 
} 

Es gibt eine Anleitung, wie Anmerkungsverarbeitung arbeiten here zu erhalten. Um zum Beispiel das obige Beispiel Prozessor zum Laufen zu bringen, würden Sie etwa wie folgt tun (abhängig von Ihrer IDE, gehe ich davon aus):

  • Erstellen Sie ein Projekt/aufspalten Glas für die beiden Klassen mcve.proc.IDExample und mcve.proc.UniqueIDProcessor. Erstellen Sie in diesem Jar ein Verzeichnis META-INF/services.
  • Erstellen Sie in diesem Verzeichnis eine Textdatei mit dem Namen javax.annotation.processing.Processor (keine Dateierweiterung), deren Inhalt der vollständig qualifizierte Name des Annotationsprozessors mcve.proc.UniqueIDProcessor ist.
  • Importieren Sie dieses Projekt/jar in Ihr Hauptprojekt als Bibliothek.
  • Evtl. mcve.proc.UniqueIDProcessor als Annotationsprozessor in z. Ihre Projekteigenschaften, wenn eine solche Einstellung existiert/notwendig ist. Ich weiß, dass Netbeans es so macht. Ich weiß nicht über andere IDEs.
+2

Danke @Radiodef für die Hilfe. Wahrscheinlich hast du eine vollständige Antwort gegeben - mehr als genug, ich brauchte, wirklich zu schätzen :) – gyan

Verwandte Themen