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.domains.sdf.kernel;
030
031import java.util.Enumeration;
032import java.util.List;
033import java.util.NoSuchElementException;
034
035import ptolemy.actor.AbstractReceiver;
036import ptolemy.actor.IOPort;
037import ptolemy.actor.NoRoomException;
038import ptolemy.actor.NoTokenException;
039import ptolemy.data.Token;
040import ptolemy.kernel.util.IllegalActionException;
041
042///////////////////////////////////////////////////////////////////
043//// SDFReceiver
044
045/**
046 A first-in, first-out (FIFO) queue receiver with variable capacity and
047 optional history. Tokens are put into the receiver with the put() method,
048 and removed from the receiver with the get() method. The token removed is
049 the one placed in the receiver before any other (i.e. the "oldest", although
050 this has nothing to do with time in the model.
051 By default, the capacity is unbounded, but
052 it can be set to any nonnegative size. If the history capacity is greater
053 than zero (or infinite, indicated by a capacity of INFINITE_CAPACITY),
054 then tokens removed from the receiver are stored in a history queue rather
055 than simply removed. By default, the history capacity is zero.
056
057 @author Steve Neuendorffer
058 @version $Id$
059 @since Ptolemy II 0.2
060 @Pt.ProposedRating Green (neuendor)
061 @Pt.AcceptedRating Green (neuendor)
062 @see ArrayFIFOQueue
063 */
064public class SDFReceiver extends AbstractReceiver {
065    /** Construct an empty receiver with no container.
066     */
067    public SDFReceiver() {
068        super();
069        _queue = new ArrayFIFOQueue();
070    }
071
072    /** Construct an empty receiver with no container and given size.
073     *  @param size The size of the queue in the receiver.
074     */
075    public SDFReceiver(int size) {
076        super();
077        _queue = new ArrayFIFOQueue(size);
078    }
079
080    /** Construct an empty receiver with the specified container.
081     *  @param container The container of the receiver.
082     *  @exception IllegalActionException If the container does
083     *   not accept this receiver.
084     */
085    public SDFReceiver(IOPort container) throws IllegalActionException {
086        super(container);
087        _queue = new ArrayFIFOQueue();
088    }
089
090    /** Construct an empty receiver with the specified container and size.
091     *  @param container The container of the receiver.
092     *  @param size The size of the queue in the receiver.
093     *  @exception IllegalActionException If the container does
094     *   not accept this receiver.
095     */
096    public SDFReceiver(IOPort container, int size)
097            throws IllegalActionException {
098        super(container);
099        _queue = new ArrayFIFOQueue(size);
100    }
101
102    ///////////////////////////////////////////////////////////////////
103    ////                         public methods                    ////
104
105    /** Clear this receiver of any contained tokens.
106     */
107    @Override
108    public void clear() {
109        _queue.clear();
110        _waitingTokens = 0;
111    }
112
113    /** Return a list with the tokens currently in the receiver, or
114     *  an empty list if there are no such tokens.
115     *  @return A list of instances of Token.
116     */
117    @Override
118    public List<Token> elementList() {
119        return _queue.elementList();
120    }
121
122    /** Remove the first token (the oldest one) from the receiver and
123     *  return it. If there is no token in the receiver, throw an
124     *  exception.
125     *  @return The oldest token in the receiver.
126     *  @exception NoTokenException If there is no token in the receiver.
127     */
128    @Override
129    public Token get() {
130        if (_queue.isEmpty()) {
131            // The queue is empty.
132            throw new NoTokenException(getContainer(),
133                    "Attempt to get token from an empty QueueReceiver.");
134        }
135
136        return (Token) _queue.take();
137    }
138
139    /** Return a token in the receiver or its history. If the offset
140     *  argument is zero, return the oldest token in the receiver.
141     *  If the offset is 1, return the second oldest token, etc. The
142     *  token is not removed from the receiver. If there is no such
143     *  token in the receiver (the offset is greater than or equal
144     *  to the number of tokens currently in the receiver), throw an
145     *  exception. If the offset is -1, return the most recent token
146     *  removed from the receiver. If it is -2, return the second
147     *  most recent token removed from the receiver, etc. If there is
148     *  no such token in the receiver's history (the history capacity
149     *  is zero or the absolute value of offset is greater than the
150     *  number of tokens currently in the receiver's history), an
151     *  exception is thrown.
152     *  @param offset The offset from the oldest token in the receiver.
153     *  @return The token at the desired offset in the receiver or its
154     *   history.
155     *  @exception NoTokenException If the offset is out of range.
156     */
157    public Token get(int offset) {
158        try {
159            return (Token) _queue.get(offset);
160        } catch (NoSuchElementException ex) {
161            throw new NoTokenException(getContainer(),
162                    "Offset " + offset + " out of range with " + _queue.size()
163                            + " tokens in the receiver and "
164                            + _queue.historySize() + " in history.");
165        }
166    }
167
168    /** Get an array of tokens from this receiver. The parameter
169     *  specifies the number of valid tokens to get in the returned
170     *  array. The length of the returned array will be equal to
171     *  <i>count</i>.
172     *  @param count The number of valid tokens to get in the
173     *   returned array.
174     *  @return An array containing <i>count</i> tokens from the
175     *   receiver.
176     *  @exception NoTokenException If there are not <i>count</i>
177     *   tokens.
178     */
179    @Override
180    public Token[] getArray(int count) {
181        // Check if we need to reallocate the cached
182        // token array.
183        if (_tokenArray == null || count != _tokenArray.length) {
184            // Reallocate token array.
185            _tokenArray = new Token[count];
186        }
187
188        _queue.takeArray(_tokenArray, count);
189        return _tokenArray;
190    }
191
192    /** Return the capacity, or INFINITE_CAPACITY if it is unbounded.
193     *  @return The capacity of the receiver.
194     *  @see #setCapacity(int)
195     */
196    public int getCapacity() {
197        return _queue.getCapacity();
198    }
199
200    /** Return the capacity of the history queue.
201     *  This will be zero if the history mechanism is disabled
202     *  and INFINITE_CAPACITY if the history capacity is unbounded.
203     *  @return The capacity of the history queue.
204     *  @see #historyElements()
205     *  @see #historySize()
206     *  @see #setHistoryCapacity(int)
207     */
208    public int getHistoryCapacity() {
209        // As of Ptolemy II 2.0.1, we are not using the SDFReceiver.*History*
210        // methods, but these are here for future use.
211        return _queue.getHistoryCapacity();
212    }
213
214    /** Return true if put() will succeed in accepting a token.
215     *  @return A boolean indicating whether a token can be put in this
216     *   receiver.
217     */
218    @Override
219    public boolean hasRoom() {
220        return !_queue.isFull();
221    }
222
223    /** Return true if put() will succeed in accepting the specified
224     *  number of tokens.
225     *  @param tokens The number of tokens.
226     *  @return A boolean indicating whether a token can be put in this
227     *   receiver.
228     *  @exception IllegalArgumentException If the argument is less
229     *   than one.  This is a runtime exception, so it need not be
230     *   declared explicitly by the caller.
231     */
232    @Override
233    public boolean hasRoom(int tokens) throws IllegalArgumentException {
234        if (tokens < 1) {
235            throw new IllegalArgumentException("The argument "
236                    + "must not be negative. It was: " + tokens);
237        }
238
239        if (_queue.getCapacity() == INFINITE_CAPACITY) {
240            // Queue has infinite capacity, so it can accept any
241            // finite number of tokens.
242            return true;
243        }
244
245        return _queue.size() + tokens <= _queue.getCapacity();
246    }
247
248    /** Return true if get() will succeed in returning a token.
249     *  @return A boolean indicating whether there is a token in this
250     *   receiver.
251     */
252    @Override
253    public boolean hasToken() {
254        return !_queue.isEmpty();
255    }
256
257    /** Return true if get() will succeed in returning a token the given
258     *  number of times.
259     *  @param tokens The number of tokens.
260     *  @return A boolean indicating whether there are the given number of
261     *  tokens in this receiver.
262     *  @exception IllegalArgumentException If the argument is less
263     *   than one.  This is a runtime exception, so it need not be
264     *   declared explicitly by the caller.
265     */
266    @Override
267    public boolean hasToken(int tokens) throws IllegalArgumentException {
268        if (tokens < 0) {
269            throw new IllegalArgumentException("The argument "
270                    + "must not be negative. It was: " + tokens);
271        }
272
273        return _queue.size() >= tokens;
274    }
275
276    /** Enumerate the tokens stored in the history queue, which are
277     *  the N most recent tokens taken from the receiver, beginning with
278     *  the oldest, where N is less than or equal to the history capacity.
279     *  If the history capacity is INFINITE_CAPACITY, then the enumeration
280     *  includes all tokens previously taken from the receiver. If the
281     *  history capacity is zero, then return an empty enumeration.
282     *  @return An enumeration of tokens.
283     *  @see #getHistoryCapacity()
284     *  @see #historySize()
285     *  @see #setHistoryCapacity(int)
286     */
287    public Enumeration historyElements() {
288        // As of Ptolemy II 2.0.1, we are not using the SDFReceiver.*History*
289        // methods, but these are here for future use.
290        return _queue.historyElements();
291    }
292
293    /** Return the number of tokens in history.
294     *  @return The number of tokens in history.
295     *  @see #getHistoryCapacity()
296     *  @see #historyElements()
297     *  @see #setHistoryCapacity(int)
298     */
299    public int historySize() {
300        // As of Ptolemy II 2.0.1, we are not using the SDFReceiver.*History*
301        // methods, but these are here for future use.
302        return _queue.historySize();
303    }
304
305    /** Put a token to the receiver. If the receiver is full, throw an
306     *  exception.
307     *  @param token The token to be put to the receiver, or null to
308     *   not put any token.
309     *  @exception NoRoomException If the receiver is full.
310     */
311    @Override
312    public void put(Token token) {
313        if (token == null) {
314            return;
315        }
316        if (!_queue.put(token)) {
317            throw new NoRoomException(getContainer(), "Queue is at capacity of "
318                    + _queue.getCapacity() + ". Cannot put a token.");
319        }
320    }
321
322    /** Set receiver capacity. Use INFINITE_CAPACITY to indicate unbounded
323     *  capacity (which is the default). If the number of tokens currently
324     *  in the receiver exceeds the desired capacity, throw an exception.
325     *  @param capacity The desired receiver capacity.
326     *  @exception IllegalActionException If the receiver has more tokens
327     *   than the proposed capacity or the proposed capacity is illegal.
328     *  @see #getCapacity()
329     */
330    public void setCapacity(int capacity) throws IllegalActionException {
331        try {
332            _queue.setCapacity(capacity);
333        } catch (IllegalActionException ex) {
334            throw new IllegalActionException(getContainer(), ex,
335                    "Failed to set capacity to " + capacity);
336        }
337    }
338
339    /** Set the capacity of the history queue. Use 0 to disable the
340     *  history mechanism and INFINITE_CAPACITY to make the history
341     *  capacity unbounded. If the size of the history queue exceeds
342     *  the desired capacity, then remove the oldest tokens from the
343     *  history queue until its size equals the proposed capacity.
344     *  Note that this can be used to clear the history queue by
345     *  supplying 0 as the argument.
346     *  @param capacity The desired history capacity.
347     *  @exception IllegalActionException If the desired capacity is illegal.
348     *  @see #getHistoryCapacity()
349     *  @see #historyElements()
350     *  @see #historySize()
351     */
352    public void setHistoryCapacity(int capacity) throws IllegalActionException {
353        // As of Ptolemy II 2.0.1, we are not using the SDFReceiver.*History*
354        // methods, but these are here for future use.
355        try {
356            _queue.setHistoryCapacity(capacity);
357        } catch (IllegalActionException ex) {
358            throw new IllegalActionException(getContainer(), ex,
359                    "Failed to set history capacity to " + capacity);
360        }
361    }
362
363    /** Return the number of tokens in the receiver.
364     *  @return The number of tokens in the receiver.
365     */
366    public int size() {
367        return _queue.size();
368    }
369
370    ///////////////////////////////////////////////////////////////////
371    ////                         public variables                  ////
372
373    /** A constant indicating that the capacity of the receiver is
374     *  unbounded.
375     */
376    public static final int INFINITE_CAPACITY = ArrayFIFOQueue.INFINITE_CAPACITY;
377
378    /** The number of tokens waiting to be consumed during scheduling. */
379    public int _waitingTokens = 0;
380
381    ///////////////////////////////////////////////////////////////////
382    ////                         private variables                 ////
383    // The queue containing the receiver data.
384    private ArrayFIFOQueue _queue;
385
386    // The token array used by this receiver to return
387    // data.
388    private Token[] _tokenArray;
389}