Created
January 7, 2016 16:24
-
-
Save edisonw/0d87e5a843ac999a668a to your computer and use it in GitHub Desktop.
Sample Annotation Processor Boilerplate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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