Skip to content

Commit 7512967

Browse files
authored
Merge pull request #21 from aboutbits/add_support_for_non_record_types
add support for custom types a classes
2 parents d601fdf + cb7c9b9 commit 7512967

74 files changed

Lines changed: 2011 additions & 388 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
import lombok.Getter;
66
import lombok.extern.slf4j.Slf4j;
77

8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
import java.lang.annotation.Target;
12+
import java.lang.reflect.Modifier;
813
import java.util.ArrayList;
914
import java.util.Arrays;
1015
import java.util.HashSet;
@@ -31,13 +36,24 @@ public void setAdditionalTypePackages(String[] additionalTypePackages) {
3136
log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan));
3237
var classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan);
3338

34-
this.relevantTypes = findAllCustomTypeRecords(classScanner);
39+
this.relevantTypes = findAllCustomTypes(classScanner);
3540
}
3641

3742
@SuppressWarnings("rawtypes")
38-
public static Set<Class<? extends CustomType>> findAllCustomTypeRecords(ClassScannerUtil.ClassScanner classScanner) {
43+
public static Set<Class<? extends CustomType>> findAllCustomTypes(ClassScannerUtil.ClassScanner classScanner) {
3944
return classScanner.getSubTypesOf(CustomType.class).stream()
40-
.filter(Record.class::isAssignableFrom)
45+
.filter(item ->
46+
!item.isInterface()
47+
&& !item.isAnonymousClass()
48+
&& !Modifier.isAbstract(item.getModifiers())
49+
&& !item.isAnnotationPresent(DisableCustomTypeConfiguration.class)
50+
)
4151
.collect(Collectors.toSet());
4252
}
53+
54+
@Target({ElementType.TYPE})
55+
@Retention(RetentionPolicy.RUNTIME)
56+
public @interface DisableCustomTypeConfiguration {
57+
58+
}
4359
}

src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.fasterxml.jackson.core.JsonParser;
44
import com.fasterxml.jackson.databind.DeserializationContext;
55
import com.fasterxml.jackson.databind.JsonDeserializer;
6-
import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil;
6+
import it.aboutbits.springboot.toolbox.reflection.util.CustomTypeReflectionUtil;
77
import it.aboutbits.springboot.toolbox.type.CustomType;
88
import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal;
99

@@ -22,7 +22,14 @@ public class CustomTypeDeserializer<T extends CustomType<?>> extends JsonDeseria
2222
public CustomTypeDeserializer(Class<T> customType) {
2323
this.customType = customType;
2424

25-
this.constructor = RecordReflectionUtil.getCanonicalConstructor(customType);
25+
try {
26+
this.constructor = CustomTypeReflectionUtil.getCustomTypeConstructor(customType);
27+
} catch (NoSuchMethodException e) {
28+
throw new CustomTypeDeserializerException(
29+
"Unable to find constructor for type: " + customType.getName(),
30+
e
31+
);
32+
}
2633

2734
this.typeConverter = getTypeConverter(
2835
constructor.getParameterTypes()[0]
@@ -49,9 +56,18 @@ public T deserialize(JsonParser jsonParser, DeserializationContext deserializati
4956
}
5057

5158
private static Function<JsonParser, Object> getTypeConverter(Class<?> wrappedType) {
59+
if (Byte.class.isAssignableFrom(wrappedType)) {
60+
return getByteConverter();
61+
}
62+
if (Boolean.class.isAssignableFrom(wrappedType)) {
63+
return getBooleanConverter();
64+
}
5265
if (String.class.isAssignableFrom(wrappedType)) {
5366
return getStringConverter();
5467
}
68+
if (Character.class.isAssignableFrom(wrappedType)) {
69+
return getCharConverter();
70+
}
5571
if (Short.class.isAssignableFrom(wrappedType)) {
5672
return getShortConverter();
5773
}
@@ -79,6 +95,26 @@ private static Function<JsonParser, Object> getTypeConverter(Class<?> wrappedTyp
7995
throw new CustomTypeDeserializerException("Value type not supported: " + wrappedType.getName());
8096
}
8197

98+
private static Function<JsonParser, Object> getByteConverter() {
99+
return jsonParser -> {
100+
try {
101+
return jsonParser.getByteValue();
102+
} catch (IOException e) {
103+
throw new CustomTypeDeserializerException("Failed to read value as Byte.", e);
104+
}
105+
};
106+
}
107+
108+
private static Function<JsonParser, Object> getBooleanConverter() {
109+
return jsonParser -> {
110+
try {
111+
return jsonParser.getBooleanValue();
112+
} catch (IOException e) {
113+
throw new CustomTypeDeserializerException("Failed to read value as Boolean.", e);
114+
}
115+
};
116+
}
117+
82118
private static Function<JsonParser, Object> getScaledBigDecimalConverter() {
83119
return jsonParser -> {
84120
try {
@@ -169,6 +205,20 @@ private static Function<JsonParser, Object> getStringConverter() {
169205
};
170206
}
171207

208+
private static Function<JsonParser, Object> getCharConverter() {
209+
return jsonParser -> {
210+
try {
211+
var value = jsonParser.getValueAsString();
212+
if (value == null || value.length() != 1) {
213+
throw new IOException();
214+
}
215+
return value.charAt(0);
216+
} catch (IOException e) {
217+
throw new CustomTypeDeserializerException("Failed to read value as Char.", e);
218+
}
219+
};
220+
}
221+
172222
public static final class CustomTypeDeserializerException extends RuntimeException {
173223
public CustomTypeDeserializerException(String message) {
174224
super(message);

src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package it.aboutbits.springboot.toolbox.persistence.javatype.base;
22

3-
import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil;
43
import it.aboutbits.springboot.toolbox.type.CustomType;
54
import lombok.SneakyThrows;
65
import org.hibernate.type.descriptor.WrapperOptions;
@@ -14,12 +13,16 @@
1413
import java.sql.Types;
1514

1615
public abstract class WrappedBigDecimalJavaType<T extends CustomType<BigDecimal>> extends AbstractClassJavaType<T> {
17-
private final transient Constructor<T> canonicalConstructor;
16+
private final transient Constructor<T> constructor;
1817

1918
protected WrappedBigDecimalJavaType(Class<T> type) {
2019
super(type);
2120

22-
this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type);
21+
try {
22+
this.constructor = type.getConstructor(BigDecimal.class);
23+
} catch (NoSuchMethodException e) {
24+
throw new IllegalStateException("No constructor found for " + type.getName(), e);
25+
}
2326
}
2427

2528
@Override
@@ -60,7 +63,7 @@ public <X> T wrap(X value, WrapperOptions wrapperOptions) {
6063
return (T) value;
6164
}
6265
if (value instanceof Double doubleValue) {
63-
return canonicalConstructor.newInstance(BigDecimal.valueOf(doubleValue));
66+
return constructor.newInstance(BigDecimal.valueOf(doubleValue));
6467
}
6568

6669
throw unknownWrap(value.getClass());

src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigIntegerJavaType.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package it.aboutbits.springboot.toolbox.persistence.javatype.base;
22

3-
import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil;
43
import it.aboutbits.springboot.toolbox.type.CustomType;
54
import lombok.SneakyThrows;
65
import org.hibernate.type.descriptor.WrapperOptions;
@@ -14,12 +13,16 @@
1413
import java.sql.Types;
1514

1615
public abstract class WrappedBigIntegerJavaType<T extends CustomType<BigInteger>> extends AbstractClassJavaType<T> {
17-
private final transient Constructor<T> canonicalConstructor;
16+
private final transient Constructor<T> constructor;
1817

1918
protected WrappedBigIntegerJavaType(Class<T> type) {
2019
super(type);
2120

22-
this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type);
21+
try {
22+
this.constructor = type.getConstructor(BigInteger.class);
23+
} catch (NoSuchMethodException e) {
24+
throw new IllegalStateException("No constructor found for " + type.getName(), e);
25+
}
2326
}
2427

2528
@Override
@@ -60,7 +63,7 @@ public <X> T wrap(X value, WrapperOptions wrapperOptions) {
6063
return (T) value;
6164
}
6265
if (value instanceof Long longValue) {
63-
return canonicalConstructor.newInstance(BigInteger.valueOf(longValue));
66+
return constructor.newInstance(BigInteger.valueOf(longValue));
6467
}
6568

6669
throw unknownWrap(value.getClass());
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package it.aboutbits.springboot.toolbox.persistence.javatype.base;
2+
3+
import it.aboutbits.springboot.toolbox.type.CustomType;
4+
import lombok.SneakyThrows;
5+
import org.hibernate.type.descriptor.WrapperOptions;
6+
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
7+
import org.hibernate.type.descriptor.jdbc.JdbcType;
8+
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
9+
10+
import java.lang.reflect.Constructor;
11+
import java.lang.reflect.InvocationTargetException;
12+
import java.sql.Types;
13+
14+
public abstract class WrappedBooleanJavaType<T extends CustomType<Boolean>> extends AbstractClassJavaType<T> {
15+
private final transient Constructor<T> constructor;
16+
17+
protected WrappedBooleanJavaType(Class<T> type) {
18+
super(type);
19+
20+
try {
21+
this.constructor = type.getConstructor(Boolean.class);
22+
} catch (NoSuchMethodException e) {
23+
throw new IllegalStateException("No constructor found for " + type.getName(), e);
24+
}
25+
}
26+
27+
@Override
28+
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
29+
return indicators.getTypeConfiguration()
30+
.getJdbcTypeRegistry()
31+
.getDescriptor(Types.BOOLEAN);
32+
}
33+
34+
@SuppressWarnings("unchecked")
35+
@Override
36+
public <X> X unwrap(T id, Class<X> aClass, WrapperOptions wrapperOptions) {
37+
var javaTypeClass = getJavaTypeClass();
38+
39+
if (id == null) {
40+
return null;
41+
}
42+
if (javaTypeClass.isAssignableFrom(aClass)) {
43+
return (X) id;
44+
}
45+
if (Boolean.class.isAssignableFrom(aClass)) {
46+
return (X) id.value();
47+
}
48+
49+
throw unknownUnwrap(aClass);
50+
}
51+
52+
@SuppressWarnings("unchecked")
53+
@SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class})
54+
@Override
55+
public <X> T wrap(X value, WrapperOptions wrapperOptions) {
56+
var clazz = getJavaTypeClass();
57+
58+
if (value == null) {
59+
return null;
60+
}
61+
if (clazz.isInstance(value)) {
62+
return (T) value;
63+
}
64+
if (value instanceof Boolean booleanValue) {
65+
return constructor.newInstance(booleanValue);
66+
}
67+
68+
throw unknownWrap(value.getClass());
69+
}
70+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package it.aboutbits.springboot.toolbox.persistence.javatype.base;
2+
3+
import it.aboutbits.springboot.toolbox.type.CustomType;
4+
import lombok.SneakyThrows;
5+
import org.hibernate.type.descriptor.WrapperOptions;
6+
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
7+
import org.hibernate.type.descriptor.jdbc.JdbcType;
8+
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
9+
10+
import java.lang.reflect.Constructor;
11+
import java.lang.reflect.InvocationTargetException;
12+
import java.sql.Types;
13+
14+
public abstract class WrappedByteJavaType<T extends CustomType<Byte>> extends AbstractClassJavaType<T> {
15+
private final transient Constructor<T> constructor;
16+
17+
protected WrappedByteJavaType(Class<T> type) {
18+
super(type);
19+
20+
try {
21+
this.constructor = type.getConstructor(Byte.class);
22+
} catch (NoSuchMethodException e) {
23+
throw new IllegalStateException("No constructor found for " + type.getName(), e);
24+
}
25+
}
26+
27+
@Override
28+
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
29+
return indicators.getTypeConfiguration()
30+
.getJdbcTypeRegistry()
31+
.getDescriptor(Types.TINYINT);
32+
}
33+
34+
@SuppressWarnings("unchecked")
35+
@Override
36+
public <X> X unwrap(T id, Class<X> aClass, WrapperOptions wrapperOptions) {
37+
var javaTypeClass = getJavaTypeClass();
38+
39+
if (id == null) {
40+
return null;
41+
}
42+
if (javaTypeClass.isAssignableFrom(aClass)) {
43+
return (X) id;
44+
}
45+
if (Byte.class.isAssignableFrom(aClass)) {
46+
return (X) id.value();
47+
}
48+
49+
throw unknownUnwrap(aClass);
50+
}
51+
52+
@SuppressWarnings("unchecked")
53+
@SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class})
54+
@Override
55+
public <X> T wrap(X value, WrapperOptions wrapperOptions) {
56+
var clazz = getJavaTypeClass();
57+
58+
if (value == null) {
59+
return null;
60+
}
61+
if (clazz.isInstance(value)) {
62+
return (T) value;
63+
}
64+
if (value instanceof Byte byteValue) {
65+
return constructor.newInstance(byteValue);
66+
}
67+
68+
throw unknownWrap(value.getClass());
69+
}
70+
}

0 commit comments

Comments
 (0)