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}