Skip to content

Commit 4ad1378

Browse files
fixed issues and improved code
1 parent 5d74c52 commit 4ad1378

File tree

5 files changed

+129
-36
lines changed

5 files changed

+129
-36
lines changed

src/main/java/codes/laivy/serializable/json/JsonSerializer.java

+37-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import codes.laivy.serializable.config.Config;
66
import codes.laivy.serializable.context.*;
77
import codes.laivy.serializable.exception.IncompatibleReferenceException;
8+
import codes.laivy.serializable.factory.context.ContextFactory;
89
import com.google.gson.*;
910
import org.jetbrains.annotations.NotNull;
1011
import org.jetbrains.annotations.Nullable;
1112

1213
import java.io.IOException;
14+
import java.util.Objects;
1315

1416
public final class JsonSerializer extends AbstractTypeSerializer<JsonElement> {
1517

@@ -40,17 +42,46 @@ public final class JsonSerializer extends AbstractTypeSerializer<JsonElement> {
4042
return serialize((Context) response);
4143
} else {
4244
// Serialize again
43-
return serialize(response);
45+
return Objects.requireNonNull(serialize(response), "serialized response returned an unexpected null");
4446
}
4547
}
4648

4749
// Deserialization
4850

4951
@Override
5052
public @Nullable Object deserializeUnsafe(@NotNull Class<?> reference, @NotNull Context context, @NotNull Config config) throws IncompatibleReferenceException {
51-
// Deserialize with factory
52-
try {
53-
return config.getContextFactory().read(reference, this, context, config);
53+
if (reference == Context.class) {
54+
return context;
55+
} else if (MapContext.class.isAssignableFrom(reference)) {
56+
if (!context.isMap()) {
57+
throw new IncompatibleReferenceException("to deserialize a map context the context must be a map: " + context);
58+
}
59+
60+
return context.getAsMap();
61+
} else if (ArrayContext.class.isAssignableFrom(reference)) {
62+
if (!context.isArray()) {
63+
throw new IncompatibleReferenceException("to deserialize an array context the context must be an array: " + context);
64+
}
65+
66+
return context.getAsArray();
67+
} else if (PrimitiveContext.class.isAssignableFrom(reference)) {
68+
if (!context.isPrimitive()) {
69+
throw new IncompatibleReferenceException("to deserialize a primitive context the context must be a primitive: " + context);
70+
}
71+
72+
return context.isPrimitive();
73+
} else if (NullContext.class.isAssignableFrom(reference)) {
74+
if (!context.isNull()) {
75+
throw new IncompatibleReferenceException("to deserialize a null context the context must be a null: " + context);
76+
}
77+
78+
return context.getAsNull();
79+
} else if (Context.class.isAssignableFrom(reference)) {
80+
throw new UnsupportedOperationException("illegal context type '" + reference + "'. You should only use Context, ArrayContext, MapContext, PrimitiveContext or NullContext");
81+
} else try {
82+
// Deserialize with factory
83+
@NotNull ContextFactory factory = config.getContextFactory();
84+
return factory.read(reference, this, context, config);
5485
} catch (@NotNull IOException e) {
5586
throw new RuntimeException(e);
5687
} catch (@NotNull InstantiationException e) {
@@ -70,7 +101,8 @@ public final class JsonSerializer extends AbstractTypeSerializer<JsonElement> {
70101
return NullContext.create();
71102
} else {
72103
// Generate using context factory
73-
@Nullable Object instance = config.getContextFactory().write(object.getClass(), object, this, config);
104+
@NotNull ContextFactory factory = config.getContextFactory();
105+
@Nullable Object instance = factory.write(object.getClass(), object, this, config);
74106

75107
if (instance instanceof Context) {
76108
return (Context) instance;

src/main/java/codes/laivy/serializable/utilities/Classes.java

+42-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313
import org.jetbrains.annotations.Nullable;
1414

1515
import java.io.*;
16-
import java.lang.reflect.Field;
17-
import java.lang.reflect.InvocationTargetException;
18-
import java.lang.reflect.Method;
19-
import java.lang.reflect.Modifier;
16+
import java.lang.reflect.*;
2017
import java.util.*;
2118
import java.util.stream.Collectors;
2219

@@ -27,6 +24,47 @@ public final class Classes {
2724

2825
// Static initializers
2926

27+
public static @NotNull Map<Type, Collection<Class<?>>> getGenericTypes(@NotNull AnnotatedType primary) {
28+
// Generics
29+
@NotNull Map<Type, Collection<Class<?>>> genericConcretes = new LinkedHashMap<>();
30+
31+
@NotNull LinkedList<AnnotatedElement> elements = new LinkedList<>();
32+
elements.add(primary);
33+
34+
int count = 0;
35+
while (!elements.isEmpty()) try {
36+
@NotNull AnnotatedElement element = elements.poll();
37+
38+
if (element instanceof AnnotatedType) {
39+
@NotNull AnnotatedType annotated = (AnnotatedType) element;
40+
@NotNull Type type = annotated.getType();
41+
42+
if (element instanceof AnnotatedParameterizedType) {
43+
@NotNull AnnotatedParameterizedType parameterized = (AnnotatedParameterizedType) element;
44+
elements.addAll(Arrays.asList(parameterized.getAnnotatedActualTypeArguments()));
45+
}
46+
47+
// Skip the first annotated element values to not catch field concretes
48+
if (count == 0) {
49+
continue;
50+
}
51+
52+
genericConcretes.putIfAbsent(type, new LinkedHashSet<>());
53+
if (type instanceof Class && isConcrete((Class<?>) type)) {
54+
genericConcretes.get(type).add((Class<?>) type);
55+
} if (annotated.isAnnotationPresent(Concrete.class)) {
56+
genericConcretes.get(type).add(annotated.getAnnotation(Concrete.class).type());
57+
} if (annotated.isAnnotationPresent(Concretes.class)) {
58+
genericConcretes.get(type).addAll(Arrays.stream(annotated.getAnnotationsByType(Concretes.class)).flatMap(concretes -> Arrays.stream(concretes.value())).map(Concrete::type).collect(Collectors.toList()));
59+
}
60+
}
61+
} finally {
62+
count++;
63+
}
64+
65+
return genericConcretes;
66+
}
67+
3068
public static @Nullable Object callWriteReplace(@NotNull Object object, boolean ignoreCasting) throws NoSuchMethodException {
3169
@NotNull Class<?> reference = object.getClass();
3270
@NotNull Method method = reference.getDeclaredMethod("writeReplace");

src/test/java/ObjectTest.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
import utilities.ArrayUtils;
99

1010
import java.time.*;
11-
import java.util.*;
11+
import java.util.Date;
12+
import java.util.LinkedList;
13+
import java.util.Objects;
14+
import java.util.UUID;
1215

1316
public final class ObjectTest {
1417

src/test/java/annotations/ConcreteTest.java

-26
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import codes.laivy.serializable.Serializer;
44
import codes.laivy.serializable.annotations.Concrete;
5-
import codes.laivy.serializable.exception.IncompatibleReferenceException;
65
import codes.laivy.serializable.json.JsonSerializer;
76
import com.google.gson.JsonElement;
87
import org.jetbrains.annotations.NotNull;
@@ -46,19 +45,6 @@ public void concreteDeclaredAtClass() {
4645
Assertions.assertEquals(gender, result, "Cannot deserialize '" + element + "' into expected generic: " + result);
4746
}
4847

49-
@Test
50-
@DisplayName("Expect fail missing generic")
51-
public void failMissingGeneric() {
52-
try {
53-
@NotNull FailWithoutConcrete generic = new FailWithoutConcrete();
54-
@NotNull JsonElement element = Serializer.toJson(generic);
55-
56-
Assertions.assertEquals(generic, Serializer.fromJson(generic.getClass(), element));
57-
Assertions.fail("Didn't failed with the missing @Concrete annotation");
58-
} catch (@NotNull IncompatibleReferenceException ignore) {
59-
}
60-
}
61-
6248
// Classes
6349

6450
@Concrete(type = Female.class)
@@ -165,18 +151,6 @@ public int hashCode() {
165151

166152
}
167153

168-
// Failures
169-
170-
@SuppressWarnings("FieldCanBeLocal")
171-
private static final class FailWithoutConcrete {
172-
173-
private final @NotNull Set<Animal> set = new HashSet<>();
174-
175-
private FailWithoutConcrete() {
176-
}
177-
178-
}
179-
180154
// Utilities classes
181155

182156
private static abstract class Animal {

src/test/java/annotations/MethodSerializationTest.java

+46
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ public void subInnerClass() {
101101
@NotNull WithSubClass.Sub deserialized = Objects.requireNonNull(Serializer.fromJson(expected.getClass(), json));
102102
Assertions.assertEquals(expected, deserialized);
103103
}
104+
@Test
105+
@DisplayName("Test configuration enheritance")
106+
public void enheritConfiguration() {
107+
@NotNull EnheritConfiguration expected = new EnheritConfiguration();
108+
@NotNull JsonElement json = Serializer.toJson(expected);
109+
110+
Assertions.assertEquals(EnheritConfiguration.expectedJson, json);
111+
}
104112

105113
// Failures
106114

@@ -178,6 +186,44 @@ public int hashCode() {
178186
}
179187

180188
}
189+
190+
@MethodSerialization
191+
private static final class EnheritConfiguration {
192+
193+
// Static initializers
194+
195+
public static final @NotNull Map<String, String> expectedMap = new LinkedHashMap<>();
196+
public static final @NotNull JsonObject expectedJson = new JsonObject();
197+
198+
static {
199+
// Expected map
200+
expectedMap.put("test1", "result1");
201+
expectedMap.put("test2", "result2");
202+
203+
// Expected json
204+
expectedJson.addProperty("test1", "result1");
205+
expectedJson.addProperty("test2", "result2");
206+
}
207+
208+
// Object
209+
210+
// Ignore this field
211+
private final @NotNull String ignore = "";
212+
213+
private EnheritConfiguration() {
214+
}
215+
216+
// Serializers
217+
218+
private static @NotNull Map<String, String> serialize(@NotNull EnheritConfiguration configuration) {
219+
return expectedMap;
220+
}
221+
private static @NotNull EnheritConfiguration deserialize(@NotNull Map<String, String> map) {
222+
return new EnheritConfiguration();
223+
}
224+
225+
}
226+
181227
@MethodSerialization(deserialization = "#deserialize01", serialization = "#serialize01")
182228
private static final class Custom {
183229

0 commit comments

Comments
 (0)