//<PLAINTEXT>
import java.util.*;

/**
 * LinkdList.java<br>
 * Illustrates the implementation of a linked list with a null object.<br>
 * Based on <a
 * href="http://java.sun.com/j2se/1.4/docs/api/java/util/LinkedList.html">Linked List</a><p>  
 *
 * Created: Mon Feb 24 21:26:57 2003
 *
 * @author <a href="mailto:shapiro@cse.buffalo.edu">Stuart C. Shapiro</a><br>
 * Documentation, in part, from <a
 * href="http://java.sun.com/j2se/1.4/docs/api/java/util/LinkedList.html">Linked List</a>
 */

public class LinkdList {
    protected Object first;
    protected LinkdList rest;
    protected boolean empty;

    /**
     * Constructs an empty <code>LinkdList</code>.
     */
    public LinkdList (){
	empty = true;
    }
    
    /**
     * Constructs a <code>LinkdList</code> consisting of an <code>Object</code>
     * prepended to another list.
     *
     * @param f the <code>Object</code> to be the first element of
     * this list.
     * @param list a <code>LinkdList</code> to be the
     * <code>rest</code> of this list.
     */
    public LinkdList(Object f, LinkdList list) {
	first = f;
	rest = list;
	empty = false;
    }

    /**
     * Returns the first element in this list.
     *
     * @return the first element in this list.
     * @exception NoSuchElementException if this list is empty.
     */
    public Object getFirst() {
	if (isEmpty()) {
	    throw new NoSuchElementException();
	} // end of if (isEmpty())
	return first;
    }

    /**
     * Returns this list omitting the first element.
     *
     * @return this list omitting the first element.
     * @exception NoSuchElementException if this list is empty.
     */
    public LinkdList getRest() {
	if (isEmpty()) {
	    throw new NoSuchElementException();
	} // end of if (isEmpty())
	return rest;
    }

    /**
     * Returns true if this list contains no elements
     *
     * @return true if this list contains no elements
     */
    public boolean isEmpty() {
	return empty;
    }
    
    /**
     * Returns the element at the specified position in this list,
     * the first element having index == 1.
     *
     * @param index index of element to return.
     * @return the element at the specified position in this list.
     * @exception IndexOutOfBoundsException if the specified index is
     * is out of range (index < 1 || index > size()).
     */
    public Object get(int index) throws IndexOutOfBoundsException {
	if (index < 1) {
	    throw new IndexOutOfBoundsException();
	} // end of if (index < 1)
	if (isEmpty()) {
	    throw new IndexOutOfBoundsException();
	} // end of if (isEmpty)
	if (index == 1) {
	    return first;
	} // end of if (index == 1)
	return rest.get(index-1);	
    }

    /**
     * Returns the number of elements in this list.
     *
     * @return the number of elements in this list.
     */
    public int size() {
	if (isEmpty()) return 0;
	return 1 + rest.size();
    }

    /**
     * Appends the specified element to the end of this list.
     *
     * @param o element to be appended to this list.
     * @return true (as per the general contract of Collection.add).
     */
    public boolean add(Object o) {
	if (isEmpty()) {
	    first = o;
	    rest = new LinkdList();
	    empty = false;
	} // end of if (isEmpty())
	else {
	    rest.add(o);
	} // end of else
	return true;
    }

    /**
     * Removes the element at the specified position in this list. Shifts
     * any subsequent elements to the left (subtracts one from their
     * indices). Returns the element that was removed from the list.
     *
     * @param index the index of the element to removed.
     * @return the element previously at the specified position.
     * @exception IndexOutOfBoundsException if the specified index is
     * out of range (index < 1 || index > size()). 
     */
    public Object remove(int index) throws IndexOutOfBoundsException {
	if (index < 1) {
	    throw new IndexOutOfBoundsException();
	} // end of if (index < 1)
	if (isEmpty()) {
	    throw new IndexOutOfBoundsException();
	} // end of if (isEmpty())
	if (index == 1) {
	    Object result = first;
	    if (empty = rest.isEmpty()) {
		first = null;
		rest = null;
	    } else {
		first = rest.getFirst();
		rest = rest.getRest();
	    } // end of else
	    return result;
	} // end of if (index == 1)
	return rest.remove(index-1);
    }

    /**
     * Returns a <code>LinkdList</code> just like this one, but with
     * <code>obj</code> inserted as the new
     * <code>ndx</code><sup>th</sup> element.
     *
     * @param obj the <code>Object</code> to be inserted.
     * @param ndx the new position for <code>obj</code>.
     * @return a <code>LinkdList</code> just like this one, but with
     * <code>obj</code> inserted as the new
     * <code>ndx</code><sup>th</sup> element.
     * @exception IndexOutOfBoundsException if <code>ndx < 1 ||
     * ndx > this.size()+1</code>.
     */
    public LinkdList insert (Object obj, int ndx) {
	if (ndx < 1) {throw new IndexOutOfBoundsException();}
	if (ndx == 1) {return new LinkdList(obj, this);}
	if (isEmpty()) {throw new IndexOutOfBoundsException();}
	rest = rest.insert(obj, ndx-1);
	return this;
    }

    /**
     * Returns a string representing the elements of this list
     * separated by commas.
     *
     * @return a string representing the elements of this list.
     */
    private String interiorToString() {
	if (isEmpty()) {
	    return "";
	} // end of if (isEmpty())
	    if (rest.isEmpty()) {
		return "" + first;
	    } // end of if (rest.isEmpty())
	return "" + first + ", " + rest.interiorToString();
    }

    /**
     * Returns a string representing this list
     * as its elements, separated by commas, surrounded by parentheses.
     *
     * @return a string representing the elements of this list.
     */
    public String toString() {
	return "(" + interiorToString() + ")";
    }
	
}// LinkdList
