/*
 * @(#)Pair.java    2.3  5 December 2003
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.quick;

import java.util.*;

/**
 * <P><CODE>Pair</CODE> constructs an immutable pair object from two
 * non-<CODE>null</CODE> objects that are referred to as A and B.</P>
 *
 * <P>The methods <CODE>equals</CODE> and <CODE>hashCode</CODE> are
 * designed so that two <CODE>Pair</CODE> objects whose contents are
 * equal will be considered equal and will return the same hash code.</P>
 *
 * @author  Richard Rasala
 * @version 2.3
 * @since   2.3
 */
public final class Pair {
    
    /** Item A of the pair. */
    private Object a = null;
    
    /** Item B of the pair. */
    private Object b = null;
    
    
    /**
     * <P>Constructor that constructs a pair using
     * two non-<CODE>null</CODE> <CODE>Object</CODE>s.</P>
     *
     * @param  a the A object for the pair
     * @param  b the B object for the pair
     * @throws <CODE>NullPointerException</CODE> if either data item is
     *         <CODE>null</CODE>
     */
    public Pair(Object a, Object b) {
        if ((a == null) || (b == null))
            throw new NullPointerException
                ("null data item in Pair(Object, Object) constructor");
        
        this.a = a;
        this.b = b;
    }
    
    
    /**
     * <P>Constructor that constructs a pair using
     * a non-<CODE>null</CODE> array of <CODE>Object</CODE>
     * such that the array has 2 non-<CODE>null</CODE> items.</P>
     *
     * @param  data the array with exactly two non-<CODE>null</CODE> items
     * @throws <CODE>NullPointerException</CODE>
     *         if data is <CODE>null</CODE>
     *         or if an array item in data is <CODE>null</CODE>
     * @throws <CODE>IllegalArgumentException</CODE>
     *         if data does not have length 2
     */
    public Pair(Object[] data) {
        if (data == null)
            throw new NullPointerException
                ("null data array in Pair(Object[]) constructor");
        
        if (data.length != 2)
            throw new IllegalArgumentException
                ("data length != 2 in Pair(Object[]) constructor");
        
        if ((data[0] == null) || (data[1] == null))
            throw new NullPointerException
                ("null data item in Pair(Object[]) constructor");
        
        a = data[0];
        b = data[1];
    }
    
    
    /**
     * Returns the A object in the pair.
     *
     * @return object A in the pair
     */
    public Object getA() { return a; }
    
    
    /**
     * Returns the B object in the pair.
     *
     * @return object B in the pair
     */
    public Object getB() { return b; }
    
    
    /**
     * <P>If the given entity is an instance of <CODE>Pair</CODE> and if the
     * corresponding items in this pair and the given entity are equal,
     * then returns <CODE>true</CODE>; otherwise returns <CODE>false</CODE>.</P>
     *
     * @param  entity the entity to compare to this pair
     * @return true if the given entity is a pair with equal items to those in
     *         this pair
     */
    public boolean equals(Object entity) {
        if (!(entity instanceof Pair))
            return false;
        
        Pair pair = (Pair) entity;
        
        return (a.equals(pair.getA())) && (b.equals(pair.getB()));
    }
    
    
    /**
     * <P>Returns a hash code value for this pair that is computed as the
     * exclusive OR of the hash code values for the two items in the pair.</P>
     *
     * @return the hash code
     */
    public int hashCode() {
        return a.hashCode() ^ b.hashCode();
    }
    
    
    /**
     * <P>Returns the <CODE>String</CODE> constructed as:
     * <CODE>(astring, bstring)</CODE>,
     * where <CODE>astring</CODE> is the <CODE>toString</CODE> for item A
     * and   <CODE>bstring</CODE> is the <CODE>toString</CODE> for item B.</P>
     *
     * @return the string formed as (A, B) from the items A, B in the pair
     */
    public String toString() {
        return "(" + a.toString() + ", " + b.toString() + ")";
    }
    
    
    /**
     * <P>Returns the contents of the pair as an array of <CODE>Object</CODE>
     * of length 2.</P>
     *
     * @return the pair as an array
     */
    public Object[] toArray() {
        return new Object[] { a, b };
    }
    
    
    /**
     * <P>Factory method that returns a <CODE>Pair</CODE> if the two objects
     * are non-<CODE>null</CODE>; otherwise returns <CODE>null</CODE>.</P>
     *
     * <P>Does not throw any exceptions.</P>
     *
     * @param  a the A object for the pair
     * @param  b the B object for the pair
     */
    public static Pair makePair(Object a, Object b) {
        if ((a == null) || (b == null))
            return null;
        
        return new Pair(a, b);
    }
    
    
    /**
     * <P>Factory method that returns a <CODE>Pair</CODE> if the data array is
     * non-<CODE>null</CODE> and has exactly two non-<CODE>null</CODE> items;
     * otherwise returns <CODE>null</CODE>.</P>
     *
     * <P>Does not throw any exceptions.</P>
     *
     * @param data the data with which to form the pair if possible
     * @see #Pair(Object[])
     */
    public static Pair makePair(Object[] data) {
        try {
            return new Pair(data);
        }
        catch (RuntimeException e) {
            return null;
        }
    }
    
    
    /**
     * <P>Returns the contents of the given <CODE>Pair[]</CODE> array as an
     * <CODE>Object[][]</CODE> array.</P>
     *
     * @param  pairs the 1-dimensional pair array
     * @return the 2-dimensional object array
     */
    public static Object[][] toArray(Pair[] pairs) {
        if (pairs == null)
            return null;
        
        int length = pairs.length;
        
        Object[][] array = new Object[length][];
        
        for (int i = 0; i < length; i++)
            array[i] = (pairs[i] == null)
                ? null
                : pairs[i].toArray();
        
        return array;
    }
    
    
    /**
     * <P>Returns the contents of the given <CODE>Object[][]</CODE> array as
     * a <CODE>Pair[]</CODE> array.</P>
     *
     * <P>Uses <CODE>makePair(Object[])</CODE> to make the individual items
     * in the returned <CODE>Pair[]</CODE> array.  In this way, items in
     * the original array that cannot make a suitable pair will map to
     * <CODE>null</CODE>.</P>
     *
     * @param  array the 2-dimensional object array
     * @return the 1-dimensional pair array
     */
    public static Pair[] toArray(Object[][] array) {
        if (array == null)
            return null;
        
        int length = array.length;
        
        Pair[] pairs = new Pair[length];
        
        for (int i = 0; i < length; i++)
            pairs[i] = makePair(array[i]);
        
        return pairs;
    }
    
    
    /**
     * Returns the array of A-objects from this array of pairs.
     *
     * @param  pairs the 1-dimensional pair array
     * @return the array of A-objects
     */
    public static Object[] getAObjects(Pair[] pairs) {
        if (pairs == null)
            return null;
        
        int length = pairs.length;
        
        Object[] array = new Object[length];
        
        for (int i = 0; i < length; i++)
            array[i] = (pairs[i] == null)
                ? null
                : pairs[i].getA();
        
        return array;
    }
    
    
    /**
     * Returns the array of B-objects from this array of pairs.
     *
     * @param  pairs the 1-dimensional pair array
     * @return the array of B-objects
     */
    public static Object[] getBObjects(Pair[] pairs) {
        if (pairs == null)
            return null;
        
        int length = pairs.length;
        
        Object[] array = new Object[length];
        
        for (int i = 0; i < length; i++)
            array[i] = (pairs[i] == null)
                ? null
                : pairs[i].getB();
        
        return array;
    }
    
}
