001/* A CaseDirector governs the execution of a case actor.
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 */
027package ptolemy.actor.lib.hoc;
028
029import java.util.Iterator;
030
031import ptolemy.actor.Actor;
032import ptolemy.actor.Director;
033import ptolemy.actor.FiringEvent;
034import ptolemy.actor.IOPort;
035import ptolemy.actor.IOPortEvent;
036import ptolemy.actor.Mailbox;
037import ptolemy.actor.NoRoomException;
038import ptolemy.actor.NoTokenException;
039import ptolemy.actor.Receiver;
040import ptolemy.actor.parameters.ParameterPort;
041import ptolemy.actor.parameters.PortParameter;
042import ptolemy.actor.util.Time;
043import ptolemy.data.StringToken;
044import ptolemy.data.Token;
045import ptolemy.kernel.ComponentEntity;
046import ptolemy.kernel.CompositeEntity;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.InternalErrorException;
049import ptolemy.kernel.util.NameDuplicationException;
050import ptolemy.kernel.util.Nameable;
051
052///////////////////////////////////////////////////////////////////
053//// CaseDirector
054
055/**
056 An CaseDirector governs the execution of a Case actor.
057 This director simply delegates to the refinement whose name
058 matches the value of the current control input.
059
060 @author Edward A. Lee
061 @version $Id$
062 @since Ptolemy II 5.2
063 @Pt.ProposedRating Yellow (eal)
064 @Pt.AcceptedRating Red (hyzheng)
065 */
066public class CaseDirector extends Director {
067
068    /** Construct a director in the given container with the given name.
069     *  The container argument must not be null, or a
070     *  NullPointerException will be thrown.
071     *  If the name argument is null, then the name is set to the
072     *  empty string. Increment the version number of the workspace.
073     *  @param container Container of this director.
074     *  @param name Name of this director.
075     *  @exception IllegalActionException If the name has a period in it, or
076     *   the director is not compatible with the specified container.
077     *  @exception NameDuplicationException If the container not a
078     *   CompositeActor and the name collides with an entity in the container.
079     */
080    public CaseDirector(CompositeEntity container, String name)
081            throws IllegalActionException, NameDuplicationException {
082        super(container, name);
083    }
084
085    ///////////////////////////////////////////////////////////////////
086    ////                         public methods                    ////
087
088    /** Schedule a firing of the given actor at the given time.
089     *  If there is an executive director, this method delegates to it.
090     *  Otherwise, it sets its own notion of current time to that
091     *  specified in the argument. The reason for this is to enable
092     *  Case to be a top-level actor and to support the design pattern
093     *  where a director requests a refiring at the next time it wishes
094     *  to be awakened, just prior to returning from fire(). DEDirector,
095     *  for example, does that, as does the SDFDirector if the period
096     *  parameter is set.
097     *  @param actor The actor scheduled to be fired.
098     *  @param time The scheduled time.
099     *  @param microstep The microstep.
100     *  @return The time returned by the executive director, or
101     *   or the specified time if there isn't one.
102     *  @exception IllegalActionException If by the executive director.
103     */
104    @Override
105    public Time fireAt(Actor actor, Time time, int microstep)
106            throws IllegalActionException {
107        // Note that the actor parameter is ignored, because it does not
108        // matter which actor requests firing.
109        Nameable container = getContainer();
110
111        if (container instanceof Actor) {
112            Actor modalModel = (Actor) container;
113            Director executiveDirector = modalModel.getExecutiveDirector();
114
115            if (executiveDirector != null) {
116                return executiveDirector.fireAt(modalModel, time, microstep);
117            }
118        }
119        setModelTime(time);
120        return time;
121    }
122
123    /** Fire the current refinement.
124     *  @exception IllegalActionException If refinement throws it.
125     */
126    @Override
127    public void fire() throws IllegalActionException {
128        if (_debugging) {
129            _debug("Calling fire()");
130        }
131        Case container = (Case) getContainer();
132        if (container._current == null) {
133            throw new IllegalActionException(container,
134                    "Has no current refinement");
135        }
136        if (_debugging) {
137            _debug(new FiringEvent(this, container._current,
138                    FiringEvent.BEFORE_FIRE));
139        }
140        container._current.fire();
141        if (_debugging) {
142            _debug("Called fire()");
143            _debug(new FiringEvent(this, container._current,
144                    FiringEvent.AFTER_FIRE));
145        }
146    }
147
148    /** Return a receiver that is a one-place buffer. A token put into the
149     *  receiver will overwrite any token already in the receiver.
150     *  @return A receiver that is a one-place buffer.
151     */
152    @Override
153    public Receiver newReceiver() {
154        return new Mailbox() {
155            @Override
156            public boolean hasRoom() {
157                return true;
158            }
159
160            @Override
161            public void put(Token token) {
162                try {
163                    if (hasToken() == true) {
164                        get();
165                    }
166                    super.put(token);
167                } catch (NoRoomException ex) {
168                    throw new InternalErrorException(
169                            "One-place buffer: " + ex.getMessage());
170                } catch (NoTokenException ex) {
171                    throw new InternalErrorException(
172                            "One-place buffer: " + ex.getMessage());
173                }
174            }
175        };
176    }
177
178    /** Read the control token input, transfer input tokens,
179     *  and invoke prefire() of the selected refinement.
180     *
181     *  @exception IllegalActionException If there is no director,
182     *   or if the director's prefire() method throws it, or if this actor
183     *   is not opaque.
184     */
185    @Override
186    public boolean prefire() throws IllegalActionException {
187        if (_debugging) {
188            _debug("Calling prefire()");
189        }
190
191        try {
192            _workspace.getReadAccess();
193            super.prefire();
194
195            Case container = (Case) getContainer();
196            // Read from port parameters, including the control port.
197            Iterator portParameters = container
198                    .attributeList(PortParameter.class).iterator();
199            while (portParameters.hasNext()) {
200                PortParameter portParameter = (PortParameter) portParameters
201                        .next();
202                portParameter.update();
203            }
204
205            String controlValue;
206            Token controlToken = container.control.getToken();
207            // If it's a string, use stringValue() otherwise there
208            // are quotes around the string.
209            if (controlToken instanceof StringToken) {
210                controlValue = ((StringToken) controlToken).stringValue();
211            } else {
212                controlValue = controlToken.toString();
213            }
214
215            ComponentEntity refinement = container.getEntity(controlValue);
216            if (!(refinement instanceof Refinement)) {
217                refinement = container._default;
218            }
219            container._current = (Refinement) refinement;
220
221            // Transfer input tokens.
222            for (Iterator inputPorts = container.inputPortList()
223                    .iterator(); inputPorts.hasNext() && !_stopRequested;) {
224                IOPort port = (IOPort) inputPorts.next();
225
226                if (!(port instanceof ParameterPort)) {
227                    Receiver[][] insideReceivers = port.deepGetReceivers();
228                    for (int i = 0; i < port.getWidth(); i++) {
229                        if (port.hasToken(i)) {
230                            Token token = port.get(i);
231                            if (insideReceivers != null
232                                    && insideReceivers[i] != null) {
233                                for (int j = 0; j < insideReceivers[i].length; j++) {
234                                    if (insideReceivers[i][j].getContainer()
235                                            .getContainer() == refinement) {
236
237                                        if (_debugging) {
238                                            _debug(new IOPortEvent(port,
239                                                    insideReceivers[i][j]
240                                                            .getContainer(),
241                                                    true, i, false, token));
242                                        }
243
244                                        insideReceivers[i][j].put(token);
245
246                                        if (_debugging) {
247                                            _debug(new IOPortEvent(port,
248                                                    insideReceivers[i][j]
249                                                            .getContainer(),
250                                                    false, i, false, token));
251                                            _debug(getFullName(),
252                                                    "transferring input from "
253                                                            + port.getFullName()
254                                                            + " to "
255                                                            + insideReceivers[i][j]
256                                                                    .getContainer()
257                                                                    .getFullName());
258                                        }
259                                    }
260                                }
261                            }
262                        }
263                    }
264                }
265            }
266            if (_stopRequested) {
267                return false;
268            }
269            if (_debugging) {
270                _debug(new FiringEvent(this, container._current,
271                        FiringEvent.BEFORE_PREFIRE));
272            }
273            boolean result = container._current.prefire();
274            if (_debugging) {
275                _debug(new FiringEvent(this, container._current,
276                        FiringEvent.AFTER_PREFIRE));
277            }
278            return result;
279        } finally {
280            _workspace.doneReading();
281        }
282    }
283
284    /** Invoke the postfire() method of the current local director.
285     *  @return True if the execution can continue into the next iteration.
286     *  @exception IllegalActionException If there is no director,
287     *   or if the director's postfire() method throws it, or if this
288     *   actor is not opaque.
289     */
290    @Override
291    public boolean postfire() throws IllegalActionException {
292        if (_debugging) {
293            _debug("Calling postfire()");
294        }
295        Case container = (Case) getContainer();
296
297        if (_debugging) {
298            _debug(new FiringEvent(this, container._current,
299                    FiringEvent.BEFORE_POSTFIRE));
300        }
301
302        boolean result = container._current.postfire();
303
304        if (_debugging) {
305            _debug(new FiringEvent(this, container._current,
306                    FiringEvent.AFTER_POSTFIRE));
307        }
308
309        return result && !_finishRequested;
310    }
311}