001/* A ComponentActor is outside component and inside actor.
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 */
028package ptolemy.component;
029
030import java.util.Iterator;
031
032import ptolemy.actor.Director;
033import ptolemy.actor.Executable;
034import ptolemy.actor.IOPort;
035import ptolemy.actor.IORelation;
036import ptolemy.actor.Manager;
037import ptolemy.actor.Receiver;
038import ptolemy.actor.TypedCompositeActor;
039import ptolemy.actor.TypedIOPort;
040import ptolemy.data.Token;
041import ptolemy.data.TupleToken;
042import ptolemy.kernel.ComponentEntity;
043import ptolemy.kernel.ComponentPort;
044import ptolemy.kernel.ComponentRelation;
045import ptolemy.kernel.CompositeEntity;
046import ptolemy.kernel.Entity;
047import ptolemy.kernel.Port;
048import ptolemy.kernel.Relation;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.InternalErrorException;
051import ptolemy.kernel.util.NameDuplicationException;
052import ptolemy.kernel.util.Workspace;
053
054///////////////////////////////////////////////////////////////////
055//// ComponentActor
056
057/**
058 A Component is outside compatible with components and inside compatable
059 with actors.
060
061 @author Yang Zhao
062 @version $Id$
063 @since Ptolemy II 11.0
064 @Pt.ProposedRating yellow (ellen_zh)
065 @Pt.AcceptedRating red (cxh)
066 */
067public class ComponentActor extends TypedCompositeActor implements Component {
068    /** Construct an entity with the given name contained by the specified
069     *  entity. The container argument must not be null, or a
070     *  NullPointerException will be thrown.  This entity will use the
071     *  workspace of the container for synchronization and version counts.
072     *  If the name argument is null, then the name is set to the empty string.
073     *  Increment the version of the workspace.
074     *  This constructor write-synchronizes on the workspace.
075     *  @param container The container entity.
076     *  @param name The name of the entity.
077     *  @exception IllegalActionException If the entity cannot be contained
078     *   by the proposed container.
079     *  @exception NameDuplicationException If the name coincides with
080     *   an entity already in the container.
081     */
082    public ComponentActor(CompositeEntity container, String name)
083            throws IllegalActionException, NameDuplicationException {
084        super(container, name);
085        setContainer(container);
086        input = new IOMethodPort(this, "input", true, false);
087
088        output = new IOMethodPort(this, "output", false, true);
089        _addIcon();
090    }
091
092    ///////////////////////////////////////////////////////////////////
093    ////                     ports and parameters                  ////
094    public IOMethodPort input;
095
096    public IOMethodPort output;
097
098    ///////////////////////////////////////////////////////////////////
099    ////                         public methods                    ////
100
101    /** Clone the actor into the specified workspace. The new object is
102     *  <i>not</i> added to the directory of that workspace (you must do this
103     *  yourself if you want it there).
104     *  The result is a composite actor with clones of the ports of the
105     *  original actor, the contained actors, and the contained relations.
106     *  The ports of the returned actor are not connected to anything.
107     *  The connections of the relations are duplicated in the new composite,
108     *  unless they cross levels, in which case an exception is thrown.
109     *  The local director is cloned, if there is one.
110     *  The executive director is not cloned.
111     *  NOTE: This will not work if there are level-crossing transitions.
112     *
113     *  @param workspace The workspace for the cloned object.
114     *  @exception CloneNotSupportedException If the actor contains
115     *   level crossing transitions so that its connections cannot be cloned,
116     *   or if one of the attributes cannot be cloned.
117     *  @return A new CompositeActor.
118     */
119    @Override
120    public Object clone(Workspace workspace) throws CloneNotSupportedException {
121        ComponentActor newObject = (ComponentActor) super.clone(workspace);
122        //newObject._inputPortsVersion = -1;
123        //newObject._outputPortsVersion = -1;
124        return newObject;
125    }
126
127    /** Invalidate the schedule and type resolution and create
128     *  new receivers if the specified port is an opaque
129     *  output port.  Also, notify the containers of any ports
130     *  deeply connected on the inside by calling their connectionsChanged()
131     *  methods, since their width may have changed.
132     *  @param port The port that has connection changes.
133     */
134
135    //FIXME: the reason that i need overwrite this method is because
136    // it checked whether the container is compositeActor to avoid
137    // infinite loop. But here it should be componentActor. We should change
138    // the check to refer to a super interface of this two...
139    @Override
140    public void connectionsChanged(Port port) {
141        if (_debugging) {
142            _debug("Connections changed on port: " + port.getName());
143        }
144
145        super.connectionsChanged(port);
146
147        if (port instanceof ComponentPort) {
148            // NOTE: deepInsidePortList() is not the right thing here
149            // since it will return the same port if it is opaque.
150            Iterator<?> insidePorts = ((ComponentPort) port).insidePortList()
151                    .iterator();
152
153            try {
154                _inConnectionsChanged = true;
155
156                while (insidePorts.hasNext()) {
157                    ComponentPort insidePort = (ComponentPort) insidePorts
158                            .next();
159                    Entity portContainer = (Entity) insidePort.getContainer();
160
161                    // Avoid an infinite loop where notifications are traded.
162                    if (!(portContainer instanceof ComponentActor)
163                            || !((ComponentActor) portContainer)._inConnectionsChanged) {
164                        portContainer.connectionsChanged(insidePort);
165                    }
166                }
167            } finally {
168                _inConnectionsChanged = false;
169            }
170        }
171
172        if (port instanceof IOPort) {
173            IOPort castPort = (IOPort) port;
174
175            if (castPort.isOpaque()) {
176                Manager manager = getManager();
177
178                if (castPort.isOutput() && getDirector() != null
179                        && manager != null && manager.getState() != Manager.IDLE
180                        && manager.getState() != Manager.INFERING_WIDTHS
181                        && manager.getState() != Manager.PREINITIALIZING) {
182
183                    // Note that even if castPort is opaque, we still have to
184                    // check for director above.
185                    try {
186                        castPort.createReceivers();
187                    } catch (IllegalActionException ex) {
188                        // Should never happen.
189                        throw new InternalErrorException(this, ex,
190                                "Cannot create receivers");
191                    }
192                }
193
194                if (castPort.isInput() && getExecutiveDirector() != null
195                        && manager != null && manager.getState() != Manager.IDLE
196                        && manager.getState() != Manager.INFERING_WIDTHS
197                        && manager.getState() != Manager.PREINITIALIZING) {
198                    try {
199                        castPort.createReceivers();
200                    } catch (IllegalActionException ex) {
201                        // Should never happen.
202                        throw new InternalErrorException(this, ex,
203                                "Cannot create receivers");
204                    }
205                }
206
207                // Invalidate the local director schedule and types
208                if (getDirector() != null) {
209                    getDirector().invalidateSchedule();
210                    getDirector().invalidateResolvedTypes();
211                }
212            }
213        }
214    }
215
216    /** If this actor is opaque, transfer any data from the input ports
217     *  of this composite to the ports connected on the inside, and then
218     *  invoke the fire() method of its local director.
219     *  The transfer is accomplished by calling the transferInputs() method
220     *  of the local director (the exact behavior of which depends on the
221     *  domain).  If the actor is not opaque, throw an exception.
222     *  This method is read-synchronized on the workspace, so the
223     *  fire() method of the director need not be (assuming it is only
224     *  called from here).  After the fire() method of the director returns,
225     *  send any output data created by calling the local director's
226     *  transferOutputs method.
227     *
228     *  @exception IllegalActionException If there is no director, or if
229     *   the director's fire() method throws it, or if the actor is not
230     *   opaque.
231     */
232    @Override
233    public void fire() throws IllegalActionException {
234        super.fire();
235        if (_debugging) {
236            _debug("Called fire()");
237        }
238
239        try {
240            _workspace.getReadAccess();
241
242            if (!isOpaque()) {
243                throw new IllegalActionException(this,
244                        "Cannot fire a non-opaque actor.");
245            }
246
247            if (_stopRequested) {
248                return;
249            }
250
251            Director director = getDirector();
252            director.fire();
253        } finally {
254            _workspace.doneReading();
255        }
256    }
257
258    /** Load the generated class and search for its fire method.
259     */
260    @Override
261    public void preinitialize() throws IllegalActionException {
262        super.preinitialize();
263    }
264
265    /** Execute the component, which in this base class means doing
266     *  nothing and returning immediately.
267     *  This is invoked after preinitialize()
268     *  and initialize(), and may be invoked repeatedly.
269     *  @exception IllegalActionException If the run cannot be completed
270     *   (not thrown in this base class).
271     */
272    @Override
273    public void run() throws IllegalActionException {
274    }
275
276    /* (non-Javadoc)
277     * @see ptolemy.kernel.Component#initialize()
278     */
279    @Override
280    public void initialize() throws IllegalActionException {
281        super.initialize();
282    }
283
284    /* (non-Javadoc)
285     * @see ptolemy.kernel.Component#wrapup()
286     */
287    @Override
288    public void wrapup() throws IllegalActionException {
289        // TODO Auto-generated method stub
290        super.wrapup();
291    }
292
293    ///////////////////////////////////////////////////////////////////
294    ////                         protected methods                 ////
295
296    /** Invoke one iteration and transfer the token to a method call.
297     */
298    protected TupleToken _executeInside() {
299        if (_debugging) {
300            _debug("execute inside");
301        }
302
303        System.out.println("call execute inside");
304
305        try {
306            int iter = iterate(1);
307
308            //System.out.println("the iterate return is: " + iter);
309            if (iter == Executable.COMPLETED) {
310                return output.call();
311            } else {
312                return TupleToken.VOID;
313            }
314        } catch (Exception ex) {
315            // this shouldn't happen.
316            throw new InternalErrorException(this, ex, null);
317        }
318    }
319
320    ///////////////////////////////////////////////////////////////////
321    ////                         private methods                   ////
322
323    /*  Create receivers for each input port.
324     *  @exception IllegalActionException If any port throws it.
325     */
326
327    //FIXME: how should I modify this mehtod.
328    //    public void createReceivers() throws IllegalActionException {
329    //        Iterator ports = portList().iterator();
330    //
331    //        while (ports.hasNext()) {
332    //            IOMethodPort onePort = (IOMethodPort) ports.next();
333    //            onePort.createReceivers();
334    //        }
335    //    }
336    @Override
337    protected void _addRelation(ComponentRelation relation)
338            throws IllegalActionException, NameDuplicationException {
339    }
340
341    // Indicator that we are in the connectionsChanged method.
342    private boolean _inConnectionsChanged = false;
343
344    ///////////////////////////////////////////////////////////////////
345    ////                         private methods                   ////
346    private void _addIcon() {
347        _attachText("_iconDescription",
348                "<svg>\n" + "<rect x=\"-30\" y=\"-20\" width=\"60\" "
349                        + "height=\"40\" style=\"fill:white\"/>\n"
350                        + "<polygon points=\"-20,-10 20,0 -20,10\" "
351                        + "style=\"fill:blue\"/>\n" + "</svg>\n");
352    }
353
354    ///////////////////////////////////////////////////////////////////
355    ////                         inner class                       ////
356    // A class that encapsulates the declared and resolved types of a
357    // field and implements the InequalityTerm interface.
358    private class IOMethodPort extends TypedIOPort implements Method {
359        /** Construct a port with the given name contained by the specified
360         *  entity. The container argument must not be null, or a
361         *  NullPointerException will be thrown.  This port will use the
362         *  workspace of the container for synchronization and version counts.
363         *  If the name argument is null, then the name is set to the empty
364         *  string.  Increment the version of the workspace.
365         *  @param container The container entity.
366         *  @param name The name of the port.
367         *  @exception IllegalActionException If the port is not of an acceptable
368         *   class for the container.
369         *  @exception NameDuplicationException If the name coincides with
370         *   a port already in the container.
371         */
372        public IOMethodPort(ComponentEntity container, String name)
373                throws IllegalActionException, NameDuplicationException {
374            super(container, name);
375        }
376
377        /** Construct an IOPort with a container and a name that is
378         *  either an input, an output, or both, depending on the third
379         *  and fourth arguments. The specified container must implement
380         *  the Actor interface or an exception will be thrown.
381         *
382         *  @param container The container actor.
383         *  @param name The name of the port.
384         *  @param isInput True if this is to be an input port.
385         *  @param isOutput True if this is to be an output port.
386         *  @exception IllegalActionException If the port is not of an acceptable
387         *   class for the container, or if the container does not implement the
388         *   Actor interface.
389         *  @exception NameDuplicationException If the name coincides with
390         *   a port already in the container.
391         */
392        public IOMethodPort(ComponentEntity container, String name,
393                boolean isInput, boolean isOutput)
394                throws IllegalActionException, NameDuplicationException {
395            this(container, name);
396            setInput(isInput);
397            setOutput(isOutput);
398        }
399
400        ///////////////////////////////////////////////////////////////
401        ////                   public inner methods                ////
402
403        /** Override the base class to attach an empty properties token.
404         *  @see ptolemy.domains.de.kernel.DEReceiver#put(ptolemy.data.Token)
405         */
406        public synchronized TupleToken call() {
407            //FIXME: we assume only one token is going to be outputed.
408            //what is the correct sematics here?
409            System.out.println("try to call outside");
410
411            if (isOutput()) {
412                try {
413                    // FIXME: This loop will only go through
414                    // the once and then return.
415                    for (int i = 0; i < getWidthInside(); /*i++*/) {
416                        if (hasTokenInside(i)) {
417                            //System.out.println("has token to transfer to a method call outside");
418                            Token t = getInside(i);
419                            Token[] tokens = new Token[1];
420                            tokens[0] = t;
421
422                            Iterator<?> ports = this.deepConnectedPortList()
423                                    .iterator();
424                            MethodCallPort port = (MethodCallPort) ports.next();
425
426                            //System.out.println("get the connected method call port");
427                            return port.call(new TupleToken(tokens));
428                        } else {
429                            return TupleToken.VOID;
430                        }
431                    }
432                } catch (Exception ex) {
433                    // this shouldn't happen.
434                    throw new InternalErrorException(this, ex, null);
435                }
436            } else {
437                // The port provided should over write this method.
438                return TupleToken.VOID;
439            }
440
441            return TupleToken.VOID;
442        }
443
444        /**
445         *  @exception IllegalActionException If the transaction fails (e.g.
446         *   the data type is incompatible).
447         */
448        @Override
449        public synchronized TupleToken call(TupleToken token)
450                throws IllegalActionException {
451            if (isInput()) {
452                int l = token.length();
453
454                //Assume only one port is connected to this.
455                Iterator<?> ports = this.deepInsidePortList().iterator();
456                IOPort port = (IOPort) ports.next();
457                Receiver[][] receivers = port.getReceivers();
458
459                for (int i = 0; i < l; i++) {
460                    Token t = token.getElement(i);
461
462                    //assume not multiple port.
463                    receivers[0][0].put(t);
464                }
465
466                return _executeInside();
467            } else {
468                return TupleToken.VOID;
469            }
470        }
471
472        /** Create new receivers for this port, replacing any that may
473         *  previously exist, and validate any instances of Settable that
474         *  this port may contain. This method should only be called on
475         *  opaque ports.
476         *  <p>
477         *  If the port is an input port, receivers are created as necessary
478         *  for each relation connecting to the port from the outside.
479         *  If the port is an output port, receivers are created as necessary
480         *  for each relation connected to the port from the inside. Note that
481         *  only composite entities will have relations connecting to ports
482         *  from the inside.
483         *  <p>
484         *  Note that it is perfectly allowable for a zero width output port to
485         *  have insideReceivers.  This can be used to allow a model to be
486         *  embedded in a container that does not connect the port to anything.
487         *  <p>
488         *  This method is <i>not</i> write-synchronized on the workspace, so the
489         *  caller should be.
490         *  @exception IllegalActionException If this port is not
491         *   an opaque input port or if there is no director.
492         */
493        @Override
494        public void createReceivers() throws IllegalActionException {
495            boolean output = isOutput();
496
497            if (output) {
498                Iterator<?> insideRelations = insideRelationList().iterator();
499
500                if (insideRelations.hasNext()) {
501                    _insideReceivers = new Receiver[1][1];
502                    _insideReceivers[0][0] = _newInsideReceiver();
503                }
504            }
505        }
506
507        /** Override the base class to return the inside receiver.
508         *  @return The local inside receivers, or an empty array if there are
509         *   none.
510         */
511        @Override
512        public Receiver[][] getInsideReceivers() {
513            return _insideReceivers;
514        }
515
516        @Override
517        public Receiver[][] getReceivers() {
518            return _insideReceivers;
519        }
520
521        /** FIXME... see IORelation.deepReceivers().
522         *
523         *  @param relation Relations that are linked on the outside or inside.
524         *  @param occurrence The occurrence number that we are interested in,
525         *   starting at 0.
526         *  @return The local receivers, or an empty array if there are none.
527         *  @exception IllegalActionException If the relation is not linked
528         *   from the outside.
529         */
530        @Override
531        public Receiver[][] getReceivers(IORelation relation, int occurrence)
532                throws IllegalActionException {
533            return _insideReceivers;
534        }
535
536        /** Override parent method to ensure compatibility of the relation
537         *  and validity of the width of the port.
538         *  If this port is not a multiport, then the width of the
539         *  relation is required to be specified to be one.  This method
540         *  allows level-crossing links.
541         *  This method is <i>not</i> synchronized on the
542         *  workspace, so the caller should be.
543         *
544         *  @param relation The relation to link to on the inside.
545         *  @exception IllegalActionException If this port has no container or
546         *   the relation is not an IORelation, or the port already linked to a
547         *   relation and is not a multiport, or the relation has width
548         *   not exactly one and the port is not a multiport, or the
549         *   relation is incompatible with this port, or the port is not
550         *   in the same workspace as the relation.
551         */
552        @Override
553        protected void _checkLiberalLink(Relation relation)
554                throws IllegalActionException {
555        }
556
557        /** Override parent method to ensure compatibility of the relation
558         *  and validity of the width of the port.
559         *  If this port is not a multiport, then the width of the
560         *  relation is required to be specified to be one.
561         *  This method is <i>not</i> synchronized on the
562         *  workspace, so the caller should be.
563         *
564         *  @param relation The relation to link to.
565         *  @exception IllegalActionException If this port has no container or
566         *   the relation is not an IORelation, or the port already linked to a
567         *   relation and is not a multiport, or if the relation has width
568         *   not exactly one and the port is not a multiport, or the port is
569         *   not in the same workspace as the relation.
570         */
571        @Override
572        protected void _checkLink(Relation relation)
573                throws IllegalActionException {
574        }
575
576        // Lists of local receivers, indexed by relation.
577        private Receiver[][] _insideReceivers;
578
579        //private boolean _isProviedPort = false;
580    }
581}