package org.simpleflatmapper.reflect.impl; import org.simpleflatmapper.reflect.getter.GetterHelper; import org.simpleflatmapper.util.EnumHelper; import org.simpleflatmapper.reflect.Getter; import org.simpleflatmapper.reflect.Instantiator; import org.simpleflatmapper.reflect.InstantiatorDefinition; import org.simpleflatmapper.reflect.InstantiatorFactory; import org.simpleflatmapper.reflect.ObjectGetterFactory; import org.simpleflatmapper.reflect.Parameter; import org.simpleflatmapper.reflect.ReflectionInstantiatorDefinitionFactory; import org.simpleflatmapper.reflect.meta.ClassVisitor; import org.simpleflatmapper.util.TypeHelper; import org.simpleflatmapper.reflect.getter.ConstantGetter; import org.simpleflatmapper.reflect.meta.FieldAndMethodCallBack; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ParamNameDeductor<T> { private final Class<T> target; private List<Accessor<T>> accessors; private final InstantiatorFactory instantiatorFactory = new InstantiatorFactory(null); public ParamNameDeductor(Class<T> target) { this.target = target; } public String findParamName(InstantiatorDefinition instantiatorDefinition, Parameter param) { if (accessors == null) { accessors = listAccessors(); } try { T value; Map<Parameter, Getter<? super Object, ?>> parameters = parametersWithExpectedValue(instantiatorDefinition, param, true); Instantiator<Object, T> instantiator = instantiatorFactory.getInstantiator(instantiatorDefinition, Object.class, parameters, false); try { // try with null values value = instantiator.newInstance(null); } catch(NullPointerException e) { // try with non null explicit values parameters = parametersWithExpectedValue(instantiatorDefinition, param, false); instantiator = instantiatorFactory.getInstantiator(instantiatorDefinition, Object.class, parameters, false); value = instantiator.newInstance(null); } if (value != null) { Object expectedPropertyValue = parameters.get(param).get(null); // iterate through all the accessor to find one that returns a matching value for (Accessor<T> accessor : accessors) { try { final Object propertyValue = accessor.getter.get(value); if (expectedPropertyValue.equals(propertyValue)) { return accessor.name; } } catch (Exception e) { // IGNORE } } } } catch(Exception e) { // IGNORE } return null; } private Map<Parameter, Getter<? super Object, ?>> parametersWithExpectedValue(InstantiatorDefinition instantiatorDefinition, Parameter param, boolean allowNull) throws Exception { Map<Parameter, Getter<? super Object, ?>> parameterGetterMap = parameters(instantiatorDefinition, allowNull); parameterGetterMap.put(param, new ConstantGetter<Object, Object>(markValue(param.getGenericType()))); return parameterGetterMap; } private Map<Parameter, Getter<? super Object, ?>> parameters(InstantiatorDefinition instantiatorDefinition, boolean allowNull) throws Exception { Map<Parameter, Getter<? super Object, ?>> parameterGetterMap = new HashMap<Parameter, Getter<? super Object, ?>>(); for(Parameter parameter : instantiatorDefinition.getParameters()) { Object value = neutralValue(parameter.getGenericType(), allowNull); parameterGetterMap.put(parameter, new ConstantGetter<Object, Object>(value)); } return parameterGetterMap; } private static final Map<Class<?>, Object> primitivesMarkValue = new HashMap<Class<?>, Object>(); static { primitivesMarkValue.put(byte.class, (byte) 1); primitivesMarkValue.put(char.class, (char) 1); primitivesMarkValue.put(short.class, (short) 1); primitivesMarkValue.put(int.class, (int) 1); primitivesMarkValue.put(long.class, (long) 1); primitivesMarkValue.put(float.class, (float) 1); primitivesMarkValue.put(double.class, (double) 1); } private static final Map<Class<?>, Object> primitivesNeutralValue = new HashMap<Class<?>, Object>(); static { primitivesNeutralValue.put(byte.class, (byte) 0); primitivesNeutralValue.put(char.class, (char) 0); primitivesNeutralValue.put(short.class, (short) 0); primitivesNeutralValue.put(int.class, (int) 0); primitivesNeutralValue.put(long.class, (long) 0); primitivesNeutralValue.put(float.class, (float) 0); primitivesNeutralValue.put(double.class, (double) 0); } @SuppressWarnings("unchecked") private <V> V markValue(Type type) throws Exception { if (TypeHelper.isPrimitive(type)) { return (V) primitivesMarkValue.get(type); } else if (TypeHelper.areEquals(type, String.class)) { return (V) "1"; } else if (TypeHelper.isAssignable(Enum.class, type)) { Enum[] values = EnumHelper.getValues(TypeHelper.<Enum>toClass(type)); return (V) (values.length > 1 ? values[1] : values[0]); } else { return createValueFromInstantiator(type); } } @SuppressWarnings("unchecked") private <V> V neutralValue(Type type, boolean allowNull) throws Exception { if (TypeHelper.isPrimitive(type)) { return (V) primitivesNeutralValue.get(type); } if (allowNull) return null; if (TypeHelper.areEquals(type, String.class)) { return (V) "0"; } else if (TypeHelper.isAssignable(Enum.class, type)) { Enum[] values = EnumHelper.getValues(TypeHelper.<Enum>toClass(type)); return (V) values[0]; } else { return createValueFromInstantiator(type); } } private <V> V createValueFromInstantiator(Type type) throws Exception { InstantiatorDefinition instantiatorDefinition = InstantiatorFactory.getSmallerConstructor(ReflectionInstantiatorDefinitionFactory.extractDefinitions(type)); Instantiator<Object, V> instantiator = instantiatorFactory.getInstantiator(instantiatorDefinition, Object.class, parameters(instantiatorDefinition, true), false); try { return instantiator.newInstance(null); } catch (NullPointerException e) { instantiator = instantiatorFactory.getInstantiator(instantiatorDefinition, Object.class, parameters(instantiatorDefinition, false), false); return instantiator.newInstance(null); } } private List<Accessor<T>> listAccessors() { final List<Accessor<T>> list = new ArrayList<Accessor<T>>(); ClassVisitor.visit(target, new FieldAndMethodCallBack() { ObjectGetterFactory objectGetterFactory = new ObjectGetterFactory(null); @Override public void method(Method method) { if (GetterHelper.isGetter(method)) { Getter<T, Object> methodGetter = objectGetterFactory.getMethodGetter(method); list.add(new Accessor<T>(GetterHelper.getPropertyNameFromMethodName(method.getName()), methodGetter)); } } @Override public void field(Field field) { Getter<T, Object> fieldGetter = objectGetterFactory.getFieldGetter(field); list.add(new Accessor<T>(field.getName(), fieldGetter)); } }); return list; } private static class Accessor<T> { private final Getter<T, ?> getter; private final String name; private Accessor(String name, Getter<T, ?> getter) { this.getter = getter; this.name = name; } } }