Skip to content

Instantly share code, notes, and snippets.

@edisonw
Created January 7, 2016 16:24
Show Gist options
  • Save edisonw/0d87e5a843ac999a668a to your computer and use it in GitHub Desktop.
Save edisonw/0d87e5a843ac999a668a to your computer and use it in GitHub Desktop.
Sample Annotation Processor Boilerplate
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
/**
* @author edi
*/
@AutoService(Processor.class)
public class AnnotationAggregator extends AbstractProcessor {
private static final Set<String> NAME;
static {
HashSet<String> set = new HashSet<>(1);
set.add(AggregatedAnnotation.class.getCanonicalName());
NAME = Collections.unmodifiableSet(set);
}
private Filer filer;
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return NAME;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Map<String, TypeSpec.Builder> builderMap = new LinkedHashMap<>();
Map<String, HashSet<String>> groupToPackage = new HashMap<>();
// Iterate over all @Factory annotated elements
System.out.print("Processing annotations...\n");
for (Element element : roundEnv.getElementsAnnotatedWith(AggregatedAnnotation.class)) {
// Check if a class has been annotated with @Factory
if (element.getKind() != ElementKind.CLASS) {
error(element, "You cannot annotate " + element.getSimpleName() + " with " + AggregatedAnnotation.class);
return true;
}
TypeElement classElement = (TypeElement) element;
AggregatedAnnotation annotationElement = classElement.getAnnotation(AggregatedAnnotation.class);
//Groups of Objects, named.
String group = annotationElement.group();
if (group == null || group.length() == 0) {
throw new IllegalArgumentException(
String.format("group() in @%s for class %s is null or empty! that's not allowed",
AggregatedAnnotation.class.getSimpleName(), classElement.getQualifiedName().toString()));
}
TypeMirror baseTypeMirror = null;
try {
annotationElement.baseClass();
} catch (MirroredTypeException mte) {
baseTypeMirror = mte.getTypeMirror();
}
if (baseTypeMirror == null) {
throw new IllegalArgumentException(
String.format("valueType() in @%s for class %s is null or empty! that's not allowed",
AggregatedAnnotation.class.getSimpleName(), classElement.getQualifiedName().toString()));
}
TypeMirror valueTypeMirror = null;
try {
annotationElement.valueType();
} catch (MirroredTypeException mte) {
valueTypeMirror = mte.getTypeMirror();
}
if (valueTypeMirror == null) {
throw new IllegalArgumentException(
String.format("valueType() in @%s for class %s is null or empty! that's not allowed",
AggregatedAnnotation.class.getSimpleName(), classElement.getQualifiedName().toString()));
}
TypeName baseClassType = TypeName.get(baseTypeMirror);
TypeName valueClassType = TypeName.get(valueTypeMirror);
if (valueClassType.toString().equals(classElement.getQualifiedName().toString())) {
System.out.print("Ignoring base type...." + classElement.getQualifiedName() + "\n");
continue;
}
System.out.print("Got base class..." + baseTypeMirror + "\n");
String baseClass = baseTypeMirror.toString();
String groupId = baseClass + "_." + group;
HashSet<String> packageGroups = groupToPackage.get(baseTypeMirror.toString());
if (packageGroups == null) {
packageGroups = new HashSet<>();
groupToPackage.put(baseClass, packageGroups);
}
packageGroups.add(groupId);
TypeSpec.Builder groupSpec = builderMap.get(groupId);
if (groupSpec == null) {
groupSpec = TypeSpec.enumBuilder(group);
groupSpec.addModifiers(Modifier.PUBLIC);
groupSpec.addSuperinterface(baseClassType);
groupSpec.addField(valueClassType,
"value", Modifier.PRIVATE, Modifier.FINAL)
.addMethod(MethodSpec.constructorBuilder()
.addParameter(valueClassType, "value")
.addStatement("this.$N = $N", "value", "value")
.build());
groupSpec.addMethod(MethodSpec.methodBuilder("value").addModifiers(Modifier.PUBLIC)
.returns(valueClassType).addStatement("return this.value").build());
builderMap.put(groupId, groupSpec);
}
groupSpec.addEnumConstant(classElement.getSimpleName().toString(),
TypeSpec.anonymousClassBuilder("new $L()", classElement) //Empty Constructor required.
.build());
}
for (String baseClass : groupToPackage.keySet()) {
/**
* public enum Classs {
* Action_B(new Integer(1)),
* Action_A(new String(""));
* <p/>
* private Object action;
* <p/>
* Classs(Object clazz) {
* action = clazz;
* }
* }
*/
HashSet<String> groups = groupToPackage.get(baseClass);
for (String groupId : groups) {
TypeSpec typeSpec = builderMap.get(groupId).build();
System.out.print("Generating class " + groupId + "\n");
try {
Writer writer = filer.createSourceFile(groupId).openWriter();
JavaFile jf = JavaFile.builder(groupId.substring(0, groupId.lastIndexOf(".")), typeSpec).build();
jf.writeTo(writer);
writer.close();
} catch (IOException e) {
throw new IllegalArgumentException("Failed to write class.", e);
}
}
}
return true;
}
private void error(Element e, String msg, Object... args) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(msg, args),
e);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment