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 org.sfm.csv.CsvColumnKey;
import org.sfm.csv.impl.*;
import org.sfm.jdbc.JdbcColumnKey;
import org.sfm.jdbc.JdbcMapper;
import org.sfm.map.FieldMapperErrorHandler;
import org.sfm.map.MappingContextFactory;
import org.sfm.map.RowHandlerErrorHandler;
import org.sfm.map.FieldMapper;
import org.sfm.map.impl.RethrowFieldMapperErrorHandler;
import org.sfm.reflect.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

public class AsmFactory {
    private final FactoryClassLoader factoryClassLoader;
	private final ConcurrentMap> setterCache = new ConcurrentHashMap>();
    private final ConcurrentMap> getterCache = new ConcurrentHashMap>();
	private final ConcurrentMap>> instantiatorCache = new ConcurrentHashMap>>();
    private final ConcurrentMap>> jdbcMapperCache = new ConcurrentHashMap>>();
    private final ConcurrentMap>> csvMapperCache = new ConcurrentHashMap>>();

	public AsmFactory(ClassLoader cl) {
		factoryClassLoader = new FactoryClassLoader(cl);
	}
	
	@SuppressWarnings("unchecked")
	public  Setter createSetter(final Method m) throws Exception {
		Setter setter = (Setter) setterCache.get(m);
		if (setter == null) {
			final String className = generateClassNameForSetter(m);
			final byte[] bytes = generateSetterByteCodes(m, className);
            final Class type = createClass(className, bytes, m.getDeclaringClass().getClassLoader());
            setter = (Setter) type.newInstance();
			setterCache.putIfAbsent(m, setter);
		}
		return setter;
	}

    @SuppressWarnings("unchecked")
    public  Setter createSetter(Field field) throws Exception {
        Setter setter = (Setter) setterCache.get(field);
        if (setter == null) {
            final String className = generateClassNameForSetter(field);
            final byte[] bytes = generateSetterByteCodes(field, className);
            final Class type = createClass(className, bytes, field.getDeclaringClass().getClassLoader());
            setter = (Setter) type.newInstance();
            setterCache.putIfAbsent(field, setter);
        }
        return setter;
    }


    private Class createClass(String className, byte[] bytes, ClassLoader declaringClassLoader) {
        return factoryClassLoader.registerClass(className, bytes, declaringClassLoader);
    }

    @SuppressWarnings("unchecked")
    public  Getter createGetter(final Method m) throws Exception {
        Getter getter = (Getter) getterCache.get(m);
        if (getter == null) {
            final String className = generateClassNameForGetter(m);
            final byte[] bytes = generateGetterByteCodes(m, className);
            final Class type = createClass(className, bytes, m.getDeclaringClass().getClassLoader());
            getter = (Getter) type.newInstance();
            getterCache.putIfAbsent(m, getter);
        }
        return getter;
    }

    @SuppressWarnings("unchecked")
    public  Getter createGetter(final Field m) throws Exception {
        Getter getter = (Getter) getterCache.get(m);
        if (getter == null) {
            final String className = generateClassNameForGetter(m);
            final byte[] bytes = generateGetterByteCodes(m, className);
            final Class type = createClass(className, bytes, m.getDeclaringClass().getClassLoader());
            getter = (Getter) type.newInstance();
            getterCache.putIfAbsent(m, getter);
        }
        return getter;
    }

    private byte[] generateGetterByteCodes(final Method m, final String className) throws Exception {
        final Class propertyType = m.getReturnType();
        if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) {
            return GetterBuilder.createPrimitiveGetter(className, m);
        } else {
            return GetterBuilder.createObjectGetter(className, m);
        }
    }

    private byte[] generateGetterByteCodes(final Field m, final String className) throws Exception {
        final Class propertyType = m.getType();
        if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) {
            return GetterBuilder.createPrimitiveGetter(className, m);
        } else {
            return GetterBuilder.createObjectGetter(className, m);
        }
    }

	private byte[] generateSetterByteCodes(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);
		}
	}

    private byte[] generateSetterByteCodes(final Field m, final String className) throws Exception {
        final Class propertyType = m.getType();
        if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) {
            return SetterBuilder.createPrimitiveSetter(className, m);
        } else {
            return SetterBuilder.createObjectSetter(className, m);
        }
    }
	
	@SuppressWarnings("unchecked")
	public  Instantiator createEmptyArgsInstantiator(final Class source, final Class target) throws Exception {
		InstantiatorKey instantiatorKey = new InstantiatorKey(target, source);
		Class> instantiatorType = instantiatorCache.get(instantiatorKey);
		if (instantiatorType == null) {
			final String className = generateClassNameForInstantiator(instantiatorKey);
			final byte[] bytes = ConstructorBuilder.createEmptyConstructor(className, source, target);
			instantiatorType = (Class>) createClass(className, bytes, target.getClassLoader());
			instantiatorCache.putIfAbsent(instantiatorKey, instantiatorType);
		}
		return  (Instantiator) instantiatorType.newInstance();
	}
	
	@SuppressWarnings("unchecked")
	public  Instantiator createInstantiator(final Class source, final InstantiatorDefinition instantiatorDefinition, final Map> injections) throws Exception {
		InstantiatorKey instantiatorKey = new InstantiatorKey(instantiatorDefinition, injections.keySet(), source);
		Class> instantiator = instantiatorCache.get(instantiatorKey);
		if (instantiator == null) {
			final String className = generateClassNameForInstantiator(instantiatorKey);
			final byte[] bytes = InstantiatorBuilder.createInstantiator(className, source, instantiatorDefinition, injections);
			instantiator = (Class>) createClass(className, bytes, instantiatorDefinition.getExecutable().getDeclaringClass().getClassLoader());
			instantiatorCache.put(instantiatorKey, instantiator);
		}

		Map> getterPerName = new HashMap>();
		for(Entry> e : injections.entrySet()) {
			getterPerName.put(e.getKey().getName(), e.getValue());
		}

		return (Instantiator) instantiator.getConstructor(Map.class).newInstance(getterPerName);
	}
	
	@SuppressWarnings("unchecked")
	public  JdbcMapper createJdbcMapper(final JdbcColumnKey[] keys,
                                                final FieldMapper[] mappers,
                                              final FieldMapper[] constructorMappers,
                                              final Instantiator instantiator,
                                              final Class target,
                                              RowHandlerErrorHandler errorHandler,
                                              MappingContextFactory mappingContextFactory) throws Exception {

        JdbcMapperKey key = new JdbcMapperKey(keys, mappers, constructorMappers, instantiator, target);
        Class> type = (Class>) jdbcMapperCache.get(key);
        if (type == null) {

            final String className = generateClassNameForJdbcMapper(mappers, constructorMappers, ResultSet.class, target);
            final byte[] bytes = JdbcMapperAsmBuilder.dump(className, mappers, constructorMappers, target);

            type = (Class>) createClass(className, bytes, target.getClass().getClassLoader());
            jdbcMapperCache.put(key, type);
        }
        final Constructor constructor = type.getDeclaredConstructors()[0];
        return (JdbcMapper) constructor.newInstance(mappers, constructorMappers, instantiator, errorHandler, mappingContextFactory);
	}

    @SuppressWarnings("unchecked")
    public  CsvMapperCellHandlerFactory createCsvMapperCellHandler(Type target,
                                                                         DelayedCellSetterFactory[] delayedCellSetterFactories, CellSetter[] setters,
                                                                         Instantiator, T> instantiator,
                                                                         CsvColumnKey[] keys,
                                                                         ParsingContextFactory parsingContextFactory,
                                                                         FieldMapperErrorHandler fieldErrorHandler,
                                                                         int maxMethodSize
                                                                         ) throws Exception {

        CsvMapperKey key = new CsvMapperKey(keys, setters, delayedCellSetterFactories, instantiator, target, fieldErrorHandler, maxMethodSize);

        Class> typeFactory = csvMapperCache.get(key);

        if (typeFactory == null) {
            final String className = generateClassNameCsvMapperCellHandler(target, delayedCellSetterFactories, setters);
            final String factoryName = className + "Factory";
            final byte[] bytes = CsvMapperCellHandlerBuilder.createTargetSetterClass(className, delayedCellSetterFactories, setters, target, fieldErrorHandler == null || fieldErrorHandler instanceof RethrowFieldMapperErrorHandler, maxMethodSize);
            final byte[] bytesFactory = CsvMapperCellHandlerBuilder.createTargetSetterFactory(factoryName, className, target);
            createClass(className, bytes, target.getClass().getClassLoader());
            typeFactory = (Class>) createClass(factoryName, bytesFactory, target.getClass().getClassLoader());

            csvMapperCache.put(key, typeFactory);
        }

        return (CsvMapperCellHandlerFactory) typeFactory
                .getConstructor(Instantiator.class, CsvColumnKey[].class, ParsingContextFactory.class, FieldMapperErrorHandler.class)
                .newInstance(instantiator, keys, parsingContextFactory, fieldErrorHandler);


    }

    private final AtomicLong classNumber = new AtomicLong();
	
	private String generateClassNameForInstantiator(final InstantiatorKey key) {
		StringBuilder sb = new StringBuilder();
		
		sb.append( "org.sfm.reflect.asm.")
		.append(getPackageName(key.getConstructor().getDeclaringClass()))
		.append(".AsmInstantiator").append(key.getConstructor().getDeclaringClass().getSimpleName());
        sb.append("From");
        sb.append(replaceArray(key.getSource().getSimpleName()));

        String[] injectedParams = key.getInjectedParams();
        if (injectedParams != null && injectedParams.length > 0) {
            sb.append("Into");
            int e = Math.min(16, injectedParams.length);
            for(int i = 0; i < e; i++) {
                if (i!=0) {
                    sb.append("And");
                }
                sb.append(injectedParams[i]);
            }

            int l = injectedParams.length - e;
            if (l >0) {
                sb.append("And").append(Integer.toString(l)).append("More");
            }
        }
		sb.append("_I").append(Long.toHexString(classNumber.getAndIncrement()));
		return sb.toString();
	}

	private String replaceArray(String simpleName) {
		return simpleName.replace('[', 's').replace(']', '_');
	}

	private String generateClassNameForSetter(final Method m) {
		return "org.sfm.reflect.asm." + getPackageName(m.getDeclaringClass()) +
					".AsmMethodSetter"
					 + replaceArray(m.getDeclaringClass().getSimpleName())
                     +"_" + m.getName()+ "_"
					 + replaceArray(m.getParameterTypes()[0].getSimpleName())
					;
	}

    private String generateClassNameForSetter(final Field field) {
        return "org.sfm.reflect.asm." + getPackageName(field.getDeclaringClass()) +
                ".AsmFieldSetter"
                + replaceArray(field.getDeclaringClass().getSimpleName())
                + "_"
                + field.getName()
                + "_"
                + replaceArray(field.getType().getSimpleName())
                ;
    }
    private String generateClassNameForGetter(final Method m) {
        return "org.sfm.reflect.asm." + getPackageName(m.getDeclaringClass()) +
                ".AsmMethodGetter"
                + replaceArray(m.getDeclaringClass().getSimpleName())
                + "_"
                + m.getName()
                ;
    }
    private String generateClassNameForGetter(final Field m) {
        return "org.sfm.reflect.asm." + getPackageName(m.getDeclaringClass()) +
                ".AsmFieldGetter"
                + replaceArray(m.getDeclaringClass().getSimpleName())
                + "_"
                + m.getName()
                ;
    }

    private  String generateClassNameCsvMapperCellHandler(Type target, DelayedCellSetterFactory[] delayedCellSetterFactories, CellSetter[] setters) {
        StringBuilder sb = new StringBuilder();

        sb.append( "org.sfm.reflect.asm.")
                .append(getPackageName(target))
                .append(".AsmCsvMapperCellHandlerTo").append(TypeHelper.toClass(target).getSimpleName());
        if (delayedCellSetterFactories.length > 0) {
            sb.append("DS").append(Integer.toString(delayedCellSetterFactories.length));
        }
        if (setters.length > 0) {
            sb.append("S").append(Integer.toString(setters.length));
        }
        sb.append("_I").append(Long.toHexString(classNumber.getAndIncrement()));
        return sb.toString();
    }

	private  String generateClassNameForJdbcMapper(final FieldMapper[] mappers,final FieldMapper[] constructorMappers, final Class source, final Class target) {
        StringBuilder sb = new StringBuilder();

        sb.append("org.sfm.reflect.asm.");
        sb.append(getPackageName(target));
        sb.append(".AsmMapperFrom").append(replaceArray(source.getSimpleName()));
        sb.append("To").append(replaceArray(target.getSimpleName()));

        if (constructorMappers.length > 0) {
            sb.append("ConstInj").append(constructorMappers.length);
        }

        if (mappers.length > 0) {
            sb.append("Inj").append(mappers.length);
        }

        sb.append("_I").append(Long.toHexString(classNumber.getAndIncrement()));

        return sb.toString();
    }

    private  String getPackageName(Type target) {

        Package targetPackage = TypeHelper.toClass(target).getPackage();
        return targetPackage != null ? targetPackage.getName() : ".none";
    }

}