io.virtdata.reflection.ConstructorResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtdata-lib-realer Show documentation
Show all versions of virtdata-lib-realer Show documentation
With inspiration from other libraries
package io.virtdata.reflection;
import io.virtdata.util.StringObjectPromoter;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Match (class,string,...) constructor signatures to a matching converted signature,
* in Object[] form.
*/
public class ConstructorResolver {
private final static Logger logger = LoggerFactory.getLogger(ConstructorResolver.class);
private Exception lastException;
public static Optional resolveAndConstructOptional(String[] classAndArgs) {
Optional extends DeferredConstructor> optionalDeferredConstructor =
createOptionalDeferredConstructor(classAndArgs);
return optionalDeferredConstructor.map(DeferredConstructor::construct);
}
public static T resolveAndConstruct(String[] classAndArgs) {
DeferredConstructor deferredConstructor = createDeferredConstructorRequired(classAndArgs);
T constructed = deferredConstructor.construct();
return constructed;
}
public static DeferredConstructor resolve(String[] classAndArgs) {
DeferredConstructor deferredConstructor = createDeferredConstructorRequired(classAndArgs);
return deferredConstructor;
}
public static DeferredConstructor resolve(Class clazz, String... args) {
DeferredConstructor deferredConstructor = createDeferredConstructorRequired(clazz, args);
return deferredConstructor;
}
public static Optional> resolveOptional(Class clazz, String... args) {
Optional> optionalResolved = createOptionalDeferredConstructor(clazz, args);
return optionalResolved;
}
@SuppressWarnings("unchecked")
public static DeferredConstructor resolve(String className, String[] args) {
Class clazz = null;
try {
clazz = (Class) Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
DeferredConstructor deferredConstructor = createDeferredConstructorRequired(clazz, args);
return deferredConstructor;
}
@SuppressWarnings("unchecked")
private static DeferredConstructor createDeferredConstructorRequired(String[] signature) {
String className = signature[0];
if (!className.contains(".")) {
throw new RuntimeException(ConstructorResolver.class.getSimpleName() + " needs a fully qualified package name.");
}
Class clazz;
try {
clazz = (Class) Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return createDeferredConstructorRequired(clazz, Arrays.copyOfRange(signature, 1, signature.length));
}
private static Object[] specializeArgs(String[] raw, Class>[] targetTypes) {
Object[] made = new Object[raw.length];
for (int paramidx = 0; paramidx < targetTypes.length; paramidx++) {
Class> ptype = targetTypes[paramidx];
made[paramidx] = StringObjectPromoter.promote(raw[paramidx], ptype);
if (!StringObjectPromoter.isAssignableForConstructor(made[paramidx].getClass(), ptype)) {
return null;
}
}
return made;
}
private static Predicate canAssignToConstructor(String[] args) {
return new Predicate() {
@Override
public boolean test(Constructor ctor) {
Object[] objects = specializeArgs(args, ctor.getParameterTypes());
return objects != null;
}
};
}
private static Optional extends DeferredConstructor>
createOptionalDeferredConstructor(String... classAndArgs) {
Class ctorClass = null;
try {
ctorClass = (Class) Class.forName(classAndArgs[0]);
} catch (ClassNotFoundException ignored) {
}
if (ctorClass != null) {
Optional extends DeferredConstructor> odc =
createOptionalDeferredConstructor(
ctorClass,
Arrays.copyOfRange(classAndArgs, 1, classAndArgs.length)
);
return odc;
}
return Optional.empty();
}
private static Optional> createOptionalDeferredConstructor(
Class clazz, String... args) {
List matchingConstructors = new ArrayList<>();
for (Constructor> constructor : clazz.getDeclaredConstructors()) {
if (constructor.getParameterCount() == args.length) {
matchingConstructors.add(constructor);
}
}
matchingConstructors = matchingConstructors.stream()
.filter(canAssignToConstructor(args)).collect(Collectors.toList());
if (matchingConstructors.size() == 0) {
logger.debug("no constructor found for " + clazz.getSimpleName() + " with " +
(args.length) + " parameters");
return Optional.empty();
}
if (matchingConstructors.size() > 1) {
List signatures = new ArrayList();
for (Constructor matchingConstructor : matchingConstructors) {
Class[] pt = matchingConstructor.getParameterTypes();
signatures.add(
Arrays.stream(pt)
.map(Class::getSimpleName)
.collect(Collectors.joining(",", "(", ")"))
);
}
String diagnosticList = signatures.stream().collect(Collectors.joining(",", "[", "]"));
logger.error("Multiple constructors found for " + clazz.getSimpleName() + " with " +
(args.length) + " parameters:" + diagnosticList
);
return Optional.empty();
}
Constructor matchingConstructor = matchingConstructors.get(0);
Object[] ctorArgs = specializeArgs(args, matchingConstructor.getParameterTypes());
// sanity check
// TODO: Reduce additional constructor invocations where possible
try {
ConstructorUtils.invokeConstructor(clazz, ctorArgs);
} catch (Exception e) {
logger.error("Unable to invoke constructor as sanity check for args:" + Arrays.toString(ctorArgs), e);
return Optional.empty();
}
DeferredConstructor dc = new DeferredConstructor<>(clazz, ctorArgs);
return Optional.of(dc);
}
private static DeferredConstructor createDeferredConstructorRequired(Class clazz, String... args) {
Optional> optional =
createOptionalDeferredConstructor(clazz, args);
return optional.orElseThrow(
() -> new RuntimeException(
"Unable to create deferred constructor for class:"
+ clazz.getCanonicalName() + " and args: "
+ Arrays.toString(args))
);
}
private static enum StringMapper {
// HINT: Do NOT put more than primitives or very common types here
STRING(String.class, null, (String i) -> i),
INTEGER(Integer.class, int.class, Integer::valueOf),
BIGDECIMAL(BigDecimal.class, null, (String i) -> BigDecimal.valueOf(Long.valueOf(i))),
BOOLEAN(Boolean.class, boolean.class, Boolean::valueOf),
SHORT(Short.class, short.class, Short::valueOf),
BYTE(Byte.class, byte.class, Byte::valueOf),
DOUBLE(Double.class, double.class, Double::valueOf),
CHAR(Character.class, char.class, (String c) -> c.charAt(0)),
FLOAT(Float.class, float.class, Float::valueOf),
LONG(Long.class, long.class, Long::valueOf);
private final Class> targetClass;
private final Class> primitiveClass;
private final Function mapperFunction;
StringMapper(Class targetClass, Class> primitiveName, Function mapperFunction) {
this.targetClass = targetClass;
this.mapperFunction = mapperFunction;
this.primitiveClass = primitiveName;
}
public static Optional valueOf(Class> targetClass) {
for (StringMapper stringMapper : StringMapper.values()) {
if (stringMapper.getTargetClass().equals(targetClass)) {
return Optional.of(stringMapper);
}
if (stringMapper.primitiveClass != null && stringMapper.primitiveClass.equals(targetClass)) {
return Optional.of(stringMapper);
}
}
throw new RuntimeException("StringMapper could not match " + targetClass);
}
public static Object mapValue(String value, Class> targetClass) {
Optional mapper = StringMapper.valueOf(targetClass);
if (mapper.isPresent()) {
return mapper.get().getMapperFunction().apply(value);
} else {
throw new RuntimeException(
"Unable to find type mapper for String and class " + targetClass.getCanonicalName()
);
}
}
public Class> getTargetClass() {
return targetClass;
}
public Function getMapperFunction() {
return mapperFunction;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy