001/* Utilities for data dependencies between actors.
002
003   Copyright (c) 2012-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.util;
029
030import java.util.HashSet;
031import java.util.Iterator;
032import java.util.Set;
033
034import ptolemy.actor.Actor;
035import ptolemy.actor.AtomicActor;
036import ptolemy.actor.IOPort;
037import ptolemy.actor.Manager;
038import ptolemy.actor.Receiver;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.KernelException;
041import ptolemy.kernel.util.NamedObj;
042
043///////////////////////////////////////////////////////////////////
044//// ActorDependencies
045
046/**
047 *  Utilities for data dependencies between actors.
048 *
049 *  @author Christopher Brooks
050 *  @version $Id$
051 *  @since Ptolemy II 10.0
052 *  @Pt.ProposedRating Yellow (cxh)
053 *  @Pt.AcceptedRating Red (cxh)
054 */
055public class ActorDependencies {
056
057    /** Construct an ActorDependencies object.
058     */
059    private ActorDependencies() {
060        // This method is private because this class has only static
061        // public methods.
062    }
063
064    /** Return a Set of dependent (downstream) atomic actors that are connected to the
065     *  target.  Opaque composite actors are searched. For output ports of the
066     *  specified actor, these are downstream actors that are connected on the outside.
067     *  For input ports of the specified actor, these are downstream actors connected
068     *  on the inside.
069     *  @param actor the Actor to be searched.
070     *  @return A Set of dependent atomic actors.
071     *  @exception KernelException If there is a problem with the receivers.
072     *  the top level or if preinitialize() fails.
073     */
074    public static Set<AtomicActor> dependents(Actor actor)
075            throws KernelException {
076        return ActorDependencies.dependents(actor, AtomicActor.class);
077    }
078
079    /** Return a Set of dependent (downstream) actors of a particular
080     *  class that are connected to the target.  Opaque composite
081     *  actors are searched. For output ports of the
082     *  specified actor, these are downstream actors that are connected on the outside.
083     *  For input ports of the specified actor, these are downstream actors connected
084     *  on the inside.
085     *  @param actor the Actor to be searched.
086     *  @param filter The class of prerequisite actors to be returned.
087     *  @return A Set of dependent atomic actors
088     *  @exception KernelException If there is a problem with the receivers.
089     *  the top level or if preinitialize() fails.
090     */
091    public static Set<AtomicActor> dependents(Actor actor, Class filter)
092            throws KernelException {
093        //System.out.println("ActorDependencies.dependents: START" + actor.getFullName());
094        Set<AtomicActor> results = new HashSet<AtomicActor>();
095
096        // preinitialize() creates connections between Publishers and
097        // Subscribers.
098        Manager.preinitializeThenWrapup(actor);
099
100        // Iterate through the output ports and add actors that are
101        // of the filter type to the result.  Opaque Composites are
102        // handled specially.
103
104        Iterator outputs = actor.outputPortList().iterator();
105        while (outputs.hasNext()) {
106            IOPort output = (IOPort) outputs.next();
107            results.addAll(_dependents(output, filter));
108        }
109        //System.out.println("ActorDependencies.dependents: END " + actor.getFullName() + " " + results);
110        return results;
111    }
112
113    /** Return a Set of dependent (downstream) actors of a particular
114     *  class that are connected to a port.  Opaque composite
115     *  actors are searched. For output ports of the specified actor,
116     *  these are downstream actors that are connected on the outside.
117     *  For input ports of the specified actor, these are downstream
118     *  actors connected on the inside.
119     *  @param port The target port.
120     *  @param filter The class of prerequisite actors to be returned.
121     *  @return A Set of dependent atomic actors
122     *  @exception KernelException If there is a problem with the receivers.
123     *  the top level or if preinitialize() fails.
124     */
125    public static Set<AtomicActor> dependents(IOPort port, Class filter)
126            throws KernelException {
127        //System.out.println("ActorDependencies.dependents: START" + actor.getFullName());
128        Set<AtomicActor> results = new HashSet<AtomicActor>();
129
130        // preinitialize() creates connections between Publishers and
131        // Subscribers.
132        Manager.preinitializeThenWrapup((Actor) port.getContainer());
133
134        results.addAll(_dependents(port, filter));
135
136        //System.out.println("ActorDependencies.dependents: END " + actor.getFullName() + " " + results);
137        return results;
138    }
139
140    /** Return a Set of AtomicActors that are connected upstream to this AtomicActor.
141     *  @param actor the Actor to be searched.
142     *  @return A Set of AtomicActors that are connected to this AtomicActor.
143     *  @exception KernelException If thrown when a Manager is added to
144     *  the top level or if preinitialize() fails.
145     */
146    public static Set<AtomicActor> prerequisites(Actor actor)
147            throws KernelException {
148        return ActorDependencies.prerequisites(actor, AtomicActor.class);
149    }
150
151    /** Return a Set of actors that match the specified filter
152     *  that are connected upstream to the specified actor.
153     *  If an upstream actor does not match the filter, then look
154     *  through it to other actors connected further upstream.
155     *  Upstream actors include any that can send data to the
156     *  specified actor.
157     *  @param actor the Actor to be searched.
158     *  @param filter The class of prerequisite actors to be returned.
159     *  @return A Set of AtomicActors that are connected to this AtomicActor.
160     *  @exception KernelException If thrown when a Manager is added to
161     *  the top level or if preinitialize() fails.
162     */
163    public static Set<AtomicActor> prerequisites(Actor actor, Class filter)
164            throws KernelException {
165        //System.out.println("ActorDependencies.prerequisites: START: " + actor.getFullName());
166        Set<AtomicActor> results = new HashSet<AtomicActor>();
167
168        // preinitialize() creates connections between Publishers and
169        // Subscribers.
170        Manager.preinitializeThenWrapup(actor);
171
172        // Iterate through the input ports and add actors that are
173        // of the filter type to the result.  Opaque Composites are
174        // handled specially.
175
176        // FIXME: this seems really wrong - overly complex?
177
178        Iterator inputs = actor.inputPortList().iterator();
179        while (inputs.hasNext()) {
180            IOPort input = (IOPort) inputs.next();
181            //            results.addAll(_prerequisites(input, filter));
182
183            Iterator ports = input.sourcePortList().iterator();
184            while (ports.hasNext()) {
185                IOPort port = (IOPort) ports.next();
186                NamedObj container = port.getContainer();
187                if (filter.isInstance(container)) {
188                    results.add((AtomicActor) container);
189                } else {
190                    results.addAll(_prerequisites(port, filter));
191                    // Handle ports in TypedComposites?
192                    // FIXME: This seems wrong.  Why getRemoteReceivers()?
193                    // Can't we simplify this?
194                    Receiver[][] receivers = port.getRemoteReceivers();
195                    if (receivers != null) {
196                        for (Receiver[] receiver : receivers) {
197                            if (receiver != null) {
198                                for (int j = 0; j < receiver.length; j++) {
199                                    if (receiver[j] != null) {
200                                        IOPort remotePort = receiver[j]
201                                                .getContainer();
202                                        if (remotePort != null) {
203                                            results.addAll(_prerequisites(
204                                                    remotePort, filter));
205                                        }
206                                    }
207                                }
208                            }
209                        }
210                    }
211                }
212            }
213        }
214        //System.out.println("ActorDependencies.prerequisites: DONE: " + actor.getFullName());
215        return results;
216    }
217
218    ///////////////////////////////////////////////////////////////////
219    ////                         private methods                   ////
220
221    /** If the container of the specified port matches the specified filter, then return it;
222     *  otherwise, return the set of all actors that match the
223     *  filter type connected downstream to the specified port.
224     *  If this is output port, these are downstream
225     *  ports connected on the outside. If this is an input port, then these
226     *  are downstream ports connected on the inside.
227     *  This method traverses opaque composites.
228     *  @param port The port to be checked
229     *  @param filter The class of dependent actors to be returned.
230     *  @return The Set of all AtomicActors connected to the port.
231     */
232    private static Set<AtomicActor> _dependents(IOPort port, Class filter)
233            throws IllegalActionException {
234        //System.out.println("ActorDependencies._dependents: START" + remotePort.getFullName());
235        Set<AtomicActor> results = new HashSet<AtomicActor>();
236        NamedObj container = port.getContainer();
237        if (filter.isInstance(container)) {
238            results.add((AtomicActor) container);
239        } else {
240            Receiver[][] receivers = null;
241            if (port.isOutput() && port.isInput()) {
242                throw new InternalError(
243                        "Can't handle port that is both input nor output: "
244                                + port);
245            }
246            if (port.isOutput()) {
247                receivers = port.getRemoteReceivers();
248            } else if (port.isInput()) {
249                receivers = port.deepGetReceivers();
250            } else {
251                throw new InternalError(
252                        "Can't handle port that is neither input nor output: "
253                                + port);
254            }
255            if (receivers != null) {
256                for (Receiver[] receiver : receivers) {
257                    if (receiver != null) {
258                        for (int j = 0; j < receiver.length; j++) {
259                            if (receiver[j] != null) {
260                                IOPort remotePort2 = receiver[j].getContainer();
261                                if (remotePort2 != null) {
262                                    //System.out.println("ActorDependencies._dependents: rempotePort2: " + remotePort2.getFullName());
263                                    results.addAll(
264                                            _dependents(remotePort2, filter));
265                                }
266                            }
267                        }
268                    }
269                }
270            }
271        }
272        //System.out.println("ActorDependencies._dependents: END" + remotePort.getFullName() + " " + results);
273        return results;
274    }
275
276    /** If the container of the specified port matches the specified filter,
277     *  then return that container; otherwise,
278     *  return the set of all actors that match the filter type connected upstream
279     *  to the port. If the port is an output, then these are actors
280     *  connected on the inside. If it is an input, then these are actors
281     *  connected on the outside. If an upstream port is not an instance
282     *  of the specified filter, then traverse it, looking for ports
283     *  that are further upstream of it.
284     *  This method traverses opaque composites.
285     *  @param port The port to be checked
286     *  @param filter The class of prerequisite actors to be returned.
287     *  @return The Set of all AtomicActors connected to the port.
288     */
289    private static Set<AtomicActor> _prerequisites(IOPort port, Class filter)
290            throws IllegalActionException {
291        //System.out.println("ActorDependencies._prerequisites: START " + remotePort.getFullName());
292        Set<AtomicActor> results = new HashSet<AtomicActor>();
293        NamedObj container = port.getContainer();
294        if (filter.isInstance(container)) {
295            results.add((AtomicActor) container);
296        } else {
297            // Handle inside connections of an output port.
298            for (IOPort insidePort : port.insideSourcePortList()) {
299                Iterator sourcePorts = insidePort.insideSourcePortList()
300                        .iterator();
301                while (sourcePorts.hasNext()) {
302                    IOPort sourcePort = (IOPort) sourcePorts.next();
303                    container = sourcePort.getContainer();
304                    if (filter.isInstance(container)) {
305                        results.add((AtomicActor) container);
306                    } else {
307                        results.addAll(_prerequisites(sourcePort, filter));
308                    }
309                }
310            }
311
312            // Handle outside connections.
313            Iterator remoteSourcePorts = port.sourcePortList().iterator();
314            while (remoteSourcePorts.hasNext()) {
315                IOPort remoteSourcePort = (IOPort) remoteSourcePorts.next();
316                container = remoteSourcePort.getContainer();
317                if (filter.isInstance(container)) {
318                    results.add((AtomicActor) container);
319                }
320                Iterator sourcePorts = remoteSourcePort.sourcePortList()
321                        .iterator();
322                while (sourcePorts.hasNext()) {
323                    IOPort sourcePort = (IOPort) sourcePorts.next();
324                    container = sourcePort.getContainer();
325                    if (filter.isInstance(container)) {
326                        results.add((AtomicActor) container);
327                    } else {
328                        results.addAll(_prerequisites(sourcePort, filter));
329                    }
330                }
331            }
332        }
333        //System.out.println("ActorDependencies._prerequisites: END " + remotePort.getFullName() + " " + results);
334        return results;
335    }
336}