/** *

* MsgQueue is a channel for messages sent by * robotic agents across a network. * Each message is of type IMsg. * There will be one message queue for each directed edge in a proximity graph * or two for each undirected edge. * See Lynch for details. * Copying of message queues from graph induced by proximity graph * at one point in statespace to the next is handled by * calling the proper graph construction functions from * implementations of IProximityGraphFunction. *

* @author Michael Schuresko * @version %I%, %G% * @since 1.0 */ public class MsgQueue implements IMsgChannel { /** * Subclass to implement linked list of messages. * Consider replacing later with something from Java libraries */ protected class MsgListNode { MsgListNode m_Next, m_Prev; IMsg m_Data; /** *

Constructor: This variant creates an empty list

* @since 1.0 */ public MsgListNode() { m_Next = null; m_Data = null; } /** *

Constructor: Preferred version *

* @param msg Message for this list node. * @param next List to follow this node (may be null) * @since 1.0 */ public MsgListNode(IMsg msg, MsgListNode next) { m_Next = next; m_Data = msg; } /** *

Clone list Constructor

* @param listToClone list to make a copy of * @since 1.0 */ public MsgListNode(MsgListNode listToClone) { m_Data = listToClone.m_Data; m_Next = null; if(listToClone.m_Next != null) { m_Next = new MsgListNode(listToClone.m_Next); } } /** *

Finds the tail of a list

* @return tail of list * @since 1.0 */ public MsgListNode findTail() { MsgListNode result = this; while(result.m_Next != null) { result = result.m_Next; } return result; } /** *

Simple append function *

* @param msg Message to be appended * @return New added node * @since 1.0 */ public MsgListNode append(IMsg msg) { m_Next = new MsgListNode(msg, null); return m_Next; } /** *

Accessor method for list element data

* @return m_Data * @since 1.0 */ public IMsg getData() { return m_Data; } /** *

Accessor method for list next element.

* @return m_Next * @since 1.0 */ public MsgListNode getNext() { return m_Next; } } /** * Standard linked-list stuff */ MsgListNode m_Head, m_Tail; /** * boolean indicating whether writes should make new * copies of internal lists */ boolean m_bCopyOnWrite; /** *

Constructor

* @since 1.0 */ public MsgQueue() { m_bCopyOnWrite = false; m_Head = null; m_Tail = null; } /** *

Constructor : * correct.

* @param src Allows this message queue to be constructed from an existing * one. * @since 1.0 */ public MsgQueue(MsgQueue src) { if(src.m_Head != null) { m_Head = new MsgListNode(src.m_Head); } if(m_Head != null) { m_Tail = m_Head.findTail(); } else { m_Tail = null; } m_bCopyOnWrite = false; } /** * clone function for IMsgChannel interface. */ public IMsgChannel makeCopy() { return (IMsgChannel)(new MsgQueue(this)); } /** *

Clone function for IMsgChannel interface * : Note the Lisp-like lazy internal copy semantics: * This reflects the design intent of modelling discrete state * transitions inspired by the functional paradigm, while modeling * continuous-time state using the array-based programming paradigm * (see * * http://en.wikipedia.org/wiki/Array-based_programming)

*

Also note that this sets m_bCopyOnWrite for the src queue, * and not for this queue. The intent is that the copied queue * will be written to more frequently then the source queue, and * the extra bookkeeping is only there to make the queues semantically * correct.

* @since 1.0 */ public IMsgChannel makeLazyCopy() { MsgQueue retVal = new MsgQueue(); retVal.m_Head = m_Head; retVal.m_Tail = m_Tail; m_bCopyOnWrite = true; retVal.m_bCopyOnWrite = false; return (IMsgChannel)retVal; } /** *

Add a message to the back of the queue

* If bugs occur, pay attention to nasty bookeeping * required to handle "read the last item sent" events. * @param msg is the message to be sent * @return This queue */ public IMsgChannel sendMsg(IMsg msg) { if(m_bCopyOnWrite) { if(m_Head != null) { m_Head = new MsgListNode(m_Head); } if(m_Head != null) { m_Tail = m_Head.findTail(); } else { m_Tail = null; } } if(m_Tail != null) { m_Tail = m_Tail.append(msg); } else { m_Tail = new MsgListNode(msg, null); m_Head = m_Tail; } return (IMsgChannel)this; } /** *

Read a message, alter state of this queue * States of queues which share data are not altered. * Garbage-collection ensures discarded head nodes are * deletedif no sharing queue needs them.

* @return IMsg just read. */ public IMsg readMsg() { IMsg result; if(m_Head == null) { return null; } result = m_Head.getData(); m_Head = m_Head.getNext(); return result; } }