package org.jdbi.v3.core.mapper.reflect;
import java.beans.ConstructorProperties;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jdbi.v3.core.internal.UtilityClassException;
public class JdbiConstructors {
private JdbiConstructors() {
throw new UtilityClassException();
}
static <T> InstanceFactory<T> findFactoryFor(Class<T> type) {
@SuppressWarnings("unchecked")
final Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors();
List<Constructor<T>> explicitConstructors = Stream.of(constructors)
.filter(constructor -> constructor.isAnnotationPresent(JdbiConstructor.class))
.filter(constructor -> !constructor.isSynthetic())
.collect(Collectors.toList());
List<Method> explicitFactoryMethods = Stream.of(type.getDeclaredMethods())
.filter(method -> method.isAnnotationPresent(JdbiConstructor.class))
.collect(Collectors.toList());
if (explicitConstructors.size() + explicitFactoryMethods.size() > 1) {
throw new IllegalArgumentException(type + " may have at most one constructor or static factory method annotated @JdbiConstructor");
}
if (explicitConstructors.size() == 1) {
return new ConstructorInstanceFactory<>(explicitConstructors.get(0));
}
if (explicitFactoryMethods.size() == 1) {
return new StaticMethodInstanceFactory<>(type, explicitFactoryMethods.get(0));
}
return new ConstructorInstanceFactory<>(findImplicitConstructorFor(type));
}
public static <T> Constructor<T> findConstructorFor(Class<T> type) {
@SuppressWarnings("unchecked")
final Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors();
List<Constructor<T>> annotatedConstructors = Stream.of(constructors)
.filter(c -> c.isAnnotationPresent(JdbiConstructor.class))
.collect(Collectors.toList());
if (annotatedConstructors.size() > 1) {
throw new IllegalArgumentException(type + " may have at most one constructor annotated @JdbiConstructor");
} else if (annotatedConstructors.size() == 1) {
return annotatedConstructors.get(0);
}
return findImplicitConstructorFor(type);
}
private static <T> Constructor<T> findImplicitConstructorFor(Class<T> type) {
@SuppressWarnings("unchecked")
final Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors();
List<Constructor<T>> annotatedConstructors = Stream.of(constructors)
.filter(c -> c.isAnnotationPresent(ConstructorProperties.class))
.collect(Collectors.toList());
if (annotatedConstructors.size() > 1) {
throw new IllegalArgumentException(type + " may have at most one constructor annotated @ConstructorProperties");
} else if (annotatedConstructors.size() == 1) {
return annotatedConstructors.get(0);
}
if (constructors.length != 1) {
throw new IllegalArgumentException(type + " must have exactly one constructor, or specify it with @JdbiConstructor or @ConstructorProperties");
}
return constructors[0];
}
}