package org.simpleflatmapper.map.asm;
import org.simpleflatmapper.map.FieldKey;
import org.simpleflatmapper.map.FieldMapper;
import org.simpleflatmapper.map.Mapper;
import org.simpleflatmapper.map.MappingContext;
import org.simpleflatmapper.reflect.BiInstantiator;
import org.simpleflatmapper.reflect.Instantiator;
import org.simpleflatmapper.reflect.asm.AsmFactory;
import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class MapperAsmFactory {
private final AsmFactory asmFactory;
public MapperAsmFactory(AsmFactory asmFactory) {
this.asmFactory = asmFactory;
}
private final ConcurrentMap<MapperKey, Class<? extends Mapper<?, ?>>> fieldMapperCache = new ConcurrentHashMap<MapperKey, Class<? extends Mapper<?, ?>>>();
private <S, T> String generateClassNameForFieldMapper(final FieldMapper<S, T>[] mappers, final FieldMapper<S, T>[] constructorMappers, final Class<? super S> source, final Class<T> target) {
StringBuilder sb = new StringBuilder();
sb.append("org.simpleflatmapper.map.generated.");
sb.append(asmFactory.getPackageName(target));
sb.append(".AsmMapperFrom").append(asmFactory.replaceArray(source.getSimpleName()));
sb.append("To").append(asmFactory.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(asmFactory.getNextClassNumber()));
return sb.toString();
}
@SuppressWarnings("unchecked")
public <S, T> Mapper<S, T> createMapper(final FieldKey<?>[] keys,
final FieldMapper<S, T>[] mappers,
final FieldMapper<S, T>[] constructorMappers,
final BiInstantiator<S, MappingContext<? super S>, T> instantiator,
final Class<? super S> source,
final Class<T> target) throws Exception {
MapperKey key = new MapperKey(keys, mappers, constructorMappers, instantiator, target, source);
Class<Mapper<S, T>> type = (Class<Mapper<S, T>>) fieldMapperCache.get(key);
if (type == null) {
final String className = generateClassNameForFieldMapper(mappers, constructorMappers, source, target);
final byte[] bytes = MapperAsmBuilder.dump(className, mappers, constructorMappers, source, target);
type = (Class<Mapper<S, T>>) asmFactory.createClass(className, bytes, target.getClass().getClassLoader());
fieldMapperCache.put(key, type);
}
final Constructor<?> constructor = type.getDeclaredConstructors()[0];
return (Mapper<S, T>) constructor.newInstance(mappers, constructorMappers, instantiator);
}
}