
package org.apache.commons.functor.adapter;

import java.io.Serializable;

import org.apache.commons.functor.BinaryProcedure;
import org.apache.commons.functor.NullaryProcedure;
import org.apache.commons.lang3.Validate;

/**
 * Adapts a
 * {@link BinaryProcedure BinaryProcedure}
 * to the
 * {@link NullaryProcedure NullaryProcedure} interface
 * using a constant left-side argument.
 * <p/>
 * Note that although this class implements
 * {@link Serializable}, a given instance will
 * only be truly <code>Serializable</code> if the
 * underlying objects are.  Attempts to serialize
 * an instance whose delegates are not
 * <code>Serializable</code> will result in an exception.
 *
 * @version $Revision: 1365377 $ $Date: 2012-07-24 21:59:23 -0300 (Tue, 24 Jul 2012) $
 */
public final class FullyBoundNullaryProcedure implements NullaryProcedure, Serializable {
    /**
     * serialVersionUID declaration.
     */
    private static final long serialVersionUID = -904891610081737737L;
    /** The {@link BinaryProcedure BinaryProcedure} I'm wrapping. */
    private final BinaryProcedure<Object, Object> procedure;
    /** The left parameter to pass to {@code procedure}. */
    private final Object left;
    /** The right parameter to pass to {@code procedure}. */
    private final Object right;

    /**
     * Create a new FullyBoundNullaryProcedure instance.
     * @param <L> left type
     * @param <R> right type
     * @param procedure the procedure to adapt
     * @param left the left argument to use
     * @param right the right argument to use
     */
    @SuppressWarnings("unchecked")
    public <L, R> FullyBoundNullaryProcedure(BinaryProcedure<? super L, ? super R> procedure, L left, R right) {
        this.procedure =
            (BinaryProcedure<Object, Object>) Validate.notNull(procedure,
                "BinaryProcedure argument was null");
        this.left = left;
        this.right = right;
    }

    /**
     * {@inheritDoc}
     */
    public void run() {
        procedure.run(left, right);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object that) {
        return that == this || (that instanceof FullyBoundNullaryProcedure
                && equals((FullyBoundNullaryProcedure) that));
    }

    /**
     * Learn whether another FullyBoundNullaryProcedure is equal to this.
     * @param that FullyBoundNullaryProcedure to test
     * @return boolean
     */
    public boolean equals(FullyBoundNullaryProcedure that) {
        return null != that && procedure.equals(that.procedure)
                && (null == left ? null == that.left : left.equals(that.left))
                && (null == right ? null == that.right : right.equals(that.right));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        int hash = "FullyBoundNullaryProcedure".hashCode();
        hash <<= 2;
        hash ^= procedure.hashCode();
        hash <<= 2;
        if (null != left) {
            hash ^= left.hashCode();
        }
        hash <<= 2;
        if (null != right) {
            hash ^= right.hashCode();
        }
        return hash;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "FullyBoundNullaryProcedure<" + procedure + "(" + left + ", " + right + ")>";
    }

    /**
     * Adapt a BinaryNullaryProcedure to the NullaryProcedure interface.
     * @param <L> left type
     * @param <R> right type
     * @param procedure to adapt
     * @param left left side argument
     * @param right right side argument
     * @return FullyBoundNullaryProcedure
     */
    public static <L, R> FullyBoundNullaryProcedure bind(
            BinaryProcedure<? super L, ? super R> procedure, L left, R right) {
        return null == procedure ? null : new FullyBoundNullaryProcedure(procedure, left, right);
    }

}
