Skip to content

Commit 6e31ea8

Browse files
committed
Add collection support #4
1 parent 00e86e3 commit 6e31ea8

File tree

6 files changed

+129
-3
lines changed

6 files changed

+129
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ For more examples see [here](/example/src/main/java/de/bethibande/serial/example
5656
| String / CharSequence ||
5757
| Any enum type ||
5858
| Arrays ||
59-
| Collections | 💡 |
59+
| Collections | |
6060
| Maps | 💡 |
6161
| Other serializable types ||
6262
| Java time types ||
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package de.bethibande.serial.allocation;
2+
3+
@FunctionalInterface
4+
public interface ParameterizedObjectAllocator<P, T> {
5+
6+
T allocate(final P parameter);
7+
8+
}

example/src/main/java/de/bethibande/serial/example/Main.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@
77
import io.netty.buffer.ByteBuf;
88
import io.netty.buffer.ByteBufAllocator;
99

10+
import java.util.ArrayList;
11+
import java.util.List;
12+
1013
public class Main {
1114

1215
public static void main(String[] args) {
13-
final ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(12);
16+
final ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(32);
1417
final Writer writer = new NettyWriter(buffer);
1518
final Reader reader = new NettyReader(buffer);
1619

1720
final TestDTO test = new TestDTO();
1821
test.setSomeString("abc");
22+
test.setStringList(List.of("a", "b", "c"));
1923
final TestDTOSerializer serializer = new TestDTOSerializer();
2024
final TestDTODeserializer deserializer = new TestDTODeserializer();
25+
deserializer.stringListNotNullAllocator(ArrayList::new);
2126

2227
serializer.bind(writer)
2328
.write(test.withSomeNumber(234))

example/src/main/java/de/bethibande/serial/example/TestDTO.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import lombok.ToString;
77
import org.jetbrains.annotations.NotNull;
88

9+
import java.util.Collection;
10+
import java.util.List;
11+
912
@Getter
1013
@Setter
1114
@ToString
@@ -19,9 +22,10 @@ public class TestDTO {
1922
@SuppressWarnings("boxedtypes")
2023
private Integer someInteger;
2124
private MyEnum someEnum;
22-
@Getter
2325
private @NotNull String[] someStringArray;
2426

27+
private List<String> stringList;
28+
2529
// We need to prevent the default lombok setter here, since lombok handles annotations incorrectly adding a null-check that shouldn't be there.
2630
public void setSomeStringArray(final String[] someStringArray) {
2731
this.someStringArray = someStringArray;

processor/src/main/java/de/bethibande/serial/processor/TypeHelper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ public static TypeMirror asType(final Class<?> clazz) {
6161
return asElement(clazz).asType();
6262
}
6363

64+
public static TypeMirror erasure(final TypeMirror type) {
65+
return SerializationProcessor.TYPES.get().erasure(type);
66+
}
67+
68+
public static boolean is(final TypeMirror a, final TypeMirror b) {
69+
return SerializationProcessor.TYPES.get().isSameType(a, b);
70+
}
71+
6472
public static boolean isCharSequence(final TypeMirror type) {
6573
return SerializationProcessor.TYPES.get().isSubtype(type, asType(CharSequence.class));
6674
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package de.bethibande.serial.processor.serializer.types;
2+
3+
import com.google.auto.service.AutoService;
4+
import com.palantir.javapoet.*;
5+
import de.bethibande.serial.allocation.ParameterizedObjectAllocator;
6+
import de.bethibande.serial.processor.TypeHelper;
7+
import de.bethibande.serial.processor.context.SerializationContext;
8+
import de.bethibande.serial.processor.generator.FieldInfo;
9+
import de.bethibande.serial.processor.serializer.ElementSerializer;
10+
import de.bethibande.serial.processor.serializer.EmbeddedTypeTransformer;
11+
import de.bethibande.serial.processor.serializer.FieldBasedObjectTransformer;
12+
13+
import javax.lang.model.element.Modifier;
14+
import javax.lang.model.type.DeclaredType;
15+
import javax.lang.model.type.TypeMirror;
16+
import java.util.Collection;
17+
import java.util.Optional;
18+
19+
@AutoService(FieldBasedObjectTransformer.class)
20+
public class CollectionTypes extends EmbeddedTypeTransformer {
21+
22+
@Override
23+
protected boolean isApplicable0(final FieldInfo field) {
24+
return !field.isNullable() && TypeHelper.isAssignable(TypeHelper.erasure(field.getType()), TypeHelper.asType(Collection.class));
25+
}
26+
27+
@Override
28+
protected FieldInfo createChildType(final FieldInfo field, final ElementSerializer serializer) {
29+
if (field.getType() instanceof DeclaredType declaredType) {
30+
final TypeMirror type = declaredType.getTypeArguments().getFirst();
31+
32+
return field.toBuilder()
33+
.type(type)
34+
.nullable(TypeHelper.isNullable(type))
35+
.build();
36+
}
37+
throw new IllegalArgumentException("Type is not a declared type");
38+
}
39+
40+
@Override
41+
public CodeBlock createSerializationCode(final FieldInfo field, final SerializationContext ctx) {
42+
return CodeBlock.builder()
43+
.addStatement("$L.writeInt($L.size())", FIELD_WRITER, FIELD_VALUE)
44+
.beginControlFlow("for ($T item : $L)", field.getFirstChild().getType(), FIELD_VALUE)
45+
.addStatement("$L(item)", embeddedMethodName(field))
46+
.endControlFlow()
47+
.addStatement("return this")
48+
.build();
49+
}
50+
51+
protected String allocatorFieldName(final FieldInfo field) {
52+
return methodName(field) + "Allocator";
53+
}
54+
55+
@Override
56+
public Optional<String> wrappedMethodNameSuffix() {
57+
return Optional.of("CollectionValue");
58+
}
59+
60+
@Override
61+
public void applyDeserializerTransformation(final TypeSpec.Builder builder,
62+
final FieldInfo field,
63+
final SerializationContext ctx) {
64+
final TypeName fieldType = TypeName.get(field.getType());
65+
final TypeName allocatorType = ParameterizedTypeName.get(
66+
ClassName.get(ParameterizedObjectAllocator.class),
67+
ClassName.get(Integer.class),
68+
fieldType
69+
);
70+
final String allocatorFieldName = allocatorFieldName(field);
71+
72+
final FieldSpec allocatorField = FieldSpec.builder(allocatorType, allocatorFieldName)
73+
.addModifiers(Modifier.PRIVATE)
74+
.build();
75+
76+
final MethodSpec allocatorMethod = MethodSpec.methodBuilder(allocatorFieldName)
77+
.addModifiers(Modifier.PUBLIC)
78+
.addParameter(ParameterSpec.builder(allocatorType, allocatorFieldName)
79+
.addModifiers(Modifier.FINAL)
80+
.build())
81+
.addStatement("this.$L = $L", allocatorFieldName, allocatorFieldName)
82+
.addStatement("return this")
83+
.returns(ctx.deserializerType())
84+
.build();
85+
86+
builder.addField(allocatorField);
87+
builder.addMethod(allocatorMethod);
88+
}
89+
90+
@Override
91+
public CodeBlock createDeserializationCode(final FieldInfo field, final SerializationContext ctx) {
92+
return CodeBlock.builder()
93+
.addStatement("final int length = $L.readInt()", FIELD_READER)
94+
.addStatement("final $T $L = $L.allocate(length)", field.getType(), FIELD_VALUE, allocatorFieldName(field))
95+
.beginControlFlow("for (int i = 0; i < length; i++)")
96+
.addStatement("$L.add($L())", FIELD_VALUE, embeddedMethodName(field))
97+
.endControlFlow()
98+
.addStatement("return $L", FIELD_VALUE)
99+
.build();
100+
}
101+
}

0 commit comments

Comments
 (0)