Rick McGuire wrote:
I'm in favor of PREVIOUS and NEXT with a required operand, and doing the wrapping, I'm not in favor of the optional flavor nor of the cursor operation.  Introducing a cursored-type iteration to this class is inconsistent with the other collection classes.  A cursored type iteration should be done as a secondary helper class, not by inserting a cursor position inside the collection itself.

O.K. as I am after the optional flavor (therefore also the need for some sort of a cursor) and OTOH I not wanting to break any logic/assumptions of the "basic" ooRexx collection classes, so I might as well implement that behaviour in a private class of mine (see below).

Having the argument to be optional allows for creating code easier, because there is no need on the programmer's side to maintain index positions (cursors) on their own. E.g. the following programs does not maintain any index positions on its own:
o=.NextableCircularQueue~of("field1", "field2", "field3")

say "cursor at index:" o~cursor
call work1next o                    -- invoke a full cycle of nexts
say "cursor at index:" o~cursor
say "---"
call work1previous o                -- invoke a full cycle of previous
say "cursor at index:" o~cursor
say "---"
call work2random o                  -- invoke random next/previous messages
say "cursor at index:" o~cursor
say "---"
call work1next o                    -- invoke a full cycle of nexts
call work1previous o                -- invoke a full cycle of previous
say copies("-",20)
exit

   /* the following procedures do not need to know which one is next/previous,
      ie. they need not maintain a "current index" on their own */
work1next : procedure
   use arg o
   do i=1 to o~items
      say "work1next:     NEXT=["o~next"]" "cursor now at index:" o~cursor
   end
   return

work1previous : procedure
   use arg o
   do i=1 to o~items
      say "work1previous: PREVIOUS=["o~previous"]" "cursor now at index:" o~cursor
   end
   return

work2random : procedure
   use arg o
   ran=random(1, o~items)  -- randomly determine how many next/previous
   bNext=(random(0,1, time("s")))     -- determine whether to use NEXT or PREVIOUS

   say "randomly looping" ran "time(s):"
   do i=1 to ran           -- cycle randomly over
      if bNext then
         say "   work2: ...     NEXT=["o~next"]     cursor now at index:" o~cursor
      else
         say "   work2: ... PREVIOUS=["o~previous"] cursor now at index:" o~cursor
   end
   return

  
May yield:
cursor at index: 0
work1next:     NEXT=[field1] cursor now at index: 1
work1next:     NEXT=[field2] cursor now at index: 2
work1next:     NEXT=[field3] cursor now at index: 3
cursor at index: 3
---
work1previous: PREVIOUS=[field2] cursor now at index: 2
work1previous: PREVIOUS=[field1] cursor now at index: 1
work1previous: PREVIOUS=[field3] cursor now at index: 3
cursor at index: 3
---
randomly looping 1 time(s):
   work2: ... PREVIOUS=[field2] cursor now at index: 2
cursor at index: 2
---
work1next:     NEXT=[field3] cursor now at index: 3
work1next:     NEXT=[field1] cursor now at index: 1
work1next:     NEXT=[field2] cursor now at index: 2
work1previous: PREVIOUS=[field1] cursor now at index: 1
work1previous: PREVIOUS=[field3] cursor now at index: 3
work1previous: PREVIOUS=[field2] cursor now at index: 2
--------------------

  
Having access to the cursor can even allow to set the position (e.g. if a user put the focus to a different input field, where the input fields are represented as a circular queue, which determines the tabbing order). A matching ooRexx program which could like:
/* A specialisation of CircularQueue which allows to cycle through the
   collected items without the need to supply an index value.
*/
::class NextableCircularQueue subclass CircularQueue
::method init              -- constructor, needed to preset value of CURSOR
  expose cursor
  cursor=0
  forward class (super)             -- let superclass instantiate

::method next              -- return next item, wrap around, if necessary
  expose cursor

  if self~items=0 then              -- indicate no items in circular queue
     return .nil

  if (arg() > 1) then               -- too many arguments given?
     raise syntax 93.902 array (1)  -- raise an error

  if arg()>0 then
  do
     parse arg index                         -- retrieve index value (must be a whole number)
     if datatype(index, "Whole")=.false then -- must be a whole number
        raise syntax 93.905 array (1, arg(1))
     cursor=index                         -- use index value as new cursor value
  end

  cursor=cursor+1                   -- increase cursor

  if cursor<1 | cursor>self~items then    -- start out with first item in collection
     cursor=1                       -- position on first element in collection

  return self~at(cursor)            -- return next item object


::method previous          -- return previous item, wrap around, if necessary
  expose cursor

  if self~items=0 then              -- indicate no items in circular queue
     return .nil

  if (arg() > 1) then               -- too many arguments given?
     raise syntax 93.902 array (1)  -- raise an error

  if arg()>0 then
  do
     parse arg index                   -- retrieve index value (must be a whole number)
     if datatype(index, "Whole")=.false then -- must be a whole number
        raise syntax 93.905 array (1, arg(1))
     cursor=index                      -- use index value as new cursor value
  end

  cursor=cursor-1                      -- decrease cursor

  if cursor<1 | cursor>self~items then -- start out with first item in collection
     cursor=self~items                 -- position on last item in collection
  return self~at(cursor)               -- return next item object


::method cursor            -- return cursor's value
  expose cursor
  return cursor

::method "cursor="         -- allow setting cursor to a whole number
  expose cursor

  if (arg() > 1) then               -- too many arguments given?
     raise syntax 93.902 array (1)  -- raise an error

  parse arg index                         -- retrieve index value (must be a whole number)
  if datatype(index, "Whole")=.false then -- must be a whole number
     raise syntax 93.905 array (1, arg(1))
  cursor=index                         -- use index value as new cursor value
  
Regards,

---rony