Skip to content

Commit 0c1b7b6

Browse files
authored
Add FieldDescriptor support to DataEncoder. (#2204)
This change allows adding custom metadata to encoded fields, which will be useful to implement protobuf encoding.
1 parent f902fe2 commit 0c1b7b6

File tree

20 files changed

+494
-51
lines changed

20 files changed

+494
-51
lines changed

encoders/firebase-encoders-json/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
version=17.0.1
15+
version=17.1.0
1616
latestReleasedVersion=17.0.0

encoders/firebase-encoders-json/src/main/java/com/google/firebase/encoders/json/JsonValueObjectEncoderContext.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import androidx.annotation.NonNull;
2020
import androidx.annotation.Nullable;
2121
import com.google.firebase.encoders.EncodingException;
22+
import com.google.firebase.encoders.FieldDescriptor;
2223
import com.google.firebase.encoders.ObjectEncoder;
2324
import com.google.firebase.encoders.ObjectEncoderContext;
2425
import com.google.firebase.encoders.ValueEncoder;
@@ -102,6 +103,44 @@ public JsonValueObjectEncoderContext add(@NonNull String name, boolean value) th
102103
return add(value);
103104
}
104105

106+
@NonNull
107+
@Override
108+
public ObjectEncoderContext add(@NonNull FieldDescriptor field, @Nullable Object obj)
109+
throws IOException {
110+
return add(field.getName(), obj);
111+
}
112+
113+
@NonNull
114+
@Override
115+
public ObjectEncoderContext add(@NonNull FieldDescriptor field, float value) throws IOException {
116+
return add(field.getName(), value);
117+
}
118+
119+
@NonNull
120+
@Override
121+
public ObjectEncoderContext add(@NonNull FieldDescriptor field, double value) throws IOException {
122+
return add(field.getName(), value);
123+
}
124+
125+
@NonNull
126+
@Override
127+
public ObjectEncoderContext add(@NonNull FieldDescriptor field, int value) throws IOException {
128+
return add(field.getName(), value);
129+
}
130+
131+
@NonNull
132+
@Override
133+
public ObjectEncoderContext add(@NonNull FieldDescriptor field, long value) throws IOException {
134+
return add(field.getName(), value);
135+
}
136+
137+
@NonNull
138+
@Override
139+
public ObjectEncoderContext add(@NonNull FieldDescriptor field, boolean value)
140+
throws IOException {
141+
return add(field.getName(), value);
142+
}
143+
105144
@NonNull
106145
@Override
107146
public ObjectEncoderContext inline(@Nullable Object value) throws IOException {
@@ -118,6 +157,12 @@ public ObjectEncoderContext nested(@NonNull String name) throws IOException {
118157
return childContext;
119158
}
120159

160+
@NonNull
161+
@Override
162+
public ObjectEncoderContext nested(@NonNull FieldDescriptor field) throws IOException {
163+
return nested(field.getName());
164+
}
165+
121166
@NonNull
122167
@Override
123168
public JsonValueObjectEncoderContext add(@Nullable String value) throws IOException {

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/EncodableProcessor.java

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
import com.google.common.collect.ArrayListMultimap;
2121
import com.google.common.collect.Multimap;
2222
import com.google.firebase.encoders.annotations.Encodable;
23+
import com.google.firebase.encoders.processor.getters.AnnotationDescriptor;
24+
import com.google.firebase.encoders.processor.getters.AnnotationProperty;
2325
import com.google.firebase.encoders.processor.getters.Getter;
2426
import com.google.firebase.encoders.processor.getters.GetterFactory;
2527
import com.squareup.javapoet.ClassName;
28+
import com.squareup.javapoet.CodeBlock;
2629
import com.squareup.javapoet.FieldSpec;
2730
import com.squareup.javapoet.JavaFile;
2831
import com.squareup.javapoet.MethodSpec;
@@ -58,6 +61,7 @@
5861
@SupportedSourceVersion(SourceVersion.RELEASE_8)
5962
public class EncodableProcessor extends AbstractProcessor {
6063

64+
private static final String CODEGEN_VERSION = "2";
6165
static final String ENCODABLE_ANNOTATION = "com.google.firebase.encoders.annotations.Encodable";
6266
private Elements elements;
6367
private Types types;
@@ -85,6 +89,11 @@ public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv
8589
}
8690

8791
private void processClass(Element element) {
92+
if (types.isAssignable(
93+
element.asType(), elements.getTypeElement("java.lang.annotation.Annotation").asType())) {
94+
return;
95+
}
96+
8897
// generates class of the following shape:
8998
//
9099
// public class AutoFooEncoder implements Configurator {
@@ -107,7 +116,7 @@ private void processClass(Element element) {
107116
Modifier.PUBLIC,
108117
Modifier.STATIC,
109118
Modifier.FINAL)
110-
.initializer("1")
119+
.initializer(CODEGEN_VERSION)
111120
.build())
112121
.addField(
113122
FieldSpec.builder(
@@ -259,12 +268,47 @@ public VisitResult<Encoder> visit(TypeMirror type) {
259268
.addAnnotation(Override.class);
260269

261270
Set<TypeMirror> result = new LinkedHashSet<>();
271+
Set<FieldSpec> descriptorFields = new LinkedHashSet<>();
272+
ClassName fieldDescriptor = ClassName.get("com.google.firebase.encoders", "FieldDescriptor");
262273
for (Getter getter : getterFactory.allGetters((DeclaredType) type)) {
263274
result.addAll(getTypesToVisit(getter.getUnderlyingType()));
264275
if (getter.inline()) {
265276
methodBuilder.addCode("ctx.inline(value.$L);\n", getter.expression());
266277
} else {
267-
methodBuilder.addCode("ctx.add($S, value.$L);\n", getter.name(), getter.expression());
278+
CodeBlock.Builder codeBuilder;
279+
if (getter.annotationDescriptors().isEmpty()) {
280+
codeBuilder = CodeBlock.builder().add("$T.of($S)", fieldDescriptor, getter.name());
281+
} else {
282+
codeBuilder =
283+
CodeBlock.builder()
284+
.add("$T.builder($S)\n", fieldDescriptor, getter.name())
285+
.indent()
286+
.indent();
287+
for (AnnotationDescriptor desc : getter.annotationDescriptors()) {
288+
ClassName annotationBuilder =
289+
builderName(
290+
ClassName.get((TypeElement) desc.type().getAnnotationType().asElement()));
291+
codeBuilder.add(".withProperty($T.builder()\n", annotationBuilder);
292+
for (AnnotationProperty property : desc.properties()) {
293+
codeBuilder.add("$>.$L($L)\n$<", property.name(), property.value());
294+
}
295+
codeBuilder.add("$>.build())\n$<");
296+
}
297+
codeBuilder.add(".build()").unindent().unindent();
298+
}
299+
descriptorFields.add(
300+
FieldSpec.builder(
301+
fieldDescriptor,
302+
getter.name().toUpperCase() + "_DESCRIPTOR",
303+
Modifier.PRIVATE,
304+
Modifier.FINAL,
305+
Modifier.STATIC)
306+
.initializer(codeBuilder.build())
307+
.build());
308+
methodBuilder.addCode(
309+
"ctx.add($L_DESCRIPTOR, value.$L);\n",
310+
getter.name().toUpperCase(),
311+
getter.expression());
268312
}
269313
}
270314

@@ -281,12 +325,25 @@ public VisitResult<Encoder> visit(TypeMirror type) {
281325
FieldSpec.builder(className, "INSTANCE", Modifier.FINAL, Modifier.STATIC)
282326
.initializer("new $T()", className)
283327
.build())
328+
.addFields(descriptorFields)
284329
.addMethod(methodBuilder.build())
285330
.build();
286331
encoded.put(types.erasure(type), encoder);
287332
return VisitResult.of(result, Encoder.create(types.erasure(type), encoder));
288333
}
289334

335+
private ClassName builderName(ClassName annotation) {
336+
return ClassName.get(annotation.packageName(), compositeName(annotation));
337+
}
338+
339+
private String compositeName(ClassName annotation) {
340+
ClassName parentName = annotation.enclosingClassName();
341+
if (parentName == null) {
342+
return "At" + annotation.simpleName();
343+
}
344+
return compositeName(parentName) + annotation.simpleName();
345+
}
346+
290347
private Set<TypeMirror> getTypesToVisit(TypeMirror type) {
291348
TypeMirror date = elements.getTypeElement("java.util.Date").asType();
292349
TypeMirror enumType = types.erasure(elements.getTypeElement("java.lang.Enum").asType());

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/annotations/AnnotationBuilder.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.squareup.javapoet.ParameterizedTypeName;
2222
import com.squareup.javapoet.TypeName;
2323
import com.squareup.javapoet.TypeSpec;
24-
import com.squareup.javapoet.TypeSpec.Builder;
2524
import com.squareup.javapoet.WildcardTypeName;
2625
import java.lang.annotation.Annotation;
2726
import java.util.ArrayList;
@@ -80,7 +79,7 @@ public static TypeSpec generate(TypeElement element) {
8079

8180
private static TypeSpec createBuilder(
8281
ClassName builderName, ClassName annotationName, AnnotationImpl annotationImpl) {
83-
Builder annotationBuilder =
82+
TypeSpec.Builder annotationBuilder =
8483
TypeSpec.classBuilder(builderName)
8584
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
8685
.addType(annotationImpl.typeSpec);
@@ -172,7 +171,7 @@ private static String compositeName(ClassName annotation) {
172171

173172
private static AnnotationImpl createAnnotationImpl(TypeElement annotation) {
174173
String implName = annotation.getSimpleName().toString() + "Impl";
175-
Builder annotationImpl =
174+
TypeSpec.Builder annotationImpl =
176175
TypeSpec.classBuilder(implName)
177176
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
178177
.addSuperinterface(TypeName.get(annotation.asType()))

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/annotations/ToStringMethod.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import com.squareup.javapoet.ClassName;
1818
import com.squareup.javapoet.CodeBlock;
1919
import com.squareup.javapoet.MethodSpec;
20-
import com.squareup.javapoet.MethodSpec.Builder;
2120
import java.util.List;
2221
import javax.lang.model.element.ExecutableElement;
2322
import javax.lang.model.element.Modifier;
@@ -27,7 +26,7 @@
2726
final class ToStringMethod {
2827
static MethodSpec generate(TypeElement element) {
2928
ClassName.get(element).reflectionName();
30-
Builder result =
29+
MethodSpec.Builder result =
3130
MethodSpec.methodBuilder("toString")
3231
.addModifiers(Modifier.PUBLIC)
3332
.returns(String.class)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.encoders.processor.getters;
16+
17+
import com.google.auto.value.AutoValue;
18+
import java.util.List;
19+
import javax.lang.model.element.AnnotationMirror;
20+
21+
/** Represents an annotation with its explicitly set properties. */
22+
@AutoValue
23+
public abstract class AnnotationDescriptor {
24+
25+
/** Annotation type. */
26+
public abstract AnnotationMirror type();
27+
28+
/** List of annotation properties. */
29+
public abstract List<AnnotationProperty> properties();
30+
31+
public static AnnotationDescriptor create(
32+
AnnotationMirror type, List<AnnotationProperty> properties) {
33+
return new AutoValue_AnnotationDescriptor(type, properties);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.encoders.processor.getters;
16+
17+
import com.google.auto.value.AutoValue;
18+
import javax.lang.model.element.AnnotationValue;
19+
20+
/** Represents an annotation property/method with its value as explicitly set in source. */
21+
@AutoValue
22+
public abstract class AnnotationProperty {
23+
/** Name of the property, e.g. \"value\". */
24+
public abstract String name();
25+
26+
/** Value of the property. */
27+
public abstract AnnotationValue value();
28+
29+
public static AnnotationProperty create(String name, AnnotationValue value) {
30+
return new AutoValue_AnnotationProperty(name, value);
31+
}
32+
}

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/getters/Getter.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.firebase.encoders.processor.getters;
1616

1717
import com.google.auto.value.AutoValue;
18+
import java.util.Set;
1819
import javax.lang.model.type.ArrayType;
1920
import javax.lang.model.type.TypeKind;
2021
import javax.lang.model.type.TypeMirror;
@@ -24,6 +25,12 @@ public abstract class Getter {
2425
/** Encoded name of the getter. */
2526
public abstract String name();
2627

28+
/**
29+
* {@link com.google.firebase.encoders.annotations.ExtraProperty} annotations present on the
30+
* getter.
31+
*/
32+
public abstract Set<AnnotationDescriptor> annotationDescriptors();
33+
2734
/**
2835
* Java expression to get the getter's value
2936
*
@@ -49,7 +56,11 @@ public TypeMirror getUnderlyingType() {
4956
}
5057

5158
public static Getter create(
52-
String name, String expression, TypeMirror returnType, boolean inline) {
53-
return new AutoValue_Getter(name, expression, returnType, inline);
59+
String name,
60+
Set<AnnotationDescriptor> descriptors,
61+
String expression,
62+
TypeMirror returnType,
63+
boolean inline) {
64+
return new AutoValue_Getter(name, descriptors, expression, returnType, inline);
5465
}
5566
}

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/getters/GetterFactory.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
package com.google.firebase.encoders.processor.getters;
1616

1717
import com.google.firebase.encoders.annotations.Encodable;
18+
import com.google.firebase.encoders.annotations.ExtraProperty;
1819
import java.util.ArrayList;
1920
import java.util.Collections;
21+
import java.util.HashSet;
2022
import java.util.LinkedHashSet;
2123
import java.util.List;
2224
import java.util.Optional;
2325
import java.util.Set;
26+
import java.util.stream.Collectors;
2427
import javax.annotation.processing.Messager;
28+
import javax.lang.model.element.AnnotationMirror;
2529
import javax.lang.model.element.ElementKind;
2630
import javax.lang.model.element.ExecutableElement;
2731
import javax.lang.model.element.Modifier;
@@ -109,7 +113,31 @@ private Optional<Getter> create(DeclaredType ownerType, ExecutableElement elemen
109113

110114
return Optional.of(
111115
Getter.create(
112-
fieldName.get(), getterExpression, returnType, field != null && field.inline()));
116+
fieldName.get(),
117+
inferDescriptors(element),
118+
getterExpression,
119+
returnType,
120+
field != null && field.inline()));
121+
}
122+
123+
private Set<AnnotationDescriptor> inferDescriptors(ExecutableElement element) {
124+
Set<AnnotationDescriptor> annotationDescriptors = new HashSet<>();
125+
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
126+
ExtraProperty extraProperty =
127+
annotationMirror.getAnnotationType().asElement().getAnnotation(ExtraProperty.class);
128+
if (extraProperty == null) {
129+
continue;
130+
}
131+
List<AnnotationProperty> annotationValues =
132+
annotationMirror.getElementValues().entrySet().stream()
133+
.map(
134+
e ->
135+
AnnotationProperty.create(
136+
e.getKey().getSimpleName().toString(), e.getValue()))
137+
.collect(Collectors.toList());
138+
annotationDescriptors.add(AnnotationDescriptor.create(annotationMirror, annotationValues));
139+
}
140+
return annotationDescriptors;
113141
}
114142

115143
private TypeMirror resolveTypeArguments(DeclaredType ownerType, TypeMirror genericType) {

0 commit comments

Comments
 (0)