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