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}