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}