1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
| package com.adiguba.xml;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
// Indique la (ou les) annotation(s) que l'on supporte :
@SupportedAnnotationTypes("com.adiguba.xml.ToXml")
// Indique la version des sources que l'on supporte :
@SupportedSourceVersion(SourceVersion.RELEASE_7)
// On peut même définir des options à passer via l'option -A de javac
// @SupportedOptions()
public class ToXmlProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// On traite les annotations que l'on a déclarer :
for (TypeElement annotation : annotations) {
// On récupère tous les éléments annotés par l'annotation en question :
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
// S'il s'agit d'un Type, on construit le XML :
if (element instanceof TypeElement) {
buildXml((TypeElement)element);
}
}
}
return false;
}
private void buildXml(TypeElement type) {
try {
// On génère une erreur de compilation si c'est une classe interne :
if (type.getNestingKind()!=NestingKind.TOP_LEVEL) {
processingEnv.getMessager().printMessage(Kind.ERROR,
"Annotation @ToXml can only be used on top-level type",
type);
return;
}
// On recherche le nom du type (ou celui indiqué dans l'annoation) :
final String name = getXmlAlias(type);
// On récupère le nom du package
// (ou la classe conteneur si on est dans un type interne, ce qui n'est pas le cas ici)
String packageName = ((PackageElement)type.getEnclosingElement()).getQualifiedName().toString();
// On génère un fichier dans le répertoire des classes :
FileObject file = processingEnv.getFiler().createResource(
StandardLocation.CLASS_OUTPUT,
packageName, name + ".xml", type);
// On génère le document XML :
try (Writer writer = file.openWriter();
PrintWriter pw = new PrintWriter(writer)) {
pw.println("<document>");
pw.println(" <doc name='" + name + "'>");
pw.println(" <operations>");
// On parcours tous les éléments du type :
for (Element e : type.getEnclosedElements()) {
// S'il s'agit d'une méthode :
if (e.getKind()==ElementKind.METHOD) {
ExecutableElement method = (ExecutableElement)e;
String opName = getXmlAlias(method);
pw.println(" <operation name='" + opName + "'>");
pw.println(" <params>");
// Pour chaque paramètre de la méthode :
for (VariableElement var : method.getParameters()) {
// On récupère le nom du paramètre
String varName = getXmlAlias(var);
// On récupère le type du paramètre
String varType = getTypeName(var.asType());
pw.print(" ");
pw.println("<params io='in' name='"
+ varName + "' type='" + varType + "'/>");
}
// Et on écrit le type de retour :
TypeMirror returnType = method.getReturnType();
String retName = "ret"; // Devrait-on utiliser une annotation pour cela ?
String retType = getTypeName(returnType);
pw.print(" ");
pw.println("<params io='out' name='"
+ retName + "' type='" + retType + "'/>");
pw.println(" </params>");
pw.println(" </operation>");
}
}
pw.println(" </operations>");
pw.println(" </doc>");
pw.println("</document>");
}
// On affiche une note de compilation :
processingEnv.getMessager().printMessage(Kind.WARNING,
"Build " + file.getName() + " in " + packageName);
} catch (IOException e) {
processingEnv.getMessager().printMessage(Kind.ERROR, "IO Error:" + e.getMessage(), type);
}
}
/**
* Retourne le nom de la variable, éventuellement selon l'annotation XmlAlias.
*/
private String getXmlAlias(Element elem) {
XmlAlias alias = elem.getAnnotation(XmlAlias.class);
if (alias!=null) {
return alias.value();
}
return elem.getSimpleName().toString();
}
/**
* Retourne le nom du type.
*/
private String getTypeName(TypeMirror type) {
switch (type.getKind()) {
case DECLARED:
// C'est une déclaration de type, on utilise simpleName pour obtenir le nom court
// (cad "String" à la place de "java.lang.String").
// Mais on pourrait opter pour une liste de type accepté par exemple...
return ((DeclaredType)type).asElement().getSimpleName().toString();
default:
}
// Par défaut on utilise toString(), qui marche très bien pour les primitifs.
// Mais on pourrait être amené à devoir gérer des cas particulier.
return type.toString();
}
} |
Partager