001/* An abstract implementation of the Receiver interface 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; 030 031import java.util.List; 032 033import ptolemy.actor.util.Time; 034import ptolemy.data.Token; 035import ptolemy.kernel.util.IllegalActionException; 036 037/////////////////////////////////////////////////////////////////// 038//// AbstractReceiver 039 040/** 041 An abstract implementation of the Receiver interface. 042 The container methods and some of the more esoteric 043 methods are implemented, while the most 044 domain-specific methods are left undefined. 045 Note that the NoTokenException and NoRoomException exceptions 046 that are thrown by several of the methods are 047 runtime exceptions, so they need not be declared explicitly by 048 the caller. 049 050 @author Steve Neuendorffer 051 @version $Id$ 052 @since Ptolemy II 1.0 053 @Pt.ProposedRating Green (eal) 054 @Pt.AcceptedRating Green (bart) 055 @see ptolemy.actor.Receiver 056 */ 057public abstract class AbstractReceiver implements Receiver { 058 /** Construct an empty receiver with no container. 059 */ 060 public AbstractReceiver() { 061 } 062 063 /** Construct an empty receiver with the specified container. 064 * @param container The container of the receiver. 065 * @exception IllegalActionException If the container does 066 * not accept this receiver. 067 */ 068 public AbstractReceiver(IOPort container) throws IllegalActionException { 069 setContainer(container); 070 } 071 072 /////////////////////////////////////////////////////////////////// 073 //// public methods //// 074 075 /** Throw an exception. By default, a receiver that extends this 076 * class does not support this method. 077 * @exception IllegalActionException Always thrown. 078 */ 079 @Override 080 public void clear() throws IllegalActionException { 081 throw new IllegalActionException(getContainer(), "Receiver class " 082 + getClass().getName() + " does not support clear()."); 083 } 084 085 /** Return a list with tokens that are currently in the receiver 086 * available for get() or getArray(). 087 * @return A list of instances of Token. 088 * @exception IllegalActionException Always thrown in this base class. 089 */ 090 @Override 091 public List<Token> elementList() throws IllegalActionException { 092 throw new IllegalActionException(_container, 093 "Receiver does not implement elementList()"); 094 } 095 096 /** Get a token from this receiver. 097 * @exception NoTokenException If there is no token. 098 */ 099 @Override 100 public abstract Token get() throws NoTokenException; 101 102 /** Get an array of tokens from this receiver. 103 * The <i>numberOfTokens</i> argument specifies the number 104 * of tokens to get. 105 * The length of the returned array will be equal to 106 * <i>numberOfTokens</i>. 107 * <p> 108 * This implementation works by calling get() repeatedly 109 * to populate an array. Derived classes may offer more 110 * efficient implementations. This implementation has two 111 * key limitations: 112 * <ul> 113 * <li> The same array is reused on the next call to 114 * this method. Thus, the caller needs to ensure that 115 * it has accessed all the tokens it needs before the 116 * next call to this method occurs. 117 * <li> The method is not synchronized. 118 * </ul> 119 * These two limitations mean that this implementation 120 * is not suitable for multithreaded domains 121 * where there might be multiple threads reading from 122 * the same receiver. It <i>is</i> suitable, however, 123 * for multithreaded domains where only one thread 124 * is reading from the receiver. This is true even if 125 * a separate thread is writing to the receiver, as long 126 * as the put() and get() methods are properly synchronized. 127 * 128 * @param numberOfTokens The number of tokens to get. 129 * @return The array of tokens. 130 * @exception NoTokenException If there are not <i>numberOfTokens</i> 131 * tokens available. Note that if this exception is thrown, then 132 * it is possible that some tokens will have been already extracted 133 * from the receiver by the calls to get(). These tokens will be 134 * lost. They will not be used on the next call to getArray(). 135 * Thus, it is highly advisable to call hasToken(int) before 136 * calling this method. 137 */ 138 @Override 139 public Token[] getArray(int numberOfTokens) throws NoTokenException { 140 // Check whether we need to reallocate the cached 141 // token array. 142 if (_tokenCache == null || numberOfTokens != _tokenCache.length) { 143 // Reallocate the token array. 144 _tokenCache = new Token[numberOfTokens]; 145 } 146 147 for (int i = 0; i < numberOfTokens; i++) { 148 try { 149 _tokenCache[i] = get(); 150 } catch (NoTokenException ex) { 151 String cardinality = "th"; 152 if (i + 1 == 1) { 153 cardinality = "st"; 154 } else if (i + 1 == 2) { 155 cardinality = "nd"; 156 } else if (i + 1 == 3) { 157 cardinality = "rd"; 158 } 159 throw new NoTokenException(getContainer(), ex, 160 "Failed to get the " + (i + 1) + cardinality 161 + " token of " + numberOfTokens); 162 163 } 164 } 165 166 return _tokenCache; 167 } 168 169 /** Return the container of this receiver, or null if there is none. 170 * @return The port containing this receiver. 171 * @see #setContainer(IOPort) 172 */ 173 @Override 174 public IOPort getContainer() { 175 return _container; 176 } 177 178 /** Return the current time associated with this receiver. For 179 * non-DT receivers, this method reverts to the director's 180 * getCurrentTime() method. In DT, there is a local time 181 * associated with every receiver. 182 * @return The current time associated with this receiver. 183 * @deprecated As of Ptolemy II 4.1, replaced by 184 * {@link #getModelTime()} 185 */ 186 @Deprecated 187 public double getCurrentTime() { 188 return getModelTime().getDoubleValue(); 189 } 190 191 /** Return the current time associated with this receiver. For 192 * non-DT receivers, this method reverts to the director's 193 * getCurrentTime() method. In DT, there is a local time 194 * associated with every receiver. 195 * @return The current time associated with this receiver. 196 */ 197 public Time getModelTime() { 198 IOPort containerPort = getContainer(); 199 Actor containerActor = (Actor) containerPort.getContainer(); 200 Director containerDirector = containerActor.getDirector(); 201 return containerDirector.getModelTime(); 202 } 203 204 /** Return true if the receiver has room to put a token into it 205 * (via the put() method). 206 * Returning true in this method guarantees that the next call to 207 * put() will not result in an exception. 208 * @return True if the next call to put() will not result in a 209 * NoRoomException. 210 */ 211 @Override 212 public abstract boolean hasRoom(); 213 214 /** Return true if the receiver has room to put the specified number of 215 * tokens into it (via the put() method). 216 * Returning true in this method guarantees that the next 217 * <i>numberOfTokens</i> calls to put() or a corresponding call 218 * to putArray() will not result in an exception. 219 * @param numberOfTokens The number of tokens to put into this receiver. 220 * @return True if the next <i>numberOfTokens</i> calls to put() 221 * will not result in a NoRoomException. 222 */ 223 @Override 224 public abstract boolean hasRoom(int numberOfTokens); 225 226 /** Return true if the receiver contains a token that can be obtained 227 * by calling the get() method. In an implementation, 228 * returning true in this method guarantees that the next 229 * call to get() will not result in an exception. 230 * @return True if the next call to get() will not result in a 231 * NoTokenException. 232 */ 233 @Override 234 public abstract boolean hasToken(); 235 236 /** Return true if the receiver contains the specified number of tokens. 237 * In an implementation, returning true in this method guarantees 238 * that the next <i>numberOfTokens</i> calls to get(), or a 239 * corresponding call to getArray(), will not result in an exception. 240 * @param numberOfTokens The number of tokens desired. 241 * @return True if the next <i>numberOfTokens</i> calls to get() 242 * will not result in a NoTokenException. 243 */ 244 @Override 245 public abstract boolean hasToken(int numberOfTokens); 246 247 /** Return <i>true</i>. Most domains have no notion of the state of 248 * the receiver being unknown. It is always known whether there is 249 * a token available. Certain domains with fixed point semantics, 250 * however, such as SR, will need to override this method. 251 * @return True. 252 */ 253 @Override 254 public boolean isKnown() { 255 return true; 256 } 257 258 /** Put the specified token into this receiver. 259 * If the specified token is null, this can be interpreted by 260 * a receiver as an assertion that no token to be sent in the 261 * current round (for domains that have a notion of absent values 262 * and a current round). 263 * @param token The token to put into the receiver, or null to 264 * put no token. 265 * @exception NoRoomException If there is no room in the receiver. 266 * @exception IllegalActionException If the put fails 267 * (e.g. because of incompatible types). 268 */ 269 @Override 270 public abstract void put(Token token) 271 throws NoRoomException, IllegalActionException; 272 273 /** Put a portion of the specified token array into this receiver. 274 * The first <i>numberOfTokens</i> elements of the token array are put 275 * into this receiver by repeated calling put(). 276 * The ability to specify a longer array than 277 * needed allows certain domains to have more efficient implementations. 278 * <p> 279 * This implementation works by calling put() repeatedly. 280 * The caller may feel free to reuse the array after this method returns. 281 * Derived classes may offer more efficient implementations. 282 * This implementation is not synchronized, so it 283 * is not suitable for multithreaded domains 284 * where there might be multiple threads writing to 285 * the same receiver. It <i>is</i> suitable, however, 286 * for multithreaded domains where only one thread 287 * is writing to the receiver. This is true even if 288 * a separate thread is reading from the receiver, as long 289 * as the put() and get() methods are properly synchronized. 290 * 291 * @param tokenArray The array containing tokens to put into this 292 * receiver. 293 * @param numberOfTokens The number of elements of the token 294 * array to put into this receiver. 295 * @exception NoRoomException If the token array cannot be put. 296 * @exception IllegalActionException If the token is not acceptable 297 * to one of the ports (e.g., wrong type). 298 */ 299 @Override 300 public void putArray(Token[] tokenArray, int numberOfTokens) 301 throws NoRoomException, IllegalActionException { 302 IOPort container = getContainer(); 303 304 // If there is no container, then perform no conversion. 305 if (container == null) { 306 for (int i = 0; i < numberOfTokens; i++) { 307 put(tokenArray[i]); 308 } 309 } else { 310 for (int i = 0; i < numberOfTokens; i++) { 311 put(container.convert(tokenArray[i])); 312 } 313 } 314 } 315 316 /** Put a sequence of tokens to all receivers in the specified array. 317 * Implementers will assume that all such receivers 318 * are of the same class. 319 * This method simply calls {@link #putArray(Token[], int)} 320 * on each receiver in the specified array, after appropriate 321 * type conversion. It also implements the functionality of 322 * ConstantPublisherPort in that it will replace the specified 323 * token with a constant value if the destination is marked to 324 * receive a constant value, and it will drop the token 325 * altogether if the destination has already received all the 326 * constant tokens it expects to receive. 327 * Note that subclasses that override this method will also 328 * have to implement this if they wish to support 329 * ConstantPublisherPort. 330 * @param tokens The sequence of token to put. 331 * @param numberOfTokens The number of tokens to put (the array might 332 * be longer). 333 * @param receivers The receivers. 334 * @exception NoRoomException If there is no room for the token. 335 * @exception IllegalActionException If the token is not acceptable 336 * to one of the ports (e.g., wrong type), or if the tokens array 337 * does not have at least the specified number of tokens. 338 */ 339 @Override 340 public void putArrayToAll(Token[] tokens, int numberOfTokens, 341 Receiver[] receivers) 342 throws NoRoomException, IllegalActionException { 343 if (numberOfTokens > tokens.length) { 344 IOPort container = getContainer(); 345 throw new IllegalActionException(container, 346 "Not enough tokens supplied."); 347 } 348 349 for (Receiver receiver : receivers) { 350 IOPort container = receiver.getContainer(); 351 // For each receiver, check to see whether the destination port 352 // is marked to receive constant data from a ConstantPublisherPort, 353 // and whether it has already received as many tokens as it expects. 354 if (container != null && container._constantToken != null) { 355 for (int i = 0; i < numberOfTokens; i++) { 356 if (container._constantLimit >= 0 357 && container._constantTokensSent >= container._constantLimit) { 358 // Do not put the token. The finite number has been reached. 359 break; 360 } else { 361 receiver.put( 362 container.convert(container._constantToken)); 363 container._constantTokensSent++; 364 } 365 } 366 } else { 367 // The following will do the conversion. 368 receiver.putArray(tokens, numberOfTokens); 369 } 370 } 371 } 372 373 /** Put to all receivers in the specified array. 374 * This method simply calls {@link #put(Token)} 375 * on each receiver in the specified array, after appropriate 376 * type conversion. It also implements the functionality of 377 * ConstantPublisherPort in that it will replace the specified 378 * token with a constant value if the destination is marked to 379 * receive a constant value, and it will drop the token 380 * altogether if the destination has already received all the 381 * constant tokens it expects to receive. 382 * Note that subclasses that override this method will also 383 * have to implement this if they wish to support 384 * ConstantPublisherPort. 385 * @param token The token to put, or null to put no token. 386 * @param receivers The receivers. 387 * @exception NoRoomException If there is no room for the token. 388 * @exception IllegalActionException If the token is not acceptable 389 * to one of the ports (e.g., wrong type). 390 */ 391 @Override 392 public void putToAll(Token token, Receiver[] receivers) 393 throws NoRoomException, IllegalActionException { 394 for (Receiver receiver : receivers) { 395 IOPort container = receiver.getContainer(); 396 // For each receiver, check to see whether the destination port 397 // is marked to receive constant data from a ConstantPublisherPort, 398 // and whether it has already received as many tokens as it expects. 399 if (container != null && container._constantToken != null) { 400 if (container._constantLimit >= 0 401 && container._constantTokensSent >= container._constantLimit) { 402 // Do not put the token. The finite number has been reached. 403 continue; 404 } else { 405 receiver.put(container.convert(container._constantToken)); 406 container._constantTokensSent++; 407 } 408 } else { 409 // If there is no container, then perform no conversion. 410 if (container == null || token == null) { 411 receiver.put(token); 412 } else { 413 receiver.put(container.convert(token)); 414 } 415 } 416 } 417 } 418 419 /** Reset this receiver to its initial state, which in this base 420 * class is the same as calling clear(). 421 * @exception IllegalActionException If reset() is not supported by 422 * the domain. 423 */ 424 @Override 425 public void reset() throws IllegalActionException { 426 clear(); 427 } 428 429 /** Set the container. 430 * @param port The container. 431 * @exception IllegalActionException If the container is not of 432 * an appropriate subclass of IOPort. Not thrown in this base class, 433 * but may be thrown in derived classes. 434 * @see #getContainer() 435 */ 436 @Override 437 public void setContainer(IOPort port) throws IllegalActionException { 438 _container = port; 439 } 440 441 /** Return the class name and the full name of the object, 442 * with syntax "className {fullName}". 443 * @return The class name and the full name. */ 444 @Override 445 public String toString() { 446 IOPort container = getContainer(); 447 return getClass().getName() + " {" 448 + (container != null ? container.getFullName() : "") 449 + ".receiver }"; 450 } 451 452 /////////////////////////////////////////////////////////////////// 453 //// private variables //// 454 // The container. 455 private IOPort _container; 456 457 // The cache used by the getArray() method to avoid reallocating. 458 private Token[] _tokenCache; 459}