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}