FUNCTION obj_list::init

; Set the size of the obj_list to 0.  Nothing else to do.

self.size = 0

RETURN, 1

END

; ------------------------------------------------------------------------------------------------

PRO obj_list::cleanup

; Just call the clear method to delete all the nodes.

self->clear

; Were done!

RETURN

END

; ------------------------------------------------------------------------------------------------

PRO obj_list::clear

; If the obj_list is empty, then just return.

IF self.size EQ 0 THEN RETURN

; Kludge )))))))))))))))))))))))))))))))))))))))))))))))))))))))

IF NOT OBJ_VALID (self.first) THEN RETURN

; Kludge )))))))))))))))))))))))))))))))))))))))))))))))))))))))

; Set obj to point to the first node on the obj_list.

obj = self.first

; Loop to move through the entire obj_list, deleting each node as it is encountered.

WHILE obj NE self.null DO BEGIN

; Set tmp to point to subsequent node in the obj_list.

   tmp = obj.next

; Destroy the node object.

   OBJ_DESTROY, obj

; Move ptr to point to the next node in the obj_list (pointed to by tmp).

   obj = tmp

ENDWHILE

; Reset size to 0.

self.size = 0

; Reset the first and last pointers.

self.first = self.null
self.last  = self.null

; All done.

RETURN

END

; ------------------------------------------------------------------------------------------------

PRO obj_list::add, node

; Make sure that node exists.  If node does not exist, then simply return (silent error).

IF N_ELEMENTS (node) EQ 0 THEN RETURN

; Make sure that node is an object.  If node is not an object, then simply return.
; (silent error)

IF self->get_type (node) NE 11 THEN RETURN

; Make sure that node inherits BASE_NODE_OBJ.  If node does not inherit this object then
; simply return (silent error).

IF NOT OBJ_ISA (node, 'BASE_NODE_OBJ') THEN RETURN

; Check if there are any items on the obj_list.
; If it is empty then we have to begin the list from scratch.

IF self.size EQ 0 THEN BEGIN

; Empty obj_list. The node that was passed to us as a parameter will be the first and
; only node on the obj_list.

   node.next = self.null
   node.prev = self.null

   self.first = node
   self.last  = node

ENDIF ELSE BEGIN

; Ok, there are already some nodes.  First check to make sure we are not adding
; a named node to the obj_list with the same name as a node already on the  obj_list.
; If we are then just return.

   IF self->search (node.name) THEN RETURN

; Now decide if need insert the new node at the begining, in the middle or at the end of the
; obj_list.  We will first check for an insertion at the start or end of the list.

; Set the flags for first, middle and last to 0 (FALSE).

   first  = 0
   last   = 0
   middle = 0

; Check for insertion at the begining of the obj_list.

   IF node.name LT self.first->get_name () THEN first = 1

; Check for insertion at the end of the obj_list.

   IF node.name GT self.last->get_name ()  THEN last = 1

; Check if neither first nor last is set.  If neither is, then we have to insert our
; node in the middle of the obj_list.

   IF NOT first AND NOT last THEN BEGIN

; Set the flag middle.

      middle = 1

; Now search through the obj_list looking for the correct place to add new node.
; Begin by setting loc to point to the first node on the obj_list.  Also set found
; to 0, this will cause the loop to exit when the correct location to insert
; the node has been found.

      loc  = self.first
      found = 0

; Loop to move through the entire obj_list, checking each node to make sure that it is
; located prior to name according to the ascii collating sequence.

      WHILE NOT found AND loc NE self.null DO BEGIN

; Check if the node should lexically come before the node pointed to by loc

         IF node.name LT loc.name THEN BEGIN

; OK, found where to insert the node.  Move loc back one node.

            loc = loc.prev

; Set found to 1 and get out of the loop.

            found = 1

         ENDIF ELSE BEGIN

; Otherwise, move loc to point to the next node on the list.

            loc = loc.next

         ENDELSE

      ENDWHILE

   ENDIF

; Check where we should insert the the new node (according to which flag is set --
; first, last or middle) and then insert the node in the proper place.

  IF first THEN BEGIN        ; Insert the node at the begining of the obj_list. ;

; Set up the pointers in the new node.

     node.next = self.first
     node.prev = self.null

; Insert the new node at the beginning of the list.

     self.first->set_prev, node
     self.first = node

  ENDIF

 IF last THEN BEGIN        ; Insert the node at the end of the obj_list.

; Set up the pointers in the new node.

     node.next = self.null
     node.prev = self.last

; Insert the new node at the end of the list.

     self.last->set_next, node
     self.last = node

  ENDIF

  IF middle THEN BEGIN      ; Insert the node in the middle of the obj_list.

; Set up the pointers in the new node.

     node.next = loc.next
     node.prev = loc

; Insert the new node into the list.

     loc.next->set_prev, node
     loc.next = node

  ENDIF

ENDELSE

; Increment the size counter of the list.

self.size = self.size + 1

; Were done! returning...

RETURN

END

; ------------------------------------------------------------------------------------------------

FUNCTION obj_list::list, EMPTY = empty

; First, check if the obj_list is empty.  If it is then we will just return -1.

IF self.size EQ 0 THEN BEGIN

   IF N_ELEMENTS (empty) NE 0 THEN empty = 1

   return, -1

ENDIF

; Create a string array to hold the name of each node in the obj_list.

list = STRARR (self.size)

; Now loop through the  entire structure and get the all the name strings.
; Set ptr to point to the first node on the obj_list.
; Use cnt to keep count of which node we are accessing.

ptr = self.first
cnt = 0

; Loop to move through the entire obj_list, copy in the name of each node into the
; array list.

WHILE ptr NE self.null DO BEGIN

; Copy the string from the node into the array list.

   list [cnt] = ptr.name

; Move ptr to point to the next node in the obj_list.

   ptr = ptr.next

; Increment cnt by 1.

   cnt = cnt + 1

ENDWHILE

; Make sure that empty is set to FALSE (if it exists).

IF N_ELEMENTS (empty) NE 0 THEN empty = 0

; Return the list of values we just extracted.

RETURN, list

END

; ------------------------------------------------------------------------------------------------

FUNCTION obj_list::search, val

; If the obj_list is empty, then just return 0 (FALSE).

IF self.size EQ 0 THEN RETURN, 0

; Force val to be a string.  In the process, remove leading and trailing blanks.

str = STRTRIM (STRING (val), 1)

; Now we need to search through the obj_list looking for the requested string.
; Begin by seting obj to point to the first node on the obj_list.

obj   = self.first

; Loop to move through the entire obj_list, checking each node to see if its name tag is set to
; the requested string.  The loop ends when all nodes have been searched.  Finding a node
; with a name tag that would occur prior to the search sting using the the ASCII collating
; sequence or finding the node that is the target of the search will cause the function
; to immediately return a result.

WHILE obj NE self.null DO BEGIN

; Check if search target is contained within the current node.
; If it is then we are done.

   IF str EQ obj.name THEN RETURN, 1

; Check if str should lexically come before the node pointed to by ptr.  If it does,
; then the target is not in the obj_list and there is no point in continueing the search.

   IF str LT obj.name THEN RETURN, 0

; Otherwise, move pointer to point to the next node on the list.

   obj = obj.next

ENDWHILE

; Oops. The search failed. Return 0.

RETURN, 0

END

; ------------------------------------------------------------------------------------------------

FUNCTION obj_list::get, val

; If the obj_list is empty, then just return 0 (FALSE).

IF self.size EQ 0 THEN RETURN, 0

; Force val to be a string.  In the process, remove leading and trailing blanks.

str = STRTRIM (STRING (val), 1)

; Now we need to search through the obj_list looking for the requested string.
; Begin by seting ptr to point to the first node on the obj_list.

ptr   = self.first

; Loop to move through the entire obj_list, checking to see if the name of each node is set to
; the requested string.  The loop ends when all nodes have been searched.  Finding a node
; with a name that would occur prior to the search sting using the the ASCII collating
; sequence or finding the node that is the target of the search will cause the function
; to immediately return a result.

WHILE ptr NE self.null DO BEGIN

; Check if search target is contained within the current node.
; If it is then return a reference to that node.

   IF str EQ ptr.name THEN RETURN, ptr

; Check if str should lexically come before the node pointed to by ptr.  If it does,
; then the target is not in the obj_list and there is no point in continueing the search.

   IF str LT ptr.name THEN RETURN, self.null

; Otherwise, move pointer to point to the next node on the list.

   ptr = ptr.next

ENDWHILE

; Oops. The search failed. Return a referance to a NULL object.

RETURN, self.null

END

; ------------------------------------------------------------------------------------------------

FUNCTION obj_list::unlink, val

; If the obj_list is empty, then just return a NULL.

IF self.size EQ 0 THEN RETURN, self.null

; Make sure that val exists.  If val does not exist, then return a NULL

IF N_ELEMENTS (val) EQ 0 THEN RETURN, self.null

; Force val to be a string.  In the process, remove leading and trailing blanks.

str = STRTRIM (STRING (val), 1)

; Now we need to search through the obj_list looking for the requested string.
; Begin by seting ptr to point to the first node on the obj_list.

ptr = self.first

; Loop to move through the entire obj_list, checking to see if the name of each node is set to
; the requested string.  The loop ends when all nodes have been searched.  Finding a node
; with a name that would occur prior to the search sting using the  ASCII collating
; sequence or finding the node that is the target of the search will cause the function
; to immediately return a result.

WHILE ptr NE self.null DO BEGIN

; Check if search target is contained within the current node.
; If it is then we have to remove it.

   IF str EQ ptr.name THEN BEGIN

; Unlink the node from the obj_list.  How we handle this task depends on the position
; of the node within the obj_list.  Special cases are the node is the first node on the
; list, the last node on the list or there is only one node on the list.  The nominal
; case is that the node is in the middle of the list.  After unlinking the node, we
; will then do an unconditional jump to the part of the routine that cleans up the
; the node and returns a pointer to it.

      IF self.size EQ 1 THEN BEGIN      ; The obj_list consist of a single node.

; Unlink the requested node from the the obj_list.

         self.first = self.null
         self.last  = self.null

; Jump to the final cleanup and return a pointer to the node.

         GOTO, CLEANUP

      ENDIF

      IF ptr EQ self.first THEN BEGIN   ; The node is the first node on the list.

; Unlink the requested node from the the obj_list.

         self.first = ptr.next
         self.first->set_prev

; Jump to the final cleanup and return a pointer to the node.

         GOTO, CLEANUP

      ENDIF

      IF ptr EQ self.last THEN  BEGIN   ; The node is the last node on the list.

; Unlink the requested node from the the obj_list.

         self.last = ptr.prev
         self.last->set_next

; Jump to the final cleanup and return a pointer to the node.

         GOTO, CLEANUP

      ENDIF

; Nominal Case                            The node is in the middle of the list.

; Unlink the requested node from the the obj_list.

      ptr.next->set_prev, ptr.prev
      ptr.prev->set_next, ptr.next

; Do the final cleanup and return a pointer to the node.

CLEANUP:

; Set the next and prev pointers in the node to NULL.

      ptr.prev = self.null
      ptr.next = self.null

; Decreement the count of the number of nodes on the obj_list by 1

      self.size = self.size - 1

; Return the pointer to the node we just unlinked.

      RETURN, ptr

   ENDIF

; Check if str should lexically come before the node pointed to by ptr.  If it does,
; then the target is not in the obj_list and there is no point in continueing the search.

   IF str LT ptr.name THEN RETURN, self.null

; Otherwise, move pointer to point to the next node on the list.

   ptr = ptr.next

ENDWHILE

; Oops. The search failed. Return a NULL.

RETURN, self.null

END

; ------------------------------------------------------------------------------------------------

FUNCTION obj_list::size

; Return the number of node in the obj_list.

RETURN, self.size

END

; ------------------------------------------------------------------------------------------------

PRO obj_list__define

struct = {OBJ_LIST,              $ ; Name of the object structure.
          first: OBJ_NEW (),       $ ; Pointer to first node on the obj_list.
          last:  OBJ_NEW (),       $ ; Pointer to last node on the obj_list.
          null:  OBJ_NEW (),       $ ; NULL pointer definition
          size: 0,               $ ; Number of nodes in the obj_list.
          INHERITS olib            $ ; object library
         }

END