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

org.simpleflatmapper.reflect.DefaultReflectionService Maven / Gradle / Ivy

package org.simpleflatmapper.reflect;

import org.simpleflatmapper.reflect.asm.AsmFactory;
import org.simpleflatmapper.reflect.asm.AsmInstantiatorDefinitionFactory;
import org.simpleflatmapper.reflect.impl.BuilderInstantiatorDefinitionFactory;
import org.simpleflatmapper.reflect.impl.ImmutableOrgHelper;
import org.simpleflatmapper.reflect.impl.JavaLangClassMetaFactoryProducer;
import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition;
import org.simpleflatmapper.reflect.instantiator.InstantiatorDefinitions;
import org.simpleflatmapper.reflect.instantiator.KotlinDefaultConstructorInstantiatorDefinition;
import org.simpleflatmapper.reflect.meta.AliasProvider;
import org.simpleflatmapper.reflect.meta.AliasProviderService;
import org.simpleflatmapper.reflect.meta.ArrayClassMeta;
import org.simpleflatmapper.reflect.meta.ClassMeta;
import org.simpleflatmapper.reflect.meta.FastTupleClassMeta;
import org.simpleflatmapper.reflect.meta.MapClassMeta;
import org.simpleflatmapper.reflect.meta.ObjectClassMeta;
//IFJAVA8_START
import org.simpleflatmapper.reflect.meta.OptionalClassMeta;
//IFJAVA8_END
import java.util.*;

import org.simpleflatmapper.reflect.meta.PassThroughClassMeta;
import org.simpleflatmapper.reflect.meta.TupleClassMeta;
import org.simpleflatmapper.util.BiConsumer;
import org.simpleflatmapper.util.Consumer;
import org.simpleflatmapper.util.ProducerServiceLoader;
import org.simpleflatmapper.util.TupleHelper;
import org.simpleflatmapper.util.TypeHelper;
import org.simpleflatmapper.util.UnaryFactory;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static org.simpleflatmapper.util.Asserts.requireNonNull;



public class DefaultReflectionService extends ReflectionService {

	private static final UnaryFactory>[] predefined =
			getPredifinedClassMetaFactory();
	private static final Consumer>>[] predefinedBuilderProducers = 
			getPredifinedBuilderProducers();

	private final AliasProvider aliasProvider;
	private final boolean builderIgnoresNullValues;
	private final boolean selfScoreFullName;

	private final ConcurrentMap> metaCache = new ConcurrentHashMap>();
	private final ConcurrentMap> builderMethods = new ConcurrentHashMap>();
	private final Map asmFactoryPerClassLoader;
	private final boolean isAsmActivated;

	public DefaultReflectionService(final AsmFactory asmFactory) {
		this(
				asmFactory != null,
				defaultAsmFactortyPerClassLoader(asmFactory),
				AliasProviderService.getAliasProvider(),
				true, false);
	}

	private static Map defaultAsmFactortyPerClassLoader(AsmFactory asmFactory) {

		Map map = new HashMap();
		if (asmFactory != null)
			map.put(asmFactory.targetClassLoader, asmFactory);
		return map;
	}

	private DefaultReflectionService(boolean isAsmActivated,
									 Map asmFactoryPerClassLoader,
                                     AliasProvider aliasProvider,
                                     boolean builderIgnoresNullValues,
                                     boolean selfScoreFullName) {

		this.isAsmActivated = isAsmActivated;
		this.asmFactoryPerClassLoader = asmFactoryPerClassLoader;
		this.aliasProvider = aliasProvider;
		this.builderIgnoresNullValues = builderIgnoresNullValues;
		this.selfScoreFullName = selfScoreFullName;
		initPredefined();
	}

	private void initPredefined() {
		for (UnaryFactory> factory : predefined) {
			ClassMeta classMeta = factory.newInstance(this);
			metaCache.put(classMeta.getType(), classMeta);
		}
		for(Consumer>> factory : predefinedBuilderProducers) {
			factory.accept(new BiConsumer>() {
				@Override
				public void accept(String s, UnaryFactory typeMemberUnaryFactory) {
					builderMethods.put(s, typeMemberUnaryFactory);
				}
			});
		}
	}
	
	@Override
	public void registerClassMeta(Type type, ClassMeta classMeta) {
		metaCache.put(type, classMeta);
	}

	@Override
	public ObjectSetterFactory getObjectSetterFactory() {
		return new ObjectSetterFactory(this);
	}

	@Override
	public InstantiatorFactory getInstantiatorFactory() {
		return new InstantiatorFactory(this);
	}


	@Override
	public boolean isAsmActivated() {
		return isAsmActivated;
	}
	@Override
	public AsmFactory getAsmFactory(ClassLoader classLoader) {
		if (classLoader == null) // system class loader override with sfm one
			classLoader = getClass().getClassLoader();

		if (!isAsmActivated) return null;
		synchronized (asmFactoryPerClassLoader) {
			AsmFactory asmFactory = asmFactoryPerClassLoader.get(classLoader);
			if (asmFactory == null) {
				asmFactory = new AsmFactory(classLoader);
				asmFactoryPerClassLoader.put(classLoader, asmFactory);
			}
			return asmFactory;
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public  ClassMeta getClassMeta(Type target) {
		requireNonNull("target", target);
		ClassMeta meta = (ClassMeta) metaCache.get(target);
		if (meta == null) {
			meta = newClassMeta(target);
			requireNonNull("meta", meta);
			metaCache.putIfAbsent(target, meta);
		}
		return meta;
	}

	@SuppressWarnings("unchecked")
	private  ClassMeta newClassMeta(Type target) {
		Class clazz = TypeHelper.toClass(target);

		if (target instanceof WildcardType) {
			Type[] upperBounds = ((WildcardType) target).getUpperBounds();
			if (upperBounds.length == 1) {
				target = upperBounds[0];
			}
		}
		if (clazz.isArray()) {
			return newArrayMeta(clazz);
			//IFJAVA8_START
		} else if (Optional.class.isAssignableFrom(clazz)) {
			return new OptionalClassMeta(target, this);
			//IFJAVA8_END
		} else if (clazz.isAnnotationPresent(PassThrough.class)) {
			return new PassThroughClassMeta(target, this);
		} else if (TupleHelper.isTuple(target)) {
			return new TupleClassMeta(target, this);
		} else if (isFastTuple(clazz)) {
            return new FastTupleClassMeta(target, this);
		} else if (Map.class.isAssignableFrom(clazz)) {
			return (ClassMeta) newMapMeta(target);
		} else if (ArrayClassMeta.supports(target)) {
			return newCollectionMeta(target);
		}

		if (isAbstractOrInterface(target)) {
			target = findImplementation(target);
		}

		return new ObjectClassMeta(target, getBuilderInstantiator(target), this);
	}

	private boolean isAbstractOrInterface(Type target) {
		Class clazz = TypeHelper.toClass(target);
		return clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
	}

	private Type findImplementation(Type target) {
		Class clazz = TypeHelper.toClass(target);
		Type implementation;

		implementation = ImmutableOrgHelper.findImplementation(clazz);

		if (implementation != null) return implementation;

		return target;
	}


	private Member getBuilderInstantiator(Type target) {
		String typeName = TypeHelper.toClass(target).getName();

		UnaryFactory builderSupplier = builderMethods.get(typeName);
		
		if (builderSupplier != null) {
			return builderSupplier.newInstance(target);
		}

		return null;
	}

	@Override
	public  ClassMeta getClassMetaExtraInstantiator(Type target, Member builderInstantiator) {
		return new ObjectClassMeta(target, builderInstantiator, this);
	}

	private  ClassMeta> newMapMeta(Type type) {
		TypeHelper.MapEntryTypes types = TypeHelper.getKeyValueTypeOfMap(type);
		return new MapClassMeta, K, V>(type, types.getKeyType(), types.getValueType(), this);
	}
	private  ClassMeta newArrayMeta(Class clazz) {
		return ArrayClassMeta.of(clazz, clazz.getComponentType(), this);
	}

	private  ClassMeta newCollectionMeta(Type type) {
		return ArrayClassMeta.of(type, TypeHelper.getComponentTypeOfListOrArray(type), this);
	}

	private  boolean isFastTuple(Class clazz) {
        Class superClass = clazz.getSuperclass();
        return superClass != null && "com.boundary.tuple.FastTuple".equals(superClass.getName());
    }

	@Override
	public String getColumnName(Method method) {
		return aliasProvider.getAliasForMethod(method);
	}
	@Override
	public String getColumnName(Field field) {
		return aliasProvider.getAliasForField(field);
	}


	@Override
	public List extractInstantiator(Type target, Member extraInstantiator) throws IOException {
		List list;

        if (!ReflectionInstantiatorDefinitionFactory.areParameterNamePresent(target)) {
            try {
				list = AsmInstantiatorDefinitionFactory.extractDefinitions(target);
			} catch (IllegalArgumentException e) {
				// byte code version issue
				list = ReflectionInstantiatorDefinitionFactory.extractDefinitions(target);
            } catch(IOException e) {
                // no access to class file
                list = ReflectionInstantiatorDefinitionFactory.extractDefinitions(target);
            }
		} else {
			list = ReflectionInstantiatorDefinitionFactory.extractDefinitions(target);
		}
		
		if (TypeHelper.isKotlinClass(target)) {
			kotlinReducationForDefaultValue(list);
		}

		if (extraInstantiator == null) {
			list.addAll(BuilderInstantiatorDefinitionFactory.extractDefinitions(target));

			if (list.isEmpty()) {
				Class enclosing = TypeHelper.toClass(target).getEnclosingClass();
				while (enclosing != null) {
					for(Method m : enclosing.getDeclaredMethods()) {
						if (Modifier.isPublic(m.getModifiers())
								&& Modifier.isStatic(m.getModifiers())
								&& TypeHelper.toClass(target).isAssignableFrom(m.getReturnType())) {
							list.add(ReflectionInstantiatorDefinitionFactory.definition(m));
						}
					}
					enclosing = enclosing.getEnclosingClass();
				}
			}
		} else {
			if (extraInstantiator instanceof Method && TypeHelper.areEquals(target, ((Method)extraInstantiator).getGenericReturnType())) {
				// factory method
				list.add(ReflectionInstantiatorDefinitionFactory.definition(((Method)extraInstantiator)));
			} else {
				final BuilderInstantiatorDefinition builder =
						BuilderInstantiatorDefinitionFactory.getDefinitionForBuilder(extraInstantiator, target);
				if (builder == null) {
					throw new IllegalArgumentException("Could not find any setters or build method on builder " + extraInstantiator);
				}
				list.add(builder);
			}
		}

		Collections.sort(list, InstantiatorDefinitions.COMPARATOR);

		return list;
	}

	private void kotlinReducationForDefaultValue(List list) {
		
		// look for potential kotlin default value
		List potentialKotlinDefaultValue = kotlingDefaultValueConstructor(list);
		
		if (potentialKotlinDefaultValue.isEmpty()) return;
		
		// remove them from original list
		list.removeAll(potentialKotlinDefaultValue);
		
		// match them to non default constructor
		for(int i = 0; i < potentialKotlinDefaultValue.size(); i++) {
			ExecutableInstantiatorDefinition def = potentialKotlinDefaultValue.get(i);
		
			for(int j = 0; j < list.size(); j++) {
				InstantiatorDefinition id = list.get(j);
				
				if (isKotlinOriginalConstructor(def, id)) {
					list.set(j, new KotlinDefaultConstructorInstantiatorDefinition((ExecutableInstantiatorDefinition) id, def));
					break;
				}
			}
		}
		
		
	}

	private List kotlingDefaultValueConstructor(List list) {
		List potentialKotlinDefaultValue = new ArrayList();

		for(int i = 0; i < list.size(); i++) {
			InstantiatorDefinition id = list.get(i);
			
			if (id instanceof ExecutableInstantiatorDefinition && 
					((ExecutableInstantiatorDefinition)id).getExecutable() instanceof Constructor) {
				Constructor c = (Constructor) ((ExecutableInstantiatorDefinition)id).getExecutable();
				if (c.isSynthetic()) {
					Class[] parameterTypes = c.getParameterTypes();
					if (parameterTypes[parameterTypes.length -1].getName().equals("kotlin.jvm.internal.DefaultConstructorMarker")) {
						// got one
						potentialKotlinDefaultValue.add((ExecutableInstantiatorDefinition) id);
					}
				}
			}
		}
		return potentialKotlinDefaultValue;
	}

	private boolean isKotlinOriginalConstructor(ExecutableInstantiatorDefinition def, InstantiatorDefinition id) {
		if (id instanceof ExecutableInstantiatorDefinition) {
			ExecutableInstantiatorDefinition eid = (ExecutableInstantiatorDefinition) id;
			if (eid.getExecutable() instanceof Constructor) {
				int nbParams = eid.getParameters().length;
				int syntheticParameters = (nbParams / Integer.SIZE) + 1 + /* DefaultConstructorMarker */ 1;
				
				if (nbParams + syntheticParameters != def.getParameters().length) {
					return false;
				}
				
				for(int i = 0; i < nbParams; i++) {
					if (!def.getParameters()[i].getType().equals(id.getParameters()[i].getType())) {
						return false;
					}
				}
				
				for(int i = nbParams; i < nbParams + syntheticParameters - 1; i++ ) {
					if (!def.getParameters()[i].getType().equals(int.class)) {
						return false;
					}
				}
				
				return true;
						
			}
		}
		return false;
	}

	@Override
	public ObjectGetterFactory getObjectGetterFactory() {
        return new ObjectGetterFactory(this);
    }


	@Override
	public DefaultReflectionService withAliasProvider(AliasProvider aliasProvider) {
		return new DefaultReflectionService(
				isAsmActivated, asmFactoryPerClassLoader,
				aliasProvider, builderIgnoresNullValues, selfScoreFullName);
	}

	@Override
	public DefaultReflectionService withBuilderIgnoresNullValues(boolean builderIgnoresNullValues) {
		return new DefaultReflectionService(
				isAsmActivated, asmFactoryPerClassLoader,
				aliasProvider, builderIgnoresNullValues, selfScoreFullName);
	}

	@Override
	public DefaultReflectionService withSelfScoreFullName(boolean selfScoreFullName) {
		return new DefaultReflectionService(
				isAsmActivated, asmFactoryPerClassLoader,
				aliasProvider, builderIgnoresNullValues, selfScoreFullName);
	}

	@Override
	public boolean builderIgnoresNullValues() {
		return builderIgnoresNullValues;
	}

	@Override
	public boolean selfScoreFullName() {
		return selfScoreFullName;
	}

	@Override
	public void registerBuilder(String name, DefaultBuilderSupplier defaultBuilderSupplier) {
		builderMethods.put(name, defaultBuilderSupplier);
	}

	private static Consumer>>[] getPredifinedBuilderProducers() {
		final List>>> list = new ArrayList>>>();
		Consumer>>> consumer = new Consumer>>>() {
			@Override
			public void accept(Consumer>> biConsumerConsumer) {
				list.add(biConsumerConsumer);
			}
		};

		ProducerServiceLoader.produceFromServiceLoader(
				ServiceLoader.load(BuilderProducer.class),
				consumer
		);

		consumer.accept(new Consumer>>() {
			@Override
			public void accept(BiConsumer> biConsumer) {
				biConsumer.accept("javax.money.MonetaryAmount",
						new DefaultBuilderSupplier("javax.money.Monetary", "getDefaultAmountFactory"));
			}
		});

		return list.toArray(new Consumer[0]);
	}

	@SuppressWarnings("unchecked")
	private static UnaryFactory>[] getPredifinedClassMetaFactory() {
		final List>> list = new ArrayList>>();
		Consumer>> consumer = new Consumer>>() {
			@Override
			public void accept(UnaryFactory> reflectionServiceClassMetaUnaryFactory) {
				list.add(reflectionServiceClassMetaUnaryFactory);
			}
		};

		new JavaLangClassMetaFactoryProducer().produce(consumer);

		ProducerServiceLoader.produceFromServiceLoader(
				ServiceLoader.load(ClassMetaFactoryProducer.class),
				consumer
		);

		return list.toArray(new UnaryFactory[0]);
	}
}