Please send questions to st10@humboldt.edu .
/* Cs132LinkedList.java 1.1                               */
/* by Sharon Tuttle                                       */
/* last modified: 3-7-03                                  */
/* originally from Georgia Tech's CS 1322 Module 27 notes */
/*    on Linked Lists, Stacks, and Queues,                */
/*    with influences from course text, "Java Structures",*/
/*    pp. 168-189.                                        */
/*                                                        */
/* A concrete subclass of Cs132AbstractList,              */
/* using a linked list implementation                     */

public class Cs132LinkedList extends Cs132AbstractList
{
    /*------------------------------------
     fields
    -------------------------------------*/

    // beginning of linked list
    private Cs132ListNode head;

    // number of items currently in linked list
    private int count;

    /*--------------------------------------
     constructors
    ---------------------------------------*/

    // creates an empty linked list
    public Cs132LinkedList()
    {
        this.head = null;
        this.count = 0;
    }
    
    /*-------------------------------------------------------
     accessors
    -------------------------------------------------------*/ 

    public Cs132ListNode getHead()
    {
        return this.head;
    }
   
    /*-------------------------------------------------------
     modifiers
    -------------------------------------------------------*/ 
    public void setHead(Cs132ListNode newHead)
    {
        this.head = newHead;
    }

    /*------------------------------------------
     overridden methods
    -------------------------------------------*/
    public String toString()
    {
        return "Cs132LinkedList[size = " + this.count + ", elements are:\n"
               + toStringHelper(this.head)
               + "]";
    }

    /*-------------------------------------------------------
     toStringHelper
     Purpose: auxiliary function for toString(), to help it print all
              of the elements in a LinkedList
     (strictly for local use...!)
    ---------------------------------------------------------*/
    private String toStringHelper(Cs132ListNode currNode)
    {
        // BASE CASE: if node is null, nothing to print --- 
        // time to stop, return an empty String.
        if (currNode == null)
        {
            return "";
        }

        // else, RECURSIVE CASE: create string for current
        // node, concatenating the strings for the rest of the
        // nodes...
        else
        {
            return "" + currNode.getData() + "\n" +
                   toStringHelper(currNode.getNext());
        }
    }
        
    /*---------------------------------------
     other methods
    ----------------------------------------*/
    
    /*-----------------------------------------------------------
     isEmpty()
     Purpose: return true if this object contains no nodes, return
              false otherwise.
    -----------------------------------------------------------------*/
    public boolean isEmpty()
    {
        return this.count == 0;
    }
    
    /*--------------------------------------------------------
     size
     Purpose: return current size of the list
     
     (why not make this an accessor? Well, I'd like to hide
     the implementation detail count --- but, whether I
     store it or not, finding the size of a list is a useful
     piece of info for many lists.)
    ----------------------------------------------------------*/
    public int size()
    {
        return this.count;
    }

    /*-------------------------------------------------------
     addFirst
     Purpose: add given data newObj to beginning
              of list.
    ---------------------------------------------------------*/
    public void addFirst(Object newObj)
    {
        // here's a list node to hold the new data
        Cs132ListNode newNode = new Cs132ListNode(newObj);

        // new list node should be set to point to 
        // current first element in list...
        // (what if list is currently empty? that's OK,
        // head is null, and new node's next field is 
        // simply reset to null, which is just what we 
        // want for a 1-element list...
        newNode.setNext(this.head);

        // now, newNode can be NEW first element in list!
        this.head = newNode;

        // and don't forget to increment the count!
        this.count = this.count + 1;
    }
    
    /*-------------------------------------------------------
     addLast()
     Purpose: add given data newObj to end
              of list.
    ---------------------------------------------------------*/
    public void addLast(Object newObj)
    {
        // here's a list node to hold the new data
        Cs132ListNode newNode = new Cs132ListNode(newObj);

        // what if list is empty? then first is last, too!
        if (this.head == null)
        {
            this.head = newNode;
        }
        
        // but, more likely, you'll need to "walk" to the end...
        else
        {
            Cs132ListNode currNode = this.head;
            
            // keep "walking" down the list until you reach the last
            // node; how do you know? When the node you are looking
            // at has NO "next" node!
            while (currNode.getNext() != null)
            {
                // make the NEXT node the next node to consider...
                currNode = currNode.getNext();
            }
            
            // you KNOW currNode is the last node, now!
            // ... and so it is the one leading to our new node-to-add
            currNode.setNext(newNode);
        }
        
        // in either case, don't forget to increase list size!
        this.count = this.count + 1;
    }
   
    /*----------------------------------------------------------------
     add()
     Purpose: add given data newElement to list at position index (where
              0 is the index of the first item in the list, 1 is the
              index of the 2nd item in the list, etc.)
              Throw a ListIndexOutOfBoundsException if the list
              index is not between 0 and the size of the array
              (I *could* add a new element to the end --- thus "size"
              instead of the usual "size - 1").
              BUT note --- those elements "after" this position ARE
              shifted "up" a position!
    -----------------------------------------------------------------*/
    public void add(int index, Object newElement) throws 
        ListIndexOutOfBoundsException
    {
        // complain if trying to add an element to an illegal position
        if ((index < 0) || (index > this.size()))
        {
            throw new ListIndexOutOfBoundsException(index);
        }
        
        // if get here, index is reasonable.
        Cs132ListNode previous = null;
        Cs132ListNode current = this.head;
        Cs132ListNode newNode = new Cs132ListNode(newElement);
        
        // what if this item goes on the beginning of the list?
        if (index == 0)
        {
            newNode.setNext(this.head);
            this.head = newNode;
        }
        else
        {
            // find out where the element SHOULD go
            int temp = 0;
            
            while (temp != index)
            {
                previous = current;
                current = current.getNext();
                temp = temp + 1;
            }
            
            // NOW we should be where the element goes...
            previous.setNext(newNode);
            newNode.setNext(current);
        }
        this.count = this.count + 1;
    }

    /*----------------------------------------------------------------
     getFirst()
     Purpose: IF list is not empty, return FIRST element in list.
              (this is its VALUE, not the node itself!)
              If it IS empty, throw a ListEmptyException
    -----------------------------------------------------------------*/
    public Object getFirst() throws ListEmptyException
    {
        if (this.size() == 0)
        {
            throw new ListEmptyException();
        }
        
        // if reach here, there IS something to get!
        return this.head.getData();
    }
    
    /*----------------------------------------------------------------
     getLast()
     Purpose: IF the list is not empty, return the LAST element in list.
              (this is its VALUE, not the node itself!)
              If it IS empty, throw a ListEmptyException
    ----------------------------------------------------------------------*/
    public Object getLast() throws ListEmptyException
    {
        if (this.size() == 0)
        {
            throw new ListEmptyException();
        }
    
        // if reach here, there IS something in the list to get
    
        // start by looking at beginning of list
        Cs132ListNode currNode = this.head;
        
        // "walk" through list until reach last element
        while (currNode.getNext() != null)
        {
            currNode = currNode.getNext();
        }
        
        // when we get here, currNode IS the last node.
        // return its value.
        return currNode.getData();
     }
     
    /*----------------------------------------------------------------
     get()
     Purpose: return the element at position index in the list
              (where 0 is the index of the first item in the list).
              If the list is empty, throw a ListEmptyException.
              If the index is not between 0 and (list's size - 1),
              throw a ListIndexOutOfBoundsException.
    ----------------------------------------------------------------------*/
    public Object get(int index) throws ListEmptyException,
        ListIndexOutOfBoundsException
    {
        // is list empty?
        if (this.size() == 0)
        {
            throw new ListEmptyException();
        }
        
        // list is NOT empty, so now
        // make sure that index is reasonable --- if not, complain
        if ((index < 0) || (index >= this.count)) 
        {
            throw new ListIndexOutOfBoundsException(index);
        }

        // index must be reasonable, if reach here ---
        // proceed to that list element        
        else
        {
            Cs132ListNode currNode = this.head;

            for (int i=0; i < index; i++) 
            {
                currNode = currNode.getNext();
            }
            
            // so, i==index now... this is the desired position
            return currNode.getData();
         }
    }   
    
    /*----------------------------------------------------------------
     remove()
     Purpose: This seeks to remove the element at position index
              of the list (where 0 is the index of
              the first element in the list). It will do so and return
              the object removed if the index is indeed a position
              in the list. (elements "above" in this list are now
              one position "lower")
    
              If the list is empty, however, it will
              throw a ListEmptyException, and if the index is not
              a position in the list, it will throw a 
              ListIndexOutOfBoundsException.
    --------------------------------------------------------------------*/
    public Object remove(int index) throws ListEmptyException,
        ListIndexOutOfBoundsException
    {
        // is list empty?
        if (this.size() == 0)
        {
            throw new ListEmptyException();
        }
        
        // list is NOT empty, so now
        // make sure that index is reasonable --- if not, complain
        if ((index < 0) || (index >= this.count)) 
        {
            throw new ListIndexOutOfBoundsException(index);
        }

        // since it is reasonable, proceed to ONE BEFORE the list element 
        // to be removed
        else
        {
            Cs132ListNode currNode = this.head;

            // special case: what if removing first element?
            if (index == 0)
            {
                this.head = (this.head).getNext();
                this.count = this.count - 1;
                return currNode.getData();
            }

            // if get here, node t be removed is NOT first in list    
            for (int i=0; i < (index-1); i++) 
            {
                currNode = currNode.getNext();
            }
            
            // so, currNode is now the node BEFORE the node to
            // be removed.
            Object removedElement = (currNode.getNext()).getData();
            currNode.setNext( (currNode.getNext()).getNext() );
            this.count = this.count - 1;
            return removedElement;
         }
    }
    
    /*----------------------------------------------------------------
     clear()
     Purpose: empties the LinkedList instance that calls this.
    ----------------------------------------------------------------------*/
    public void clear()
    {
        this.head = null;
        this.count = 0;
    }
}