001/* The receiver for use with FixedPointDirector or any of its subclasses.
002
003 Copyright (c) 2006-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 */
028package ptolemy.actor.sched;
029
030import java.util.LinkedList;
031import java.util.List;
032
033import ptolemy.actor.AbstractReceiver;
034import ptolemy.actor.IOPort;
035import ptolemy.actor.NoTokenException;
036import ptolemy.data.Token;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.InvalidStateException;
039
040///////////////////////////////////////////////////////////////////
041//// FixedPointReceiver
042
043/**
044 The receiver for use with FixedPointDirector or any of its subclasses.
045 This receiver has capacity 1.
046 The status of this receiver can be either <i>known</i> or <i>unknown</i>.
047 If it is known, then it can be either <i>present</i> or <i>absent</i>.
048 If it is present, then it has a token, which provides a value.
049 <p>
050 At first, an instance of this class has status unknown.
051 The clear() method makes the status known and absent.
052 The put() method makes the status known and present, and provides a value.
053 The reset() method reverts the status to unknown.
054 Once the status of a receiver becomes known, the value
055 cannot be changed, nor can the status be changed from present to absent
056 or vice versa. To change the value or the status, call reset() first.
057 Normally, the reset() method is called only by the director and constructors.
058 <p>
059 The isKnown() method returns true if the receiver has status known.
060 The hasRoom() method returns true if the receiver has status unknown.
061 If the receiver has a known status, the hasToken() method returns true
062 if the receiver contains a token. If the receiver has an unknown status,
063 the hasToken() method will throw an InvalidStateException.
064 <p>
065 This class is based on the original SRReceiver, written by Paul Whitaker.
066
067 @author Haiyang Zheng and Edward A. Lee
068 @version $Id$
069 @since Ptolemy II 5.2
070 @Pt.ProposedRating Green (hyzheng)
071 @Pt.AcceptedRating Yellow (eal)
072 */
073public class FixedPointReceiver extends AbstractReceiver {
074
075    /** Construct an FixedPointReceiver with unknown status.
076     *  This constructor does not need a director.
077     */
078    public FixedPointReceiver() {
079        this(null);
080    }
081
082    /** Construct an FixedPointReceiver with unknown status.
083     *  @param director The director of this receiver.
084     */
085    public FixedPointReceiver(FixedPointDirector director) {
086        super();
087        reset();
088        _director = director;
089    }
090
091    ///////////////////////////////////////////////////////////////////
092    ////                         public methods                    ////
093
094    /** Set the status of this receiver to be known and absent.
095     *  @exception IllegalActionException If this receiver is known and
096     *   present.
097     */
098    @Override
099    public void clear() throws IllegalActionException {
100        if (isKnown()) {
101            if (hasToken()) {
102                throw new IllegalActionException(getContainer(),
103                        "Cannot change the status from present"
104                                + " to absent.");
105            }
106        } else {
107            _token = null;
108            _known = true;
109            if (_director != null) {
110                _director._receiverChanged();
111            }
112        }
113    }
114
115    /** Return a list with the token currently in the receiver, or
116     *  an empty list if there is no such token.
117     *  @return A list of instances of Token.
118     */
119    @Override
120    public List<Token> elementList() {
121        List<Token> result = new LinkedList<Token>();
122        if (_token != null) {
123            result.add(_token);
124        }
125        return result;
126    }
127
128    /** Return the contained token.  If there is no token or the status
129     *  of this receiver is unknown, throw an exception.
130     *  @return The token contained in the receiver.
131     *  @exception NoTokenException If there is no token.
132     *  @exception InvalidStateException If the status is unknown.
133     */
134    @Override
135    public Token get() throws NoTokenException {
136        if (!isKnown()) {
137            throw new InvalidStateException(
138                    "FixedPointReceiver: get() called on an "
139                            + "FixedPointReceiver " + "with status unknown.");
140        }
141        if (_token == null) {
142            throw new NoTokenException(_director,
143                    "FixedPointReceiver: Attempt to get data from an "
144                            + "empty receiver.");
145        }
146        return _token;
147    }
148
149    /** Return true if the status of the receiver is unknown.
150     *  @return True if the status of the receiver is unknown.
151     *  @see #isKnown()
152     */
153    @Override
154    public boolean hasRoom() {
155        return !isKnown();
156    }
157
158    /** If the argument is 1, return true if the status of the receiver
159     *  is unknown. Otherwise, throw an exception. This receiver has
160     *  capacity one.
161     *  @param numberOfTokens The number of tokens to put into the receiver.
162     *  @return True if the receiver can accept a token.
163     *  @exception IllegalArgumentException If the argument is not positive.
164     *  @see #isKnown()
165     *  @see #hasRoom()
166     */
167    @Override
168    public boolean hasRoom(int numberOfTokens) throws IllegalArgumentException {
169        if (numberOfTokens < 1) {
170            throw new IllegalArgumentException(
171                    "FixedPointReceiver: hasRoom() requires a "
172                            + "positive argument.");
173        }
174        if (numberOfTokens == 1) {
175            return !isKnown();
176        }
177        return false;
178    }
179
180    /** Return true if the receiver contains a token, and false otherwise.
181     *  If the receiver has status unknown, this method will throw an
182     *  exception.
183     *  @return True if this receiver contains a token.
184     *  @exception InvalidStateException If the status is unknown.
185     */
186    @Override
187    public boolean hasToken() {
188        if (isKnown()) {
189            return _token != null;
190        } else {
191            throw new InvalidStateException(getContainer(),
192                    "hasToken() called on FixedPointReceiver with "
193                            + "unknown status.");
194        }
195    }
196
197    /** If the argument is 1, return true if the receiver
198     *  contains a token, and false otherwise. If the argument is
199     *  larger than 1, return false (this receiver has capacity one).
200     *  If the receiver has status unknown, throw an exception.
201     *  @param numberOfTokens The number of tokens.
202     *  @return True if the argument is 1 and the receiver has a token.
203     *  @exception IllegalArgumentException If the argument is not positive.
204     *  @see #hasToken()
205     *  @exception InvalidStateException If the status is unknown.
206     */
207    @Override
208    public boolean hasToken(int numberOfTokens) {
209        if (!isKnown()) {
210            throw new InvalidStateException(getContainer(), "hasToken(int)"
211                    + " called on FixedPointReceiver with unknown status.");
212        }
213        if (numberOfTokens < 1) {
214            throw new IllegalArgumentException(
215                    "FixedPointReceiver: hasToken(int) requires a "
216                            + "positive argument.");
217        }
218        if (numberOfTokens == 1) {
219            return hasToken();
220        }
221        return false;
222    }
223
224    /** Return true if this receiver has status known, that is, this
225     *  receiver either is either known to have a token or known to
226     *  not to have a token.
227     *  @return True if this receiver has status known.
228     */
229    @Override
230    public boolean isKnown() {
231        IOPort container = getContainer();
232        if (container != null && container.sourcePortList().size() == 0
233                && container.insideSourcePortList().size() == 0) {
234            // There are no sources connected to the container port,
235            // so the port is presumably empty and known.
236            return true;
237        }
238        return _known;
239    }
240
241    /** If the specified token is non-null, then
242     *  set the status of this receiver to known and present, and to contain the
243     *  specified token. If the receiver is already known and the value of
244     *  the contained token is different from that of the new token, throw
245     *  an exception. If the specified token is null, then set the status to
246     *  be known and absent (by calling {@link #clear()}).
247     *  @param token The token to be put into this receiver.
248     *  @exception IllegalArgumentException If the argument is null.
249     *  @exception IllegalActionException If the status is known and absent,
250     *   or a token is present but not have the same value, or a token
251     *   is present and cannot be compared to the specified token.
252     */
253    @Override
254    public void put(Token token) throws IllegalActionException {
255        if (token == null) {
256            clear();
257            return;
258        }
259        if (!isKnown()) {
260            _token = token;
261            _known = true;
262            if (_director != null) {
263                _director._receiverChanged();
264            }
265        } else {
266            if (!hasToken()) {
267                throw new IllegalActionException(getContainer(),
268                        "Cannot change from an absent status "
269                                + "to a present status.  Call reset() first.");
270            } else {
271                if (!token.isEqualTo(_token).booleanValue()) {
272                    throw new IllegalActionException(getContainer(),
273                            "Cannot put a token with a different value " + token
274                                    + " into a receiver with an already established value "
275                                    + _token);
276                }
277            }
278        }
279    }
280
281    /** Reset the receiver by deleting any contained tokens and setting
282     *  the status of this receiver to unknown, unless the containing port
283     *  has no sources, in which case set to known and absent.  This is called
284     *  by the , normally in its initialize() and postfire()
285     *  methods.
286     */
287    @Override
288    public void reset() {
289        _token = null;
290        _known = false;
291    }
292
293    /** Set the container. This overrides the base class so that
294     *  if the container is being set to null, and if the director of
295     *  this receiver is not null, this method removes the receiver
296     *  from the list in that director.
297     *  @param port The container.
298     *  @exception IllegalActionException If the container is not of
299     *   an appropriate subclass of IOPort. Not thrown in this base class,
300     *   but may be thrown in derived classes.
301     *  @see #getContainer()
302     */
303    @Override
304    public void setContainer(IOPort port) throws IllegalActionException {
305        if (port == null && _director != null) {
306            _director._receivers.remove(this);
307        }
308        super.setContainer(port);
309    }
310
311    ///////////////////////////////////////////////////////////////////
312    ////                         protected fields                  ////
313
314    /** The director of this receiver. */
315    protected FixedPointDirector _director;
316
317    /** A flag indicating whether this receiver has status known. */
318    protected boolean _known = false;
319
320    /** The token held. */
321    protected Token _token = null;
322}