MutatorByMethod.java
package org.codefilarete.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import org.codefilarete.tool.Reflections;
import org.codefilarete.tool.Reflections.MemberNotFoundException;
import org.codefilarete.tool.function.ThreadSafeLazyInitializer;
/**
* @author mary
*/
public class MutatorByMethod<C, T> extends AbstractMutator<C, T>
implements MutatorByMember<C, T, Method>, ReversibleMutator<C, T>, ValueAccessPointByMethod<C> {
private final Method setter;
private final Supplier<Accessor<C, T>> accessor;
public MutatorByMethod(Method setter) {
Reflections.ensureAccessible(setter);
this.setter = setter;
this.accessor = new ThreadSafeLazyInitializer<Accessor<C, T>>() {
@Override
protected Accessor<C, T> createInstance() {
return findCompatibleAccessor();
}
};
}
MutatorByMethod(Method setter, Accessor<C, T> accessor) {
this.setter = setter;
this.accessor = () -> accessor;
}
/**
* Constructor for a setter-equivalent method
*
* @param declaringClass type that declares the method
* @param setterName name of the mutator
* @param argTypes argument types
*/
public MutatorByMethod(Class<C> declaringClass, String setterName, Class ... argTypes) {
this(Reflections.getMethod(declaringClass, setterName, argTypes));
}
@Override
public Method getSetter() {
return setter;
}
@Override
public Method getMethod() {
return getSetter();
}
@Override
public Class<T> getPropertyType() {
return (Class<T>) getMethod().getParameterTypes()[0];
}
@Override
protected void doSet(C c, T t) throws IllegalAccessException, InvocationTargetException {
try {
getSetter().invoke(c, t);
} catch (RuntimeException e) {
// converting "argument type mismatch" cases
throw new ExceptionConverter().convertException(e, c, this, t);
}
}
@Override
protected String getSetterDescription() {
return Reflections.toString(getSetter());
}
/**
* @return an accessor based on the equivalent getter method if exists, else based on field direct access
* @throws NonReversibleAccessor if neither getter nor field could be found
*/
@Override
public Accessor<C, T> toAccessor() {
return this.accessor.get();
}
private Accessor<C, T> findCompatibleAccessor() {
Class<?> declaringClass = getSetter().getDeclaringClass();
String propertyName = Reflections.propertyName(getSetter());
AccessorByMethod<C, T> accessorByMethod = Accessors.accessorByMethod(declaringClass, propertyName,(Class<T>) getSetter().getParameterTypes()[0], this);
if (accessorByMethod == null) {
try {
return Accessors.accessorByField((Class<C>) declaringClass, propertyName);
} catch (MemberNotFoundException e) {
throw new NonReversibleAccessor("Can't find a mutator for " + Reflections.toString(getSetter()), e);
}
} else {
return accessorByMethod;
}
}
@Override
public boolean equals(Object other) {
// we base our implementation on the setter description because a setAccessible() call on the member changes its internal state
// and I don't think it sould be taken into account for comparison
return this == other ||
(other instanceof MutatorByMethod && getSetterDescription().equals(((MutatorByMethod) other).getSetterDescription()));
}
@Override
public int hashCode() {
return getSetter().hashCode();
}
}