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