14
14
15
15
package com .google .firebase .encoders .processor ;
16
16
17
+ import androidx .annotation .VisibleForTesting ;
17
18
import com .google .auto .service .AutoService ;
19
+ import com .google .auto .value .AutoValue ;
18
20
import com .google .firebase .encoders .annotations .Encodable ;
19
21
import com .google .firebase .encoders .processor .getters .Getter ;
20
22
import com .google .firebase .encoders .processor .getters .GetterFactory ;
28
30
import com .squareup .javapoet .TypeSpec ;
29
31
import com .squareup .javapoet .WildcardTypeName ;
30
32
import java .io .IOException ;
33
+ import java .util .HashMap ;
31
34
import java .util .LinkedHashMap ;
32
35
import java .util .LinkedHashSet ;
33
36
import java .util .Map ;
37
+ import java .util .Optional ;
34
38
import java .util .Set ;
35
39
import javax .annotation .processing .AbstractProcessor ;
36
40
import javax .annotation .processing .ProcessingEnvironment ;
@@ -56,7 +60,6 @@ public class EncodableProcessor extends AbstractProcessor {
56
60
private Elements elements ;
57
61
private Types types ;
58
62
private GetterFactory getterFactory ;
59
- private TypeTraversal traversal ;
60
63
61
64
@ Override
62
65
public synchronized void init (ProcessingEnvironment processingEnvironment ) {
@@ -120,25 +123,116 @@ private void processClass(Element element) {
120
123
.addModifiers (Modifier .PUBLIC )
121
124
.addAnnotation (Override .class );
122
125
126
+ Map <String , TypeSpec > autoValueSupportClasses = new HashMap <>();
127
+
123
128
for (Encoder encoder : encoders ) {
124
129
encoderBuilder .addType (encoder .code ());
125
130
126
131
configureMethod .addCode (
127
132
"cfg.registerEncoder($T.class, $N.INSTANCE);\n " ,
128
133
types .erasure (encoder .type ()),
129
134
encoder .code ());
135
+ autoValueSupport (
136
+ Names .packageName (element ),
137
+ element .getSimpleName ().toString (),
138
+ encoder ,
139
+ configureMethod )
140
+ .ifPresent (
141
+ spec -> {
142
+ String packageName = Names .packageName (types .asElement (encoder .type ()));
143
+ autoValueSupportClasses .put (packageName , spec );
144
+ });
130
145
}
131
146
encoderBuilder .addMethod (configureMethod .build ());
132
147
133
148
JavaFile file = JavaFile .builder (Names .packageName (element ), encoderBuilder .build ()).build ();
134
149
135
150
try {
136
151
file .writeTo (processingEnv .getFiler ());
152
+ for (Map .Entry <String , TypeSpec > autoValue : autoValueSupportClasses .entrySet ()) {
153
+ JavaFile .builder (autoValue .getKey (), autoValue .getValue ())
154
+ .build ()
155
+ .writeTo (processingEnv .getFiler ());
156
+ }
137
157
} catch (IOException ex ) {
138
158
throw new RuntimeException (ex );
139
159
}
140
160
}
141
161
162
+ private Optional <TypeSpec > autoValueSupport (
163
+ String rootPackageName ,
164
+ String containingClassName ,
165
+ Encoder encoder ,
166
+ MethodSpec .Builder configureMethod ) {
167
+ Element element = types .asElement (encoder .type ());
168
+ AutoValue autoValue = element .getAnnotation (AutoValue .class );
169
+ if (autoValue == null ) {
170
+ return Optional .empty ();
171
+ }
172
+ String typePackageName = Names .packageName (element );
173
+
174
+ if (rootPackageName .equals (typePackageName )) {
175
+ configureMethod .addCode (
176
+ "cfg.registerEncoder(AutoValue_$T.class, $N.INSTANCE);\n " ,
177
+ types .erasure (encoder .type ()),
178
+ encoder .code ());
179
+ return Optional .empty ();
180
+ }
181
+
182
+ // the generated class has a rather long name but provides uniqueness guarantees.
183
+ TypeSpec supportClass =
184
+ TypeSpec .classBuilder (
185
+ String .format (
186
+ "Encodable%s%s%sAutoValueSupport" ,
187
+ packageNameToCamelCase (rootPackageName ),
188
+ containingClassName ,
189
+ element .getSimpleName ()))
190
+ .addModifiers (Modifier .FINAL , Modifier .PUBLIC )
191
+ .addField (
192
+ FieldSpec .builder (
193
+ ParameterizedTypeName .get (
194
+ ClassName .get (Class .class ),
195
+ WildcardTypeName .subtypeOf (ClassName .get (encoder .type ()))),
196
+ "TYPE" ,
197
+ Modifier .PUBLIC ,
198
+ Modifier .STATIC ,
199
+ Modifier .FINAL )
200
+ .initializer ("AutoValue_$T.class" , encoder .type ())
201
+ .build ())
202
+ .build ();
203
+
204
+ String packageName = Names .packageName (types .asElement (encoder .type ()));
205
+ configureMethod .addCode (
206
+ "cfg.registerEncoder($L.$N.TYPE, $N.INSTANCE);\n " ,
207
+ packageName ,
208
+ supportClass ,
209
+ encoder .code ());
210
+
211
+ return Optional .of (supportClass );
212
+ }
213
+
214
+ @ VisibleForTesting
215
+ static String packageNameToCamelCase (String packageName ) {
216
+ if (packageName .isEmpty ()) {
217
+ return packageName ;
218
+ }
219
+ packageName = Character .toUpperCase (packageName .charAt (0 )) + packageName .substring (1 );
220
+
221
+ int dotIndex = packageName .indexOf ('.' );
222
+ while (dotIndex > -1 ) {
223
+ String prefix = packageName .substring (0 , dotIndex );
224
+ String suffix = "" ;
225
+ if (dotIndex < packageName .length () - 1 ) {
226
+ suffix =
227
+ Character .toUpperCase (packageName .charAt (dotIndex + 1 ))
228
+ + (dotIndex < packageName .length () - 2 ? packageName .substring (dotIndex + 2 ) : "" );
229
+ }
230
+ packageName = prefix + suffix ;
231
+ dotIndex = packageName .indexOf ('.' );
232
+ }
233
+ return packageName ;
234
+ }
235
+
142
236
class GetterVisitor implements TypeVisitor <Encoder > {
143
237
final Map <TypeMirror , TypeSpec > encoded = new LinkedHashMap <>();
144
238
0 commit comments