org.simpleflatmapper.reflect.InstantiatorFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sfm-reflect Show documentation
Show all versions of sfm-reflect Show documentation
Java library to map flat record - ResultSet, csv - to java object with minimum configuration and low footprint.
package org.simpleflatmapper.reflect;
import org.simpleflatmapper.reflect.asm.AsmFactory;
import org.simpleflatmapper.reflect.asm.AsmFactoryProvider;
import org.simpleflatmapper.reflect.asm.BiInstantiatorKey;
import org.simpleflatmapper.reflect.asm.InstantiatorKey;
import org.simpleflatmapper.reflect.getter.IdentityGetter;
import org.simpleflatmapper.reflect.impl.BuilderInstantiator;
import org.simpleflatmapper.reflect.impl.EmptyConstructorBiInstantiator;
import org.simpleflatmapper.reflect.impl.EmptyConstructorInstantiator;
import org.simpleflatmapper.reflect.impl.EmptyStaticMethodBiInstantiator;
import org.simpleflatmapper.reflect.impl.EmptyStaticMethodInstantiator;
import org.simpleflatmapper.reflect.impl.InjectConstructorBiInstantiator;
import org.simpleflatmapper.reflect.impl.InjectConstructorInstantiator;
import org.simpleflatmapper.reflect.impl.InjectStaticMethodBiInstantiator;
import org.simpleflatmapper.reflect.impl.InjectStaticMethodInstantiator;
import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition;
import org.simpleflatmapper.reflect.instantiator.InstantiatorDefinitions;
import org.simpleflatmapper.reflect.instantiator.KotlinDefaultConstructorInstantiatorDefinition;
import org.simpleflatmapper.util.BiFunction;
import org.simpleflatmapper.util.ErrorHelper;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class InstantiatorFactory {
private final AsmFactoryProvider asmFactory;
private final boolean failOnAsmError;
public InstantiatorFactory(final AsmFactoryProvider asmFactory) {
this(asmFactory, false);
}
public InstantiatorFactory(AsmFactoryProvider asmFactory, boolean faileOnAsmError) {
this.asmFactory = asmFactory;
this.failOnAsmError = faileOnAsmError;
}
@SuppressWarnings("unchecked")
public BiInstantiator getBiInstantiator(Type target, final Class> s1, final Class> s2, List constructors, Map> injections, boolean useAsmIfEnabled, boolean builderIgnoresNullValues) throws SecurityException {
final InstantiatorDefinition instantiatorDefinition = getSmallerConstructor(constructors, injections.keySet());
if (instantiatorDefinition == null) {
throw new IllegalArgumentException("No constructor available for " + target);
}
return this.getBiInstantiator(instantiatorDefinition, s1, s2, injections, useAsmIfEnabled, builderIgnoresNullValues);
}
@SuppressWarnings("unchecked")
public BiInstantiator getBiInstantiator(InstantiatorDefinition instantiatorDefinition, Class> s1, Class> s2, Map> injections, boolean useAsmIfEnabled, boolean builderIgnoresNullValues) {
checkParameters(instantiatorDefinition, injections.keySet());
if (instantiatorDefinition instanceof KotlinDefaultConstructorInstantiatorDefinition) {
KotlinDefaultConstructorInstantiatorDefinition kid = (KotlinDefaultConstructorInstantiatorDefinition) instantiatorDefinition;
injections = new HashMap>(injections);
kid.addDefaultValueFlagBi(injections);
instantiatorDefinition = kid.getDefaultValueConstructor();
}
if (asmFactory != null && useAsmIfEnabled) {
ClassLoader targetClassLoader = BiInstantiatorKey.getDeclaringClass(instantiatorDefinition).getClassLoader();
AsmFactory asmFactory = this.asmFactory.getAsmFactory(targetClassLoader);
if (asmFactory != null) {
if (instantiatorDefinition instanceof ExecutableInstantiatorDefinition) {
ExecutableInstantiatorDefinition executableInstantiatorDefinition = (ExecutableInstantiatorDefinition) instantiatorDefinition;
Member executable = executableInstantiatorDefinition.getExecutable();
if (Modifier.isPublic(executable.getModifiers())) {
try {
return asmFactory.createBiInstantiator(s1, s2, executableInstantiatorDefinition, injections, builderIgnoresNullValues);
} catch (Exception e) {
// fall back on reflection
if (failOnAsmError) ErrorHelper.rethrow(e);
}
}
} else {
try {
return asmFactory.createBiInstantiator(s1, s2, (BuilderInstantiatorDefinition) instantiatorDefinition, injections, builderIgnoresNullValues);
} catch (Exception e) {
// fall back on reflection
if (failOnAsmError) ErrorHelper.rethrow(e);
}
}
}
}
switch (instantiatorDefinition.getType()) {
case CONSTRUCTOR:
return constructorBiInstantiator((ExecutableInstantiatorDefinition)instantiatorDefinition, injections);
case METHOD:
return methodBiInstantiator((ExecutableInstantiatorDefinition)instantiatorDefinition, injections);
case BUILDER:
return builderBiInstantiator((BuilderInstantiatorDefinition)instantiatorDefinition, injections, useAsmIfEnabled, builderIgnoresNullValues);
default:
throw new IllegalArgumentException("Unsupported executable type " + instantiatorDefinition);
}
}
@SuppressWarnings("unchecked")
public Instantiator getInstantiator(Type target, final Class source, List constructors, Map> injections, boolean useAsmIfEnabled, boolean builderIgnoresNullValues) throws SecurityException {
final InstantiatorDefinition instantiatorDefinition = getSmallerConstructor(constructors, injections.keySet());
if (instantiatorDefinition == null) {
throw new IllegalArgumentException("No constructor available for " + target);
}
return getInstantiator(instantiatorDefinition, source, injections, useAsmIfEnabled, builderIgnoresNullValues);
}
@SuppressWarnings("unchecked")
public Instantiator getInstantiator(InstantiatorDefinition instantiatorDefinition, Class source, Map> injections, boolean useAsmIfEnabled, boolean builderIgnoresNullValues) {
for(Parameter p : injections.keySet()) {
if (!instantiatorDefinition.hasParam(p)) {
throw new IllegalArgumentException("Could not find " + p + " in " + instantiatorDefinition + " raise issue");
}
}
if (instantiatorDefinition instanceof KotlinDefaultConstructorInstantiatorDefinition) {
KotlinDefaultConstructorInstantiatorDefinition kid = (KotlinDefaultConstructorInstantiatorDefinition) instantiatorDefinition;
injections = new HashMap>(injections);
kid.addDefaultValueFlag(injections);
instantiatorDefinition = kid.getDefaultValueConstructor();
}
if (asmFactory != null && useAsmIfEnabled) {
ClassLoader targetClassLoader = InstantiatorKey.getDeclaringClass(instantiatorDefinition).getClassLoader();
AsmFactory asmFactory = this.asmFactory.getAsmFactory(targetClassLoader);
if (asmFactory != null) {
if (instantiatorDefinition instanceof ExecutableInstantiatorDefinition) {
ExecutableInstantiatorDefinition executableInstantiatorDefinition = (ExecutableInstantiatorDefinition) instantiatorDefinition;
Member executable = executableInstantiatorDefinition.getExecutable();
if (Modifier.isPublic(executable.getModifiers())) {
try {
return asmFactory.createInstantiator(source, executableInstantiatorDefinition, injections, builderIgnoresNullValues);
} catch (Exception e) {
e.printStackTrace();
// fall back on reflection
if (failOnAsmError) ErrorHelper.rethrow(e);
}
}
} else {
try {
return asmFactory.createInstantiator(source, (BuilderInstantiatorDefinition) instantiatorDefinition, injections, builderIgnoresNullValues);
} catch (Exception e) {
// fall back on reflection
if (failOnAsmError) ErrorHelper.rethrow(e);
}
}
}
}
switch (instantiatorDefinition.getType()) {
case CONSTRUCTOR:
return constructorInstantiator((ExecutableInstantiatorDefinition)instantiatorDefinition, injections);
case METHOD:
return methodInstantiator((ExecutableInstantiatorDefinition)instantiatorDefinition, injections);
case BUILDER:
return builderInstantiator((BuilderInstantiatorDefinition)instantiatorDefinition, injections, useAsmIfEnabled, builderIgnoresNullValues);
default:
throw new IllegalArgumentException("Unsupported executable type " + instantiatorDefinition);
}
}
private static boolean checkParameters(InstantiatorDefinition instantiatorDefinition, Set parameters) {
for(Parameter p : parameters) {
if (!instantiatorDefinition.hasParam(p)) {
return false;
}
}
return true;
}
@SuppressWarnings({"unchecked", "SuspiciousToArrayCall"})
private Instantiator builderInstantiator(BuilderInstantiatorDefinition instantiatorDefinition,
Map> injections, boolean useAsmIfEnabled, boolean builderIgnoresNullValues) {
final Instantiator buildInstantiator =
getInstantiator(instantiatorDefinition.getBuilderInstantiator(), Void.class,
new HashMap>(), useAsmIfEnabled, builderIgnoresNullValues);
List> chainedArguments = new ArrayList>();
List> unchainedArguments = new ArrayList>();
for(Map.Entry> e : injections.entrySet()) {
final MethodGetterPair arguments =
new MethodGetterPair(instantiatorDefinition.getSetters().get(e.getKey()), e.getValue());
if (Void.TYPE.equals(arguments.getMethod().getReturnType())) {
unchainedArguments.add(arguments);
} else {
chainedArguments.add(arguments);
}
}
return new BuilderInstantiator(buildInstantiator,
chainedArguments.toArray(new MethodGetterPair[0]),
unchainedArguments.toArray(new MethodGetterPair[0]),
instantiatorDefinition.getBuildMethod(), builderIgnoresNullValues);
}
private Instantiator methodInstantiator(
ExecutableInstantiatorDefinition instantiatorDefinition,
Map> injections) {
Method m = (Method) instantiatorDefinition.getExecutable();
if (m.getParameterTypes().length == 0) {
return new EmptyStaticMethodInstantiator(m);
} else {
return new InjectStaticMethodInstantiator(instantiatorDefinition, injections);
}
}
@SuppressWarnings("unchecked")
private Instantiator constructorInstantiator(
ExecutableInstantiatorDefinition instantiatorDefinition,
Map> injections) {
Constructor extends T> c = (Constructor extends T>) instantiatorDefinition.getExecutable();
if (c.getParameterTypes().length == 0) {
return new EmptyConstructorInstantiator(c);
} else {
return new InjectConstructorInstantiator(instantiatorDefinition, injections);
}
}
@SuppressWarnings({"unchecked", "SuspiciousToArrayCall"})
public BuilderBiInstantiator builderBiInstantiator(BuilderInstantiatorDefinition instantiatorDefinition,
Map> injections, boolean useAsmIfEnabled, boolean builderIgnoresNullValues) {
final Instantiator buildInstantiator =
getInstantiator(instantiatorDefinition.getBuilderInstantiator(), Void.class,
new HashMap>(), useAsmIfEnabled, builderIgnoresNullValues);
List> chainedArguments = new ArrayList>();
List> unchainedArguments = new ArrayList>();
int i = 0;
for(Map.Entry> e : injections.entrySet()) {
final MethodBiFunctionPair arguments =
new MethodBiFunctionPair(instantiatorDefinition.getSetters().get(e.getKey()), e.getValue());
if (Void.TYPE.equals(arguments.getMethod().getReturnType())) {
unchainedArguments.add(arguments);
} else {
chainedArguments.add(arguments);
}
}
return new BuilderBiInstantiator(buildInstantiator,
chainedArguments.toArray(new MethodBiFunctionPair[0]),
unchainedArguments.toArray(new MethodBiFunctionPair[0]),
instantiatorDefinition.getBuildMethod(), builderIgnoresNullValues);
}
private BiInstantiator methodBiInstantiator(
ExecutableInstantiatorDefinition instantiatorDefinition,
Map> injections) {
Method m = (Method) instantiatorDefinition.getExecutable();
if (m.getParameterTypes().length == 0) {
return new EmptyStaticMethodBiInstantiator(m);
} else {
return new InjectStaticMethodBiInstantiator(instantiatorDefinition, injections);
}
}
@SuppressWarnings("unchecked")
private BiInstantiator constructorBiInstantiator(
ExecutableInstantiatorDefinition instantiatorDefinition,
Map> injections) {
Constructor extends T> c = (Constructor extends T>) instantiatorDefinition.getExecutable();
if (c.getParameterTypes().length == 0) {
return new EmptyConstructorBiInstantiator(c);
} else {
return new InjectConstructorBiInstantiator(instantiatorDefinition, injections);
}
}
public static InstantiatorDefinition getSmallerConstructor(final List constructors, Set parameters) {
if (constructors == null) {
return null;
}
InstantiatorDefinition selectedConstructor = null;
for(InstantiatorDefinition c : constructors) {
if (checkParameters(c, parameters) && (selectedConstructor == null || InstantiatorDefinitions.COMPARATOR.compare(c, selectedConstructor) < 0)) {
selectedConstructor = c;
}
}
return selectedConstructor;
}
public Instantiator getArrayInstantiator(final Class> elementType, final int length) {
return new ArrayInstantiator(elementType, length);
}
public BiInstantiator getArrayBiInstantiator(final Class> elementType, final int length) {
return new ArrayBiInstantiator(elementType, length);
}
@SuppressWarnings("unchecked")
public Instantiator getOneArgIdentityInstantiator(InstantiatorDefinition id, boolean builderIgnoresNullValues) {
if (id.getParameters().length != 1) {
throw new IllegalArgumentException("Definition does not have one param " + Arrays.asList(id.getParameters()));
}
Map> injections = new HashMap>();
injections.put(id.getParameters()[0], new IdentityGetter());
return getInstantiator(id, (Class) id.getParameters()[0].getType(), injections, true, builderIgnoresNullValues);
}
private static final class ArrayInstantiator implements Instantiator {
private final Class> elementType;
private final int length;
public ArrayInstantiator(Class> elementType, int length) {
this.elementType = elementType;
this.length = length;
}
@SuppressWarnings("unchecked")
@Override
public T newInstance(S s) throws Exception {
return (T) Array.newInstance(elementType, length);
}
}
private static final class ArrayBiInstantiator implements BiInstantiator {
private final Class> elementType;
private final int length;
public ArrayBiInstantiator(Class> elementType, int length) {
this.elementType = elementType;
this.length = length;
}
@SuppressWarnings("unchecked")
@Override
public T newInstance(S1 s1, S2 s2) throws Exception {
return (T) Array.newInstance(elementType, length);
}
}
}