Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
Expand All @@ -31,13 +36,24 @@ public void setAdditionalTypePackages(String[] additionalTypePackages) {
log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan));
var classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan);

this.relevantTypes = findAllCustomTypeRecords(classScanner);
this.relevantTypes = findAllCustomTypes(classScanner);
}

@SuppressWarnings("rawtypes")
public static Set<Class<? extends CustomType>> findAllCustomTypeRecords(ClassScannerUtil.ClassScanner classScanner) {
public static Set<Class<? extends CustomType>> findAllCustomTypes(ClassScannerUtil.ClassScanner classScanner) {
return classScanner.getSubTypesOf(CustomType.class).stream()
.filter(Record.class::isAssignableFrom)
.filter(item ->
!item.isInterface()
&& !item.isAnonymousClass()
&& !Modifier.isAbstract(item.getModifiers())
&& !item.isAnnotationPresent(DisableCustomTypeConfiguration.class)
)
.collect(Collectors.toSet());
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DisableCustomTypeConfiguration {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil;
import it.aboutbits.springboot.toolbox.reflection.util.CustomTypeReflectionUtil;
import it.aboutbits.springboot.toolbox.type.CustomType;
import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal;

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

this.constructor = RecordReflectionUtil.getCanonicalConstructor(customType);
try {
this.constructor = CustomTypeReflectionUtil.getCustomTypeConstructor(customType);
} catch (NoSuchMethodException e) {
throw new CustomTypeDeserializerException(
"Unable to find constructor for type: " + customType.getName(),
e
);
}

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

private static Function<JsonParser, Object> getTypeConverter(Class<?> wrappedType) {
if (Byte.class.isAssignableFrom(wrappedType)) {
return getByteConverter();
}
if (Boolean.class.isAssignableFrom(wrappedType)) {
return getBooleanConverter();
}
if (String.class.isAssignableFrom(wrappedType)) {
return getStringConverter();
}
if (Character.class.isAssignableFrom(wrappedType)) {
return getCharConverter();
}
if (Short.class.isAssignableFrom(wrappedType)) {
return getShortConverter();
}
Expand Down Expand Up @@ -79,6 +95,26 @@ private static Function<JsonParser, Object> getTypeConverter(Class<?> wrappedTyp
throw new CustomTypeDeserializerException("Value type not supported: " + wrappedType.getName());
}

private static Function<JsonParser, Object> getByteConverter() {
return jsonParser -> {
try {
return jsonParser.getByteValue();
} catch (IOException e) {
throw new CustomTypeDeserializerException("Failed to read value as Byte.", e);
}
};
}

private static Function<JsonParser, Object> getBooleanConverter() {
return jsonParser -> {
try {
return jsonParser.getBooleanValue();
} catch (IOException e) {
throw new CustomTypeDeserializerException("Failed to read value as Boolean.", e);
}
};
}

private static Function<JsonParser, Object> getScaledBigDecimalConverter() {
return jsonParser -> {
try {
Expand Down Expand Up @@ -169,6 +205,20 @@ private static Function<JsonParser, Object> getStringConverter() {
};
}

private static Function<JsonParser, Object> getCharConverter() {
return jsonParser -> {
try {
var value = jsonParser.getValueAsString();
if (value == null || value.length() != 1) {
throw new IOException();
}
return value.charAt(0);
} catch (IOException e) {
throw new CustomTypeDeserializerException("Failed to read value as Char.", e);
}
};
}

public static final class CustomTypeDeserializerException extends RuntimeException {
public CustomTypeDeserializerException(String message) {
super(message);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package it.aboutbits.springboot.toolbox.persistence.javatype.base;

import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil;
import it.aboutbits.springboot.toolbox.type.CustomType;
import lombok.SneakyThrows;
import org.hibernate.type.descriptor.WrapperOptions;
Expand All @@ -14,12 +13,16 @@
import java.sql.Types;

public abstract class WrappedBigDecimalJavaType<T extends CustomType<BigDecimal>> extends AbstractClassJavaType<T> {
private final transient Constructor<T> canonicalConstructor;
private final transient Constructor<T> constructor;

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

this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type);
try {
this.constructor = type.getConstructor(BigDecimal.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No constructor found for " + type.getName(), e);
}
}

@Override
Expand Down Expand Up @@ -60,7 +63,7 @@ public <X> T wrap(X value, WrapperOptions wrapperOptions) {
return (T) value;
}
if (value instanceof Double doubleValue) {
return canonicalConstructor.newInstance(BigDecimal.valueOf(doubleValue));
return constructor.newInstance(BigDecimal.valueOf(doubleValue));
}

throw unknownWrap(value.getClass());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package it.aboutbits.springboot.toolbox.persistence.javatype.base;

import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil;
import it.aboutbits.springboot.toolbox.type.CustomType;
import lombok.SneakyThrows;
import org.hibernate.type.descriptor.WrapperOptions;
Expand All @@ -14,12 +13,16 @@
import java.sql.Types;

public abstract class WrappedBigIntegerJavaType<T extends CustomType<BigInteger>> extends AbstractClassJavaType<T> {
private final transient Constructor<T> canonicalConstructor;
private final transient Constructor<T> constructor;

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

this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type);
try {
this.constructor = type.getConstructor(BigInteger.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No constructor found for " + type.getName(), e);
}
}

@Override
Expand Down Expand Up @@ -60,7 +63,7 @@ public <X> T wrap(X value, WrapperOptions wrapperOptions) {
return (T) value;
}
if (value instanceof Long longValue) {
return canonicalConstructor.newInstance(BigInteger.valueOf(longValue));
return constructor.newInstance(BigInteger.valueOf(longValue));
}

throw unknownWrap(value.getClass());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package it.aboutbits.springboot.toolbox.persistence.javatype.base;

import it.aboutbits.springboot.toolbox.type.CustomType;
import lombok.SneakyThrows;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.Types;

public abstract class WrappedBooleanJavaType<T extends CustomType<Boolean>> extends AbstractClassJavaType<T> {
private final transient Constructor<T> constructor;

protected WrappedBooleanJavaType(Class<T> type) {
super(type);

try {
this.constructor = type.getConstructor(Boolean.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No constructor found for " + type.getName(), e);
}
}

@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
return indicators.getTypeConfiguration()
.getJdbcTypeRegistry()
.getDescriptor(Types.BOOLEAN);
}

@SuppressWarnings("unchecked")
@Override
public <X> X unwrap(T id, Class<X> aClass, WrapperOptions wrapperOptions) {
var javaTypeClass = getJavaTypeClass();

if (id == null) {
return null;
}
if (javaTypeClass.isAssignableFrom(aClass)) {
return (X) id;
}
if (Boolean.class.isAssignableFrom(aClass)) {
return (X) id.value();
}

throw unknownUnwrap(aClass);
}

@SuppressWarnings("unchecked")
@SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class})
@Override
public <X> T wrap(X value, WrapperOptions wrapperOptions) {
var clazz = getJavaTypeClass();

if (value == null) {
return null;
}
if (clazz.isInstance(value)) {
return (T) value;
}
if (value instanceof Boolean booleanValue) {
return constructor.newInstance(booleanValue);
}

throw unknownWrap(value.getClass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package it.aboutbits.springboot.toolbox.persistence.javatype.base;

import it.aboutbits.springboot.toolbox.type.CustomType;
import lombok.SneakyThrows;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.Types;

public abstract class WrappedByteJavaType<T extends CustomType<Byte>> extends AbstractClassJavaType<T> {
private final transient Constructor<T> constructor;

protected WrappedByteJavaType(Class<T> type) {
super(type);

try {
this.constructor = type.getConstructor(Byte.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No constructor found for " + type.getName(), e);
}
}

@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
return indicators.getTypeConfiguration()
.getJdbcTypeRegistry()
.getDescriptor(Types.TINYINT);
}

@SuppressWarnings("unchecked")
@Override
public <X> X unwrap(T id, Class<X> aClass, WrapperOptions wrapperOptions) {
var javaTypeClass = getJavaTypeClass();

if (id == null) {
return null;
}
if (javaTypeClass.isAssignableFrom(aClass)) {
return (X) id;
}
if (Byte.class.isAssignableFrom(aClass)) {
return (X) id.value();
}

throw unknownUnwrap(aClass);
}

@SuppressWarnings("unchecked")
@SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class})
@Override
public <X> T wrap(X value, WrapperOptions wrapperOptions) {
var clazz = getJavaTypeClass();

if (value == null) {
return null;
}
if (clazz.isInstance(value)) {
return (T) value;
}
if (value instanceof Byte byteValue) {
return constructor.newInstance(byteValue);
}

throw unknownWrap(value.getClass());
}
}
Loading