DefaultReadWritePropertyAccessPoint.java

package org.codefilarete.reflection;

import org.codefilarete.tool.function.Predicates;

/**
 * Equivalent of {@link ReadWriteAccessPoint} dedicated to property bean, which means that it doesn't allow accessibility
 * for {@link ListAccessor} or {@link ArrayAccessor}.
 *
 * @param <C> the class declaring the property
 * @param <T> the property type
 * @author Guillaume Mary
 */
public class DefaultReadWritePropertyAccessPoint<C, T>
		implements ReversibleAccessor<C, T>, ReversibleMutator<C, T>,
		ReadWritePropertyAccessPoint<C, T>
{
	/**
	 * Static constructor, because its signature would conflict with the one with {@link Accessor}, {@link Mutator}
	 * @param accessor a method reference to a getter
	 * @param mutator a method reference to a setter
	 * @param <C> bean type
	 * @param <T> property type
	 * @return a {@link DefaultReadWriteAccessPoint} that will access a property through the given getter and getter
	 */
	public static <C, T> DefaultReadWritePropertyAccessPoint<C, T> fromMethodReference(SerializablePropertyAccessor<C, T> accessor, SerializablePropertyMutator<C, T> mutator) {
		return new DefaultReadWritePropertyAccessPoint<>(new AccessorByMethodReference<>(accessor), new MutatorByMethodReference<>(mutator));
	}
	
	private final PropertyAccessor<C, T> accessor;
	private final PropertyMutator<C, T> mutator;
	
	public DefaultReadWritePropertyAccessPoint(ReversibleAccessor<C, T> accessor) {
		this((PropertyAccessor<C, T>) accessor, (PropertyMutator<C, T>) accessor.toMutator());
	}
	
	public DefaultReadWritePropertyAccessPoint(ReversibleMutator<C, T> mutator) {
		this((PropertyAccessor<C, T>) mutator.toAccessor(), (PropertyMutator<C, T>) mutator);
	}
	
	public DefaultReadWritePropertyAccessPoint(PropertyAccessor<C, T> accessor, PropertyMutator<C, T> mutator) {
		this.accessor = accessor;
		this.mutator = mutator;
	}
	
	@Override
	public PropertyAccessor<C, T> getReader() {
		return accessor;
	}
	
	@Override
	public PropertyMutator<C, T> getWriter() {
		return mutator;
	}
	
	@Override
	public Accessor<C, T> toAccessor() {
		return accessor;
	}
	
	@Override
	public Mutator<C, T> toMutator() {
		return mutator;
	}
	
	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		} else if (!(other instanceof DefaultReadWritePropertyAccessPoint)) {
			return super.equals(other);
		} else {
			return Predicates.equalOrNull(this.getReader(), ((DefaultReadWritePropertyAccessPoint) other).getReader())
					&& Predicates.equalOrNull(this.getWriter(), ((DefaultReadWritePropertyAccessPoint) other).getWriter());
		}
	}
	
	@Override
	public int hashCode() {
		// Implementation based on both readWriteAccessPoint and readWriteAccessPoint. Accessor is taken first but it doesn't matter
		return 31 * getReader().hashCode() + getWriter().hashCode();
	}
	
	/**
	 * Implemented for trace in errors or debug
	 * @return the getter description if available, else defaults to super.toString()
	 */
	@Override
	public String toString() {
		return "property readWriteAccessPoint by " + (this.getReader() instanceof AbstractAccessor
				? ((AbstractAccessor<C, T>) this.getReader()).getGetterDescription()
				: this.getReader().toString());
	}
}