All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sfm.reflect.InstantiatorFactory Maven / Gradle / Ivy

package org.sfm.reflect;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.sfm.reflect.asm.AsmFactory;
import org.sfm.reflect.asm.ConstructorDefinition;
import org.sfm.reflect.asm.ConstructorParameter;
import org.sfm.reflect.impl.InjectConstructorInstantiator;
import org.sfm.reflect.impl.StaticConstructorInstantiator;

public class InstantiatorFactory {
	private static final Object[] EMPTY_ARGS = new Object[]{};
	
	@SuppressWarnings("serial")
	private static final Map, Object> DEFAULT_VALUES = new HashMap, Object>() {
		{
			put(boolean.class, true);
			put(byte.class, (byte)0);
			put(char.class, (char)0);
			put(short.class, (short)0);
			put(int.class, 0);
			put(long.class, 0l);
			put(float.class, 0f);
			put(double.class, 0.0);
		}
	};
	
	private final AsmFactory asmFactory;
	
	public InstantiatorFactory(final AsmFactory asmFactory) {
		this.asmFactory = asmFactory;
	}

	public  Instantiator getInstantiator(final Class source, final Class target) throws NoSuchMethodException, SecurityException {
		final Constructor constructor = getSmallerConstructor(target);
		
		if (constructor == null) {
			throw new NoSuchMethodException("No available constructor for " + target);
		}
		
		Object[] args;
		
		if (constructor.getParameterTypes().length == 0) {
			
			if (asmFactory != null && Modifier.isPublic(constructor.getModifiers())) {
				try {
					return asmFactory.createEmptyArgsInstatiantor(source, target);
				} catch (Exception e) {
					// fall back on reflection
				}
			}
			
			args = EMPTY_ARGS;
		} else {
			args = new Object[constructor.getParameterTypes().length];
			for(int i = 0; i < args.length; i++) {
				if (constructor.getParameterTypes()[i].isPrimitive()) {
					args[i] = DEFAULT_VALUES.get(constructor.getParameterTypes()[i]);
				}
			}
		}
		
		constructor.setAccessible(true);
		
		return new StaticConstructorInstantiator(constructor, args); 
	}
	
	public  Instantiator getInstantiator(final Class source, List> constructors, Map> injections) throws NoSuchMethodException, SecurityException {
		return getInstantiator(source, constructors, injections, true);
	}
	
	public  Instantiator getInstantiator(final Class source, List> constructors, Map> injections, boolean useAsmIfEnabled) throws NoSuchMethodException, SecurityException {
		final ConstructorDefinition constructorDefinition = getSmallerConstructor(constructors);
		
		Constructor constructor = constructorDefinition.getConstructor();
		
		if (asmFactory != null && Modifier.isPublic(constructor.getModifiers()) && useAsmIfEnabled) {
			try {
				return asmFactory.createInstatiantor(source, constructorDefinition, injections);
			} catch (Exception e) {
				// fall back on reflection
			}
		}
		
		constructor.setAccessible(true);
		
		if (constructor.getParameterTypes().length == 0) {
			return new StaticConstructorInstantiator(constructor, EMPTY_ARGS); 
		} else {
			return new InjectConstructorInstantiator(constructorDefinition, injections);
		}
	}


	@SuppressWarnings("unchecked")
	private  Constructor getSmallerConstructor(final Class target) {
		Constructor selectedConstructor = null;
		
		for(Constructor c : target.getDeclaredConstructors()) {
			if (selectedConstructor == null || (compare(c, selectedConstructor) < 0)) {
				selectedConstructor = (Constructor) c;
			}
		}
		
		return selectedConstructor;
	}

	private  ConstructorDefinition getSmallerConstructor(final List> constructors) {
		ConstructorDefinition selectedConstructor = null;
		
		for(ConstructorDefinition c : constructors) {
			if (selectedConstructor == null || (c.getParameters().length < selectedConstructor.getParameters().length)) {
				selectedConstructor = c;
			}
		}
		
		return selectedConstructor;
	}
	
	private int compare(final Constructor c1, final Constructor c2) {
		if (Modifier.isPublic(c1.getModifiers())) {
			if (Modifier.isPublic(c2.getModifiers())) {
				return c1.getParameterTypes().length - c2.getParameterTypes().length;
			} else {
				return -1;
			}
		} else {
			if (Modifier.isPublic(c2.getModifiers())) {
				return 1;
			} else {
				return c1.getParameterTypes().length - c2.getParameterTypes().length;
			}
		}
		
	}

	public  Instantiator getArrayInstantiator(final Class elementType, final int length) {
		return new Instantiator() {
			@SuppressWarnings("unchecked")
			@Override
			public T newInstance(S s) throws Exception {
				return (T) Array.newInstance(elementType, length);
			}
		};
	}
}	




© 2015 - 2025 Weber Informatics LLC | Privacy Policy