1+ package com .reflectoring .annotation .processor ;
2+
3+ import com .google .auto .service .AutoService ;
4+ import com .squareup .javapoet .*;
5+ import org .apache .commons .text .CaseUtils ;
6+
7+ import javax .annotation .processing .*;
8+ import javax .lang .model .SourceVersion ;
9+ import javax .lang .model .element .Element ;
10+ import javax .lang .model .element .ElementKind ;
11+ import javax .lang .model .element .Modifier ;
12+ import javax .lang .model .element .TypeElement ;
13+ import javax .lang .model .type .TypeMirror ;
14+ import javax .lang .model .util .Elements ;
15+ import javax .tools .Diagnostic ;
16+ import javax .tools .JavaFileObject ;
17+ import java .io .IOException ;
18+ import java .io .PrintWriter ;
19+ import java .util .ArrayList ;
20+ import java .util .List ;
21+ import java .util .Set ;
22+ import java .util .stream .Collectors ;
23+
24+ @ SupportedAnnotationTypes ("com.reflectoring.annotation.processor.Builder" )
25+ @ SupportedSourceVersion (SourceVersion .RELEASE_11 )
26+ @ AutoService (Processor .class )
27+ public class BuilderProcessor extends AbstractProcessor {
28+
29+ private Filer filer ;
30+ private Messager messager ;
31+ private Elements elementUtils ;
32+
33+ @ Override
34+ public synchronized void init (ProcessingEnvironment processingEnv ) {
35+
36+ super .init (processingEnv );
37+ filer = processingEnv .getFiler ();
38+ messager = processingEnv .getMessager ();
39+ elementUtils = processingEnv .getElementUtils ();
40+ }
41+
42+ @ Override
43+ public boolean process (Set <? extends TypeElement > annotations , RoundEnvironment roundEnv ) {
44+
45+ for (Element typeElement : roundEnv .getElementsAnnotatedWith (Builder .class )) {
46+
47+ List <Element > fieldElements = typeElement .getEnclosedElements ().stream ().filter (e -> ElementKind .FIELD .equals (e .getKind ())).collect (
48+ Collectors .toList ());
49+
50+ String packageName = elementUtils .getPackageOf (typeElement ).getQualifiedName ().toString ();
51+ String className = typeElement .getSimpleName ().toString ();
52+ String builderName = String .format ("%sBuilder" , typeElement .getSimpleName ().toString ());
53+ String classVariableName = CaseUtils .toCamelCase (typeElement .getSimpleName ().toString (), false , '_' );
54+
55+ try {
56+ //writeBuilderClass(packageName, className, classVariableName, builderName, fieldElements);
57+
58+ writeJavaPoetBuilderClass (packageName , className , classVariableName , builderName , fieldElements , typeElement );
59+ } catch (IOException e ) {
60+ messager .printMessage (Diagnostic .Kind .ERROR , "Failed to write file for element" , typeElement );
61+ }
62+ }
63+
64+ return true ;
65+ }
66+
67+ private String getBaseName (String name ) {
68+
69+ int lastPeriodIndex = name .lastIndexOf ('.' );
70+ if (lastPeriodIndex > 0 ) {
71+ name = name .substring (lastPeriodIndex + 1 );
72+ }
73+
74+ return name ;
75+ }
76+
77+ private void writeBuilderClass (String packageName , String className , String classVariableName , String builderName ,
78+ List <Element > fieldElements ) throws IOException {
79+
80+ JavaFileObject builder = processingEnv .getFiler ().createSourceFile (builderName );
81+
82+ try (PrintWriter out = new PrintWriter (builder .openWriter ())) {
83+
84+ // Write the Package name
85+ out .print ("package " );
86+ out .print (packageName );
87+ out .println (";" );
88+ out .println ();
89+
90+ // Write the Class name
91+ out .print ("public final class " );
92+ out .print (builderName );
93+ out .println (" {" );
94+ out .println ();
95+
96+ // Write the Field names
97+ for (Element fieldElement : fieldElements ) {
98+
99+ TypeMirror typeMirror = fieldElement .asType ();
100+
101+ String fieldTypeName = getBaseName (typeMirror .toString ());
102+ String fieldName = getBaseName (fieldElement .getSimpleName ().toString ());
103+
104+ out .print ("private " );
105+ out .print (fieldTypeName );
106+ out .print (" " );
107+ out .print (fieldName );
108+ out .print (";" );
109+ out .println ();
110+ }
111+
112+ out .println ();
113+
114+ // Write the Setters
115+ for (Element fieldElement : fieldElements ) {
116+
117+ TypeMirror typeMirror = fieldElement .asType ();
118+
119+ String fieldTypeName = getBaseName (typeMirror .toString ());
120+ String fieldName = getBaseName (fieldElement .getSimpleName ().toString ());
121+
122+ out .print ("public " );
123+ out .print (" " );
124+ out .print (builderName );
125+ out .print (" " );
126+ out .print (fieldName );
127+ out .print ("(" );
128+ out .print (fieldTypeName );
129+ out .print (" " );
130+ out .print (fieldName );
131+ out .print (") {" );
132+ out .println ();
133+ out .print (" this." );
134+ out .print (fieldName );
135+ out .print (" = " );
136+ out .print (fieldName );
137+ out .print (";" );
138+ out .println ();
139+ out .print (" return this;" );
140+ out .println ();
141+ out .print ("}" );
142+ out .println ();
143+ out .println ();
144+ }
145+
146+ // Write the build function
147+ out .print ("public " );
148+ out .print (" " );
149+ out .print (className );
150+ out .print (" build() {" );
151+ out .println ();
152+ out .print (" " );
153+ out .print (className );
154+ out .print (" " );
155+ out .print (classVariableName );
156+ out .print (" = new " );
157+ out .print (className );
158+ out .print ("();" );
159+ out .println ();
160+
161+ for (Element fieldElement : fieldElements ) {
162+
163+ TypeMirror typeMirror = fieldElement .asType ();
164+
165+ String fieldTypeName = getBaseName (typeMirror .toString ());
166+ String fieldName = getBaseName (fieldElement .getSimpleName ().toString ());
167+
168+ out .print (" " );
169+ out .print (classVariableName );
170+ out .print (".set" );
171+ out .print (CaseUtils .toCamelCase (fieldName , true , '_' ));
172+ out .print ("(this." );
173+ out .print (fieldName );
174+ out .println (");" );
175+ }
176+
177+ out .println ();
178+ out .print (" return " );
179+ out .print (classVariableName );
180+ out .print (";" );
181+ out .println ();
182+ out .println (" }" );
183+ out .println ("}" );
184+ }
185+ }
186+
187+ private void writeJavaPoetBuilderClass (String packageName , String className , String classVariableName , String builderName ,
188+ List <Element > fieldElements , Element typeElement ) throws IOException {
189+
190+ ClassName builderType = ClassName .get (packageName , builderName );
191+
192+ List <FieldSpec > fields = new ArrayList <>(fieldElements .size ());
193+ List <MethodSpec > fieldSetters = new ArrayList <>(fieldElements .size ());
194+
195+ // Generate the fields and field setters
196+ generateFieldsAndSetters (fields , fieldSetters , fieldElements , builderType );
197+
198+ TypeName targetType = TypeName .get (typeElement .asType ());
199+
200+ // Generate the build method
201+ MethodSpec buildMethod = generateBuildMethod (targetType , classVariableName , fields );
202+
203+ TypeSpec builder = TypeSpec .classBuilder (builderType )
204+ .addModifiers (Modifier .PUBLIC , Modifier .FINAL )
205+ .addFields (fields )
206+ .addMethods (fieldSetters )
207+ .addMethod (buildMethod ).build ();
208+
209+ JavaFile file = JavaFile .builder (builderType .packageName (), builder .toBuilder ().build ()).build ();
210+
211+ file .writeTo (filer );
212+ }
213+
214+ private void generateFieldsAndSetters (List <FieldSpec > fields , List <MethodSpec > fieldSetters , List <Element > fieldElements , ClassName builderType ){
215+
216+ for (Element fieldElement : fieldElements ) {
217+
218+ TypeName typeName = TypeName .get (fieldElement .asType ());
219+ String fieldName = getBaseName (fieldElement .getSimpleName ().toString ());
220+
221+ fields .add (FieldSpec .builder (typeName , fieldName , Modifier .PRIVATE ).build ());
222+
223+ fieldSetters .add (
224+ MethodSpec .methodBuilder (fieldName )
225+ .addModifiers (Modifier .PUBLIC )
226+ .returns (builderType )
227+ .addParameter (typeName , fieldName )
228+ .addStatement ("this.$N = $N" , fieldName , fieldName )
229+ .addStatement ("return this" ).build ());
230+ }
231+ }
232+
233+ private MethodSpec generateBuildMethod (TypeName targetType , String variableName , List <FieldSpec > fields ) {
234+
235+ MethodSpec .Builder buildMethodBuilder = MethodSpec .methodBuilder ("build" )
236+ .addModifiers (Modifier .PUBLIC )
237+ .returns (targetType )
238+ .addStatement ("$1T $2N = new $1T()" , targetType , variableName );
239+
240+ for (FieldSpec field : fields ) {
241+
242+ buildMethodBuilder .addStatement ("$1N.set$2N(this.$3N)" , variableName , CaseUtils .toCamelCase (field .name , true , '_' ), field .name );
243+ }
244+
245+ buildMethodBuilder .addStatement ("return $N" , variableName );
246+
247+ return buildMethodBuilder .build ();
248+ }
249+ }
0 commit comments