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

org.sfm.reflect.asm.AsmFactory Maven / Gradle / Ivy

package org.sfm.reflect.asm;

import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;

import org.objectweb.asm.Opcodes;
import org.sfm.jdbc.JdbcMapper;
import org.sfm.jdbc.JdbcMapperErrorHandler;
import org.sfm.map.FieldMapper;
import org.sfm.reflect.Getter;
import org.sfm.reflect.Instantiator;
import org.sfm.reflect.Setter;

public class AsmFactory implements Opcodes {
	private static class FactoryClassLoader extends ClassLoader {

		public FactoryClassLoader(final ClassLoader parent) {
			super(parent);
		}

		private final Map> classes = new HashMap>();
		
		@Override
		protected Class findClass(final String name) throws ClassNotFoundException {
			final Class type = classes.get(name);
			
			if (type != null) {
				return type; 
			} else {
				return super.findClass(name);
			}
		}
		
		public Class registerGetter(final String name, final byte[] bytes) {
			Class type = classes.get(name);
			if (type == null) {
				type = defineClass(name, bytes, 0, bytes.length);
				return type;
			} else {
				throw new RuntimeException("Setter " + name + " already defined");
			}
		}
	}
	
	private final FactoryClassLoader factoryClassLoader;
	
	private final Map> setters = new HashMap>();
	private final Map> instantiators = new HashMap>();
	
	public AsmFactory() {
		this(Thread.currentThread().getContextClassLoader());
	}
	
	public AsmFactory(ClassLoader cl) {
		factoryClassLoader = new FactoryClassLoader(cl);
	}
	
	@SuppressWarnings("unchecked")
	public  Setter createSetter(final Method m) throws Exception {
		Setter setter = (Setter) setters.get(m);
		if (setter == null) {
			final String className = generateClassName(m);
			final byte[] bytes = generateClass(m, className);
			final Class type = factoryClassLoader.registerGetter(className, bytes);
			setter = (Setter) type.newInstance();
			setters.put(m, setter);
		}
		return setter;
	}

	private byte[] generateClass(final Method m, final String className) throws Exception {
		final Class propertyType = m.getParameterTypes()[0];
		if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) {
			return SetterBuilder.createPrimitiveSetter(className, m);
		} else {
			return SetterBuilder.createObjectSetter(className, m);
		}
	}
	
	@SuppressWarnings("unchecked")
	public  Instantiator createEmptyArgsInstatiantor(final Class source, final Class target) throws Exception {
		InstantiatorKey instantiatorKey = new InstantiatorKey(target);
		Instantiator instantiator = (Instantiator) instantiators.get(instantiatorKey);
		if (instantiator == null) {
			final String className = generateInstantiatorClassName(instantiatorKey);
			final byte[] bytes = ConstructorBuilder.createEmptyConstructor(className, source, target);
			final Class type = factoryClassLoader.registerGetter(className, bytes);
			instantiator = (Instantiator) type.newInstance();
			instantiators.put(instantiatorKey, instantiator);
		}
		return instantiator;
	}
	
	@SuppressWarnings("unchecked")
	public  Instantiator createInstatiantor(final Class source, final ConstructorDefinition constructorDefinition,final Map> injections) throws Exception {
		InstantiatorKey instantiatorKey = new InstantiatorKey(constructorDefinition, injections.keySet());
		Instantiator instantiator = (Instantiator) instantiators.get(instantiatorKey);
		if (instantiator == null) {
			final String className = generateInstantiatorClassName(instantiatorKey);
			final byte[] bytes = InstantiatorBuilder.createInstantiator(className, source, constructorDefinition, injections);
			final Class type = factoryClassLoader.registerGetter(className, bytes);
			
			Map> getterPerName = new HashMap<>();
			for(Entry> e : injections.entrySet()) {
				getterPerName.put(e.getKey().getName(), e.getValue());
			}
			
			instantiator = (Instantiator) type.getConstructor(Map.class).newInstance(getterPerName);
			instantiators.put(instantiatorKey, instantiator);
		}
		return instantiator;
	}
	
	@SuppressWarnings("unchecked")
	public  JdbcMapper createJdbcMapper(final FieldMapper[] mappers, final Instantiator instantiator, final Class target, JdbcMapperErrorHandler errorHandler) throws Exception {
		final String className = generateClassName(mappers, ResultSet.class, target);
		final byte[] bytes = JdbcMapperBuilder.dump(className, mappers, instantiator, target);
		final Class type = factoryClassLoader.registerGetter(className, bytes);
		return (JdbcMapper) type.getDeclaredConstructors()[0].newInstance(mappers, instantiator, errorHandler);
	}
	
	private final AtomicLong classNumber = new AtomicLong();
	
	private String generateInstantiatorClassName(final InstantiatorKey key) {
		StringBuilder sb = new StringBuilder();
		
		sb.append( "org.sfm.reflect.asm.")
		.append(key.getConstructor().getDeclaringClass().getPackage().getName())
		.append(".AsmInstantiator").append(key.getConstructor().getDeclaringClass().getSimpleName());
		String[] injectedParams = key.getInjectedParams();
		if (injectedParams != null) {
			for(String str : injectedParams) {
				sb.append(str.substring(0, Math.min(str.length(), 3)));
			}
		}
		sb.append(Long.toHexString(classNumber.getAndIncrement()));
		return sb.toString();
	}

	private String generateClassName(final Method m) {
		return "org.sfm.reflect.asm." + m.getDeclaringClass().getPackage().getName() + 
					".AsmSetter" + m.getName()
					 + m.getDeclaringClass().getSimpleName()
					 + m.getParameterTypes()[0].getSimpleName()
					;
	}
	
	private  String generateClassName(final FieldMapper[] mappers, final Class source, final Class target) {
		return "org.sfm.reflect.asm." + target.getPackage().getName() + 
					".AsmMapper" + source.getSimpleName() + "2" +  target.getSimpleName() + mappers.length + "_" + classNumber.getAndIncrement(); 
	}
}