001/* List of cross-references. 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 027 */ 028package ptolemy.kernel.util; 029 030import java.util.Enumeration; 031import java.util.NoSuchElementException; 032 033/////////////////////////////////////////////////////////////////// 034//// CrossRefList 035 036/** 037 038 CrossRefList is a list that maintains pointers to other CrossRefLists. 039 CrossRefList is meant to be used to keep track of links between ports 040 and relations in Ptolemy II. It is a highly specialized form of a 041 list. An instance has a container (an Object), which is immutable, 042 meaning that the container cannot be changed after the instance is 043 constructed. CrossRefList enumerators and query methods do not return 044 references to other CrossRefLists; instead, references to the 045 containers of the CrossRefLists (which can be any Object) are 046 returned. 047 048 <p> 049 For efficiency, CrossRefList maintains a list of pairwise links 050 between Objects (CrossRefs). That is, each member of a set of objects 051 has a list of references to other members of the set, and each 052 reference has a similar list that contains a corresponding back 053 reference. Each reference list is an instance of . The class 054 is used as if it were a simple list of references to objects, but it 055 ensures the symmetry of the references, and supports efficient removal 056 of links. Removing a reference in one list automatically updates N 057 back-references in O(N) time, independent of the sizes of the 058 cross-referenced lists. 059 <p> 060 It is possible to create links at specified points in the list (called 061 the <i>index number</i>). This may result in gaps in the list at 062 lower index numbers. Gaps are representing by null in an enumeration 063 of the list. 064 065 @author Geroncio Galicia, Contributor: Edward A. Lee 066 @version $Id$ 067 @since Ptolemy II 0.2 068 @Pt.ProposedRating Green (eal) 069 @Pt.AcceptedRating Green (bart) 070 */ 071public final class CrossRefList { 072 /** Construct a list with the specified container. 073 * The container is immutable (it cannot be changed). 074 * If the argument is null, then a null pointer exception will result 075 * (eventually). 076 * @param container The container of the object to be constructed. 077 */ 078 public CrossRefList(Object container) { 079 _container = container; 080 } 081 082 /** Create a new CrossRefList that is linked to the same 083 * CrossRefLists as the original CrossRefList except that this 084 * new one has a new container. This method synchronizes on the 085 * original list. Note that this behaves like a copy constructor, 086 * except that the remote list is affected so that it will now point 087 * back to both lists. If either argument is null, then a null 088 * pointer exception will be thrown. 089 * @param container The container of the object to be constructed. 090 * @param originalList The model to copy. 091 */ 092 public CrossRefList(Object container, CrossRefList originalList) { 093 this(container); 094 095 synchronized (originalList) { 096 if (originalList.size() == 0) { 097 return; // List to copy is empty. 098 } 099 100 for (CrossRef p = originalList._headNode; p != null; p = p._next) { 101 if (p._far._nearList() != null) { 102 try { 103 link(p._far._nearList()); 104 } catch (IllegalActionException ex) { 105 // This should not be thrown. 106 throw new InternalErrorException(null, ex, null); 107 } 108 } 109 } 110 } 111 } 112 113 /////////////////////////////////////////////////////////////////// 114 //// public methods //// 115 116 /** Return the first container linked to this list, or 117 * null if the list is empty. Note that we may also return null 118 * if the list is not empty, but the first link happens to not point 119 * anywhere. Thus, do not use this method to check to see whether 120 * the list is empty. 121 * Time complexity: O(1). 122 * @return The first entry, or null if there is none. 123 */ 124 public synchronized Object first() { 125 if (_headNode != null) { 126 return _headNode._farContainer(); 127 } else { 128 return null; 129 } 130 } 131 132 /** Get the container at the specified index. If there is no such 133 * container, return null. Indices begin with 0. 134 * @param index The index of the container to return. 135 * @return The container at the specified index. 136 */ 137 public synchronized Object get(int index) { 138 if (index < 0 || index >= _size) { 139 return null; 140 } 141 142 int count = 0; 143 CrossRef result = _headNode; 144 145 while (result != null && count++ < index) { 146 result = result._next; 147 } 148 149 if (result != null) { 150 return result._farContainer(); 151 } else { 152 return null; 153 } 154 } 155 156 /** Enumerate the containers linked to this list. The 157 * enumeration returns the container object itself and not the 158 * CrossRefList instance in this list that the object owns or 159 * contains. Note that an object may be enumerated more than 160 * once if more than one link to it has been established. 161 * Also, there may be elements in the list that are null, 162 * indicating that nothing is linked at that index position. 163 * Time complexity: O(1). 164 * NOTE: This method is not well named, since it returns the 165 * containers of other instances of CrossRefList that are linked 166 * to this one, and not the container of this one. 167 * @return An enumeration of remote referenced objects. 168 */ 169 public synchronized Enumeration getContainers() { 170 return new CrossRefEnumeration(); 171 } 172 173 /** Insert a link to the specified CrossRefList (<i>farList</i>) at 174 * the specified position (<i>index</i>). The index can be any 175 * non-negative integer. If the index is greater than or equal to the 176 * size of this list, then null links are created to make the list 177 * big enough. If there are already links with indices equal to or 178 * larger than <i>index</i>, then their indices become one larger. 179 * This method creates a back reference in the far list, unless the 180 * <i>farList</i> argument is null. That back reference is appended 181 * to the end of the far list. 182 * <p> 183 * Note that this method specifies the index on the local reference 184 * list, but not on the remote reference list. There is no mechanism 185 * provided to specify both indices. This is because this class is 186 * used to linked ports to relations, and indices of links have 187 * no meaning in relations. Thus, there is no need for a richer 188 * interface. 189 * <p> 190 * Note that this method does not synchronize on the remote object. 191 * Thus, this method should be called within a write-synchronization of 192 * the common workspace. 193 * @param index The position in the list at which to insert this link. 194 * @param farList The cross reference list contained by the remote object. 195 * @exception IllegalActionException If this list tries linking to 196 * itself. 197 */ 198 public synchronized void insertLink(int index, CrossRefList farList) 199 throws IllegalActionException { 200 if (farList == this) { 201 throw new IllegalActionException( 202 "CrossRefLink.link: Illegal self-link."); 203 } 204 205 ++_listVersion; 206 207 CrossRef localCrossRef = new CrossRef(index); 208 209 if (farList != null) { 210 localCrossRef._far = farList.new CrossRef(localCrossRef); 211 } 212 } 213 214 /** Return true if the specified container is linked to this 215 * list. If no container is passed or if this CrossRefList has 216 * no links then just return. 217 * Time complexity: O(n). 218 * @param obj An object that might be referenced. 219 * @return A boolean indicating whether the object is referenced. 220 */ 221 public synchronized boolean isLinked(Object obj) { 222 if (obj == null || _size == 0) { 223 return false; 224 } 225 226 for (CrossRef p = _headNode; p != null; p = p._next) { 227 Object far = p._farContainer(); 228 229 if (far != null && far.equals(obj)) { 230 return true; 231 } 232 } 233 234 return false; 235 } 236 237 /** Link this list to a different CrossRefList (<i>farList</i>). 238 * The link is appended at the end of the list. 239 * This action additionally creates a back-reference in the far list, 240 * also at the end, unless the <i>farList</i> argument is null, in 241 * which case the new link is a null link. 242 * Redundant entries are allowed; that is, you can link more than 243 * once to the same remote list. 244 * Note that this method does not synchronize on the remote object. 245 * Thus, this method should be called within a write-synchronization of 246 * the common workspace. 247 * Time complexity: O(1). 248 * @param farList The cross reference list contained by the remote object. 249 * @exception IllegalActionException If this list tries linking to 250 * itself. 251 */ 252 public synchronized void link(CrossRefList farList) 253 throws IllegalActionException { 254 if (farList == this) { 255 throw new IllegalActionException( 256 "CrossRefLink.link: Illegal self-link."); 257 } 258 259 ++_listVersion; 260 261 CrossRef localCrossRef = new CrossRef(); 262 263 if (farList != null) { 264 localCrossRef._far = farList.new CrossRef(localCrossRef); 265 } 266 } 267 268 /** Return size of this list. 269 * Time complexity: O(1). 270 * @return A non-negative integer. 271 */ 272 public synchronized int size() { 273 return _size; 274 } 275 276 // /** Return a String represetation of this object. 277 // * For debugging use only. 278 // * @return A String representation of this object. 279 // */ 280 // public String toString() { 281 // StringBuffer results = new StringBuffer("container: " + _container 282 // + " size: " + _size 283 // + "\n _headNode: " 284 // + (_headNode == null ? "null" : _headNode.description()) 285 // + "\n _lastNode: " 286 // + (_lastNode == null ? "null" : _lastNode.description())); 287 // for (CrossRef p = _headNode; p != null; p = p._next) { 288 // results.append("\n" + p.description()); 289 // } 290 // return results.toString(); 291 // } 292 293 /** Delete the link at the specified index. If there is no such 294 * link, ignore. Back references are likewise updated. 295 * Note that the index numbers of any links at higher indices 296 * will decrease by one. 297 * Time complexity: O(n). 298 * @param index The index of the link to delete. 299 */ 300 public synchronized void unlink(int index) { 301 int count = 0; 302 CrossRef toDelete = _headNode; 303 304 while (toDelete != null && count++ < index) { 305 toDelete = toDelete._next; 306 } 307 308 if (toDelete != null) { 309 toDelete._dissociate(); 310 } 311 } 312 313 /** Delete all links to the specified container. If there is no such 314 * link, ignore. Back references are likewise updated. 315 * In the case of redundant links this deletes the first link 316 * to that container. 317 * Time complexity: O(n). 318 * @param obj The object to delete. 319 */ 320 public synchronized void unlink(Object obj) { 321 if (obj == null || _size == 0) { 322 return; 323 } 324 325 CrossRef p = _headNode; 326 327 while (p != null) { 328 CrossRef n = p._next; 329 Object far = p._farContainer(); 330 331 if (far != null && far.equals(obj)) { 332 p._dissociate(); 333 } 334 335 p = n; 336 } 337 } 338 339 /** Delete all cross references. 340 * Time complexity: O(n). 341 */ 342 public synchronized void unlinkAll() { 343 if (_size == 0) { 344 return; 345 } 346 347 CrossRef p = _headNode; 348 349 while (p != null) { 350 CrossRef n = p._next; 351 p._dissociate(); 352 p = n; 353 } 354 355 // Mark the list empty. 356 _headNode = null; 357 _lastNode = null; 358 _size = 0; 359 } 360 361 /////////////////////////////////////////////////////////////////// 362 //// private variables //// 363 364 /** @serial Version number is incremented each time the list is modified. 365 * This is used to make sure that elements accessed via an enumeration 366 * are valid. This is inspired by a similar mechanism in Doug Lea's 367 * Java Collections. 368 */ 369 private long _listVersion = 0; 370 371 /** @serial The code ensures that if this is non-zero, then _headNode 372 * is non-null. 373 */ 374 private int _size = 0; 375 376 /** @serial Head Node */ 377 private CrossRef _headNode; 378 379 /** @serial Last Node */ 380 private CrossRef _lastNode; 381 382 /** @serial NOTE: In jdk 1.2 or higher, this could be made final to 383 * prohibit what is called "reference reseating" (not resetting), 384 * i.e. to make the variable immutable. 385 */ 386 private Object _container; 387 388 /////////////////////////////////////////////////////////////////// 389 //// inner classes //// 390 391 /** 392 * Objects of this type form the elements of the list. 393 * They occur in pairs, one in each list at each end of a link. 394 * A CrossRef is similar to a "link" in a doubly-linked list. 395 * (Note that the usage of "link" here is different from "link" 396 * in the comments for the above public methods. "Link" here 397 * refers to an element of a doubly-linked list.) 398 */ 399 protected class CrossRef { 400 // /** Return a String represetation of this object. 401 // * For debugging use only. 402 // * @return A String representation of this object. 403 // */ 404 // public String description() { 405 // return toString() 406 // + "\n\t_far: " + _far 407 // + "\n\t_next: " + _next 408 // + "\n\t_previous: " + _previous 409 // + "\n\t_nearContainer(): " + _nearContainer() 410 // + "\n\t_farContainer(): " + _farContainer(); 411 // } 412 413 /** The far CrossRef. */ 414 protected CrossRef _far; 415 416 private CrossRef _next; 417 418 private CrossRef _previous; 419 420 private CrossRef() { 421 this(null); 422 } 423 424 private CrossRef(CrossRef spouse) { 425 _far = spouse; 426 427 if (_size > 0) { 428 _previous = _lastNode; 429 _lastNode._next = this; 430 _lastNode = this; 431 } else { 432 // List is empty. 433 _lastNode = this; 434 _headNode = this; 435 } 436 437 ++_size; 438 } 439 440 // Insert an empty link at the specified index number, which 441 // can be any non-negative integer. 442 // This may result in empty links being created at lower 443 // index numbers, if there are not already links present. 444 private CrossRef(int index) { 445 if (index == 0) { 446 if (_size == 0) { 447 // list is empty 448 _lastNode = this; 449 _headNode = this; 450 } else { 451 _next = _headNode; 452 _headNode._previous = this; 453 _headNode = this; 454 } 455 } else { 456 // Index is not the first. 457 // Chain down the list, creating empty links if necessary. 458 // First chaining step is special, setting "previous" to 459 // get us started. 460 CrossRef previous = _headNode; 461 462 if (previous == null && index > 0) { 463 // List is empty. 464 previous = new CrossRef(); 465 _headNode = previous; 466 _lastNode = previous; 467 } 468 469 int ind = 1; 470 471 while (ind++ < index) { 472 // previous cannot possibly be null here. 473 if (previous._next == null) { 474 // We are off the end of the list. 475 CrossRef newCrossRef = new CrossRef(); 476 previous._next = newCrossRef; 477 newCrossRef._previous = previous; 478 _lastNode = newCrossRef; 479 previous = newCrossRef; 480 } else { 481 // There is an entry in the list. 482 previous = previous._next; 483 } 484 } 485 486 // Now we are assured that there are at least index-1 entries 487 // in the list. 488 if (previous != null) { 489 // There is at least one entry. 490 // If the new node is the last in the list, update the 491 // list's pointer to the last node. 492 if (_lastNode == previous) { 493 _lastNode = this; 494 } 495 496 _next = previous._next; 497 if (previous._next != null) { 498 previous._next._previous = this; 499 } 500 previous._next = this; 501 _previous = previous; 502 } else { 503 // List is empty. 504 _lastNode = this; 505 _headNode = this; 506 } 507 } 508 509 ++_size; 510 } 511 512 // NOTE: It is essential that this method not be 513 // synchronized, since it is called by _farContainer(), 514 // which is. Having it synchronized can lead to 515 // deadlock. Fortunately, it is an atomic action, 516 // so it need not be synchronized. 517 private Object _nearContainer() { 518 return _container; 519 } 520 521 private synchronized Object _farContainer() { 522 if (_far != null) { 523 return _far._nearContainer(); 524 } else { 525 return null; 526 } 527 } 528 529 // NOTE: This need not be synchronized as it is an atomic action. 530 private CrossRefList _nearList() { 531 return CrossRefList.this; 532 } 533 534 private synchronized void _dissociate() { 535 _unlink(); // Remove this. 536 537 // NOTE: Deadlock risk here! If _far is waiting 538 // on a lock to this CrossRef, then we will get 539 // deadlock. However, this will only happen if 540 // we have two threads simultaneously modifying a 541 // model. At the moment (4/29/04), we have no 542 // mechanism for doing that without first 543 // acquiring write permission the workspace(). 544 // Two threads cannot simultaneously hold that 545 // write access. 546 if (_far != null) { 547 _far._unlink(); // Remove far 548 } 549 } 550 551 private synchronized void _unlink() { 552 ++_listVersion; 553 554 // Removes this from enclosing CrossRefList. 555 if (_next != null) { 556 _next._previous = _previous; // Modify next. 557 } else { 558 _lastNode = _previous; 559 } 560 561 if (_previous != null) { 562 _previous._next = _next; // Modify previous. 563 } else { 564 _headNode = _next; 565 } 566 567 _size--; // Modify list. 568 } 569 } 570 571 /** Enumerate the objects pointed to by the list. 572 * @see CrossRefList 573 */ 574 private class CrossRefEnumeration implements Enumeration { 575 public CrossRefEnumeration() { 576 _enumeratorVersion = _listVersion; 577 _ref = _headNode; 578 } 579 580 /** Return true if there are more elements to enumerate. */ 581 @Override 582 public boolean hasMoreElements() { 583 if (_enumeratorVersion != _listVersion) { 584 throw new InvalidStateException( 585 "CrossRefList.hasMoreElements(): " 586 + "The list has been modified."); 587 } 588 589 if (_ref == null) { 590 return false; 591 } else { 592 return true; 593 } 594 } 595 596 /** Return the next element in the enumeration. 597 * @exception java.util.NoSuchElementException If the enumeration is 598 * exhausted. 599 */ 600 @Override 601 public Object nextElement() throws NoSuchElementException { 602 if (_enumeratorVersion != _listVersion) { 603 throw new InvalidStateException("CrossRefList.nextElement(): " 604 + "The list has been modified."); 605 } 606 607 if (_ref == null) { 608 throw new NoSuchElementException("exhausted enumeration"); 609 } else { 610 CrossRef p = _ref; 611 Object v = _ref._farContainer(); 612 _ref = p._next; 613 return v; 614 } 615 } 616 617 private long _enumeratorVersion; 618 619 private CrossRef _ref; 620 } 621}