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}