PropertyAccessorResolver.java
package org.codefilarete.stalactite.engine.configurer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.codefilarete.reflection.Accessor;
import org.codefilarete.reflection.AccessorByField;
import org.codefilarete.reflection.AccessorByMember;
import org.codefilarete.reflection.AccessorByMethod;
import org.codefilarete.reflection.AccessorByMethodReference;
import org.codefilarete.reflection.AccessorDefinition;
import org.codefilarete.reflection.AccessorDefinitionDefiner;
import org.codefilarete.reflection.Accessors;
import org.codefilarete.reflection.Mutator;
import org.codefilarete.reflection.MutatorByField;
import org.codefilarete.reflection.MutatorByMember;
import org.codefilarete.reflection.MutatorByMethod;
import org.codefilarete.reflection.MutatorByMethodReference;
import org.codefilarete.reflection.PropertyAccessor;
import org.codefilarete.reflection.ReversibleAccessor;
import org.codefilarete.stalactite.dsl.MappingConfigurationException;
import org.codefilarete.tool.Reflections;
import org.codefilarete.tool.Reflections.MemberNotFoundException;
import org.codefilarete.tool.Strings;
import org.danekja.java.util.function.serializable.SerializableBiConsumer;
import org.danekja.java.util.function.serializable.SerializableFunction;
/**
* Resolver for {@link PropertyAccessor} in context of mapping definition.
* - The API lets one defines property by getter or setter, but persistence engine requires both to fill entity while
* loading them, also to get their values to persist them. Meanwhile, those methods may not exist, then a direct access
* to underlying property ({@link Field}) is required.
* - Moreover, entity may not follow Bean Naming Convention.
* This class is meant to deal with all this.
*
* @param <C>
* @param <O>
* @author Guillaume Mary
*/
public class PropertyAccessorResolver<C, O> {
private final PropertyMapping<C, O> propertyMapping;
public PropertyAccessorResolver(PropertyMapping<C, O> propertyMapping) {
this.propertyMapping = propertyMapping;
}
public ReversibleAccessor<C, O> resolve() {
Accessor<C, O> accessor = null;
Mutator<C, O> mutator = null;
AccessorDefinition accessorDefinition = null;
if (this.propertyMapping.getGetter() != null) {
AccessorByMethodReference<C, O> getterAsMethodReferenceAccessor = Accessors.accessorByMethodReference(this.propertyMapping.getGetter());
accessor = getterAsMethodReferenceAccessor;
MutatorByMember<C, O, ?> propertySetter;
if (this.propertyMapping.getField() != null) {
propertySetter = new MutatorByField<>(this.propertyMapping.getField());
accessorDefinition = new AccessorDefinition(getterAsMethodReferenceAccessor.getDeclaringClass(), getterAsMethodReferenceAccessor.getMethodName(), getterAsMethodReferenceAccessor.getPropertyType());
} else {
propertySetter = resolveMutator(getterAsMethodReferenceAccessor);
}
mutator = propertySetter;
} else if (this.propertyMapping.getSetter() != null) {
MutatorByMethodReference<C, O> setterAsMethodReferenceMutator = Accessors.mutatorByMethodReference(this.propertyMapping.getSetter());
mutator = setterAsMethodReferenceMutator;
AccessorByMember<C, O, ?> propertyGetter;
if (this.propertyMapping.getField() != null) {
propertyGetter = new AccessorByField<>(this.propertyMapping.getField());
accessorDefinition = new AccessorDefinition(setterAsMethodReferenceMutator.getDeclaringClass(), setterAsMethodReferenceMutator.getMethodName(), setterAsMethodReferenceMutator.getPropertyType());
} else {
propertyGetter = resolveAccessor(setterAsMethodReferenceMutator);
}
accessor = propertyGetter;
}
if (accessorDefinition != null) {
return new DefinedPropertyAccessor<>(accessor, mutator, accessorDefinition);
} else {
return new PropertyAccessor<>(accessor, mutator);
}
}
private MutatorByMember<C, O, ?> resolveMutator(AccessorByMethodReference<C, O> getterAsMethodReferenceAccessor) {
AccessorDefinition accessorDefinition;
try {
accessorDefinition = AccessorDefinition.giveDefinition(getterAsMethodReferenceAccessor);
} catch (MemberNotFoundException memberNotFoundException) {
// the getter doesn't follow Java Bean Naming Convention and user didn't give us a field name to set the property
// we can't go further, so we throw the exception
throw new MappingConfigurationException("Can't find a property matching getter name "
+ getterAsMethodReferenceAccessor.getMethodName() + ", setter can't be deduced," +
" provide a field name to fix it", memberNotFoundException);
}
String capitalizedProperty = Strings.capitalize(accessorDefinition.getName());
String methodPrefix = boolean.class.equals(accessorDefinition.getMemberType()) || Boolean.class.equals(accessorDefinition.getMemberType())
? "is"
: "set";
Method method = Reflections.findMethod(accessorDefinition.getDeclaringClass(), methodPrefix + capitalizedProperty, accessorDefinition.getMemberType());
if (method != null) {
return new MutatorByMethod<>(method);
} else {
// Note that getField will throw an exception if field doesn't exist which shouldn't be the case since AccessorDefinition ensures it
return new MutatorByField<>(Reflections.getField(accessorDefinition.getDeclaringClass(), accessorDefinition.getName()));
}
}
private AccessorByMember<C, O, ?> resolveAccessor(MutatorByMethodReference<C, O> setterAsMethodReferenceMutator) {
AccessorDefinition accessorDefinition;
try {
accessorDefinition = AccessorDefinition.giveDefinition(setterAsMethodReferenceMutator);
} catch (MemberNotFoundException memberNotFoundException) {
// the getter doesn't follow Java Bean Naming Convention and user didn't give us a field name to set the property
// we can't go further, so we throw the exception
throw new MappingConfigurationException("Can't find a property matching setter name "
+ setterAsMethodReferenceMutator.getMethodName() + ", getter can't be deduced," +
" provide a field name to fix it", memberNotFoundException);
}
String capitalizedProperty = Strings.capitalize(accessorDefinition.getName());
String methodPrefix = boolean.class.equals(accessorDefinition.getMemberType()) || Boolean.class.equals(accessorDefinition.getMemberType())
? "is"
: "get";
Method method = Reflections.findMethod(accessorDefinition.getDeclaringClass(), methodPrefix + capitalizedProperty);
if (method != null) {
return new AccessorByMethod<>(method);
} else {
// Note that getField will throw an exception if field doesn't exist which shouldn't be the case since AccessorDefinition ensures it
return new AccessorByField<>(Reflections.getField(accessorDefinition.getDeclaringClass(), accessorDefinition.getName()));
}
}
public interface PropertyMapping<C, O> {
SerializableFunction<C, O> getGetter();
SerializableBiConsumer<C, O> getSetter();
Field getField();
}
private static class DefinedPropertyAccessor<C, T> extends PropertyAccessor<C, T> implements AccessorDefinitionDefiner<C> {
private final AccessorDefinition accessorDefinition;
public DefinedPropertyAccessor(Accessor<C, T> accessor, Mutator<C, T> mutator, AccessorDefinition accessorDefinition) {
super(accessor, mutator);
this.accessorDefinition = accessorDefinition;
}
@Override
public AccessorDefinition asAccessorDefinition() {
return accessorDefinition;
}
@Override
public Accessor<C, T> toAccessor() {
return this;
}
@Override
public Mutator<C, T> toMutator() {
return this;
}
}
}