001/* A queue with constant capacity and optional history. 002 003 Copyright (c) 1997-2018 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.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.InternalErrorException; 039import ptolemy.kernel.util.Nameable; 040 041/////////////////////////////////////////////////////////////////// 042//// ArrayFIFOQueue 043 044/** 045 A first-in, first-out (FIFO) queue with variable capacity and optional 046 history. Objects are appended to the queue with the put() method, 047 and removed from the queue with the take() method. The object 048 removed is the oldest one in the queue. By default, the capacity is 049 infinite, but it can be set to any nonnegative size. If the history 050 capacity is greater than zero (or infinite, by setting the capacity to 051 INFINITE_CAPACITY), then objects removed from the queue are transferred 052 to a history queue rather than simply removed. By default, the history 053 capacity is zero. 054 <p> 055 This queue is implemented as a circular array. When the array becomes full, 056 it is transparently doubled in size. 057 058 @author Steve Neuendorffer, contributor: Brian Hudson 059 @version $Id$ 060 @since Ptolemy II 0.2 061 @Pt.ProposedRating Yellow (neuendor) 062 @Pt.AcceptedRating Yellow (johnr) 063 */ 064public final class ArrayFIFOQueue implements Cloneable { 065 /** Construct an empty queue with no container, and an infinite capacity. 066 */ 067 public ArrayFIFOQueue() { 068 _queueArray = new Object[STARTING_ARRAYSIZE]; 069 _queueMaxCapacity = INFINITE_CAPACITY; 070 } 071 072 /** Construct an empty queue with no container and the given capacity. 073 * @param size The size of the queue. 074 */ 075 public ArrayFIFOQueue(int size) { 076 _queueArray = new Object[size]; 077 _queueMaxCapacity = size; 078 } 079 080 /** Construct an empty queue with the specified container. The 081 * container is only used for error reporting. 082 * @param container The container of the queue. 083 */ 084 public ArrayFIFOQueue(Nameable container) { 085 this(); 086 _container = container; 087 } 088 089 /** Construct an empty queue with the specified container and the 090 * given size. The container is only used for error reporting. 091 * @param container The container of the queue. 092 * @param size The size of the queue. 093 */ 094 public ArrayFIFOQueue(Nameable container, int size) { 095 this(size); 096 _container = container; 097 } 098 099 /** Copy constructor. Create a copy of the specified queue, but 100 * with no container. This is useful to permit enumerations over 101 * a queue while the queue continues to be modified. The objects 102 * in the queue themselves are not cloned. 103 * @param model The queue to be copied. 104 */ 105 public ArrayFIFOQueue(ArrayFIFOQueue model) { 106 this(); 107 108 synchronized (model) { 109 _queueSize = model._queueSize; 110 _queueArray = new Object[model._queueArray.length]; 111 _queueFront = model._queueFront; 112 _queueBack = model._queueBack; 113 System.arraycopy(model._queueArray, 0, _queueArray, 0, 114 _queueArray.length); 115 if (model._historyList != null) { 116 _historyList = new LinkedList(model._historyList); 117 } 118 } 119 } 120 121 /////////////////////////////////////////////////////////////////// 122 //// public methods //// 123 124 /** Clear this queue of any contained objects. 125 */ 126 public void clear() { 127 _queueFront = 0; 128 _queueBack = 0; 129 _queueSize = 0; 130 } 131 132 /** Clone this queue. The cloned queue has no container. The 133 * objects in the queue themselves are not cloned. 134 * @return A clone of this queue 135 */ 136 @Override 137 public Object clone() { 138 return new ArrayFIFOQueue(this); 139 } 140 141 /** Enumerate the objects in the queue, beginning with the oldest. 142 * @return An enumeration of objects. 143 */ 144 public Enumeration elements() { 145 return Collections.enumeration(elementList()); 146 } 147 148 /** Return a list containing all the elements in the queue, beginning 149 * with the oldest. 150 * @return A list of objects 151 */ 152 public List elementList() { 153 LinkedList l = new LinkedList(); 154 int i; 155 156 if (_queueFront < _queueBack || isFull()) { 157 for (i = _queueBack; i < _queueArray.length; i++) { 158 l.addLast(_queueArray[i]); 159 } 160 161 for (i = 0; i < _queueFront; i++) { 162 l.addLast(_queueArray[i]); 163 } 164 } else { 165 for (i = _queueBack; i < _queueFront; i++) { 166 l.addLast(_queueArray[i]); 167 } 168 } 169 170 return l; 171 } 172 173 /** Return an object in the queue or history. The object is not 174 * removed from the queue or history. If the offset argument is 175 * zero, return the oldest object in the queue. If the offset is 176 * 1, return the second oldest object, etc. If there is no such 177 178 * object in the queue (the offset is greater than or equal to 179 * the current queue size), throw an exception. If the argument 180 * is -1, return the most recent object that was put in the 181 * history. If the argument is -2, return the second most recent 182 * object in the history, etc. If there is no such object in the 183 * history (the history capacity is zero or the absolute value 184 * of the offset is greater than the current size of the history 185 * queue), throw an exception. 186 * @param offset The position of the desired object. 187 * @return The desired object in the queue or history. 188 * @exception NoSuchElementException If the offset is out of range. 189 */ 190 public Object get(int offset) throws NoSuchElementException { 191 Object object = null; 192 193 if (offset >= 0) { 194 if (offset >= size()) { 195 String message = "."; 196 197 if (_container != null) { 198 message = " contained by " + _container.getFullName(); 199 } 200 201 throw new NoSuchElementException("No object at offset " + offset 202 + " in the FIFOQueue" + message); 203 } 204 205 int location = _queueBack + offset; 206 207 if (location >= _queueArray.length) { 208 location = location % _queueArray.length; 209 } 210 211 object = _queueArray[location]; 212 } else { 213 if (_historyList != null) { 214 try { 215 object = _historyList.get(historySize() + offset); 216 } catch (Exception ex) { 217 String message = "."; 218 219 if (_container != null) { 220 message = " contained by " + _container.getFullName(); 221 } 222 223 throw new NoSuchElementException("No object at offset " 224 + offset + " in the FIFOQueue" + message); 225 } 226 } else { 227 String message = ""; 228 229 if (_container != null) { 230 message = " contained by " + _container.getFullName(); 231 } 232 233 throw new NoSuchElementException("No object at offset " + offset 234 + " in the FIFOQueue" + message + " has no history."); 235 } 236 } 237 238 return object; 239 } 240 241 /** Return the queue capacity. 242 * This will be INFINITE_CAPACITY if the capacity is infinite. 243 * @return The capacity of the queue. 244 * @see #setCapacity(int) 245 */ 246 public int getCapacity() { 247 return _queueMaxCapacity; 248 } 249 250 /** Return the container of the queue, or null if there is none. 251 * @return The container of the queue. 252 * @see #setContainer(Nameable) 253 */ 254 public Nameable getContainer() { 255 return _container; 256 } 257 258 /** Return the capacity of the history queue. 259 * This will be zero if the history mechanism is disabled and 260 * INFINITE_CAPACITY if the history capacity is infinite. 261 * @return The capacity of the history queue. 262 * @see #setHistoryCapacity(int) 263 */ 264 public int getHistoryCapacity() { 265 return _historyCapacity; 266 } 267 268 /** Enumerate the objects in the history, which are the N most recent 269 * objects taken from the queue, beginning with the oldest, where 270 * N is less than or equal to the history capacity. If the history 271 * capacity is infinite, then the enumeration includes all objects 272 * previously taken from the queue. If the history capacity is zero, 273 * then return an empty enumeration. 274 * @return An enumeration of objects in the history. 275 */ 276 public Enumeration historyElements() { 277 return Collections.enumeration( 278 _historyList != null ? _historyList : Collections.EMPTY_LIST); 279 } 280 281 /** Return the number of objects in the history. 282 * @return The current number of objects in the history. 283 */ 284 public int historySize() { 285 return _historyList != null ? _historyList.size() : 0; 286 } 287 288 /** Return true if the number of objects in the queue is zero. 289 * @return A boolean indicating whether the queue is empty. 290 */ 291 public boolean isEmpty() { 292 return _queueSize == 0; 293 } 294 295 /** Return true if the number of objects in the queue equals the 296 * queue capacity. 297 * @return A boolean indicating whether the queue is full. 298 */ 299 public boolean isFull() { 300 return _queueSize >= _queueArray.length; 301 } 302 303 /** Put an object in the queue and return true if this will not 304 * cause the capacity to be exceeded. Otherwise, do not put 305 * the object in the queue and return false. 306 * @param element An object to be put in the queue. 307 * @return A boolean indicating success. 308 */ 309 public boolean put(Object element) { 310 if (_queueArray.length - _queueSize >= 1) { 311 _queueArray[_queueFront] = element; 312 _queueFront += 1; 313 314 if (_queueFront >= _queueArray.length) { 315 _queueFront = _queueFront % _queueArray.length; 316 } 317 318 _queueSize++; 319 return true; 320 } else { 321 if (_queueMaxCapacity == INFINITE_CAPACITY) { 322 _resizeArray(_queueArray.length * 2); 323 return put(element); 324 } else { 325 return false; 326 } 327 } 328 } 329 330 /** Put an array of objects in the queue and return true if this will not 331 * cause the capacity to be exceeded. Otherwise, do not put 332 * any of the object in the queue and return false. 333 * @param element An array of objects to be put in the queue. 334 * @return A boolean indicating success. 335 */ 336 public boolean putArray(Object[] element) { 337 int count = element.length; 338 return putArray(element, count); 339 } 340 341 /** Put an array of objects in the queue and return true if this will not 342 * cause the capacity to be exceeded. Otherwise, do not put 343 * any of the object in the queue and return false. The 344 * specified number of objects from the array will be put 345 * in the queue. 346 * 347 * @param element An array of objects to be put in the queue. 348 * @param count The number of objects to be put in the queue. 349 * @return A boolean indicating success. 350 */ 351 public boolean putArray(Object[] element, int count) { 352 if (_queueArray.length - _queueSize >= count) { 353 if (count <= _queueArray.length - _queueFront) { 354 System.arraycopy(element, 0, _queueArray, _queueFront, count); 355 _queueFront += count; 356 357 if (_queueFront >= _queueArray.length) { 358 _queueFront = _queueFront % _queueArray.length; 359 } 360 361 _queueSize += count; 362 } else { 363 System.arraycopy(element, 0, _queueArray, _queueFront, 364 _queueArray.length - _queueFront); 365 System.arraycopy(element, _queueArray.length - _queueFront, 366 _queueArray, 0, 367 count - (_queueArray.length - _queueFront)); 368 _queueFront += count; 369 370 if (_queueFront >= _queueArray.length) { 371 _queueFront = _queueFront % _queueArray.length; 372 } 373 374 _queueSize += count; 375 } 376 377 return true; 378 } else { 379 if (_queueMaxCapacity == INFINITE_CAPACITY) { 380 try { 381 _resizeArray(_queueArray.length * 2); 382 } catch (Exception e) { 383 e.printStackTrace(); 384 } 385 386 return putArray(element, count); 387 } else { 388 return false; 389 } 390 } 391 } 392 393 /** Set queue capacity. Use INFINITE_CAPACITY to indicate unbounded 394 * capacity (which is the default). If the current size of the 395 * queue exceeds the desired capacity, throw an exception. 396 * @param capacity The desired capacity. 397 * @exception IllegalActionException If the queue contains more 398 * objects than the proposed capacity or the proposed capacity 399 * is illegal. 400 * @see #getCapacity() 401 */ 402 public void setCapacity(int capacity) throws IllegalActionException { 403 if (capacity == INFINITE_CAPACITY) { 404 _queueMaxCapacity = INFINITE_CAPACITY; 405 return; 406 } 407 408 if (capacity < -1) { 409 throw new IllegalActionException(_container, 410 "Queue Capacity cannot be negative"); 411 } 412 413 if (size() > capacity) { 414 throw new IllegalActionException(_container, "Queue contains " 415 + "more elements than the proposed capacity."); 416 } 417 418 _queueMaxCapacity = capacity; 419 _resizeArray(capacity); 420 } 421 422 /** Set the container of the queue. The container is only used 423 * for error reporting. 424 * @param container The container of this queue. 425 * @see #getContainer() 426 */ 427 public void setContainer(Nameable container) { 428 _container = container; 429 } 430 431 /** Set the capacity of the history queue. Use 0 to disable the 432 * history mechanism and INFINITE_CAPACITY to make the history 433 * capacity unbounded. If the size of the history queue exceeds 434 * the desired capacity, remove the oldest objects from the 435 * history queue until its size equals the proposed capacity. 436 * Note that this can be used to clear the history queue by 437 * supplying 0 as the argument. 438 * @param capacity The desired capacity of the history queue. 439 * @exception IllegalActionException If the desired capacity 440 * is illegal. 441 * @see #getHistoryCapacity() 442 */ 443 public void setHistoryCapacity(int capacity) throws IllegalActionException { 444 if (capacity > 0) { 445 if (_historyList == null) { 446 _historyList = new LinkedList(); 447 } 448 while (_historyList.size() > capacity) { 449 _historyList.removeFirst(); 450 } 451 } else if (capacity == 0) { 452 _historyList.clear(); 453 _historyList = null; 454 } else if (capacity != INFINITE_CAPACITY) { 455 throw new IllegalActionException(_container, 456 "Cannot set history capacity to " + capacity); 457 } 458 459 _historyCapacity = capacity; 460 } 461 462 /** Return the number of objects in the queue. 463 * @return The number of objects in the queue. 464 */ 465 public int size() { 466 return _queueSize; 467 } 468 469 /** Remove the oldest object from the queue and return it. 470 * If there is no such object in the queue (the queue is empty), 471 * throw an exception. If the history mechanism is enabled, 472 * then put the taken object in the history queue. If the capacity 473 * of the history queue would be exceeded by this, then first remove 474 * the oldest object in the history queue. 475 * @return An object from the queue. 476 * @exception NoSuchElementException If the queue is empty. 477 */ 478 public Object take() { 479 Object object = null; 480 481 if (isEmpty()) { 482 String message = ""; 483 484 if (_container != null) { 485 message = " contained by " + _container.getFullName(); 486 } 487 488 throw new NoSuchElementException( 489 "The FIFOQueue" + message + " is empty!"); 490 } 491 492 // Remove it from the buffer. 493 object = _queueArray[_queueBack]; 494 _queueArray[_queueBack] = null; 495 _queueBack++; 496 497 if (_queueBack >= _queueArray.length) { 498 _queueBack = _queueBack % _queueArray.length; 499 } 500 501 _queueSize--; 502 503 // Add it to the history buffer. 504 if (_historyCapacity != 0) { 505 if (_historyList == null) { 506 _historyList = new LinkedList(); 507 } 508 if (_historyCapacity == _historyList.size()) { 509 _historyList.removeFirst(); 510 } 511 512 _historyList.addLast(object); 513 } 514 515 return object; 516 } 517 518 /** Remove the count oldest objects from the queue and return them. 519 * If there is no such object in the queue (the queue is empty), 520 * throw an exception. If the history mechanism is enabled, 521 * then put the taken object in the history queue. If the capacity 522 * of the history queue would be exceeded by this, then first remove 523 * the oldest object in the history queue. 524 * 525 * @param objects An array of objects from the queue. 526 * @exception NoSuchElementException If the queue is empty. 527 */ 528 public void takeArray(Object[] objects) throws NoSuchElementException { 529 int count = objects.length; 530 takeArray(objects, count); 531 } 532 533 /** Remove the count oldest objects from the queue and return them. 534 * If there is no such object in the queue (the queue is empty), 535 * throw an exception. If the history mechanism is enabled, 536 * then put the taken object in the history queue. If the capacity 537 * of the history queue would be exceeded by this, then first remove 538 * the oldest object in the history queue. 539 * 540 * @param objects An array of objects from the queue. 541 * @param count The number of objects to return. 542 * @exception NoSuchElementException If the queue is empty. 543 */ 544 public void takeArray(Object[] objects, int count) 545 throws NoSuchElementException { 546 if (size() < count) { 547 String message = ""; 548 549 if (_container != null) { 550 message = " contained by " + _container.getFullName(); 551 } 552 553 throw new NoSuchElementException("The FIFOQueue" + message 554 + " does not contain enough elements!"); 555 } 556 557 if (count <= _queueArray.length - _queueBack) { 558 System.arraycopy(_queueArray, _queueBack, objects, 0, count); 559 } else { 560 System.arraycopy(_queueArray, _queueBack, objects, 0, 561 _queueArray.length - _queueBack); 562 System.arraycopy(_queueArray, 0, objects, 563 _queueArray.length - _queueBack, 564 count - (_queueArray.length - _queueBack)); 565 } 566 567 _queueBack += count; 568 569 if (_queueBack >= _queueArray.length) { 570 _queueBack = _queueBack % _queueArray.length; 571 } 572 573 _queueSize -= count; 574 575 if (_historyCapacity != 0) { 576 if (_historyCapacity == _historyList.size()) { 577 _historyList.removeFirst(); 578 } 579 580 _historyList.addLast(objects); 581 } 582 } 583 584 /////////////////////////////////////////////////////////////////// 585 //// public variables //// 586 587 /** Used to indicate that the size of the queue or the history 588 * queue is infinite. 589 */ 590 public static final int INFINITE_CAPACITY = -1; 591 592 /** 593 * The default capacity of the queue. 594 */ 595 public static final int DEFAULT_CAPACITY = INFINITE_CAPACITY; 596 597 /** 598 * The starting size of the circular buffer, if the capacity is 599 * infinite. 600 */ 601 public static final int STARTING_ARRAYSIZE = 4; 602 603 /** 604 * The default capacity of the history queue. 605 */ 606 public static final int DEFAULT_HISTORY_CAPACITY = 0; 607 608 /////////////////////////////////////////////////////////////////// 609 //// private methods //// 610 611 /** Resize the internal circular array to have the given size. 612 * @exception InternalErrorException If the proposed size is greater than 613 * the declared maximum size, or if the queue contains more 614 * objects than the proposed size or the proposed size 615 * is illegal. 616 */ 617 private void _resizeArray(int newSize) { 618 if (newSize < 0) { 619 throw new InternalErrorException( 620 "Buffer size of " + newSize + " is less than zero."); 621 } 622 623 if (size() > newSize) { 624 throw new InternalErrorException("Queue contains " 625 + "more elements than the proposed array size."); 626 } 627 628 if (_queueMaxCapacity != INFINITE_CAPACITY 629 && newSize > _queueMaxCapacity) { 630 throw new InternalErrorException("The proposed" 631 + " array size exceeds the maximum declared queue size."); 632 } 633 634 Object[] newArray = new Object[newSize]; 635 636 if (newSize == 0) { 637 _queueFront = 0; 638 } else if (_queueFront < _queueBack || isFull()) { 639 System.arraycopy(_queueArray, _queueBack, newArray, 0, 640 _queueArray.length - _queueBack); 641 System.arraycopy(_queueArray, 0, newArray, 642 _queueArray.length - _queueBack, _queueFront); 643 _queueFront = _queueArray.length - _queueBack + _queueFront; 644 645 // NOTE: The following is probably not needed, but paranoid programming. 646 if (_queueFront >= newArray.length) { 647 _queueFront = _queueFront % newArray.length; 648 } 649 } else { 650 System.arraycopy(_queueArray, _queueBack, newArray, 0, 651 _queueFront - _queueBack); 652 _queueFront = _queueFront - _queueBack; 653 654 if (_queueFront >= newArray.length) { 655 _queueFront = _queueFront % newArray.length; 656 } 657 } 658 659 _queueArray = newArray; 660 _queueBack = 0; 661 } 662 663 /////////////////////////////////////////////////////////////////// 664 //// private variables //// 665 666 /** The container, if there is one. */ 667 private Nameable _container = null; 668 669 /** The maximum capacity of the queue. */ 670 private int _queueMaxCapacity = INFINITE_CAPACITY; 671 672 /** The list of objects currently in the queue. */ 673 private Object[] _queueArray; 674 675 /** The location of the next place to insert in _queueArray. */ 676 private int _queueFront = 0; 677 678 /** The location of the next place to remove from _queueArray. */ 679 private int _queueBack = 0; 680 681 /** The number of elements in the queue. */ 682 private int _queueSize = 0; 683 684 /** The capacity of the history queue, defaulting to zero. */ 685 private int _historyCapacity = DEFAULT_HISTORY_CAPACITY; 686 687 /** The list of objects recently removed from the queue. */ 688 private LinkedList _historyList = null; 689}