package org.jdbi.v3.core.mapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import org.jdbi.v3.core.config.JdbiCache;
import org.jdbi.v3.core.config.JdbiCaches;
import org.jdbi.v3.core.enums.EnumByName;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.enums.Enums;
import org.jdbi.v3.core.internal.JdbiOptionals;
import org.jdbi.v3.core.result.UnableToProduceResultException;
import org.jdbi.v3.core.statement.StatementContext;
public abstract class EnumMapper<E extends Enum<E>> implements ColumnMapper<E> {
EnumMapper() {}
public static <E extends Enum<E>> ColumnMapper<E> byName(Class<E> type) {
return new EnumByNameColumnMapper<>(type);
}
public static <E extends Enum<E>> ColumnMapper<E> byOrdinal(Class<E> type) {
return new EnumByOrdinalColumnMapper<>(type);
}
static class EnumByNameColumnMapper<E extends Enum<E>> implements ColumnMapper<E> {
private static final JdbiCache<Class<? extends Enum<?>>, JdbiCache<String, Enum<?>>> BY_NAME_CACHE =
JdbiCaches.declare(e -> JdbiCaches.declare(
name -> e.cast(getValueByName(e, name))));
private final Class<E> enumClass;
private EnumByNameColumnMapper(Class<E> enumClass) {
this.enumClass = enumClass;
}
@Override
public E map(ResultSet rs, int columnNumber, StatementContext ctx) throws SQLException {
String name = rs.getString(columnNumber);
return name == null || name.isEmpty()
? null
: enumClass.cast(BY_NAME_CACHE.get(enumClass, ctx).get(name, ctx));
}
private static Object getValueByName(Class<? extends Enum<?>> enumClass, String name) {
final Enum<?>[] enumConstants = enumClass.getEnumConstants();
return JdbiOptionals.findFirstPresent(
() -> Arrays.stream(enumConstants).filter(e -> e.name().equals(name)).findFirst(),
() -> Arrays.stream(enumConstants).filter(e -> e.name().equalsIgnoreCase(name)).findFirst()
)
.orElseThrow(() -> new UnableToProduceResultException(
String.format("no %s value could be matched to the name %s", enumClass.getSimpleName(), name)));
}
}
static class EnumByOrdinalColumnMapper<E extends Enum<E>> implements ColumnMapper<E> {
private final Class<E> enumClass;
private final E[] enumConstants;
private EnumByOrdinalColumnMapper(Class<E> enumClass) {
this.enumClass = enumClass;
this.enumConstants = enumClass.getEnumConstants();
}
@Override
public E map(ResultSet rs, int columnNumber, StatementContext ctx) throws SQLException {
int ordinal = rs.getInt(columnNumber);
try {
return rs.wasNull() ? null : enumConstants[ordinal];
} catch (ArrayIndexOutOfBoundsException oob) {
throw new UnableToProduceResultException(String.format(
"no %s value could be matched to the ordinal %s", enumClass.getSimpleName(), ordinal),
oob, ctx);
}
}
}
}