Last active
January 17, 2018 10:20
-
-
Save AhmedMourad0/50ba123464d6390b47b6a11767aad1bd to your computer and use it in GitHub Desktop.
This project is still in its early stages of construction, it's not in Alpha, it's not in Beta, it's not even in Gamma it's still underdeveloped!
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
package com.jpattern.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Target(ElementType.TYPE) | |
@Retention(RetentionPolicy.SOURCE) | |
public @interface Builder { | |
} |
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
package com.jpattern.processors; | |
import com.google.common.base.CaseFormat; | |
import com.google.common.collect.ImmutableSet; | |
import com.jpattern.annotations.Builder; | |
import com.squareup.javapoet.ClassName; | |
import com.squareup.javapoet.FieldSpec; | |
import com.squareup.javapoet.JavaFile; | |
import com.squareup.javapoet.MethodSpec; | |
import com.squareup.javapoet.TypeName; | |
import com.squareup.javapoet.TypeSpec; | |
import javax.annotation.processing.AbstractProcessor; | |
import javax.annotation.processing.Filer; | |
import javax.annotation.processing.Messager; | |
import javax.annotation.processing.ProcessingEnvironment; | |
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.TypeElement; | |
import javax.lang.model.element.VariableElement; | |
import javax.lang.model.util.ElementFilter; | |
import javax.tools.Diagnostic; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Set; | |
import static javax.lang.model.element.Modifier.FINAL; | |
import static javax.lang.model.element.Modifier.PRIVATE; | |
import static javax.lang.model.element.Modifier.PUBLIC; | |
public class BuilderProcessor extends AbstractProcessor { | |
private Messager messager; | |
private Filer filer; | |
public BuilderProcessor() { | |
super(); | |
} | |
@Override | |
public synchronized void init(ProcessingEnvironment processingEnv) { | |
super.init(processingEnv); | |
this.messager = processingEnv.getMessager(); | |
this.filer = processingEnv.getFiler(); | |
} | |
@Override | |
public SourceVersion getSupportedSourceVersion() { | |
return SourceVersion.latestSupported(); | |
} | |
@Override | |
public Set<String> getSupportedAnnotationTypes() { | |
return ImmutableSet.of(Builder.class.getCanonicalName()); | |
} | |
@Override | |
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | |
for (Element element : roundEnv.getElementsAnnotatedWith(Builder.class)) { | |
if (element.getKind() != ElementKind.CLASS) { | |
messager.printMessage(Diagnostic.Kind.ERROR, Builder.class.getSimpleName() + " annotation can only be applied to a class."); | |
return true; | |
} | |
// get element metadata | |
String packageName = getPackageName(element); | |
String targetName = lowerCamelCase(element.getSimpleName().toString(), element); | |
List<VariableElement> vars = getNonPrivateVariables(element); | |
String builderName = String.format("%sBuilder", element.getSimpleName()); | |
ClassName builderType = ClassName.get(packageName, builderName); | |
// create private fields and public setters | |
List<FieldSpec> fields = new ArrayList<>(vars.size()); | |
List<MethodSpec> setters = new ArrayList<>(vars.size()); | |
for (VariableElement var : vars) { | |
TypeName typeName = TypeName.get(var.asType()); | |
String name = var.getSimpleName().toString(); | |
// create the field | |
fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); | |
// create the setter | |
setters.add(MethodSpec.methodBuilder(name) | |
.addModifiers(PUBLIC) | |
.returns(builderType) | |
.addParameter(typeName, name) | |
.addStatement("this.$N = $N", name, name) | |
.addStatement("return this") | |
.build()); | |
} | |
// create the build method | |
TypeName targetType = TypeName.get(element.asType()); | |
MethodSpec.Builder buildMethodBuilder = | |
MethodSpec.methodBuilder("build") | |
.addModifiers(PUBLIC) | |
.returns(targetType) | |
.addStatement("$1T $2N = new $1T()", targetType, targetName); | |
for (FieldSpec field : fields) { | |
buildMethodBuilder | |
.addStatement("$1N.$2N = this.$2N", targetName, field); | |
} | |
buildMethodBuilder.addStatement("return $N", targetName); | |
MethodSpec buildMethod = buildMethodBuilder.build(); | |
// create the builder type | |
TypeSpec builder = TypeSpec.classBuilder(builderType) | |
.addModifiers(PUBLIC, FINAL) | |
.addFields(fields) | |
.addMethods(setters) | |
.addMethod(buildMethod) | |
.build(); | |
// write the java source file | |
JavaFile file = JavaFile | |
.builder(builderType.packageName(), builder) | |
.build(); | |
try { | |
file.writeTo(filer); | |
} catch (IOException e) { | |
messager.printMessage(Diagnostic.Kind.ERROR,"Failed to write file for element", element); | |
} | |
} | |
return true; | |
} | |
private String getPackageName(Element element) { | |
return processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString(); | |
} | |
private String lowerCamelCase(String n, Element element) { | |
String name; | |
if (n.length() == 1) | |
return n.toLowerCase(); | |
try { | |
name = CaseFormat.valueOf(n).to(CaseFormat.LOWER_CAMEL, n); | |
} catch (IllegalArgumentException e) { | |
name = Character.toLowerCase(n.charAt(0)) + n.substring(1); | |
messager.printMessage(Diagnostic.Kind.WARNING,"Unknown class name format", element); | |
} | |
return name; | |
} | |
private List<VariableElement> getNonPrivateVariables(Element element) { | |
return ElementFilter.fieldsIn(element.getEnclosedElements()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment