Skip to content

Instantly share code, notes, and snippets.

@fbiville
Created March 17, 2015 21:37
Show Gist options
  • Save fbiville/988b2aefa6b85de61fdb to your computer and use it in GitHub Desktop.
Save fbiville/988b2aefa6b85de61fdb to your computer and use it in GitHub Desktop.
Level 2 / exo 1
package fr.devoxx.niveau2.exo1.etape2;
import java.util.List;
import javax.annotation.Nonnull;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* ClassAnnotationsExplorer -
*
* @author Sébastien Lesaint
*/
public class ClassAnnotationsExplorer {
@Nonnull
private final Elements elementUtils;
@Nonnull
private final Types typeUtils;
public ClassAnnotationsExplorer(@Nonnull Elements elementUtils, @Nonnull Types typeUtils) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
}
/**
* Extrait la liste des annotations de la classe sous forme de {@link AnnotationMirror}.
*
* @param annotatedClassElement le {@link Element} de {@link fr.devoxx.niveau2.exo1.etape2.AnnotatedClass}
*
* @return une liste contenant un {@link AnnotationMirror} représentant l'annotation {@link fr.devoxx.niveau2.exo1.Flag}
* puis un représentant l'annotation {@link Meta}
*/
public List<? extends AnnotationMirror> extractAnnotations(@Nonnull Element annotatedClassElement) {
return elementUtils.getAllAnnotationMirrors(annotatedClassElement);
}
/**
* Extrait l'annotation {@link Meta} de la classe.
*
* @param annotatedClassElement le {@link Element} de {@link fr.devoxx.niveau2.exo1.etape2.AnnotatedClass}
*
* @return l'instance de {@link Meta} sur la classe {@link fr.devoxx.niveau2.exo1.etape2.AnnotatedClass}
*/
public Meta extractMetaAnnotation(@Nonnull Element annotatedClassElement) {
return annotatedClassElement.getAnnotation(Meta.class);
}
/**
* Extrait la valeur du membre "value" de l'annotation {@link Meta} sur la classe.
*
* @param annotatedClassElement le {@link Element} de {@link fr.devoxx.niveau2.exo1.etape2.AnnotatedClass}
*
* @return "La robe est blanc et or, moi je te dis !"
*/
public String extractMetaAnnotationValue(@Nonnull Element annotatedClassElement) {
return extractMetaAnnotation(annotatedClassElement).value();
}
/**
* Extrait la valeur du membre "id" de l'annotation {@link fr.devoxx.niveau2.exo1.etape2.Meta} sur la classe à partir
* de l'{@link AnnotationMirror} qui représente l'annotation.
* <p>
* Notez qu'aucune valeur n'est donnée au membre "id" sur la classe
* {@link fr.devoxx.niveau2.exo1.etape2.AnnotatedClass}, la valeur récupérée est donc la valeur par défaut. L'API
* ne permet pas de savoir si la valeur a été définie explicitement sur la classe
* {@link fr.devoxx.niveau2.exo1.etape2.AnnotatedClass} ou pas (mais est-ce important ?).
* </p>
*
* @param annotatedClassElement le {@link Element} de {@link AnnotatedClass}
*
* @return -99812
*/
public int extractMetaAnnotationId(@Nonnull Element annotatedClassElement) {
return extractMetaAnnotation(annotatedClassElement).id();
}
}
package fr.devoxx.niveau2.exo1.etape1;
import java.lang.reflect.Type;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor6;
import javax.lang.model.util.Types;
/**
* ClassDeclarationExplorer -
*
* @author Sébastien Lesaint
*/
public class ClassDeclarationExplorer {
@Nonnull
private final Elements elementUtils;
@Nonnull
private final Types typeUtils;
public ClassDeclarationExplorer(@Nonnull Elements elementUtils, @Nonnull Types typeUtils) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
}
/**
* Extrait le nom de la classe à partir du {@link javax.lang.model.element.Element} qui la représente.
*
* @param basicClassElement le {@link javax.lang.model.element.Element} de
* {@link fr.devoxx.niveau2.exo1.etape1.BasicClass}
*
* @return "BasicClass" sous forme de {@link javax.lang.model.element.Name}
*/
public Name extractClassName(@Nonnull Element basicClassElement) {
return basicClassElement.getSimpleName();
}
/**
* Transforme un {@link javax.lang.model.element.Element} en un {@link javax.lang.model.element.TypeElement} si
* applicable, autrement retourne {@code null}.
* <p>
* L'usage de {@code instanceof}, bien que pouvant fonctionner, n'est pas la méthode préconisée par l'API est l'usage
* d'un {@link javax.lang.model.element.ElementVisitor} et de
* {@link Element#accept(javax.lang.model.element.ElementVisitor, Object)}
* (astuce: {@link javax.lang.model.util.SimpleElementVisitor6} fournit un adapteur qui réduira la taille de votre
* code).
* </p>
*
* @param element le {@link javax.lang.model.element.Element} de {@link fr.devoxx.niveau2.exo1.etape1.BasicClass}
*
* @return un {@link javax.lang.model.element.TypeElement} ou {@code null}
*/
public TypeElement asTypeElement(@Nonnull Element element) {
return element.accept(new SimpleElementVisitor6<TypeElement, Void>() {
public TypeElement visitType(TypeElement e, Void ignored) {
return e;
}
}, null);
}
/**
* Extrait le nom qualifié de la classe à partir du {@link javax.lang.model.element.Element} qui la représente.
* <p>
* Tous les {@link javax.lang.model.element.Element} n'ont pas de nom qualifié, mais c'est le cas du type qui
* représente les classes, {@link TypeElement}.
* </p>
*
* @param basicClassElement le {@link javax.lang.model.element.Element} de
* {@link fr.devoxx.niveau2.exo1.etape1.BasicClass}
*
* @return "fr.devoxx.niveau2.exo1.etape1.BasicClass" sous forme de {@link javax.lang.model.element.Name}
*/
public Name extractClassQualifiedName(@Nonnull Element basicClassElement) {
return asTypeElement(basicClassElement).getQualifiedName();
}
/**
* Extrait le nom du package de la classe à partir du {@link javax.lang.model.element.TypeElement} qui la
* représente.
* <p>
* Vous pouvez essayer de récupérer le package d'une classe à partir de son {@link TypeElement} en remontant
* la hiérarchie des élements via le lien {@link Element#getEnclosingElement()} mais ce besoin est tellement
* commun que {@link Elements} propose une méthode {@link Elements#getPackageOf}
* </p>
*
* @param basicClassTypeElement le {@link javax.lang.model.element.TypeElement} de
* {@link fr.devoxx.niveau2.exo1.etape1.BasicClass}
*
* @return "etape1" sous forme de {@link javax.lang.model.element.Name}
*/
public PackageElement extractPackage(@Nonnull TypeElement basicClassTypeElement) {
return elementUtils.getPackageOf(basicClassTypeElement).accept(new SimpleElementVisitor6<PackageElement, Void>() {
public PackageElement visitPackage(PackageElement e, Void ignored) {
return e;
}
}, null);
}
/**
* Extrait le nom qualifié du package de la classe à partir du {@link javax.lang.model.element.TypeElement} qui la
* représente.
*
* @param basicClassTypeElement le {@link javax.lang.model.element.TypeElement} de
* {@link fr.devoxx.niveau2.exo1.etape1.BasicClass}
*
* @return "fr.devoxx.niveau2.exo1.etape1" sous forme de {@link javax.lang.model.element.Name}
*/
public Name extractPackageName(@Nonnull TypeElement basicClassTypeElement) {
return extractPackage(basicClassTypeElement).getQualifiedName();
}
/**
* Extrait les "modifiers" de la classe à partir du {@link javax.lang.model.element.TypeElement} qui la
* représente.
*
* @param basicClassTypeElement le {@link javax.lang.model.element.TypeElement} de
* {@link fr.devoxx.niveau2.exo1.etape1.BasicClass}
*
* @return "public" et "final" sous la forme de {@link javax.lang.model.element.Modifier} dans un
* {@link java.util.Set}
*/
public Set<Modifier> extractModifiers(@Nonnull TypeElement basicClassTypeElement) {
return basicClassTypeElement.getModifiers();
}
}
package fr.devoxx.niveau2.exo1.etape3;
import fr.devoxx.niveau2.exo1.etape1.ClassDeclarationExplorer;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor6;
import javax.lang.model.util.Types;
/**
* ClassInterfacesExplorer -
*
* @author Sébastien Lesaint
*/
public class ClassInterfacesExplorer {
@Nonnull
private final Elements elementUtils;
@Nonnull
private final Types typeUtils;
private final ClassDeclarationExplorer classDeclarationUtils;
public ClassInterfacesExplorer(@Nonnull Elements elementUtils, @Nonnull Types typeUtils) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
classDeclarationUtils = new ClassDeclarationExplorer(this.elementUtils, this.typeUtils);
}
/**
* Extrait les interfaces implémentées par la classe à partir du {@link TypeElement} qui la représente.
*
* @param implementsInterfacesClassTypeElement le {@link Element} de
* {@link fr.devoxx.niveau2.exo1.etape3.ImplementsInterfacesClass}
*
* @return une liste contenant les {@link TypeMirror} représentant les interfaces {@link java.io.Serializable} et
* {@link java.lang.Comparable} implémentées par la classe
* {@link fr.devoxx.niveau2.exo1.etape3.ImplementsInterfacesClass}
*/
public List<? extends TypeMirror> extractInterfaces(@Nonnull TypeElement implementsInterfacesClassTypeElement) {
return implementsInterfacesClassTypeElement.getInterfaces();
}
/**
* Transforme une liste de {@link TypeMirror} en la liste des {@link TypeElement} qu'ils "référencent".
* <p>
* (astuce: pour la transformation de {@link TypeMirror} en {@link Element}, la classe {@link Elements} peut
* vous aider)
* </p>
* <p>
* (rappel: la tranformation de {@link Element} à {@link TypeElement} a été couverte lors de l'étape 1).
* </p>
*
* @param implementsInterfacesClassTypeElement le {@link TypeElement} de
* {@link fr.devoxx.niveau2.exo1.etape3.ImplementsInterfacesClass}
*
* @return une liste contenant les {@link TypeElement} des interfaces {@link java.io.Serializable} et
* {@link java.lang.Comparable}
*/
public List<TypeElement> extractInterfaceAsTypeElements(@Nonnull TypeElement implementsInterfacesClassTypeElement) {
return extractInterfaces(implementsInterfacesClassTypeElement).stream()
.map(mirror -> classDeclarationUtils.asTypeElement(typeUtils.asElement(mirror)))
.collect(Collectors.<TypeElement>toList());
}
/**
* Extrait le {@link TypeMirror} qui représente l'interface {@link java.io.Serializable} à partir du
* {@link TypeElement} représentant la classe.
* <p>
* Pour identifier le bon {@link TypeMirror}, il convient de filtrer sur le nom qualifié de la classe
* (ici "java.io.Serializable"). Cette information est portée par le {@link TypeElement} "référencé" par le
* {@link TypeMirror}. Les transformations vues lors des exercices prédécents vous seront donc utiles.
* </p>
*
* @param interfaces la liste des {@link TypeMirror} des interfaces implémentées par
* {@link fr.devoxx.niveau2.exo1.etape3.ImplementsInterfacesClass}
*
* @return le {@link TypeElement} qui représente l'interface {@link java.io.Serializable}
*/
public TypeMirror extractSerializableInterface(@Nonnull List<? extends TypeMirror> interfaces) {
return interfaces.stream()
.map(mirror -> typeUtils.asElement(mirror))
.map(classDeclarationUtils::asTypeElement)
.filter(element -> classDeclarationUtils.extractClassQualifiedName(element).contentEquals("java.io.Serializable"))
.map(element -> element.asType())
.findFirst()
.get();
}
}
package fr.devoxx.niveau2.exo1.etape4;
import fr.devoxx.niveau2.exo1.etape1.ClassDeclarationExplorer;
import javax.annotation.Nonnull;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.List;
import java.util.stream.Collectors;
/**
* ClassSuperClassExplorer -
*
* @author Sébastien Lesaint
*/
public class ClassSuperClassExplorer {
@Nonnull
private final Elements elementUtils;
@Nonnull
private final Types typeUtils;
private final ClassDeclarationExplorer classDeclarationExplorer;
public ClassSuperClassExplorer(@Nonnull Elements elementUtils, @Nonnull Types typeUtils) {
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
classDeclarationExplorer = new ClassDeclarationExplorer(elementUtils, typeUtils);
}
/**
* @param myListClassTypeElement le {@link TypeElement} de {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}
*
* @return le {@link TypeMirror} de la déclaration de {@link java.util.ArrayList} comme super classe de
* {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}
*/
public TypeMirror extractSuperClass(@Nonnull TypeElement myListClassTypeElement) {
return typeUtils.directSupertypes(myListClassTypeElement.asType()).iterator().next();
}
/**
* @param arrayListTypeMirror le {@link TypeMirror} de la déclaration de {@link java.util.ArrayList} comme super
* classe de {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}
*
* @return
*/
public DeclaredType asDeclaredType(@Nonnull TypeMirror arrayListTypeMirror) {
return (DeclaredType) typeUtils.capture(arrayListTypeMirror);
}
/**
* Extrait le {@link TypeMirror} qui représente l'argument générique de la déclaration de super classe de
* {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}.
* <p>
* L'objectif de cette méthode est d'extraire le {@link TypeMirror} qui représente l'argument générique "String" dans
* la déclaration de super classe de {@link fr.devoxx.niveau2.exo1.etape4.MyListClass} :
* {@code extends ArrayList<String>"}. Cette information est portée par le {@link TypeMirror} de la super classe
* de {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}.
* </p>
* <p>
* Cette méthode et la suivante ont pour objectif de mettre en avant la différence entre un type (classe dans le
* package "javax.lang.model.type") et un element (classe dans le package "javax.lang.model.element"). Et pour cela, les
* génériques sont l'illustration la plus efficace.
* </p>
*
* @param myListClassTypeElement le {@link TypeElement} de {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}
*
* @return le {@link TypeMirror} représentant l'argument générique "String" dans la déclaration de super class
* {@code extends ArrayList<String>"}
*/
public TypeMirror extractSuperClassTypeArgument(@Nonnull TypeElement myListClassTypeElement) {
return asDeclaredType(extractSuperClass(myListClassTypeElement)).getTypeArguments().iterator().next();
}
/**
* Extrait le {@link Name} qui représente le paramètre générique de la super classe de
* {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}: {@link java.util.ArrayList}.
* <p>
* L'objectif de cette méthode est d'extraire le {@link TypeParameterElement} qui représente le paramètre générique
* de la classe {@link java.util.ArrayList}. Cette information est portée par le {@link TypeElement} qui représente la
* classe {@link java.util.ArrayList}.
* </p>
* <p>
* Cette méthode et la précédente ont pour objectif de mettre en avant la différence entre un type (classe dans le
* package "javax.lang.model.type") et un element (classe dans le package "javax.lang.model.element"). Et pour cela, les
* génériques sont l'illustration la plus efficace.
* </p>
*
* @param myListClassTypeElement le {@link TypeElement} de {@link fr.devoxx.niveau2.exo1.etape4.MyListClass}
*
* @return {@link Name} représentant le paramètre générique "E" dans la déclaration de classe de
* {@link java.util.ArrayList}
*/
public Name extractSuperClassTypeParameterName(@Nonnull TypeElement myListClassTypeElement) {
return classDeclarationExplorer.asTypeElement(typeUtils.asElement(extractSuperClass(myListClassTypeElement)))
.getTypeParameters()
.iterator()
.next().getSimpleName();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment