001/* Relation supporting message passing. 002 003 Copyright (c) 1997-2018 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 028 */ 029package ptolemy.actor; 030 031import java.util.Collections; 032import java.util.Enumeration; 033import java.util.HashMap; 034import java.util.Iterator; 035import java.util.LinkedList; 036import java.util.List; 037 038import ptolemy.data.IntToken; 039import ptolemy.data.expr.Parameter; 040import ptolemy.data.type.BaseType; 041import ptolemy.kernel.ComponentRelation; 042import ptolemy.kernel.CompositeEntity; 043import ptolemy.kernel.Entity; 044import ptolemy.kernel.Port; 045import ptolemy.kernel.Relation; 046import ptolemy.kernel.util.Attribute; 047import ptolemy.kernel.util.IllegalActionException; 048import ptolemy.kernel.util.InternalErrorException; 049import ptolemy.kernel.util.InvalidStateException; 050import ptolemy.kernel.util.KernelException; 051import ptolemy.kernel.util.NameDuplicationException; 052import ptolemy.kernel.util.Nameable; 053import ptolemy.kernel.util.Settable; 054import ptolemy.kernel.util.Workspace; 055 056/////////////////////////////////////////////////////////////////// 057//// IORelation 058 059/** 060 This class mediates connections between ports that can send data to 061 one another via message passing. One purpose of this relation is to 062 ensure that IOPorts are only connected to IOPorts. A second purpose 063 is to support the notion of a <i>width</i> to represent something 064 like a bus. By default an IORelation is a bus for which the width 065 will be inferred, which corresponds to a width equal to Auto. In 066 Vergil you can change the width from Auto to a specific value, to 067 explicitly specify the width of a relation. Specifying a width 068 equal to zero will disable the relation. 069 A width equal equal to -1 is equivalent to a width equal to Auto, 070 in which case the width will be inferred (if possible) from the 071 context. In particular, if this relation is linked on the inside 072 to a port with some width, then the width of this relation will 073 be inferred to be the enough so that the widths of all inside 074 linked relations adds up to the outside width of the port. 075 If this IORelation is linked to another 076 instance of IORelation, then the width of the two IORelations is 077 constrained to be the same. 078 <p> 079 Instances of IORelation can only be linked to instances of IOPort 080 or instances of IORelation. 081 Derived classes may further constrain this to subclasses of IOPort 082 of IORelation. 083 Such derived classes should override the protected methods _checkPort() 084 and _checkRelation() to throw an exception. 085 <p> 086 To link a IOPort to an IORelation, use the link() or 087 liberalLink() method in the IOPort class. To remove a link, 088 use the unlink() method. To link (unlink) an IORelation to an IORelation, 089 use the link() (unlink()) method of IORelation. 090 <p> 091 The container for instances of this class can only be instances of 092 CompositeActor. Derived classes may wish to further constrain the 093 container to subclasses of ComponentEntity. To do this, they should 094 override the _checkContainer() method. 095 096 @author Edward A. Lee, Jie Liu, Contributor: Bert Rodiers 097 @version $Id$ 098 @since Ptolemy II 0.2 099 @Pt.ProposedRating Green (eal) 100 @Pt.AcceptedRating Green (acataldo) 101 */ 102public class IORelation extends ComponentRelation { 103 /** Construct a relation in the default workspace with an empty string 104 * as its name. Add the relation to the directory of the workspace. 105 */ 106 public IORelation() { 107 super(); 108 _init(); 109 } 110 111 /** Construct a relation in the specified workspace with an empty 112 * string as a name. You can then change the name with setName(). 113 * If the workspace argument is null, then use the default workspace. 114 * Add the relation to the workspace directory. 115 * 116 * @param workspace The workspace that will list the relation. 117 */ 118 public IORelation(Workspace workspace) { 119 super(workspace); 120 _init(); 121 } 122 123 /** Construct a relation with the given name contained by the specified 124 * entity. The container argument must not be null, or a 125 * NullPointerException will be thrown. This relation will use the 126 * workspace of the container for synchronization and version counts. 127 * If the name argument is null, then the name is set to the empty string. 128 * This constructor write-synchronizes on the workspace. 129 * 130 * @param container The container. 131 * @param name The name of the relation. 132 * @exception IllegalActionException If the container is incompatible 133 * with this relation. 134 * @exception NameDuplicationException If the name coincides with 135 * a relation already in the container. 136 */ 137 public IORelation(CompositeEntity container, String name) 138 throws IllegalActionException, NameDuplicationException { 139 super(container, name); 140 _init(); 141 } 142 143 /////////////////////////////////////////////////////////////////// 144 //// parameters //// 145 146 /** The width of this relation. This is an integer that defaults 147 * to WIDTH_TO_INFER, which means that the width will be inferred. 148 */ 149 public Parameter width; 150 151 /////////////////////////////////////////////////////////////////// 152 //// public methods //// 153 154 /** React to a change in an attribute. This method is called by 155 * a contained attribute when its value changes. This overrides 156 * the base class so that if the attribute is an instance of 157 * Parameter and the name is "width", then the width of the Relation 158 * is set. 159 * @param attribute The attribute that changed. 160 * @exception IllegalActionException If the change is not acceptable 161 * to this container. 162 */ 163 @Override 164 public void attributeChanged(Attribute attribute) 165 throws IllegalActionException { 166 if (attribute instanceof Parameter 167 && "width".equals(attribute.getName())) { 168 IntToken t = (IntToken) ((Parameter) attribute).getToken(); 169 170 if (t != null) { 171 int width = t.intValue(); 172 _setWidth(width); 173 } 174 } 175 } 176 177 /** Clone the object into the specified workspace. The new object is 178 * <i>not</i> added to the directory of that workspace (you must do this 179 * yourself if you want it there). 180 * The result is a new relation with no links and no container, but with 181 * the same width as the original. 182 * 183 * @param workspace The workspace for the cloned object. 184 * @exception CloneNotSupportedException If one or more of the attributes 185 * cannot be cloned. 186 * @return A new ComponentRelation. 187 */ 188 @Override 189 public Object clone(Workspace workspace) throws CloneNotSupportedException { 190 IORelation newObject = (IORelation) super.clone(workspace); 191 newObject._inferredWidthVersion = -1; 192 newObject._cachedWidth = -2; 193 return newObject; 194 } 195 196 /** Return the receivers of all input ports linked to this 197 * relation, directly or indirectly through a relation group, 198 * except those in the port 199 * given as an argument. The returned value is an array of 200 * arrays. The first index (the row) specifies the group, where 201 * a group receives the same data from a channel. 202 * Each channel normally receives distinct data. The 203 * second index (the column) specifies the receiver number within 204 * the group of receivers that get copies from the same channel. 205 * <p> 206 * The number of groups (rows) is less than or equal to the 207 * width of the relation, which is always at least one. If 208 * there are no receivers then return null. 209 * <p> 210 * For each channel, there may be any number of receivers in the group. 211 * The individual receivers are selected using the second index of the 212 * returned array of arrays. If there are no receivers in the group, 213 * then the channel is represented by null. I.e., if the returned 214 * array of arrays is <i>x</i> and the channel number is <i>c</i>, 215 * then <i>x</i>[<i>c</i>] is null. Otherwise, it is an array, where 216 * the size of the array is the number of receivers in the group. 217 * <p> 218 * NOTE: This method may have the effect of creating new receivers in the 219 * remote input ports and losing the previous receivers in those ports, 220 * together with any data they may contain. This occurs only if the 221 * topology has changed since the receivers were created, and that change 222 * resulting in one of those ports not having the right number of 223 * receivers. 224 * <p> 225 * This method read-synchronizes on the workspace. 226 * 227 * @see IOPort#getRemoteReceivers 228 * @param except The port to exclude, or null to not 229 * exclude any ports. 230 * @return The receivers associated with this relation. 231 * @exception IllegalActionException If thrown while determining 232 * the cascade. 233 */ 234 public Receiver[][] deepReceivers(IOPort except) 235 throws /*InvalidStateException,*/IllegalActionException { 236 try { 237 _workspace.getReadAccess(); 238 239 Receiver[][] result = new Receiver[0][0]; 240 Iterator<?> inputs = linkedDestinationPortList(except).iterator(); 241 Receiver[][] receivers; //= new Receiver[0][0]; 242 243 // NOTE: We have to be careful here to keep track of 244 // multiple occurrences of a port in this list. 245 // EAL 7/30/00. 246 HashMap<IOPort, Integer> seen = new HashMap<IOPort, Integer>(); 247 248 while (inputs.hasNext()) { 249 IOPort p = (IOPort) inputs.next(); 250 251 if (p.isInsideGroupLinked(this) && !p.isOpaque()) { 252 // if p is a transparent port and this relation links 253 // from the inside, then get the Receivers outside p. 254 try { 255 receivers = p.getRemoteReceivers(this); 256 } catch (IllegalActionException ex) { 257 throw new InternalErrorException(this, ex, null); 258 } 259 } else { 260 // if p not a transparent port, or this relation is linked 261 // to p from the outside. 262 try { 263 // Note that this may be an inside or outside linked 264 // relation. 265 // NOTE: We have to be careful here to keep track of 266 // multiple occurrences of a port in this list. 267 // EAL 7/30/00. 268 int occurrence = 0; 269 270 if (seen.containsKey(p)) { 271 occurrence = seen.get(p).intValue(); 272 occurrence++; 273 } 274 275 seen.put(p, Integer.valueOf(occurrence)); 276 277 receivers = p._getReceiversLinkedToGroup(this, 278 occurrence); 279 } catch (IllegalActionException ex) { 280 throw new InternalErrorException(this, ex, null); 281 } 282 } 283 284 result = _cascade(result, receivers); 285 } 286 287 return result; 288 } finally { 289 _workspace.doneReading(); 290 } 291 } 292 293 /** Return the width of the IORelation, which is always at least one. 294 * If the width has been set to the value of WIDTH_TO_INFER, then 295 * the relation is a bus with 296 * unspecified width, and the width needs to be inferred from the 297 * way the relation is connected. This is done by checking the 298 * ports that this relation is linked to from the inside and setting 299 * the width to the maximum of those port widths, minus the widths of 300 * other relations linked to those ports on the inside. Each such port is 301 * allowed to have at most one inside relation with an unspecified 302 * width, or an exception is thrown. If this inference yields a width 303 * of zero, then return one. 304 * 305 * @return The width, which is at least zero. 306 * @exception IllegalActionException If thrown while getting the 307 * user width, determining if the width inference is needed or if 308 * thrown by inferWidths(). 309 * @see #setWidth(int) 310 */ 311 public int getWidth() throws IllegalActionException { 312 int width = _getUserWidth(); 313 if (_USE_NEW_WIDTH_INFERENCE_ALGO) { 314 315 // If _width equals to the value of WIDTH_TO_INFER 316 // we might need to infer it. Since the width inference 317 // is cached we only need to infer it in case 318 // needsWidthInference() returns true. 319 if (width == WIDTH_TO_INFER) { 320 if (needsWidthInference()) { 321 322 Nameable container = getContainer(); 323 324 if (container instanceof CompositeActor) { 325 ((CompositeActor) container).inferWidths(); 326 } else { 327 throw new IllegalActionException(this, 328 "Can't infer the widths " 329 + "of the relations since no container or container is not a CompositeActor."); 330 } 331 332 assert _inferredWidthVersion == _workspace.getVersion(); 333 assert !needsWidthInference(); 334 } 335 return _inferredWidth; 336 } 337 return width; 338 } else { 339 if (width == 0) { 340 return _inferWidth(); 341 } 342 return width; 343 } 344 } 345 346 /** Return true if the relation has a definite width (i.e., 347 * setWidth() has not been called with a value equal to 348 * WIDTH_TO_INFER. 349 * @return True if the width has been set to a positive value. 350 * @exception IllegalActionException If the expression for the width cannot 351 * be parsed or cannot be evaluated, or if the result of evaluation 352 * violates type constraints, or if the result of evaluation is null 353 * and there are variables that depend on this one. 354 */ 355 public boolean isWidthFixed() throws IllegalActionException { 356 int width = _getUserWidth(); 357 return width != WIDTH_TO_INFER; 358 } 359 360 /** List the input ports that this relation connects to from the 361 * outside, and the output ports that it connects to from 362 * the inside. I.e., list the ports through or to which we 363 * could send data. Two ports are connected if they are 364 * linked to relations in the same relation group. 365 * This method read-synchronizes on the workspace. 366 * @see ptolemy.kernel.Relation#linkedPorts 367 * @return An enumeration of IOPort objects. 368 */ 369 public List<IOPort> linkedDestinationPortList() { 370 return linkedDestinationPortList(null); 371 } 372 373 /** List the input ports that this relation connects to from the 374 * outside and the output ports that it connects to from 375 * the inside, except the port given as an argument. 376 * I.e., list the ports through or to which we 377 * could send data. Two ports are connected if they are 378 * linked to relations in the same relation group. 379 * This method read-synchronizes on the workspace. 380 * @see ptolemy.kernel.Relation#linkedPortList(ptolemy.kernel.Port) 381 * @param except The port not included in the returned list, or 382 * null to not exclude any ports. 383 * @return A list of IOPort objects. 384 */ 385 public List<IOPort> linkedDestinationPortList(IOPort except) { 386 try { 387 _workspace.getReadAccess(); 388 389 // NOTE: The result could be cached for efficiency, but 390 // it would have to be cached in a hashtable indexed by the 391 // except argument. Probably not worth it. 392 LinkedList<IOPort> resultPorts = new LinkedList<IOPort>(); 393 Iterator<?> ports = linkedPortList().iterator(); 394 395 while (ports.hasNext()) { 396 IOPort p = (IOPort) ports.next(); 397 398 if (p != except) { 399 if (p.isInsideGroupLinked(this)) { 400 // Linked from the inside 401 if (p.isOutput()) { 402 resultPorts.addLast(p); 403 } 404 } else { 405 if (p.isInput()) { 406 resultPorts.addLast(p); 407 } 408 } 409 } 410 } 411 412 return resultPorts; 413 } finally { 414 _workspace.doneReading(); 415 } 416 } 417 418 /** Enumerate the input ports that we are linked to from the outside, 419 * and the output ports that we are linked to from the inside. 420 * I.e., enumerate the ports through or to which we could send data. 421 * This method is deprecated and calls linkedDestinationPortList(). 422 * This method read-synchronizes on the workspace. 423 * @see ptolemy.kernel.Relation#linkedPorts 424 * @deprecated Use linkedDestinationPortList() instead. 425 * @return An enumeration of IOPort objects. 426 */ 427 @Deprecated 428 @SuppressWarnings("unchecked") 429 public Enumeration linkedDestinationPorts() { 430 return linkedDestinationPorts(null); 431 } 432 433 /** Enumerate the input ports that we are linked to from the 434 * outside, and the output ports that we are linked to from 435 * the inside, except the port given as an argument. 436 * I.e., enumerate the ports through or to which we could send data. 437 * This method is deprecated and calls 438 * linkedDestinationPortList(IOPort). 439 * This method read-synchronizes on the workspace. 440 * @see ptolemy.kernel.Relation#linkedPorts(ptolemy.kernel.Port) 441 * @param except The port not included in the returned Enumeration. 442 * @deprecated Use linkDestinationPortList(IOPort) instead. 443 * @return An enumeration of IOPort objects. 444 */ 445 @Deprecated 446 @SuppressWarnings("unchecked") 447 public Enumeration linkedDestinationPorts(IOPort except) { 448 return Collections.enumeration(linkedDestinationPortList(except)); 449 } 450 451 /** List the output ports that this relation connects to from the 452 * outside and the input ports that it connects to from 453 * the inside. 454 * I.e., list the ports through or from which we 455 * could receive data. Two ports are connected if they are 456 * linked to relations in the same relation group. 457 * This method read-synchronizes on the workspace. 458 * @see ptolemy.kernel.Relation#linkedPorts 459 * @return An enumeration of IOPort objects. 460 */ 461 public List<IOPort> linkedSourcePortList() { 462 return linkedSourcePortList(null); 463 } 464 465 /** List the output ports that this relation connects to from the 466 * outside and the input ports that it connects to from 467 * the inside, except the port given as an argument. 468 * I.e., list the ports through or from which we 469 * could receive data. Two ports are connected if they are 470 * linked to relations in the same relation group. 471 * This method read-synchronizes on the workspace. 472 * @see ptolemy.kernel.Relation#linkedPortList(ptolemy.kernel.Port) 473 * @param except The port not included in the returned list. 474 * @return A list of IOPort objects. 475 */ 476 public List<IOPort> linkedSourcePortList(IOPort except) { 477 try { 478 _workspace.getReadAccess(); 479 480 // NOTE: The result could be cached for efficiency, but 481 // it would have to be cached in a hashtable indexed by the 482 // except argument. Probably not worth it. 483 LinkedList<IOPort> resultPorts = new LinkedList<IOPort>(); 484 Iterator<?> ports = linkedPortList().iterator(); 485 486 while (ports.hasNext()) { 487 IOPort p = (IOPort) ports.next(); 488 489 if (p != except) { 490 if (p.isInsideGroupLinked(this)) { 491 // Linked from the inside 492 if (p.isInput()) { 493 resultPorts.addLast(p); 494 } 495 } else { 496 if (p.isOutput()) { 497 resultPorts.addLast(p); 498 } 499 } 500 } 501 } 502 503 return resultPorts; 504 } finally { 505 _workspace.doneReading(); 506 } 507 } 508 509 /** Enumerate the output ports that we are linked to from the outside 510 * and the input ports that we are linked to from the inside. 511 * I.e. enumerate the ports from or through which we might receive 512 * data. This method is deprecated and calls 513 * linkedSourcePortList(). 514 * This method read-synchronizes on the workspace. 515 * @see ptolemy.kernel.Relation#linkedPorts 516 * @deprecated Use linkedSourcePortList() instead. 517 * @return An enumeration of IOPort objects. 518 */ 519 @Deprecated 520 @SuppressWarnings("unchecked") 521 public Enumeration linkedSourcePorts() { 522 return Collections.enumeration(linkedSourcePortList()); 523 } 524 525 /** Enumerate the output ports that we are linked to from the outside 526 * and the input ports that we are linked to from the inside. 527 * I.e. enumerate the ports from or through which we might receive 528 * This method is deprecated and calls 529 * linkedSourcePortList(IOPort). 530 * This method read-synchronizes on the workspace. 531 * @see ptolemy.kernel.Relation#linkedPorts(ptolemy.kernel.Port) 532 * @param except The port not included in the returned Enumeration. 533 * @deprecated Use linkedSourcePortList(IOPort) instead. 534 * @return An enumeration of IOPort objects. 535 */ 536 @Deprecated 537 @SuppressWarnings("unchecked") 538 public Enumeration linkedSourcePorts(IOPort except) { 539 return Collections.enumeration(linkedSourcePortList(except)); 540 } 541 542 /** 543 * Determine whether for this relation width inference needs to be performed. 544 * @return True when width inference needs to be performed. 545 * @exception IllegalActionException If the expression for the width cannot 546 * be parsed or cannot be evaluated, or if the result of evaluation 547 * violates type constraints, or if the result of evaluation is null 548 * and there are variables that depend on this one. 549 */ 550 public boolean needsWidthInference() throws IllegalActionException { 551 // In case we width has been fixed or the version has not changed since the last width 552 // inference, we of course don't need to do width inference. 553 // In case the version changed it still might happen that we do not have to 554 // width inference, since the change might not be related to or has no influence 555 // on the width of relations. Hence we check with the director who is aware or changes 556 // related to the connectivity. 557 int width = _getUserWidth(); 558 boolean widthInferenceValid = width != WIDTH_TO_INFER 559 || _inferredWidthVersion == _workspace.getVersion(); 560 if (!widthInferenceValid) { 561 Nameable container = getContainer(); 562 563 if (container instanceof CompositeActor) { 564 widthInferenceValid = !((CompositeActor) container) 565 .needsWidthInference(); 566 if (widthInferenceValid) { 567 _inferredWidthVersion = _workspace.getVersion(); 568 } 569 } else { 570 // If we don't have a director or manager we can't determine the inferred width. 571 // You could argue it is wrong to set the _inferredWidth = 0, however 572 // the user can't run the model anyway and hence needs to add a director 573 // to run it, at which time the width inference will be executed again. 574 // If there is no manager but a director it means the user has not run 575 // the model. Since we don't update the version we make sure that the 576 // width is updated when the model is initialized. 577 _inferredWidth = 0; 578 widthInferenceValid = true; 579 } 580 } 581 return !widthInferenceValid; 582 } 583 584 /** Specify the container, adding the relation to the list 585 * of relations in the container. 586 * If this relation already has a container, remove it 587 * from that container first. Otherwise, remove it from 588 * the list of objects in the workspace. If the argument is null, then 589 * unlink the ports from the relation, remove it from 590 * its container, and add it to the list of objects in the workspace. 591 * If the relation is already contained by the container, do nothing. 592 * <p> 593 * The container must be an 594 * instance of CompositeActor or null, otherwise an exception is thrown. 595 * Derived classes may further constrain the class of the container 596 * to a subclass of CompositeActor. 597 * <p> 598 * This method invalidates the schedule and resolved types of the 599 * director of the container, if there is one. 600 * <p> 601 * This method is write-synchronized on the workspace. 602 * 603 * @param container The proposed container. 604 * @exception IllegalActionException If the container is not a 605 * CompositeActor or null, or this entity and the container are not in 606 * the same workspace. 607 * @exception NameDuplicationException If the name collides with a name 608 * already on the relations list of the container. 609 */ 610 @Override 611 public void setContainer(CompositeEntity container) 612 throws IllegalActionException, NameDuplicationException { 613 if (!(container instanceof CompositeActor) && container != null) { 614 throw new IllegalActionException(this, container, 615 "IORelation can only be contained by CompositeActor."); 616 } 617 618 // Invalidate schedule and type resolution of the old container. 619 Nameable oldContainer = getContainer(); 620 621 if (oldContainer instanceof CompositeActor) { 622 Director director = ((CompositeActor) oldContainer).getDirector(); 623 624 if (director != null) { 625 director.invalidateSchedule(); 626 director.invalidateResolvedTypes(); 627 } 628 } 629 630 // Invalidate schedule and type resolution of the new container. 631 632 // Either container == null or container instanceof CompositeActor == true 633 if (container != null) { 634 Director director = ((CompositeActor) container).getDirector(); 635 636 if (director != null) { 637 director.invalidateSchedule(); 638 director.invalidateResolvedTypes(); 639 } 640 } 641 642 super.setContainer(container); 643 } 644 645 /** Set the width of this relation and all relations in its 646 * relation group. The width is the number of 647 * channels that the relation represents. If the argument 648 * is equal to WIDTH_TO_INFER, then the relation becomes a bus with 649 * unspecified width, 650 * and the width will be inferred from the way the relation is used 651 * (but will never be less than zero). 652 * This method invalidates 653 * the resolved types on the director of the container, if there is 654 * one, and notifies each connected actor that its connections 655 * have changed. 656 * This method write-synchronizes on the workspace. 657 * 658 * @param widthValue The width of the relation. 659 * @exception IllegalActionException If the argument is not zero, one, 660 * or equal to WIDTH_TO_INFER and the relation is linked to a 661 * non-multiport. Or when the argument is less than zero and different 662 * from WIDTH_TO_INFER. 663 * @see ptolemy.kernel.util.Workspace#getWriteAccess() 664 * @see #getWidth() 665 */ 666 public void setWidth(int widthValue) throws IllegalActionException { 667 width.setToken(new IntToken(widthValue)); 668 } 669 670 /////////////////////////////////////////////////////////////////// 671 //// public variables //// 672 673 /** Indicate that the description(int) method should describe the width 674 * of the relation, and whether it has been fixed. 675 */ 676 public static final int CONFIGURATION = 512; 677 678 /** The value of the width we should infer. */ 679 public static final int WIDTH_TO_INFER = -1; 680 681 /////////////////////////////////////////////////////////////////// 682 //// protected methods //// 683 684 /** Throw an exception if the specified port cannot be linked to this 685 * relation (is not of class IOPort). 686 * @param port The candidate port to link to. 687 * @exception IllegalActionException If the port is not an IOPort. 688 */ 689 @Override 690 protected void _checkPort(Port port) throws IllegalActionException { 691 if (!(port instanceof IOPort)) { 692 throw new IllegalActionException(this, port, 693 "IORelation can only link to a IOPort."); 694 } 695 } 696 697 /** Throw an exception if the specified relation is not an instance 698 * of IORelation or if it does not have the same width as this relation. 699 * @param relation The relation to link to. 700 * @param symmetric If true, the call _checkRelation() on the specified 701 * relation with this as an argument. 702 * @exception IllegalActionException If this relation has no container, 703 * or if this relation is not an acceptable relation for the specified 704 * relation, or if this relation and the specified relation do not 705 * have the same width. 706 */ 707 @Override 708 protected void _checkRelation(Relation relation, boolean symmetric) 709 throws IllegalActionException { 710 if (!(relation instanceof IORelation)) { 711 throw new IllegalActionException(this, relation, 712 "IORelation can only link to an IORelation."); 713 } 714 715 int otherWidth = ((IORelation) relation)._getUserWidth(); 716 int width = _getUserWidth(); 717 if (otherWidth != width) { 718 // If one of both widths equals WIDTH_TO_INFER we set both to the other width 719 // (which will be different from WIDTH_TO_INFER). 720 if (otherWidth == WIDTH_TO_INFER) { 721 ((IORelation) relation).setWidth(width); 722 } else if (width == WIDTH_TO_INFER) { 723 setWidth(otherWidth); 724 } else { 725 throw new IllegalActionException(this, relation, 726 "Relations have different widths: " + _getUserWidth() 727 + " != " 728 + ((IORelation) relation)._getUserWidth()); 729 } 730 } 731 732 super._checkRelation(relation, symmetric); 733 } 734 735 /** Return a description of the object. The level of detail depends 736 * on the argument, which is an or-ing of the static final constants 737 * defined in the NamedObj class and in this class. 738 * Lines are indented according to 739 * to the level argument using the protected method _getIndentPrefix(). 740 * Zero, one or two brackets can be specified to surround the returned 741 * description. If one is specified it is the the leading bracket. 742 * This is used by derived classes that will append to the description. 743 * Those derived classes are responsible for the closing bracket. 744 * An argument other than 0, 1, or 2 is taken to be equivalent to 0. 745 * <p> 746 * If the detail argument sets the bit defined by the constant 747 * CONFIGURATION, then append to the description is a field 748 * of the form "configuration {width <i>integer</i> ?fixed?}", where the 749 * word "fixed" is present if the relation has fixed width, and is 750 * absent if the relation is a bus with inferred width (isWidthFixed() 751 * returns false). 752 * 753 * This method is read-synchronized on the workspace. 754 * 755 * @param detail The level of detail. 756 * @param indent The amount of indenting. 757 * @param bracket The number of surrounding brackets (0, 1, or 2). 758 * @return A description of the object. 759 * @exception IllegalActionException If thrown while getting the 760 * description of subcomponents. 761 */ 762 @Override 763 protected String _description(int detail, int indent, int bracket) 764 throws IllegalActionException { 765 try { 766 _workspace.getReadAccess(); 767 768 String result; 769 770 if (bracket == 1 || bracket == 2) { 771 result = super._description(detail, indent, 1); 772 } else { 773 result = super._description(detail, indent, 0); 774 } 775 776 if ((detail & CONFIGURATION) != 0) { 777 if (result.trim().length() > 0) { 778 result += " "; 779 } 780 781 result += "configuration {"; 782 result += "width " + getWidth(); 783 784 if (isWidthFixed()) { 785 result += " fixed"; 786 } 787 788 result += "}"; 789 } 790 791 if (bracket == 2) { 792 result += "}"; 793 } 794 795 return result; 796 } finally { 797 _workspace.doneReading(); 798 } 799 } 800 801 /**Determines whether width inference should be skipped or not. 802 * @return True when width inference needs to be skipped. 803 */ 804 protected boolean _skipWidthInference() { 805 return false; 806 } 807 808 /////////////////////////////////////////////////////////////////// 809 //// packaged methods //// 810 811 /** Set the inferred width of this relation. The width is the number of 812 * channels that the relation represents. 813 * This method is not synchronized on the workspace. 814 * This packaged method is only meant for the width inference 815 * algorithm. It should not be used in other circumstances. 816 * Precondition: you should only infer the width in case it 817 is not set by the user. 818 * @param width The inferred width of the relation. 819 * @exception IllegalActionException If the expression for the width cannot 820 * be parsed or cannot be evaluated, or if the result of evaluation 821 * violates type constraints, or if the result of evaluation is null 822 * and there are variables that depend on this one. 823 */ 824 void _setInferredWidth(int width) throws IllegalActionException { 825 assert _getUserWidth() == WIDTH_TO_INFER; 826 // Precondition: you should only infer the width in case it 827 // is not set by the user. 828 assert width >= 0; 829 _inferredWidthVersion = _workspace.getVersion(); 830 _inferredWidth = width; 831 } 832 833 /////////////////////////////////////////////////////////////////// 834 //// private parameters //// 835 836 /** A parameter to be able to set the width to Auto to automatically 837 * infer widths. This is an integer that equals WIDTH_TO_INFER. 838 */ 839 private Parameter _auto = null; 840 841 /////////////////////////////////////////////////////////////////// 842 //// private methods //// 843 844 /** Cascade two Receiver arrays to form a new array. For each row, each 845 * element of the second array is appended behind the elements of the 846 * first array. This method is solely for deepReceivers. 847 * The two input arrays must have the same number of rows. 848 * @exception IllegalActionException 849 */ 850 private Receiver[][] _cascade(Receiver[][] array1, Receiver[][] array2) 851 throws InvalidStateException, IllegalActionException { 852 if (array1 == null || array1.length <= 0) { 853 return array2; 854 } 855 856 if (array2 == null || array2.length <= 0) { 857 return array1; 858 } 859 860 int width = getWidth(); 861 Receiver[][] result = new Receiver[width][0]; 862 863 for (int i = 0; i < width; i++) { 864 if (array1[i] == null) { 865 result[i] = array2[i]; 866 } else if (array1[i].length <= 0) { 867 result[i] = array2[i]; 868 } else if (array2[i] == null) { 869 result[i] = array1[i]; 870 } else if (array2[i].length <= 0) { 871 result[i] = array1[i]; 872 } else { 873 int m1 = array1[i].length; 874 int m2 = array2[i].length; 875 result[i] = new Receiver[m1 + m2]; 876 877 for (int j = 0; j < m1; j++) { 878 result[i][j] = array1[i][j]; 879 } 880 881 for (int j = m1; j < m1 + m2; j++) { 882 result[i][j] = array2[i][j - m1]; 883 } 884 } 885 } 886 887 return result; 888 } 889 890 /** Return the width set by the user 891 * @return The width set by the user. 892 * @exception IllegalActionException If the expression for the width cannot 893 * be parsed or cannot be evaluated, or if the result of evaluation 894 * violates type constraints, or if the result of evaluation is null 895 * and there are variables that depend on this one. 896 */ 897 private int _getUserWidth() throws IllegalActionException { 898 if (_cachedWidth == -2) { 899 IntToken t = (IntToken) width.getToken(); 900 901 if (t != null) { 902 int width = t.intValue(); 903 _setWidth(width); 904 } 905 } 906 return _cachedWidth; 907 } 908 909 /** Determine whether widths are currently being inferred or not. 910 * @return True When widths are currently being inferred. 911 * @exception IllegalActionException If toplevel not a CompositeActor. 912 */ 913 private boolean _inferringWidths() throws IllegalActionException { 914 Nameable container = getContainer(); 915 916 if (container instanceof CompositeActor) { 917 return ((CompositeActor) container).inferringWidths(); 918 } 919 return false; 920 } 921 922 /** Infer the width of the port from how it is connected. 923 * Throw a runtime exception if this cannot be done (normally, 924 * the methods that construct a topology ensure that it can be 925 * be done). The returned value is always at least one. 926 * This method is not read-synchronized on the workspace, so the caller 927 * should be. 928 * @return The inferred width. 929 * @exception IllegalActionException 930 */ 931 private int _inferWidth() throws IllegalActionException { 932 //The old algorithm for width inference 933 assert !_USE_NEW_WIDTH_INFERENCE_ALGO; 934 long version = _workspace.getVersion(); 935 936 if (version != _inferredWidthVersion) { 937 _inferredWidth = 1; 938 939 Iterator<?> ports = linkedPortList().iterator(); 940 941 // Note that depending on the order of the ports get iterated, 942 // the inferred width may be different if different ports have 943 // different widths. This is nondeterministic. 944 // However, the model behavior is not affected by this because 945 // the relation with the smallest width along a path decides 946 // the number of signals that can be passed through. 947 while (ports.hasNext()) { 948 IOPort p = (IOPort) ports.next(); 949 950 // Infer the width of this port from the linked connections. 951 // Note we assume that this method is only called upon a 952 // multiport. 953 // To guarantee this method successfully infer widths, we have 954 // to check and ensure that there is at most one input relation 955 // or one output relation whose width is not fixed (unknown). 956 // This requirement is conservative. For example, an output 957 // port may have two buses with their widths not fixed. 958 // Furthermore, if one of the buses is connected to an input 959 // port and its width can be determined from the internal 960 // connections associated with that input port, the width of 961 // the other bus can also be resolved. However, to support this, 962 // a fixed-point iteration has to be performed, but there is 963 // no guarantee of existence of a useful fixed-point whose 964 // widths are all non-zero. Therefore, we take the conservative 965 // approach. 966 // To infer the unknown width, we resolve the equation where 967 // the sum of the widths of input relations equals the sum of 968 // those of output relations. 969 int portInsideWidth = 0; 970 int portOutsideWidth = 0; 971 int difference = 0; 972 973 if (p.isInsideGroupLinked(this)) { 974 // I am linked on the inside... 975 portInsideWidth = p._getInsideWidth(this); 976 portOutsideWidth = p._getOutsideWidth(null); 977 978 // the same as portOutsideWidth = p.getWidth(); 979 difference = portOutsideWidth - portInsideWidth; 980 } else if (p.isLinked(this)) { 981 // I am linked on the outside... 982 portInsideWidth = p._getInsideWidth(null); 983 portOutsideWidth = p._getOutsideWidth(this); 984 difference = portInsideWidth - portOutsideWidth; 985 } 986 987 if (difference > _inferredWidth) { 988 _inferredWidth = difference; 989 } 990 } 991 992 _inferredWidthVersion = version; 993 } 994 995 return _inferredWidth; 996 } 997 998 /** Create an initialize the width parameter. */ 999 private void _init() { 1000 try { 1001 1002 _auto = new Parameter(this, "Auto"); 1003 _auto.setExpression(Integer.toString(WIDTH_TO_INFER)); 1004 _auto.setTypeEquals(BaseType.INT); 1005 _auto.setVisibility(Settable.NONE); 1006 _auto.setPersistent(false); 1007 1008 width = new Parameter(this, "width"); 1009 width.setExpression("Auto"); 1010 width.setTypeEquals(BaseType.INT); 1011 1012 } catch (KernelException ex) { 1013 throw new InternalErrorException(ex); 1014 } 1015 } 1016 1017 /** Set the width of this relation and all relations in its 1018 * relation group. The width is the number of 1019 * channels that the relation represents. If the argument 1020 * is equal to the value of WIDTH_TO_INFER, then the relation becomes 1021 * a bus with unspecified width, 1022 * and the width will be inferred from the way the relation is used 1023 * (but will never be less than zero). 1024 * This method invalidates 1025 * the resolved types on the director of the container, if there is 1026 * one, and notifies each connected actor that its connections 1027 * have changed. 1028 * This method write-synchronizes on the workspace. 1029 * 1030 * @param width The width of the relation. 1031 * @exception IllegalActionException If the argument is not zero, one, 1032 * or equal to WIDTH_TO_INFER and the relation is linked to a 1033 * non-multiport. Or when the argument is less than zero and different 1034 * from WIDTH_TO_INFER. 1035 * @see ptolemy.kernel.util.Workspace#getWriteAccess() 1036 * @see #getWidth() 1037 */ 1038 private void _setWidth(int width) throws IllegalActionException { 1039 if (_USE_NEW_WIDTH_INFERENCE_ALGO) { 1040 if (width == _cachedWidth) { 1041 // No change. 1042 return; 1043 } 1044 try { 1045 _workspace.getWriteAccess(); 1046 1047 // Check legitimacy of the change. 1048 if (width < 0 && width != WIDTH_TO_INFER) { 1049 throw new IllegalActionException(this, "" + width 1050 + " is not a valid width for this relation."); 1051 } 1052 1053 /* rodiers: I'd rather keep the following exception since it makes the 1054 * model more consistent, but some tests seem to use this pattern. 1055 * 1056 // Check for non-multiports on a link: should either be 0, 1 or should be inferred. 1057 if (width != 1 && width != 0 && width != WIDTH_TO_INFER) { 1058 for (Object object : linkedPortList()) { 1059 IOPort p = (IOPort) object; 1060 if (!p.isMultiport()) { 1061 throw new IllegalActionException(this, p, 1062 "Cannot make bus because the " 1063 + "relation is linked to a non-multiport."); 1064 } 1065 } 1066 } 1067 */ 1068 1069 _cachedWidth = width; 1070 1071 // Set the width of all relations in the relation group. 1072 Iterator<?> relations = relationGroupList().iterator(); 1073 1074 while (!_suppressWidthPropagation && relations.hasNext()) { 1075 IORelation relation = (IORelation) relations.next(); 1076 1077 if (relation == this) { 1078 continue; 1079 } 1080 1081 // If the relation has a width parameter, set that 1082 // value. Otherwise, just set its width directly. 1083 // Have to disable back propagation. 1084 try { 1085 relation._suppressWidthPropagation = true; 1086 relation.width.setToken(new IntToken(width)); 1087 } finally { 1088 relation._suppressWidthPropagation = false; 1089 } 1090 } 1091 1092 // Invalidate schedule and type resolution. 1093 Nameable container = getContainer(); 1094 1095 if (container instanceof CompositeActor) { 1096 ((CompositeActor) container).notifyConnectivityChange(); 1097 Director director = ((CompositeActor) container) 1098 .getDirector(); 1099 1100 if (director != null) { 1101 director.invalidateSchedule(); 1102 director.invalidateResolvedTypes(); 1103 1104 } 1105 } 1106 1107 // According to the comments this used to happen for this reason: 1108 // Do this as a second pass so that it does not 1109 // get executed if the change is aborted 1110 // above by an exception. 1111 // FIXME: Haven't completely dealt with this 1112 // possibility since the above changes may have 1113 // partially completed. 1114 // With the new width inference algorithm the code below does not 1115 // need to be executed for this reason. However some actors 1116 // do some initialization in the connectionsChanged method that they 1117 // don't do at other points of time. 1118 1119 for (Object port : linkedPortList()) { 1120 IOPort p = (IOPort) port; 1121 Entity portContainer = (Entity) p.getContainer(); 1122 1123 if (portContainer != null) { 1124 portContainer.connectionsChanged(p); 1125 } 1126 } 1127 } finally { 1128 // About conditionally executing doneWriting: 1129 // When the user updates the width we want to increase 1130 // the workspace version to invalidate widths that are being 1131 // cached by IOPort. 1132 // Parameters however have in some sense a strange behavior, 1133 // when the user sets a Parameter (such as width) this typically 1134 // happens with setToken, which - in case the Parameter is not 1135 // lazy - immediately results in the call attributedChanged, which 1136 // will call _setWidth. However when the project is opened, setExpression 1137 // is used. In the case the expression in not immediately evaluated, but 1138 // only when it is necessary. When you call getToken, the expression is evaluated 1139 // which results in the call of attributedChanged, which again results 1140 // in the call _setWidth. In this case however the model didn't change this 1141 // time, but earlier. Typically this happens after opening the model, of creating 1142 // a new relation. This already increased the version of the model, and hence 1143 // cached values or refreshed. Triggering of the width then happens when doing 1144 // width inference. Then we don't want to increase the version number since it would 1145 // invalidate all widths immediately. 1146 // Probably this construct works in practise, but it remains a dangerous one... 1147 if (_inferringWidths()) { 1148 _workspace.doneTemporaryWriting(); 1149 } else { 1150 _workspace.doneWriting(); 1151 } 1152 } 1153 } else { 1154 if (width == _cachedWidth) { 1155 // No change. 1156 return; 1157 } 1158 try { 1159 _workspace.getWriteAccess(); 1160 1161 if (width <= 0) { 1162 // Check legitimacy of the change. 1163 try { 1164 _inferWidth(); 1165 } catch (InvalidStateException ex) { 1166 throw new IllegalActionException(this, ex, 1167 "Cannot use unspecified width on this relation " 1168 + "because of its links."); 1169 } 1170 } 1171 1172 // Check for non-multiports on a link. 1173 /* This is now allowed. 1174 if (width != 1) { 1175 Iterator ports = linkedPortList().iterator(); 1176 1177 while (ports.hasNext()) { 1178 IOPort p = (IOPort) ports.next(); 1179 1180 // Check for non-multiports. 1181 if (!p.isMultiport()) { 1182 throw new IllegalActionException(this, p, 1183 "Cannot make bus because the " 1184 + "relation is linked to a non-multiport."); 1185 } 1186 } 1187 } 1188 */ 1189 _cachedWidth = width; 1190 1191 // Set the width of all relations in the relation group. 1192 Iterator<?> relations = relationGroupList().iterator(); 1193 1194 while (!_suppressWidthPropagation && relations.hasNext()) { 1195 IORelation relation = (IORelation) relations.next(); 1196 1197 if (relation == this) { 1198 continue; 1199 } 1200 1201 // If the relation has a width parameter, set that 1202 // value. Otherwise, just set its width directly. 1203 // Have to disable back propagation. 1204 try { 1205 relation._suppressWidthPropagation = true; 1206 relation.width.setToken(new IntToken(width)); 1207 } finally { 1208 relation._suppressWidthPropagation = false; 1209 } 1210 } 1211 1212 // Do this as a second pass so that it does not 1213 // get executed if the change is aborted 1214 // above by an exception. 1215 // FIXME: Haven't completely dealt with this 1216 // possibility since the above changes may have 1217 // partially completed. 1218 Iterator<?> ports = linkedPortList().iterator(); 1219 1220 while (ports.hasNext()) { 1221 IOPort p = (IOPort) ports.next(); 1222 Entity portContainer = (Entity) p.getContainer(); 1223 1224 if (portContainer != null) { 1225 portContainer.connectionsChanged(p); 1226 } 1227 } 1228 1229 // Invalidate schedule and type resolution. 1230 Nameable container = getContainer(); 1231 1232 if (container instanceof CompositeActor) { 1233 Director director = ((CompositeActor) container) 1234 .getDirector(); 1235 1236 if (director != null) { 1237 director.invalidateSchedule(); 1238 director.invalidateResolvedTypes(); 1239 } 1240 } 1241 } finally { 1242 _workspace.doneWriting(); 1243 } 1244 } 1245 } 1246 1247 /////////////////////////////////////////////////////////////////// 1248 //// packaged variables //// 1249 1250 /** Indicate whether the new or the old width inference algo should be used 1251 * This is a packaged field. 1252 */ 1253 public static final boolean _USE_NEW_WIDTH_INFERENCE_ALGO = true; 1254 1255 /////////////////////////////////////////////////////////////////// 1256 //// private variables //// 1257 1258 // Cached inferred width. 1259 private transient int _inferredWidth = -1; 1260 1261 // The workspace version for the cached inferred width. 1262 private transient long _inferredWidthVersion = -1; 1263 1264 // Suppress propagation of width changes. 1265 private boolean _suppressWidthPropagation = false; 1266 1267 // The cached value of the width parameter. 1268 private int _cachedWidth = -2; 1269 1270}