package org.simpleflatmapper.reflect.meta; import org.simpleflatmapper.reflect.getter.GetterHelper; import org.simpleflatmapper.reflect.getter.NullGetter; import org.simpleflatmapper.reflect.impl.ParamNameDeductor; import org.simpleflatmapper.reflect.*; import org.simpleflatmapper.reflect.setter.NullSetter; import org.simpleflatmapper.reflect.setter.SetterHelper; import org.simpleflatmapper.reflect.InstantiatorDefinition; import org.simpleflatmapper.util.Consumer; import org.simpleflatmapper.util.ErrorHelper; import org.simpleflatmapper.util.ListCollector; import org.simpleflatmapper.util.Predicate; import org.simpleflatmapper.util.TypeHelper; import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.util.*; public final class ObjectClassMeta<T> implements ClassMeta<T> { private final List<PropertyMeta<T, ?>> properties; private final List<ConstructorPropertyMeta<T, ?>> constructorProperties; private final List<InstantiatorDefinition> instantiatorDefinitions; private final ReflectionService reflectService; private final Type target; private final Map<String, String> fieldAliases; public ObjectClassMeta(Type target, ReflectionService reflectService) { this(target, null, reflectService); } public ObjectClassMeta(Type target, Member builderInstantiator, ReflectionService reflectService) { try { this.target = target; this.reflectService = reflectService; this.instantiatorDefinitions = reflectService.extractInstantiator(target, builderInstantiator); this.constructorProperties = listConstructorProperties(instantiatorDefinitions); this.fieldAliases = Collections.unmodifiableMap(aliases(reflectService, TypeHelper.<T>toClass(target))); this.properties = Collections.unmodifiableList(listProperties(reflectService, target)); } catch(Exception e) { ErrorHelper.rethrow(e); throw new IllegalStateException(); } } public ObjectClassMeta(Type target, List<InstantiatorDefinition> instantiatorDefinitions, List<ConstructorPropertyMeta<T, ?>> constructorProperties, Map<String, String> fieldAliases, List<PropertyMeta<T, ?>> properties, ReflectionService reflectService) { this.target = target; this.reflectService = reflectService; this.instantiatorDefinitions = instantiatorDefinitions; this.constructorProperties = constructorProperties; this.fieldAliases = fieldAliases; this.properties = properties; } private Map<String, String> aliases(final ReflectionService reflectService, Class<T> target) { final Map<String, String> map = new HashMap<String, String>(); ClassVisitor.visit(target, new FieldAndMethodCallBack() { @Override public void method(Method method) { String alias = reflectService.getColumnName(method); if (alias != null) { final String name; if (SetterHelper.isSetter(method)) { name = SetterHelper.getPropertyNameFromMethodName(method.getName()); } else if (GetterHelper.isGetter(method)) { name = GetterHelper.getPropertyNameFromMethodName(method.getName()); } else { throw new IllegalArgumentException("Annotation on non accessor method " + method); } map.put(name, alias); } } @Override public void field(Field field) { String alias = reflectService.getColumnName(field); if (alias != null) { map.put(field.getName(), alias); } } }); return map; } private List<ConstructorPropertyMeta<T, ?>> listConstructorProperties(List<InstantiatorDefinition> instantiatorDefinitions) { if (instantiatorDefinitions == null) return null; List<ConstructorPropertyMeta<T, ?>> constructorProperties = new ArrayList<ConstructorPropertyMeta<T, ?>>(); ParamNameDeductor<T> paramNameDeductor = null; for(InstantiatorDefinition cd : instantiatorDefinitions) { for(org.simpleflatmapper.reflect.Parameter param : cd.getParameters()) { String paramName = param.getName(); if (paramName == null) { if (paramNameDeductor == null) { paramNameDeductor = new ParamNameDeductor<T>(TypeHelper.<T>toClass(target)); } paramName = paramNameDeductor.findParamName(cd, param); } constructorProperties.add(constructorMeta(param, paramName, cd)); } } return constructorProperties; } private <P> ConstructorPropertyMeta<T, P> constructorMeta(org.simpleflatmapper.reflect.Parameter param, String paramName, InstantiatorDefinition instantiatorDefinition) { return new ConstructorPropertyMeta<T, P>(paramName, target, reflectService, param, instantiatorDefinition, null); } private List<PropertyMeta<T, ?>> listProperties(final ReflectionService reflectService, final Type targetType) { final Class<T> target = TypeHelper.<T>toClass(targetType); final List<PropertyMeta<T, ?>> properties = new ArrayList<PropertyMeta<T, ?>>(); final Map<TypeVariable<?>, Type> typeVariableTypeMap = TypeHelper.getTypesMap(targetType); ClassVisitor.visit(target, new FieldAndMethodCallBack() { @Override public void method(Method method) { final String name = method.getName(); if (SetterHelper.isSetter(method)) { final String propertyName = SetterHelper.getPropertyNameFromMethodName(name); Setter<T, Object> methodSetter = reflectService.getObjectSetterFactory().getMethodSetter(method); register(propertyName, method.getGenericParameterTypes()[0], ScoredGetter.<T, Object>nullGetter(), ScoredSetter.ofMethod(method, methodSetter), getDefineProperties(method)); } else if (GetterHelper.isGetter(method)) { final String propertyName = GetterHelper.getPropertyNameFromMethodName(name); Getter<T, Object> methodGetter = reflectService.getObjectGetterFactory().getMethodGetter(method); register(propertyName, method.getGenericReturnType(), ScoredGetter.ofMethod(method, methodGetter), ScoredSetter.<T, Object>nullSetter(), getDefineProperties(method)); } } @SuppressWarnings("unchecked") private <P> void register(String propertyName, Type type, ScoredGetter<T, P> getter, ScoredSetter<T, P> setter, Object[] defineProperties) { if (type instanceof TypeVariable) { Type mappedType = typeVariableTypeMap.get(type); if (mappedType != null) { type = mappedType; } } int indexOfProperty = findProperty(constructorProperties, propertyName); if (indexOfProperty != -1) { ConstructorPropertyMeta<T, P> constructorPropertyMeta = (ConstructorPropertyMeta<T, P>) constructorProperties.get(indexOfProperty); if (defineProperties != null && defineProperties.length > 0) { constructorPropertyMeta = constructorPropertyMeta.defineProperties(defineProperties); constructorProperties.set(indexOfProperty, constructorPropertyMeta); } if (getter != null && GetterHelper.isCompatible(constructorPropertyMeta.getPropertyType(), type)) { constructorPropertyMeta = constructorPropertyMeta.getter(getter); constructorProperties.set(indexOfProperty, constructorPropertyMeta); } if (setter != null && SetterHelper.isCompatible(constructorPropertyMeta.getPropertyType(), type)) { constructorPropertyMeta = constructorPropertyMeta.setter(setter); constructorProperties.set(indexOfProperty, constructorPropertyMeta); } } else { indexOfProperty = findProperty(properties, propertyName); if (indexOfProperty == -1) { properties.add(new ObjectPropertyMeta<T, P>(propertyName, targetType, reflectService, type, getter, setter, defineProperties)); } else { ObjectPropertyMeta<T, P> meta = (ObjectPropertyMeta<T, P>) properties.get(indexOfProperty); ScoredGetter<T, P> compatibleGetter = GetterHelper.isCompatible(meta.getPropertyType(), type) ? getter : ScoredGetter.<T, P>nullGetter(); ScoredSetter<T, P> compatibleSetter = SetterHelper.isCompatible(meta.getPropertyType(), type) ? setter : ScoredSetter.<T, P>nullSetter(); properties.set(indexOfProperty, meta.getterSetter(compatibleGetter, compatibleSetter, defineProperties)); } } } @Override public void field(Field field) { final String name = field.getName(); if (!Modifier.isStatic(field.getModifiers())) { if (Modifier.isPublic(field.getModifiers())) { ScoredGetter<T, Object> getter = ScoredGetter.<T, Object>ofField(field, reflectService.getObjectGetterFactory().<T, Object>getFieldGetter(field)); ScoredSetter<T, Object> setter; if (!Modifier.isFinal(field.getModifiers())) { setter = ScoredSetter.<T, Object>ofField(field, reflectService.getObjectSetterFactory().<T, Object>getFieldSetter(field)); } else { setter = ScoredSetter.<T, Object>nullSetter(); } register(name, field.getGenericType(), getter, setter, getDefineProperties(field)); } else { register(name, field.getGenericType(), ScoredGetter.<T, Object>nullGetter(), ScoredSetter.<T, Object>nullSetter(), getDefineProperties(field)); } } } private int findProperty(List<? extends PropertyMeta<T, ?>> properties, String name) { for(int i = 0; i < properties.size(); i++) { PropertyMeta<T, ?> propertyMeta = properties.get(i); String propertyMetaName = propertyMeta.getName(); if (propertyMetaName != null && propertyMetaName.equals(name)) { return i; } } return -1; } }); // filter out private field only; for(Iterator<PropertyMeta<T, ?>> it = properties.iterator(); it.hasNext();) { PropertyMeta<T, ?> propertyMeta = it.next(); if (NullSetter.isNull(propertyMeta.getSetter()) && NullGetter.isNull(propertyMeta.getGetter())) { it.remove(); } } return properties; } private Object[] getDefineProperties(AnnotatedElement annotatedElement) { ListCollector<Object> properties = new ListCollector<Object>(); AnnotationToPropertyService annotationToPropertyService = AnnotationToPropertyUtil.getAnnotationToPropertyService(); for(Annotation annotation : annotatedElement.getAnnotations()) { annotationToPropertyService.generateProperty(annotation, properties); } return properties.getList().toArray(); } protected String getAlias(String propertyName) { String columnName = this.fieldAliases.get(propertyName); if (columnName == null) { columnName = propertyName; } return columnName; } @Override public List<InstantiatorDefinition> getInstantiatorDefinitions() { return instantiatorDefinitions; } @Override public void forEachProperties(Consumer<? super PropertyMeta<T, ?>> consumer) { for(ConstructorPropertyMeta<T, ?> prop : constructorProperties) { consumer.accept(prop); } for(PropertyMeta<T, ?> prop : properties) { consumer.accept(prop); } } List<PropertyMeta<T, ?>> getProperties() { return properties; } List<ConstructorPropertyMeta<T, ?>> getConstructorProperties() { return constructorProperties; } @Override public ReflectionService getReflectionService() { return reflectService; } @Override public PropertyFinder<T> newPropertyFinder(Predicate<PropertyMeta<?, ?>> propertyFilter) { return new ObjectPropertyFinder<T>(this, propertyFilter); } @Override public Type getType() { return target; } public int getNumberOfProperties() { return constructorProperties.size() + properties.size(); } public PropertyMeta<T, ?> getFirstProperty() { if (!constructorProperties.isEmpty()) { return constructorProperties.get(0); } if (!properties.isEmpty()) { return properties.get(0); } return null; } }