/*
 * @(#)StringObjectPair.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>StringObjectPair</CODE> constructs an immutable pair object from
 * a non-<CODE>null</CODE> <CODE>String</CODE> and
 * a non-<CODE>null</CODE> <CODE>Object</CODE>.</P>
 *
 * <P>The methods <CODE>equals</CODE> and <CODE>hashCode</CODE> are designed
 * so that two <CODE>StringObjectPair</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 StringObjectPair {
    
    /** The String item of the pair. */
    private String string = null;
    
    /** The Object item of the pair. */
    private Object object = null;
    
    
    /**
     * <P>Constructor that constructs a pair using
     * a non-<CODE>null</CODE> <CODE>String</CODE> and
     * a non-<CODE>null</CODE> <CODE>Object</CODE>.</P>
     *
     * @param  string the string for the pair
     * @param  object the object for the pair
     * @throws <CODE>NullPointerException</CODE> if either data item is
     *         <CODE>null</CODE>
     */
    public StringObjectPair(String string, Object object) {
        if ((string == null) || (object == null))
            throw new NullPointerException
                ("null data item in StringObjectPair(String, Object) constructor");
        
        this.string = string;
        this.object = object;
    }
    
    
    /**
     * <P>Constructor that constructs a pair using
     * a non-<CODE>null</CODE> array of <CODE>Object</CODE>
     * such that the array has 2 items,
     * item 0 is a non-<CODE>null</CODE> <CODE>String</CODE> and
     * item 1 is a non-<CODE>null</CODE> <CODE>Object</CODE>.</P>
     *
     * @param  data the data array
     * @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
     *         or if data[0] is not of type <CODE>String</CODE>
     */
    public StringObjectPair(Object[] data) {
        if (data == null)
            throw new NullPointerException
                ("null data array in StringObjectPair(Object[]) constructor");
        
        if (data.length != 2)
            throw new IllegalArgumentException
                ("data length != 2 in StringObjectPair(Object[]) constructor");
        
        if ((data[0] == null) || (data[1] == null))
            throw new NullPointerException
                ("null data item in StringObjectPair(Object[]) constructor");
        
        if (! (data[0] instanceof String))
            throw new IllegalArgumentException
                ("data[0] is not String in StringObjectPair(Object[]) constructor");
        
        string = (String) data[0];
        object = data[1];
    }
    
    
    /**
     * Returns the string in the pair.
     *
     * @return object string in the pair
     */
    public String getString() { return string; }
    
    
    /**
     * Returns the object in the pair.
     *
     * @return object in the pair
     */
    public Object getObject() { return object; }
    
    
    /**
     * <P>If the given entity is an instance of <CODE>StringObjectPair</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 string-object pair with equal items
     *         to those in this string-object pair
     */
    public boolean equals(Object entity) {
        if (!(entity instanceof StringObjectPair))
            return false;
        
        StringObjectPair pair = (StringObjectPair) entity;
        
        return (string.equals(pair.getString()))
            && (object.equals(pair.getObject()));
    }
    
    
    /**
     * <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 string.hashCode() ^ object.hashCode();
    }
    
    
    /**
     * <P>Returns the <CODE>String</CODE> constructed as:
     * <CODE>(getString(), getObject().toString())</CODE>.</P>
     *
     * @return the string formed conceptually as (string, object)
     */
    public String toString() {
        return "(" + string + ", " + object.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[] { string, object };
    }
    
    
    /**
     * <P>Factory method that returns a <CODE>StringObjectPair</CODE> if the two
     * objects are non-<CODE>null</CODE> and the first is a <CODE>String</CODE>;
     * otherwise returns <CODE>null</CODE>.</P>
     *
     * <P>Does not throw any exceptions.</P>
     *
     * @param  a the string for the pair
     * @param  b the object for the pair
     */
    public static StringObjectPair makeStringObjectPair(Object a, Object b) {
        if ((a == null) || (b == null))
            return null;
        
        if (! (a instanceof String))
            return null;
        
        return new StringObjectPair((String) a, b);
    }
    
    
    /**
     * <P>Factory method that returns a <CODE>StringObjectPair</CODE> if the data
     * array is non-<CODE>null</CODE>, has exactly two non-<CODE>null</CODE> items,
     * and the first item is a <CODE>String</CODE>;
     * 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 #StringObjectPair(Object[])
     */
    public static StringObjectPair makeStringObjectPair(Object[] data) {
        try {
            return new StringObjectPair(data);
        }
        catch (RuntimeException e) {
            return null;
        }
    }
    
    
    /**
     * <P>Returns the contents of the given <CODE>StringObjectPair[]</CODE> array
     * as an <CODE>Object[][]</CODE> array.</P>
     *
     * @param  the 1-dimensional pair array
     * @return the 2-dimensional object array
     */
    public static Object[][] toArray(StringObjectPair[] 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>StringObjectPair[]</CODE> array.</P>
     *
     * <P>Uses <CODE>makeStringObjectPair(Object[])</CODE> to make the individual
     * items in the returned <CODE>StringObjectPair[]</CODE> array.  In this way,
     * items in the original array that cannot make a suitable pair will map to
     * <CODE>null</CODE>.</P>
     *
     * @param  the 2-dimensional object array
     * @return the 1-dimensional pair array
     */
    public static StringObjectPair[] toArray(Object[][] array) {
        if (array == null)
            return null;
        
        int length = array.length;
        
        StringObjectPair[] pairs = new StringObjectPair[length];
        
        for (int i = 0; i < length; i++)
            pairs[i] = makeStringObjectPair(array[i]);
        
        return pairs;
    }
    
    
    /**
     * Returns the array of strings from this array of pairs.
     *
     * @param  pairs the 1-dimensional pair array
     * @return the array of strings
     */
    public static String[] getStrings(StringObjectPair[] pairs) {
        if (pairs == null)
            return null;
        
        int length = pairs.length;
        
        String[] array = new String[length];
        
        for (int i = 0; i < length; i++)
            array[i] = (pairs[i] == null)
                ? null
                : pairs[i].getString();
        
        return array;
    }
    
    
    /**
     * Returns the array of objects from this array of pairs.
     *
     * @param  pairs the 1-dimensional pair array
     * @return the array of objects
     */
    public static Object[] getObjects(StringObjectPair[] 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].getObject();
        
        return array;
    }
    
}
