package com.mastfrog.acteur.annotations;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
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.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import org.openide.util.lookup.ServiceProvider;
/**
*
* @author Tim Boudreau
*/
@ServiceProvider(service = Processor.class)
@SupportedAnnotationTypes("com.mastfrog.acteur.annotations.GuiceModule")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class GuiceModuleAnnotationProcessor extends AbstractProcessor {
public static final String META_INF_PATH = "META-INF/http/modules.list";
private ProcessingEnvironment env;
@Override
public void init(ProcessingEnvironment processingEnv) {
this.env = processingEnv;
}
private boolean isModuleSubtype(Element e) {
Types types = env.getTypeUtils();
Elements elements = env.getElementUtils();
TypeElement moduleType = elements.getTypeElement("com.google.inject.Module");
if (moduleType == null) {
return false;
}
return types.isSubtype(e.asType(), moduleType.asType());
}
private String canonicalize(TypeMirror tm, Types types) {
TypeElement e = (TypeElement) types.asElement(tm);
StringBuilder nm = new StringBuilder(e.getQualifiedName().toString());
Element enc = e.getEnclosingElement();
while (enc != null && enc.getKind() != ElementKind.PACKAGE) {
int ix = nm.lastIndexOf(".");
if (ix > 0) {
nm.setCharAt(ix, '$');
}
enc = enc.getEnclosingElement();
}
return nm.toString();
}
public boolean checkConstructors(TypeElement el) {
int ccount = 0;
for (Element e : el.getEnclosedElements()) {
if (e.getKind() == ElementKind.CONSTRUCTOR && e instanceof ExecutableElement) {
ExecutableElement ee = (ExecutableElement) e;
if (ee.getParameters() != null && ee.getParameters().isEmpty()) {
return true;
}
}
}
return ccount == 0;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
boolean result = true;
StringBuilder lines = new StringBuilder("# " + new Date() + " compiled by " + System.getProperty("user.name"));
try {
Set<Element> elements = new HashSet<>();
for (Element e : roundEnv.getElementsAnnotatedWith(GuiceModule.class)) {
GuiceModule anno = e.getAnnotation(GuiceModule.class);
if (anno == null) {
continue;
}
if (!isModuleSubtype(e)) {
env.getMessager().printMessage(Diagnostic.Kind.ERROR, "Not a subclass of Module: " + e.asType(), e);
continue;
}
if (!checkConstructors((TypeElement) e)) {
env.getMessager().printMessage(Diagnostic.Kind.ERROR, "Module must have a no-argument constructor", e);
continue;
}
elements.add(e);
lines.append('\n');
lines.append(canonicalize(e.asType(), env.getTypeUtils()));
}
if (lines.length() > 0) {
if (!elements.isEmpty()) {
String path = GuiceModule.META_INF_PATH;
try {
env.getMessager().printMessage(Diagnostic.Kind.NOTE, "Found the following Guice module classes annotated with @GuiceModule:\n" + lines);
FileObject fo = env.getFiler().createResource(StandardLocation.CLASS_OUTPUT,
"", path, elements.toArray(new Element[0]));
try (OutputStream out = fo.openOutputStream()) {
try (PrintStream ps = new PrintStream(out)) {
ps.println(lines);
}
}
} catch (FilerException ex) {
ex.printStackTrace();
}
}
}
} catch (IOException ex) {
Logger.getLogger(HttpCallAnnotationProcessor.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
return result;
}
}