001/* An output port that publishes its data on a named channel. 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 */ 027package ptolemy.actor; 028 029import ptolemy.data.expr.Parameter; 030import ptolemy.data.expr.StringParameter; 031import ptolemy.data.type.ArrayType; 032import ptolemy.data.type.BaseType; 033import ptolemy.kernel.ComponentEntity; 034import ptolemy.kernel.Entity; 035import ptolemy.kernel.InstantiableNamedObj; 036import ptolemy.kernel.util.HierarchyListener; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.InternalErrorException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.NamedObj; 041import ptolemy.kernel.util.Workspace; 042 043/////////////////////////////////////////////////////////////////// 044//// PubSubPort 045 046/** 047 An abstract base class for publisher and subscriber ports. 048 049 @author Edward A. Lee 050 @version $Id$ 051 @since Ptolemy II 10.0 052 @Pt.ProposedRating Yellow (eal) 053 @Pt.AcceptedRating Red (eal) 054 */ 055public abstract class PubSubPort extends TypedIOPort 056 implements HierarchyListener, Initializable { 057 058 /** Construct a subscriber port with a containing actor and a name. 059 * This is always an input port. 060 * @param container The container actor. 061 * @param name The name of the port. 062 * @exception IllegalActionException If the port is not of an acceptable 063 * class for the container, or if the container does not implement the 064 * Actor interface. 065 * @exception NameDuplicationException If the name coincides with 066 * a port already in the container. 067 */ 068 public PubSubPort(ComponentEntity container, String name) 069 throws IllegalActionException, NameDuplicationException { 070 super(container, name); 071 channel = new StringParameter(this, "channel"); 072 073 global = new Parameter(this, "global"); 074 global.setTypeEquals(BaseType.BOOLEAN); 075 global.setExpression("false"); 076 077 initialTokens = new Parameter(this, "initialTokens") { 078 /** Override the base class to to allow the type to be unknown. 079 * @return True if the current type is acceptable. 080 */ 081 @Override 082 public boolean isTypeAcceptable() { 083 return super.isTypeAcceptable() 084 || getType().equals(BaseType.UNKNOWN); 085 } 086 }; 087 setTypeAtLeast(ArrayType.elementType(initialTokens)); 088 } 089 090 /////////////////////////////////////////////////////////////////// 091 //// parameters //// 092 093 /** If set, then this port is used to communicate over a named 094 * publish and subscribe channel, rather than over manually 095 * established connections. 096 */ 097 public StringParameter channel; 098 099 /** Specification of whether the published data is global. 100 * This is ignored if {@link #channel} is empty. 101 * If this is set to true, then a subscriber anywhere in the model that 102 * references the same channel by name will see values published by 103 * this port. If this is set to false (the default), then only 104 * those subscribers that are controlled by the same director will see 105 * values published on this channel. 106 */ 107 public Parameter global; 108 109 /** The values that will be made available in the initialize method. 110 * By default, this is empty, indicating that no initial tokens are 111 * available. If you wish for this port to have initial tokens, 112 * then give this parameter an array value specifying 113 * the sequence of initial values. If this is an output port, 114 * these initial values will be sent in the initialize() phase. 115 * If this is an input port, then these initial values will be 116 * available for reading after the initialize() phase. 117 * Changes to this parameter after initialize() has been invoked 118 * are ignored until the next execution of the model. 119 */ 120 public Parameter initialTokens; 121 122 /////////////////////////////////////////////////////////////////// 123 //// public methods //// 124 125 /** Throw an exception. 126 * Adding initializables to the container is not supported. 127 */ 128 @Override 129 public void addInitializable(Initializable initializable) { 130 throw new InternalErrorException( 131 "Cannot add Initializables to publisher and subscriber ports."); 132 } 133 134 /** Clone the actor into the specified workspace. This calls the 135 * base class and then resets the type constraints. 136 * @param workspace The workspace for the new object. 137 * @return A new actor. 138 * @exception CloneNotSupportedException If a derived class contains 139 * an attribute that cannot be cloned. 140 */ 141 @Override 142 public Object clone(Workspace workspace) throws CloneNotSupportedException { 143 PubSubPort newObject = (PubSubPort) super.clone(workspace); 144 145 // Set the type constraints. 146 try { 147 newObject.setTypeAtLeast( 148 ArrayType.elementType(newObject.initialTokens)); 149 } catch (IllegalActionException e) { 150 throw new InternalErrorException(e); 151 } 152 153 return newObject; 154 } 155 156 /** Notify this object that the containment hierarchy above it has 157 * changed. This method does nothing because instead we use 158 * {@link #preinitialize()} to handle re-establishing the connections. 159 * @exception IllegalActionException If the change is not 160 * acceptable. 161 */ 162 @Override 163 public void hierarchyChanged() throws IllegalActionException { 164 // Make sure we are registered as to be initialized 165 // with the container. 166 Initializable container = _getInitializableContainer(); 167 if (container != null) { 168 container.addInitializable(this); 169 } 170 } 171 172 /** Notify this object that the containment hierarchy above it will be 173 * changed, which results in this port being removed from the set 174 * of initializables of the container. 175 * @exception IllegalActionException If unlinking to a published port fails. 176 */ 177 @Override 178 public void hierarchyWillChange() throws IllegalActionException { 179 // Unregister to be initialized with the initializable container. 180 // We will be re-registered when hierarchyChanged() is called. 181 Initializable container = _getInitializableContainer(); 182 if (container != null) { 183 container.removeInitializable(this); 184 } 185 } 186 187 /** Do nothing. */ 188 @Override 189 public void initialize() throws IllegalActionException { 190 } 191 192 /** Do nothing. Subclasses should check to see if the port 193 * is in the top level and throw an exception that suggests 194 * using a Publisher or Subscriber. 195 */ 196 @Override 197 public void preinitialize() throws IllegalActionException { 198 } 199 200 /** Do nothing. */ 201 @Override 202 public void removeInitializable(Initializable initializable) { 203 } 204 205 /** Override the base class to register as an 206 * {@link Initializable} 207 * so that preinitialize() is invoked, and as a 208 * {@link HierarchyListener}, so that we are notified of 209 * changes in the hierarchy above. 210 * @param container The proposed container. 211 * @exception IllegalActionException If the action would result in a 212 * recursive containment structure, or if 213 * this entity and container are not in the same workspace. 214 * @exception NameDuplicationException If the container already has 215 * an entity with the name of this entity. 216 */ 217 @Override 218 public void setContainer(Entity container) 219 throws IllegalActionException, NameDuplicationException { 220 Initializable previousInitializableContainer = _getInitializableContainer(); 221 NamedObj previousContainer = getContainer(); 222 if (previousContainer != container) { 223 hierarchyWillChange(); 224 try { 225 super.setContainer(container); 226 Initializable newInitializableContainer = _getInitializableContainer(); 227 if (previousInitializableContainer != newInitializableContainer) { 228 if (previousInitializableContainer != null) { 229 previousInitializableContainer 230 .removeInitializable(this); 231 } 232 if (newInitializableContainer != null) { 233 newInitializableContainer.addInitializable(this); 234 } 235 } 236 if (previousContainer != container) { 237 if (previousContainer != null) { 238 previousContainer.removeHierarchyListener(this); 239 } 240 if (container != null) { 241 container.addHierarchyListener(this); 242 } 243 } 244 } finally { 245 hierarchyChanged(); 246 } 247 } 248 } 249 250 /** Do nothing. */ 251 @Override 252 public void wrapup() throws IllegalActionException { 253 } 254 255 /////////////////////////////////////////////////////////////////// 256 //// protected variables //// 257 258 /** Cached channel name, for publish and subscribe. */ 259 protected String _channel; 260 261 /** Cached variable indicating whether publishing or subscribing is global. */ 262 protected boolean _global; 263 264 /////////////////////////////////////////////////////////////////// 265 //// private methods //// 266 267 /** Return the first Initializable encountered above this 268 * in the hierarchy that will be initialized (i.e., it is either 269 * an atomic actor or an opaque composite actor). 270 * @return The first Initializable above this in the hierarchy, 271 * or null if there is none. 272 */ 273 private Initializable _getInitializableContainer() { 274 NamedObj container = getContainer(); 275 if (container instanceof InstantiableNamedObj) { 276 if (((InstantiableNamedObj) container).isWithinClassDefinition()) { 277 return null; 278 } 279 } 280 while (container != null) { 281 if (container instanceof Initializable) { 282 if (container instanceof CompositeActor) { 283 if (((CompositeActor) container).isOpaque()) { 284 return (Initializable) container; 285 } 286 } else { 287 return (Initializable) container; 288 } 289 } 290 container = container.getContainer(); 291 } 292 return null; 293 } 294}