package org.simpleflatmapper.reflect.meta; import org.simpleflatmapper.reflect.ConstructorNotFoundException; import org.simpleflatmapper.reflect.Getter; import org.simpleflatmapper.reflect.ScoredGetter; import org.simpleflatmapper.reflect.ScoredSetter; import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition; import org.simpleflatmapper.reflect.InstantiatorDefinition; import org.simpleflatmapper.reflect.Parameter; import org.simpleflatmapper.reflect.ReflectionService; import org.simpleflatmapper.util.Consumer; import org.simpleflatmapper.util.Predicate; import org.simpleflatmapper.util.TupleHelper; import org.simpleflatmapper.util.TypeHelper; import org.simpleflatmapper.util.ErrorHelper; import java.lang.reflect.Constructor; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.ListIterator; public class TupleClassMeta<T> implements ClassMeta<T> { private final ReflectionService reflectionService; private final Type type; private final InstantiatorDefinition instantiatorDefinition; private final List<ConstructorPropertyMeta<T, ?>> propertyMetas; public TupleClassMeta(Type type, ReflectionService reflectionService) { this.type = type; this.reflectionService = reflectionService; try { this.instantiatorDefinition = getInstantiatorDefinition(type, reflectionService); this.propertyMetas = getPropertyMetas(instantiatorDefinition, reflectionService, type); } catch(Exception e) { ErrorHelper.rethrow(e); throw new IllegalStateException(); } } private static <T> List<ConstructorPropertyMeta<T, ?>> getPropertyMetas(InstantiatorDefinition instantiatorDefinition, ReflectionService reflectionService, Type type) { int size = instantiatorDefinition.getParameters().length; List<ConstructorPropertyMeta<T, ?>> propertyMetas = new ArrayList<ConstructorPropertyMeta<T, ?>>(); for(int i = 0; i < size; i++) { propertyMetas.add(TupleClassMeta.<T, Object>newConstructorPropertyMeta(instantiatorDefinition, i, reflectionService, type)); } return propertyMetas; } private static <T, E> ConstructorPropertyMeta<T, E> newConstructorPropertyMeta(InstantiatorDefinition instantiatorDefinition, int i, ReflectionService reflectionService, Type type) { Class<T> tClass = TypeHelper.toClass(type); final Parameter parameter = instantiatorDefinition.getParameters()[i]; Getter<T, E> getter = reflectionService.getObjectGetterFactory().getGetter(tClass, parameter.getName()); return new ConstructorPropertyMeta<T, E>("element" + i, type, reflectionService, parameter, ScoredGetter.<T, E>of(getter, Integer.MAX_VALUE), ScoredSetter.<T, E>nullSetter(), instantiatorDefinition, null); } private InstantiatorDefinition getInstantiatorDefinition(Type type, ReflectionService reflectionService) throws java.io.IOException { final List<InstantiatorDefinition> definitions = reflectionService.extractInstantiator(type); ListIterator<InstantiatorDefinition> iterator = definitions.listIterator(); while(iterator.hasNext()) { final InstantiatorDefinition definition = iterator.next(); if (isTupleConstructor(type, definition)) { return respecifyParameterNames((ExecutableInstantiatorDefinition)definition); } } throw new ConstructorNotFoundException("Cannot find eligible tuple constructor definition for " + type); } @SuppressWarnings("unchecked") private InstantiatorDefinition respecifyParameterNames(ExecutableInstantiatorDefinition definition) { final Parameter[] parameters = definition.getParameters(); if (parameters.length > 0 && parameters[0].getName() == null) { Parameter[] newParams = new Parameter[parameters.length]; final ElementNameGenerator nameGenerator = elementNameGenerator(definition.getExecutable().getDeclaringClass()); for(int i = 0; i < parameters.length; i++) { newParams[i] = new Parameter(i, nameGenerator.name(i), parameters[i].getType(), parameters[i].getGenericType()); } return new ExecutableInstantiatorDefinition((Constructor<? extends T>) definition.getExecutable(), newParams); } return definition; } private boolean isTupleConstructor(Type type, InstantiatorDefinition definition) { if (type instanceof ParameterizedType && definition.getType() != InstantiatorDefinition.Type.BUILDER) { ParameterizedType pt = (ParameterizedType) type; return pt.getActualTypeArguments().length == definition.getParameters().length; } return true; } @Override public ReflectionService getReflectionService() { return reflectionService; } @Override public PropertyFinder<T> newPropertyFinder(Predicate<PropertyMeta<?, ?>> propertyFilter) { return new TuplePropertyFinder<T>(this, propertyFilter); } public Type getType() { return type; } private static ElementNameGenerator elementNameGenerator(Type type) { Class<?> clazz = TypeHelper.toClass(type); if (TupleHelper.isJoolTuple(clazz)) { return new JoolTupleNameGenerator(); } return new SFMTupleNameGenerator(); } public List<InstantiatorDefinition> getInstantiatorDefinitions() { return Arrays.asList(instantiatorDefinition); } @Override public void forEachProperties(Consumer<? super PropertyMeta<T, ?>> consumer) { for(PropertyMeta<T, ?> prop : propertyMetas) { consumer.accept(prop); } } public int getTupleSize() { return instantiatorDefinition.getParameters().length; } interface ElementNameGenerator { String name(int i); } static class SFMTupleNameGenerator implements ElementNameGenerator { public String name(int i) { return "element" + i; } } static class JoolTupleNameGenerator implements ElementNameGenerator { public String name(int i) { return "v" + (i+1); } } }