001/* A merge actor whose output type is the union of input types. 002 003 Copyright (c) 2006-2015 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.lib; 029 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.Map; 033import java.util.Map.Entry; 034import java.util.Set; 035 036import ptolemy.actor.IOPort; 037import ptolemy.actor.Manager; 038import ptolemy.actor.TypedAtomicActor; 039import ptolemy.actor.TypedIOPort; 040import ptolemy.actor.util.ConstructAssociativeType; 041import ptolemy.actor.util.ExtractFieldType; 042import ptolemy.data.UnionToken; 043import ptolemy.data.type.UnionType; 044import ptolemy.graph.Inequality; 045import ptolemy.kernel.CompositeEntity; 046import ptolemy.kernel.util.IllegalActionException; 047import ptolemy.kernel.util.NameDuplicationException; 048import ptolemy.kernel.util.NamedObj; 049import ptolemy.kernel.util.Workspace; 050 051/////////////////////////////////////////////////////////////////// 052//// UnionMerge 053 054/** 055On each firing, read all tokens from every input port and wrap each token 056into a UnionToken of which the label matches the name of the originating 057input port. All produced UnionTypes are sent to the output port in a single 058firing. 059 060<p> 061The type of the output port is a UnionType of which the labels much match 062the names of the input ports. This is achieved using two type constraints: 063The labels for the output UnionToken are the names of the input 064ports. This is achieved using two type constraints:</p> 065 066<ul> 067 068<li><tt>output ≥ {| x = typeOf(inputPortX), y = typeOf(inputPortY), .. |} 069</tt>, which requires the types of the input ports to be compatible 070with the corresponding types in the output union.</li> 071 072<li><tt>each input ≤ the type of the corresponding field inside the 073output union</tt>, which is similar to the usual default constraints, 074however this constraint establishes a dependency between the inputs of 075this actor and the fields inside the output union, instead of just 076between its inputs and outputs.</li> 077 078</ul> 079 080<p>Note that the output UnionType is required to contain a corresponding 081field for every input. However, due to the subtyping relation of UnionType 082that is opposite to the subtyping of RecordType, the type constraint 083that the output port of this actor must be greater than or equal to the GLB 084of the types of its receivers (implied by the connections), is always 085satisfied.</p> 086 087<p> 088To use this actor, instantiate it, and then add input ports 089(instances of TypedIOPort). 090</p> 091@author Edward A. Lee, Haiyang Zheng, Yuhong Xiong, Marten Lohstroh 092@version $Id$ 093@since Ptolemy II 10.0 094@Pt.ProposedRating Red (yuhongx) 095@Pt.AcceptedRating Red (yuhongx) 096 */ 097public class UnionMerge extends TypedAtomicActor { 098 099 /** Construct an actor in the specified container with the specified 100 * name. 101 * @param container The container. 102 * @param name The name. 103 * @exception NameDuplicationException If an actor 104 * with an identical name already exists in the container. 105 * @exception IllegalActionException If the actor cannot be contained 106 * by the proposed container. 107 */ 108 public UnionMerge(CompositeEntity container, String name) 109 throws NameDuplicationException, IllegalActionException { 110 super(container, name); 111 112 output = new TypedIOPort(this, "output", false, true); 113 114 _attachText("_iconDescription", 115 "<svg>\n" + "<polygon points=\"-10,20 10,10 10,-10, -10,-20\" " 116 + "style=\"fill:green\"/>\n" + "</svg>\n"); 117 } 118 119 /////////////////////////////////////////////////////////////////// 120 //// ports and parameters //// 121 122 /** The output port. The type of this port will be the union of the 123 * type of the input ports. 124 */ 125 public TypedIOPort output; 126 127 /////////////////////////////////////////////////////////////////// 128 //// public methods //// 129 130 /** Clone the actor into the specified workspace. 131 * @param workspace The workspace for the new object. 132 * @return A new actor. 133 * @exception CloneNotSupportedException If a derived class contains 134 * an attribute that cannot be cloned. 135 */ 136 @Override 137 public Object clone(Workspace workspace) throws CloneNotSupportedException { 138 UnionMerge newObject = (UnionMerge) super.clone(workspace); 139 newObject._portMap = new HashMap<String, TypedIOPort>(); 140 return newObject; 141 } 142 143 /** Read all available tokens from each input port, wrap each of them into 144 * a UnionToken of which the label matches the name of the originating 145 * input port. All produced UnionTypes are sent to the output port in a 146 * single firing. 147 * @exception IllegalActionException If there is no director, or 148 * the input can not be read, or the output can not be sent. 149 */ 150 @Override 151 public void fire() throws IllegalActionException { 152 super.fire(); 153 154 for (IOPort port : inputPortList()) { 155 while (port.hasNewToken(0)) { 156 output.send(0, new UnionToken(port.getName(), port.get(0))); 157 } 158 } 159 } 160 161 /** React to a name change of contained ports. Update the internal 162 * mapping from names and aliases to port objects, and invalidate 163 * the resolved types. 164 * @param object The object that changed. 165 */ 166 @Override 167 public void notifyOfNameChange(NamedObj object) { 168 if (object instanceof TypedIOPort) { 169 _mapPorts(); 170 } 171 } 172 173 /////////////////////////////////////////////////////////////////// 174 //// protected methods //// 175 176 /** Set up and return two type constraints. 177 * <ul> 178 * <li><tt>output ≥ {x = typeOf(inputPortX), y = typeOf(inputPortY), ..} 179 * </tt>, which requires the types of the input ports to be compatible 180 * with the corresponding types in the output union. 181 * </li> 182 * <li><tt>each input ≤ the type of the corresponding field inside the 183 * output union</tt>, which is similar to the usual default constraints, 184 * however this constraint establishes a dependency between the inputs of 185 * this actor and the fields inside the output union, instead of just 186 * between its inputs and outputs. 187 * </li> 188 * </ul> 189 * @return A set of type constraints 190 * @see ConstructAssociativeType 191 * @see ExtractFieldType 192 */ 193 @Override 194 protected Set<Inequality> _customTypeConstraints() { 195 Set<Inequality> result = new HashSet<Inequality>(); 196 197 // make sure the ports are mapped 198 _mapPorts(); 199 200 // constrain the fields in the output union to be greater than or 201 // equal to the declared or resolved types of the input ports: 202 // output >= {x = typeOf(outputPortX), y = typeOf(outputPortY), ..|} 203 result.add(new Inequality(new ConstructAssociativeType( 204 _portMap.values(), UnionType.class), output.getTypeTerm())); 205 206 for (Entry<String, TypedIOPort> entry : _portMap.entrySet()) { 207 String inputName = entry.getKey(); 208 TypedIOPort input = entry.getValue(); 209 // constrain the type of every input to be >= the resolved type 210 // of the corresponding field in the output union 211 result.add(new Inequality(new ExtractFieldType(output, inputName), 212 input.getTypeTerm())); 213 } 214 215 // NOTE: refrain from using port.setTypeAtMost() or 216 // port.setTypeAtLeast(), because after removing an output port, the 217 // constraint referring to this removed port will remain to exist in 218 // the input port, which will result in type errors. 219 220 return result; 221 } 222 223 /** Do not establish the usual default type constraints. 224 * @return null 225 */ 226 @Override 227 protected Set<Inequality> _defaultTypeConstraints() { 228 return null; 229 } 230 231 /////////////////////////////////////////////////////////////////// 232 //// private methods //// 233 234 /** Map port names or aliases to port objects. If the mapping 235 * has changed, then invalidate the resolved types, which 236 * forces new type constraints with appropriate field names 237 * to be generated. 238 */ 239 private void _mapPorts() { 240 // Retrieve the manager. 241 Manager manager = this.getManager(); 242 243 // Generate a new mapping from names/aliases to ports. 244 Map<String, TypedIOPort> oldMap = _portMap; 245 _portMap = new HashMap<String, TypedIOPort>(); 246 for (TypedIOPort p : this.inputPortList()) { 247 String name = p.getName(); 248 String alias = p.getDisplayName(); 249 // ignore unconnected ports 250 if (p.numberOfSources() < 1) { 251 continue; 252 } 253 if (alias == null || alias.equals("")) { 254 _portMap.put(name, p); 255 } else { 256 _portMap.put(alias, p); 257 } 258 } 259 260 // Only invalidate resolved types if there actually was a name change. 261 // As a result, new type constraints will be generated. 262 if (manager != null && (oldMap == null || !_portMap.equals(oldMap))) { 263 manager.invalidateResolvedTypes(); 264 } 265 } 266 267 /////////////////////////////////////////////////////////////////// 268 //// private variables //// 269 270 /** Keeps track of which name or alias is associated with which port. */ 271 private Map<String, TypedIOPort> _portMap; 272 273}