001/* A FIFO queue receiver with variable capacity and optional history.
002
003 Copyright (c) 1997-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027
028 */
029package ptolemy.actor;
030
031import java.util.Collections;
032import java.util.Enumeration;
033import java.util.List;
034import java.util.NoSuchElementException;
035
036import ptolemy.actor.util.FIFOQueue;
037import ptolemy.data.Token;
038import ptolemy.kernel.util.IllegalActionException;
039
040///////////////////////////////////////////////////////////////////
041//// QueueReceiver
042
043/**
044 A first-in, first-out (FIFO) queue receiver with variable capacity and
045 optional history. Tokens are put into the receiver with the put() method,
046 and removed from the receiver with the get() method. The token removed is
047 the oldest one in the receiver. By default, the capacity is unbounded, but
048 it can be set to any nonnegative size. If the history capacity is greater
049 than zero (or infinite, indicated by a capacity of INFINITE_CAPACITY),
050 then tokens removed from the receiver are stored in a history queue rather
051 than simply removed. By default, the history capacity is zero.
052
053 @author Edward A. Lee, Lukito Muliadi, Xiaojun Liu
054 @version $Id$
055 @since Ptolemy II 0.2
056 @Pt.ProposedRating Green (eal)
057 @Pt.AcceptedRating Green (liuj)
058 @see ptolemy.actor.util.FIFOQueue
059 */
060public class QueueReceiver extends AbstractReceiver {
061    /** Construct an empty receiver with no container.
062     */
063    public QueueReceiver() {
064        super();
065    }
066
067    /** Construct an empty receiver with the specified container.
068     *  @param container The container of the receiver.
069     *  @exception IllegalActionException If the container does
070     *   not accept this receiver.
071     */
072    public QueueReceiver(IOPort container) throws IllegalActionException {
073        super(container);
074    }
075
076    ///////////////////////////////////////////////////////////////////
077    ////                         public methods                    ////
078
079    /** Clear this receiver of any contained tokens.
080     */
081    @Override
082    public void clear() {
083        _queue.clear();
084    }
085
086    /** List the tokens in the receiver, beginning with the oldest.
087     *  @return An enumeration of tokens.
088     */
089    @Override
090    public List<Token> elementList() {
091        return _queue.elementList();
092    }
093
094    /** Enumerate the tokens in the receiver, beginning with the oldest.
095     *  @deprecated Used elementList() instead.
096     *  @return An enumeration of tokens.
097     */
098    @Deprecated
099    public Enumeration elements() {
100        return Collections.enumeration(elementList());
101    }
102
103    /** Remove the first token (the oldest one) from the receiver and
104     *  return it. If there is no token in the receiver, throw an
105     *  exception.
106     *  @return The oldest token in the receiver.
107     *  @exception NoTokenException If there is no token in the receiver.
108     */
109    @Override
110    public Token get() {
111        Token t = null;
112
113        try {
114            t = (Token) _queue.take();
115        } catch (NoSuchElementException ex) {
116            // The queue is empty.
117            throw new NoTokenException(getContainer(),
118                    "Attempt to get token from an empty QueueReceiver.");
119        }
120
121        return t;
122    }
123
124    /** Return a token in the receiver or its history. If the offset
125     *  argument is zero, return the oldest token in the receiver.
126     *  If the offset is 1, return the second oldest token, etc. The
127     *  token is not removed from the receiver. If there is no such
128     *  token in the receiver (the offset is greater than or equal
129     *  to the number of tokens currently in the receiver), throw an
130     *  exception. If the offset is -1, return the most recent token
131     *  removed from the receiver. If it is -2, return the second
132     *  most recent token removed from the receiver, etc. If there is
133     *  no such token in the receiver's history (the history capacity
134     *  is zero or the absolute value of offset is greater than the
135     *  number of tokens currently in the receiver's history), an
136     *  exception is thrown.
137     *  @param offset The offset from the oldest token in the receiver.
138     *  @return The token at the desired offset in the receiver or its
139     *   history.
140     *  @exception NoTokenException If the offset is out of range.
141     */
142    public Token get(int offset) {
143        try {
144            return (Token) _queue.get(offset);
145        } catch (NoSuchElementException ex) {
146            throw new NoTokenException(getContainer(),
147                    "Offset " + offset + " out of range with " + _queue.size()
148                            + " tokens in the receiver and "
149                            + _queue.historySize() + " in history.");
150        }
151    }
152
153    /** Return the capacity, or INFINITE_CAPACITY if it is unbounded.
154     *  @return The capacity of the receiver.
155     *  @see #setCapacity(int)
156     */
157    public int getCapacity() {
158        return _queue.getCapacity();
159    }
160
161    /** Return the capacity of the history queue.
162     *  This will be zero if the history mechanism is disabled
163     *  and INFINITE_CAPACITY if the history capacity is unbounded.
164     *  @return The capacity of the history queue.
165     *  @see #setHistoryCapacity(int)
166     */
167    public int getHistoryCapacity() {
168        return _queue.getHistoryCapacity();
169    }
170
171    /** Return true if the next call to put() will succeed without
172     *  a NoRoomException.
173     *  @return True if the queue has room for one more token.
174     */
175    @Override
176    public boolean hasRoom() {
177        return !_queue.isFull();
178    }
179
180    /** Return true if the queue has room to put the given number of
181     *  tokens into it (via the put() method).
182     *  @param numberOfTokens The number of tokens to put into the queue.
183     *  @return True if the queue has room for the specified number of tokens.
184     *  @exception IllegalArgumentException If the number of tokens is less
185     *   than one.  This is a runtime exception, and hence does not need to
186     *   be explicitly declared by the caller.
187     */
188    @Override
189    public boolean hasRoom(int numberOfTokens) throws IllegalArgumentException {
190        if (numberOfTokens < 1) {
191            throw new IllegalArgumentException(
192                    "The number of tokens must be greater than 0");
193        }
194
195        return _queue.size() + numberOfTokens < _queue.getCapacity();
196    }
197
198    /** Return true if the next call to get() will succeed without a
199     *  a NoTokenException.
200     *  @return True if the queue has at least one token in it.
201     */
202    @Override
203    public boolean hasToken() {
204        return _queue.size() > 0;
205    }
206
207    /** Return true if the specified number of tokens is available in the
208     *  queue.
209     *  @param numberOfTokens The number of tokens to get from the queue.
210     *  @return True if the specified number of tokens is available.
211     *  @exception IllegalArgumentException If the number of tokens is less
212     *   than one.  This is a runtime exception, and hence does not need to
213     *   be explicitly declared by the caller.
214     */
215    @Override
216    public boolean hasToken(int numberOfTokens)
217            throws IllegalArgumentException {
218        if (numberOfTokens < 1) {
219            throw new IllegalArgumentException(
220                    "The number of tokens must be greater than 0");
221        }
222
223        return _queue.size() >= numberOfTokens;
224    }
225
226    /** List the tokens stored in the history queue, which are
227     *  the N most recent tokens taken from the receiver, beginning with
228     *  the oldest, where N is less than or equal to the history capacity.
229     *  If the history capacity is INFINITE_CAPACITY, then the enumeration
230     *  includes all tokens previously taken from the receiver. If the
231     *  history capacity is zero, then return an empty enumeration.
232     *  @return An enumeration of tokens.
233     *  @deprecated Used historyElementList() instead.
234     */
235    @Deprecated
236    public List historyElementList() {
237        return _queue.historyElementList();
238    }
239
240    /** Enumerate the tokens stored in the history queue, which are
241     *  the N most recent tokens taken from the receiver, beginning with
242     *  the oldest, where N is less than or equal to the history capacity.
243     *  If the history capacity is INFINITE_CAPACITY, then the enumeration
244     *  includes all tokens previously taken from the receiver. If the
245     *  history capacity is zero, then return an empty enumeration.
246     *  @return An enumeration of tokens.
247     *  @deprecated Used historyElementList() instead.
248     */
249    @Deprecated
250    public Enumeration historyElements() {
251        return Collections.enumeration(historyElementList());
252    }
253
254    /** Return the number of tokens in history.
255     *  @return The number of tokens in history.
256     */
257    public int historySize() {
258        return _queue.historySize();
259    }
260
261    /** Put a token to the receiver. If the receiver is full, throw an
262     *  exception. If the argument is null, do nothing.
263     *  @param token The token to be put to the receiver.
264     *  @exception NoRoomException If the receiver is full.
265     */
266    @Override
267    public void put(Token token) {
268        if (token == null) {
269            return;
270        }
271        if (!_queue.put(token)) {
272            throw new NoRoomException(getContainer(),
273                    "Queue is at capacity. Cannot put a token.");
274        }
275    }
276
277    /** Set receiver capacity. Use INFINITE_CAPACITY to indicate unbounded
278     *  capacity (which is the default). If the number of tokens currently
279     *  in the receiver exceeds the desired capacity, throw an exception.
280     *  @param capacity The desired receiver capacity.
281     *  @exception IllegalActionException If the receiver has more tokens
282     *   than the proposed capacity or the proposed capacity is illegal.
283     *  @see #getCapacity()
284     */
285    public void setCapacity(int capacity) throws IllegalActionException {
286        try {
287            _queue.setCapacity(capacity);
288        } catch (IllegalActionException ex) {
289            throw new IllegalActionException(getContainer(), ex,
290                    "Failed to set capacity to " + capacity);
291        }
292    }
293
294    /** Set the capacity of the history queue. Use 0 to disable the
295     *  history mechanism and INFINITE_CAPACITY to make the history
296     *  capacity unbounded. If the size of the history queue exceeds
297     *  the desired capacity, then remove the oldest tokens from the
298     *  history queue until its size equals the proposed capacity.
299     *  Note that this can be used to clear the history queue by
300     *  supplying 0 as the argument.
301     *  @param capacity The desired history capacity.
302     *  @exception IllegalActionException If the desired capacity is illegal.
303     *  @see #getHistoryCapacity()
304     */
305    public void setHistoryCapacity(int capacity) throws IllegalActionException {
306        try {
307            _queue.setHistoryCapacity(capacity);
308        } catch (IllegalActionException ex) {
309            throw new IllegalActionException(getContainer(), ex,
310                    "Failed to setHistoryCapacity to " + capacity);
311        }
312    }
313
314    /** Return the number of tokens in the receiver.
315     *  @return The number of tokens in the receiver.
316     */
317    public int size() {
318        return _queue.size();
319    }
320
321    ///////////////////////////////////////////////////////////////////
322    ////                         public variables                  ////
323
324    /** Used to indicate that the size of this queue receiver is infinite.
325     */
326    public static final int INFINITE_CAPACITY = FIFOQueue.INFINITE_CAPACITY;
327
328    ///////////////////////////////////////////////////////////////////
329    ////                         private variables                 ////
330
331    /** This is the queue in which data is stored.
332     */
333    protected FIFOQueue _queue = new FIFOQueue();
334}