// The Science of Computing List class.

// Copyright (C) 2004. Charles River Media. All rights reserved.

// History:
//   August 2003 -- Created by Greg Scragg.
//   October 2003 -- updates to several methods by GWS
//   February 2004 -- updated references and variable names to correspond to text changes
//   March 2004 -- Minor code cleanup and correction of typos by Doug Baldwin.


package geneseo.cs.sc;

import java.io.*;

/**
 * Represents lists of arbitrary objects. This class reflects a recursive
 *  definition of "list", specifically a definition that says that a list is either
 *  <ul>
 *    <li>Empty, or</li>
 *    <li>An object (the list's head) followed by another list (the list's
 *      tail)</li>
 *  </ul> 
 * These lists handle the common messages needed for any list class. Some
 * messages are destructive (i.e., cause a list to change itself), and therefore
 * do not require assignment within the client code when modifying lists.
 * <p>This class was created to support the text <cite>Algorithms & Data Structures:
 * The Science of Computing</cite> by Doug Baldwin and Greg Scragg. All references herein
 * to &quot;the text&quot; refer to that book. This class is the one described in
 * Chapter 11, with a few extensions and with some methods completed that were &quot;left
 * to an exercise&quot; in the text.</p>
 */
 
public class List implements Serializable  {
    
    
    private Object head;
    private List tail; 
        

    //constructors 

    /**
     * Initialize a list to be empty. For example
     * <p><code>List things = new List();</code></p>
     */
 
    public List() {
        head = null;
        tail = null;
    }
    

    /**
     * Initialize a List to contain one element. For example
     *  <p><code>List words = new List( "lion" );</code></p>
     * @param newItem The single item the List will initially contain.
     */
    
    public List(Object newItem) {
        head = newItem;
        tail = makeNewList();
    }

    
    /**
     * Create a new list that is an instance of the same class as this list.
     * This message is used, for example, when adding an item to a list requires
     * creating a new tail for that list. The new tail should be an instance of
     * the same class as the list itself, even if that list is an instance of
     * a subclass of <code>List</code>. Every subclass of <code>List</code>
     * must therefore provide a method for handling this message that returns
     * a new instance of that subclass. Clients are unlikely to ever invoke
     * this method directly, but it is essential to the correct functioning of
     * other methods defined for lists.
     * @return A new, empty, list, of the same class as the one that received
     *  the <code>makeNewList</code> message
     */
    
    public  List makeNewList() {
        return new List();
    }
    

    //                                   Basic public functions

    /**
     * Extracts the head of a list. The list itself is left unchanged.
     *   For example
     *   <p><code>Object obj = myList.getFirst();</code></p>
     * @return The head of the list. 
     */

    public Object getFirst() {
        return this.head;
    }


    /**
     * Extracts the tail of a list. The list itself is left unchanged.
     *   For example
     *   <p><code>List theRest = myList.getRest();</code></p>
     * @return The tail of the list. 
     */

    public List getRest() {
        return this.tail;
    }
    
    
    /**
     * Determines whether a list is empty or not. For example
     *   <p><code>if ( myList.isEmpty() ) ...</code></p>
     * @return True if the list is empty; false otherwise.
     */

    public boolean isEmpty() {
        return (this.head  == null && this.tail == null);
    }
    

    /**
     * Removes a list's head. This alters the list, but does not return
     *   the item removed. For example
     *   <p><code>myList.removeItem();</code></p>
     */
    
    public void removeItem() {
        this.head = this.tail.head;
        this.tail = this.tail.tail;
    }
    

    /**
     * Removes a list's first item and returns it. For example
     *   <p><code>Object oldHead = myList.getAndRemove();</code></p>
     * Note that this is both a value and side effect producing operation.
     * @return The item removed from the list (which was the list's head).  
     */

    public Object getAndRemove() {
        Object temp = this.head;
        this.head = this.tail.head;
        this.tail = this.tail.tail;
        return temp;
    }
    

    /** 
     * Adds an object to a list. The object becomes the new head of the list,
     *   while all objects previously in the list effectively move one place
     *   down it. For example
     *   <p><code>words.addItem( "wombat" );</code></p>
     * @param newItem The object to be added to the list.
     */

    public void addItem(Object newItem) {
        List temp = makeNewList();
        temp.head = this.head;
        temp.tail = this.tail;
        this.head = newItem;
        this.tail = temp;
    }
    

    /** 
     * Replaces the head of a list with a new object. For example
     *   <p><code>words.setFirstItem( "aardvark" );</code></p> 
     * @param value The object that will become the new head of the list.
     */

    protected void setFirstItem(Object value) {
        this.head = value;
    }
     

    /** 
     * Replaces the tail of a list with a new list. For example
     *   <p><code>myList.setRest( new List() );</code></p>
     * Note that the replacement must be an entire list, not just a new element.
     * @param newTail The new tail for the list.
     */
 
    protected void setRest (List newTail) {
        this.tail = newTail;
    }
     

    // Standard traversal methods 

    /**
     * Concatenates two lists. All the items in the parameter list are added to
     *   the end of the list that receives the <code>concat</code> message.
     *   For example
     *   <p><code>myList.concat( new List("more") );</code></p>
     * @param extraList The list to be added to the end of the current list.
     */

    public void concat(List extraList) {
        if ( this.isEmpty() ) {
            this.head = extraList.head;
            this.tail = extraList.tail;
        }
        else if(this.tail.isEmpty())
            this.tail = extraList;
        else this.tail.concat(extraList);
    }
     

    /**
     * Print the elements in a list from first to last. Prints elements from the
     *   front (head) of the list to the back, which, in effect, prints the last 
     *   element entered first. For example
     *     <p><code>myList.printList();</code></p>
     *   Since this is intended for student use, it provides a marker at the end of the
     *   list to aid with testing and debugging.
     */

    public void printList() {
        if (!this.isEmpty())  {
            System.out.println(this.getFirst());
            this.getRest().printList();
        }
        else System.out.println("--");
    }
     

    /**
     * Print the elements in a list from last to first. Prints elements from the
     *   back of the list to front (head), which, in effect, prints the first
     *   element entered first. For example
     *   <p><code>myList.printListForward();</code></p>
     *   Since this is intended for student use, it provides a marker at the end of the
     *   list to aid with testing and debugging.
     */
     
    public void printListForward() {
        if (!this.isEmpty())  {
            this.getRest().printListForward();
            System.out.println(this.getFirst());
        }
        else System.out.println("--");
    }
    

    /**
     * Search a list for an object. Uses <code>equals</code> to compare
     * objects. For example
     *   <p><code>if ( words.find("capybara") ) ...</code></p>
     * @param target The object to look for in the list.
     * @return True if one of the elements of the list is equal to the
     *   target, false otherwise.
     */
 
    public boolean find(Object target) {
        if (this.isEmpty())
            return false;
        else if(this.getFirst().equals(target))
            return true;    
        else return this.getRest().find(target);
    }


    /**
     * Remove an object from a list. This removes the first occurrence of the
     *   requested object, altering the list in doing so. Uses <code>equals</code>
     *   to compare the object to elements of the list. For example
     *   <p><code>words.delete("monkey");</code></p>
     * @param value The object to remove from the list.
     */

    public void delete(Object value) {
        if(!this.isEmpty())
            if(this.getFirst().equals(value))
                this.removeItem();
            else this.getRest().delete(value);
    }


    /**
     * Create a duplicate of a list. For example
     *   <p><code>List savedList = myList.copy();</code></p>
     * @return The duplicate of the list.
     */
 
    public List copy() {
        if(this.isEmpty()) 
            return  new List();
        else {
            List temp =  this.getRest().copy();
            temp.addItem(this.getFirst());
            return (temp);
        }
    }
    

    /**
     * Write a List to a file, replacing any previous data in that file.
     *   For example
     *   <p><code>myList.save( "ListFile" );</code></p>
     * @param fileName The name of the file to write the list to.
     */
    
    // This method opens a stream to the file, writes this list to the stream, and
    // closes the stream. If anything goes wrong while doing these things, this method
    // prints an error message but doesn't signal its client that anything has failed.
    
    public void save( String fileName ) {

        try {

            ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream( fileName ) );
            out.writeObject( this );
            out.close();
        }
        catch ( Exception error ) {
            System.err.println( "Unable to save list: " + error );
        }
    } 
    
    
    
    
    /**
     * Restore a list from a file. For example
     *   <p><code>myList.restore( "ListFile" );</code></p>
     * After a list handles this message, the list's contents are
     * replaced by the contents of the file, if possible. If for some
     * reason the file couldn't be read, the contents of the list are
     * unchanged.
     * @param fileName The name of the file to restore the list from.
     */

    // This opens an input stream and reads a list from that stream. If the read succeeds,
    // this then copies the new lists's members into the list executing this method, thus
    // changing that list into the one read. If anything goes wrong in reading, this prints
    // an error message, but doesn't let client code know there was an error.
    
    public void restore( String fileName ) {

        try {

            ObjectInputStream in = new ObjectInputStream( new FileInputStream( fileName ) );
            List retrieved = (List) in.readObject();
            in.close();

            head = retrieved.head;
            tail  = retrieved.tail;
        }
        catch ( Exception error ) {
            System.err.println( "Unable to restore a list: " + error );
        }
    } 
    
    
    /**
     * Generate a string representation of a list. For example
     *   <p><code>String text = myList.toString();</code></p>
     * @return The string representation of the list.
     */

    // This generates the string recursively, using the insight that an empty
    // list can be represented by the string "< >", while a non-empty
    // one can be represented by the head, and the tail
    //   enclosed in "<" and ">".
    
    public String toString() {
        if ( this.isEmpty() ) {
            return "<>";
        }
        else {
         return "<" +  head  +  " "  +  tail  +  " >";
        }
    } 

}