001/* A queue 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.util;
030
031import java.util.Collections;
032import java.util.Enumeration;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.NoSuchElementException;
036
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.Nameable;
039
040///////////////////////////////////////////////////////////////////
041//// FIFOQueue
042
043/**
044 A first-in, first-out (FIFO) queue with variable capacity and optional
045 history. Objects are appended to the queue with the put() method,
046 and removed from the queue with the take() method. The object
047 removed is the oldest one in the queue. By default, the capacity is
048 infinite, but it can be set to any nonnegative size. If the history
049 capacity is greater than zero (or infinite, by setting the capacity to
050 INFINITE_CAPACITY), then objects removed from the queue are transferred
051 to a history queue rather than simply removed. By default, the history
052 capacity is zero.
053
054 @author Edward A. Lee, Xiaojun Liu
055 @version $Id$
056 @since Ptolemy II 0.2
057 @Pt.ProposedRating Green (eal)
058 @Pt.AcceptedRating Green (liuj)
059 */
060public class FIFOQueue implements Cloneable {
061    /** Construct an empty queue with no container.
062     */
063    public FIFOQueue() {
064        _queueList = new LinkedList();
065        _historyList = new LinkedList();
066    }
067
068    /** Construct an empty queue with the specified container. The
069     *  container is only used for error reporting.
070     *  @param container The container of the queue.
071     */
072    public FIFOQueue(Nameable container) {
073        this();
074        _container = container;
075    }
076
077    /** Copy constructor. Create a copy of the specified queue, but
078     *  with no container. This is useful to permit enumerations over
079     *  a queue while the queue continues to be modified. The objects
080     *  in the queue themselves are not cloned.
081     *  @param model The queue to be copied.
082     */
083    public FIFOQueue(FIFOQueue model) {
084        this();
085
086        synchronized (model) {
087            _queueList.addAll(model.elementList());
088            _historyList.addAll(model.historyElementList());
089        }
090    }
091
092    ///////////////////////////////////////////////////////////////////
093    ////                         public methods                    ////
094
095    /** Remove all items currently stored in the queue and
096     *  clear the history queue. The queue capacity, history
097     *  capacity and container remain unchanged.
098     */
099    public void clear() {
100        _queueList.clear();
101        _historyList.clear();
102    }
103
104    /** Clone this queue. The cloned queue has no container. The
105     *  objects in the queue themselves are not cloned.
106     *  @return A clone of this queue.
107     *  @exception CloneNotSupportedException If thrown by object.clone().F
108     */
109    @Override
110    public Object clone() throws CloneNotSupportedException {
111        // This method used to not call super.clone() and return "new FIFOQueue(this)"
112        // FindBugs reports this as:
113        // "clone method does not call super.clone()"
114
115        //  "This non-final class defines a clone() method that does
116        //  not call super.clone(). If this class ("A") is extended by
117        //  a subclass ("B"), and the subclass B calls super.clone(),
118        //  then it is likely that B's clone() method will return an
119        //  object of type A, which violates the standard contract for
120        //  clone()."
121
122        // "If all clone() methods call super.clone(), then they are
123        // guaranteed to use Object.clone(), which always returns an
124        // object of the correct type."
125
126        // Instead we call super.clone() and then adjust the fields of the clone.
127
128        FIFOQueue returnValue = (FIFOQueue) super.clone();
129        returnValue._queueList = new LinkedList();
130        returnValue._historyList = new LinkedList();
131        synchronized (this) {
132            returnValue._queueList.addAll(this.elementList());
133            returnValue._historyList.addAll(this.historyElementList());
134        }
135        return returnValue;
136    }
137
138    /** List the objects in the queue, beginning with the oldest.
139     *  @return A list of objects.
140     */
141    public List elementList() {
142        return _queueList;
143    }
144
145    /** Enumerate the objects in the queue, beginning with the oldest.
146     *  This method is deprecated and calls elementList()
147     *  @return An enumeration of objects.
148     *  @deprecated Used elementList() instead.
149     */
150    @Deprecated
151    public Enumeration elements() {
152        return Collections.enumeration(_queueList);
153    }
154
155    /** Return an object in the queue or history. The object is not
156     *  removed from the queue or history. If the offset argument is
157     *  zero, return the oldest object in the queue. If the offset is
158     *  1, return the second oldest object, etc. If there is no such
159     *  object in the queue (the offset is greater than or equal to
160     *  the current queue size), throw an exception. If the argument
161     *  is -1, return the most recent object that was put in the
162     *  history. If the argument is -2, return the second most recent
163     *  object in the history, etc. If there is no such object in the
164     *  history (the history capacity is zero or the absolute value
165     *  of the offset is greater than the current size of the history
166     *  queue), throw an exception.
167     *  @param offset The position of the desired object.
168     *  @return The desired object in the queue or history.
169     *  @exception NoSuchElementException If the offset is out of range.
170     */
171    public Object get(int offset) throws NoSuchElementException {
172        Object resultObject = null;
173
174        try {
175            if (offset >= 0) {
176                resultObject = _queueList.get(offset);
177            } else {
178                resultObject = _historyList.get(historySize() + offset);
179            }
180        } catch (IndexOutOfBoundsException ex) {
181            String str = ".";
182
183            if (_container != null) {
184                str = " contained by " + _container.getFullName();
185            }
186
187            throw new NoSuchElementException("No object at offset " + offset
188                    + " in the FIFOQueue" + str);
189        }
190
191        return resultObject;
192    }
193
194    /** Return the queue capacity, or INFINITE_CAPACITY if it is unbounded.
195     *  @return The capacity of the queue.
196     *  @see #setCapacity
197     */
198    public int getCapacity() {
199        return _queueCapacity;
200    }
201
202    /** Return the container of the queue, or null if there is none.
203     *  @return The container of the queue.
204     *  @see #setContainer
205     */
206    public Nameable getContainer() {
207        return _container;
208    }
209
210    /** Return the capacity of the history queue.
211     *  This will be zero if the history mechanism is disabled and
212     *  INFINITE_CAPACITY if the history capacity is infinite.
213     *  @return The capacity of the history queue.
214     *  @see #setHistoryCapacity
215     */
216    public int getHistoryCapacity() {
217        return _historyCapacity;
218    }
219
220    /** List the objects in the history, which are the N most recent
221     *  objects taken from the queue, beginning with the oldest, where
222     *  N is less than or equal to the history capacity. If the history
223     *  capacity is infinite, then the list includes all objects
224     *  previously taken from the queue. If the history capacity is zero,
225     *  then return an empty list.
226     *  @return A list of objects in the history.
227     */
228    public List historyElementList() {
229        return _historyList;
230    }
231
232    /** Enumerate the objects in the history, which are the N most recent
233     *  objects taken from the queue, beginning with the oldest, where
234     *  N is less than or equal to the history capacity. This method is
235     *  deprecated and calls historyElementList().
236     *  @return An enumeration of objects in the history.
237     *  @deprecated Use historyElementList() instead.
238     */
239    @Deprecated
240    public Enumeration historyElements() {
241        return Collections.enumeration(_historyList);
242    }
243
244    /** Return the number of objects in the history.
245     *  @return The current number of objects in the history.
246     */
247    public int historySize() {
248        return _historyList.size();
249    }
250
251    /** Return true if the number of objects in the queue equals the
252     *  queue capacity.
253     *  @return A boolean indicating whether the queue is full.
254     */
255    public boolean isFull() {
256        return _queueList.size() == _queueCapacity;
257    }
258
259    /** Put an object in the queue and return true if this will not
260     *  cause the capacity to be exceeded. Otherwise, do not put
261     *  the object in the queue and return false.
262     *  @param element An object to be put in the queue.
263     *  @return A boolean indicating success.
264     */
265    public boolean put(Object element) {
266        if (_queueCapacity == INFINITE_CAPACITY
267                || _queueCapacity > _queueList.size()) {
268            _queueList.addLast(element);
269            return true;
270        } else {
271            return false;
272        }
273    }
274
275    /** Set queue capacity. Use INFINITE_CAPACITY to indicate unbounded
276     *  capacity (which is the default). If the current size of the
277     *  queue exceeds the desired capacity, throw an exception.
278     *  @param capacity The desired capacity.
279     *  @exception IllegalActionException If the queue contains more
280     *   objects than the proposed capacity or the proposed capacity
281     *   is illegal.
282     *  @see #getCapacity
283     */
284    public void setCapacity(int capacity) throws IllegalActionException {
285        if (capacity < 0 && capacity != INFINITE_CAPACITY) {
286            throw new IllegalActionException(_container,
287                    "Cannot set queue capacity to " + capacity);
288        }
289
290        if (capacity != INFINITE_CAPACITY && size() > capacity) {
291            throw new IllegalActionException(_container,
292                    "Queue contains more elements than the proposed capacity.");
293        }
294
295        _queueCapacity = capacity;
296    }
297
298    /** Set the container of the queue. The container is only used
299     *  for error reporting.
300     *  @param container The container of this queue.
301     *  @see #getContainer
302     */
303    public void setContainer(Nameable container) {
304        _container = container;
305    }
306
307    /** Set the capacity of the history queue. Use 0 to disable the
308     *  history mechanism and INFINITE_CAPACITY to make the history
309     *  capacity unbounded. If the size of the history queue exceeds
310     *  the desired capacity, remove the oldest objects from the
311     *  history queue until its size equals the proposed capacity.
312     *  Note that this can be used to clear the history queue by
313     *  supplying 0 as the argument.
314     *  @param capacity The desired capacity of the history queue.
315     *  @exception IllegalActionException If the desired capacity
316     *   is illegal.
317     *  @see #getHistoryCapacity
318     */
319    public void setHistoryCapacity(int capacity) throws IllegalActionException {
320        if (capacity > 0) {
321            while (_historyList.size() > capacity) {
322                _historyList.removeFirst();
323            }
324        } else if (capacity == 0) {
325            _historyList.clear();
326        } else if (capacity != INFINITE_CAPACITY) {
327            throw new IllegalActionException(_container,
328                    "Cannot set history capacity to " + capacity);
329        }
330
331        _historyCapacity = capacity;
332    }
333
334    /** Return the number of objects in the queue.
335     *  @return The number of objects in the queue.
336     */
337    public int size() {
338        return _queueList.size();
339    }
340
341    /** Remove the oldest object from the queue and return it.
342     *  If there is no such object in the queue (the queue is empty),
343     *  throw an exception. If the history mechanism is enabled,
344     *  then put the taken object in the history queue. If the capacity
345     *  of the history queue would be exceeded by this, then first remove
346     *  the oldest object in the history queue.
347     *  @return An object from the queue.
348     *  @exception NoSuchElementException If the queue is empty.
349     */
350    public Object take() throws NoSuchElementException {
351        Object resultObject = null;
352
353        try {
354            resultObject = _queueList.removeFirst();
355        } catch (NoSuchElementException ex) {
356            String str = "";
357
358            if (_container != null) {
359                str = " contained by " + _container.getFullName();
360            }
361
362            throw new NoSuchElementException(
363                    "The FIFOQueue" + str + " is empty!");
364        }
365
366        if (_historyCapacity != 0) {
367            if (_historyCapacity == _historyList.size()) {
368                _historyList.removeFirst();
369            }
370
371            _historyList.addLast(resultObject);
372        }
373
374        return resultObject;
375    }
376
377    ///////////////////////////////////////////////////////////////////
378    ////                         public variables                  ////
379
380    /** Used to indicate that the size of the queue or the history
381     *  queue is infinite.
382     */
383    public static final int INFINITE_CAPACITY = -1;
384
385    ///////////////////////////////////////////////////////////////////
386    ////                         private variables                 ////
387    // The container, if there is one.
388    private Nameable _container = null;
389
390    // The capacity of the queue, defaulting to infinite.
391    private int _queueCapacity = INFINITE_CAPACITY;
392
393    // The list of objects currently in the queue.
394    private LinkedList _queueList;
395
396    // The capacity of the history queue, defaulting to zero.
397    private int _historyCapacity = 0;
398
399    // The list of objects recently removed from the queue.
400    private LinkedList _historyList = null;
401}