View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.list;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.ObjectOutputStream;
22  import java.io.Serializable;
23  import java.lang.ref.WeakReference;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.ConcurrentModificationException;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.ListIterator;
30  
31  /**
32   * A {@code List} implementation with a {@code ListIterator} that
33   * allows concurrent modifications to the underlying list.
34   * <p>
35   * This implementation supports all of the optional {@link List} operations.
36   * It extends {@code AbstractLinkedList} and thus provides the
37   * stack/queue/dequeue operations available in {@link java.util.LinkedList}.
38   * </p>
39   * <p>
40   * The main feature of this class is the ability to modify the list and the
41   * iterator at the same time. Both the {@link #listIterator()} and {@link #cursor()}
42   * methods provides access to a {@code Cursor} instance which extends
43   * {@code ListIterator}. The cursor allows changes to the list concurrent
44   * with changes to the iterator. Note that the {@link #iterator()} method and
45   * sublists do <strong>not</strong> provide this cursor behavior.
46   * </p>
47   * <p>
48   * The {@code Cursor} class is provided partly for backwards compatibility
49   * and partly because it allows the cursor to be directly closed. Closing the
50   * cursor is optional because references are held via a {@code WeakReference}.
51   * For most purposes, simply modify the iterator and list at will, and then let
52   * the garbage collector to the rest.
53   * </p>
54   * <p>
55   * <strong>Note that this implementation is not synchronized.</strong>
56   * </p>
57   *
58   * @param <E> the type of the elements in the list.
59   * @see java.util.LinkedList
60   * @since 1.0
61   * @deprecated parent {@link AbstractLinkedList} is source incompatible with List methods added in Java 21
62   */
63  @Deprecated
64  public class CursorableLinkedList<E> extends AbstractLinkedList<E> implements Serializable {
65  
66      /**
67       * An extended {@code ListIterator} that allows concurrent changes to
68       * the underlying list.
69       *
70       * @param <E> the type of elements in this cursor.
71       */
72      public static class Cursor<E> extends AbstractLinkedList.LinkedListIterator<E> {
73          /** Is the cursor valid (not closed) */
74          boolean valid = true;
75          /** Is the next index valid */
76          boolean nextIndexValid = true;
77          /** Flag to indicate if the current element was removed by another object. */
78          boolean currentRemovedByAnother;
79  
80          /**
81           * Constructs a new cursor.
82           *
83           * @param parent  the parent list
84           * @param index  the index to start from
85           */
86          protected Cursor(final CursorableLinkedList<E> parent, final int index) {
87              super(parent, index);
88              valid = true;
89          }
90  
91          /**
92           * Adds an object to the list.
93           * The object added here will be the new 'previous' in the iterator.
94           *
95           * @param obj  the object to add
96           */
97          @Override
98          public void add(final E obj) {
99              // overridden, as the nodeInserted() method updates the iterator state
100             super.add(obj);
101             // matches the (next.previous == node) clause in nodeInserted()
102             // thus next gets changed - reset it again here
103             next = next.next;
104         }
105 
106         /**
107          * Override superclass modCount check, and replace it with our valid flag.
108          */
109         @Override
110         protected void checkModCount() {
111             if (!valid) {
112                 throw new ConcurrentModificationException("Cursor closed");
113             }
114         }
115 
116         // set is not overridden, as it works ok
117         // note that we want it to throw an exception if the element being
118         // set has been removed from the real list (compare this with the
119         // remove method where we silently ignore this case)
120 
121         /**
122          * Mark this cursor as no longer being needed. Any resources
123          * associated with this cursor are immediately released.
124          * In previous versions of this class, it was mandatory to close
125          * all cursor objects to avoid memory leaks. It is <em>no longer</em>
126          * necessary to call this close method; an instance of this class
127          * can now be treated exactly like a normal iterator.
128          */
129         public void close() {
130             if (valid) {
131                 ((CursorableLinkedList<E>) parent).unregisterCursor(this);
132                 valid = false;
133             }
134         }
135 
136         /**
137          * Gets the index of the next element to be returned.
138          *
139          * @return the next index
140          */
141         @Override
142         public int nextIndex() {
143             if (!nextIndexValid) {
144                 if (next == parent.header) {
145                     nextIndex = parent.size();
146                 } else {
147                     int pos = 0;
148                     Node<E> temp = parent.header.next;
149                     while (temp != next) {
150                         pos++;
151                         temp = temp.next;
152                     }
153                     nextIndex = pos;
154                 }
155                 nextIndexValid = true;
156             }
157             return nextIndex;
158         }
159 
160         /**
161          * Handle event from the list when a node has changed.
162          *
163          * @param node  the node that changed
164          */
165         protected void nodeChanged(final Node<E> node) {
166             // do nothing
167         }
168 
169         /**
170          * Handle event from the list when a node has been added.
171          *
172          * @param node  the node that was added
173          */
174         protected void nodeInserted(final Node<E> node) {
175             if (node.previous == current || next.previous == node) {
176                 next = node;
177             } else {
178                 nextIndexValid = false;
179             }
180         }
181 
182         /**
183          * Handle event from the list when a node has been removed.
184          *
185          * @param node  the node that was removed
186          */
187         protected void nodeRemoved(final Node<E> node) {
188             if (node == next && node == current) {
189                 // state where next() followed by previous()
190                 next = node.next;
191                 current = null;
192                 currentRemovedByAnother = true;
193             } else if (node == next) {
194                 // state where next() not followed by previous()
195                 // and we are matching next node
196                 next = node.next;
197                 currentRemovedByAnother = false;
198             } else if (node == current) {
199                 // state where next() not followed by previous()
200                 // and we are matching current (last returned) node
201                 current = null;
202                 currentRemovedByAnother = true;
203                 nextIndex--;
204             } else {
205                 nextIndexValid = false;
206                 currentRemovedByAnother = false;
207             }
208         }
209 
210         /**
211          * Removes the item last returned by this iterator.
212          * <p>
213          * There may have been subsequent alterations to the list
214          * since you obtained this item, however you can still remove it.
215          * You can even remove it if the item is no longer in the main list.
216          * However, you can't call this method on the same iterator more
217          * than once without calling next() or previous().
218          *
219          * @throws IllegalStateException if there is no item to remove
220          */
221         @Override
222         public void remove() {
223             // overridden, as the nodeRemoved() method updates the iterator
224             // state in the parent.removeNode() call below
225             if (current == null && currentRemovedByAnother) { // NOPMD
226                 // quietly ignore, as the last returned node was removed
227                 // by the list or some other iterator
228                 // by ignoring it, we keep this iterator independent of
229                 // other changes as much as possible
230             } else {
231                 checkModCount();
232                 parent.removeNode(getLastNodeReturned());
233             }
234             currentRemovedByAnother = false;
235         }
236     }
237 
238     /**
239      * A cursor for the sublist based on LinkedSubListIterator.
240      *
241      * @param <E> the type of elements in this cursor.
242      * @since 3.2
243      */
244     protected static class SubCursor<E> extends Cursor<E> {
245 
246         /** The parent list */
247         protected final LinkedSubList<E> sub;
248 
249         /**
250          * Constructs a new cursor.
251          *
252          * @param sub  the sub list
253          * @param index  the index to start from
254          */
255         protected SubCursor(final LinkedSubList<E> sub, final int index) {
256             super((CursorableLinkedList<E>) sub.parent, index + sub.offset);
257             this.sub = sub;
258         }
259 
260         @Override
261         public void add(final E obj) {
262             super.add(obj);
263             sub.expectedModCount = parent.modCount;
264             sub.size++;
265         }
266 
267         @Override
268         public boolean hasNext() {
269             return nextIndex() < sub.size;
270         }
271 
272         @Override
273         public boolean hasPrevious() {
274             return previousIndex() >= 0;
275         }
276 
277         @Override
278         public int nextIndex() {
279             return super.nextIndex() - sub.offset;
280         }
281 
282         @Override
283         public void remove() {
284             super.remove();
285             sub.expectedModCount = parent.modCount;
286             sub.size--;
287         }
288     }
289 
290     /** Ensure serialization compatibility */
291     private static final long serialVersionUID = 8836393098519411393L;
292 
293     /** A list of the cursor currently open on this list */
294     private transient List<WeakReference<Cursor<E>>> cursors;
295 
296     /**
297      * Constructor that creates.
298      */
299     public CursorableLinkedList() {
300         init(); // must call init() as use super();
301     }
302 
303     /**
304      * Constructor that copies the specified collection
305      *
306      * @param coll  the collection to copy
307      */
308     public CursorableLinkedList(final Collection<? extends E> coll) {
309         super(coll);
310     }
311 
312     /**
313      * Inserts a new node into the list.
314      *
315      * @param nodeToInsert  new node to insert
316      * @param insertBeforeNode  node to insert before
317      * @throws NullPointerException if either node is null
318      */
319     @Override
320     protected void addNode(final Node<E> nodeToInsert, final Node<E> insertBeforeNode) {
321         super.addNode(nodeToInsert, insertBeforeNode);
322         broadcastNodeInserted(nodeToInsert);
323     }
324 
325     /**
326      * Informs all of my registered cursors that the specified
327      * element was changed.
328      *
329      * @param node  the node that was changed
330      */
331     protected void broadcastNodeChanged(final Node<E> node) {
332         final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator();
333         while (it.hasNext()) {
334             final WeakReference<Cursor<E>> ref = it.next();
335             final Cursor<E> cursor = ref.get();
336             if (cursor == null) {
337                 it.remove(); // clean up list
338             } else {
339                 cursor.nodeChanged(node);
340             }
341         }
342     }
343 
344     /**
345      * Informs all of my registered cursors that the specified
346      * element was just added to my list.
347      *
348      * @param node  the node that was changed
349      */
350     protected void broadcastNodeInserted(final Node<E> node) {
351         final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator();
352         while (it.hasNext()) {
353             final WeakReference<Cursor<E>> ref = it.next();
354             final Cursor<E> cursor = ref.get();
355             if (cursor == null) {
356                 it.remove(); // clean up list
357             } else {
358                 cursor.nodeInserted(node);
359             }
360         }
361     }
362 
363     /**
364      * Informs all of my registered cursors that the specified
365      * element was just removed from my list.
366      *
367      * @param node  the node that was changed
368      */
369     protected void broadcastNodeRemoved(final Node<E> node) {
370         final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator();
371         while (it.hasNext()) {
372             final WeakReference<Cursor<E>> ref = it.next();
373             final Cursor<E> cursor = ref.get();
374             if (cursor == null) {
375                 it.remove(); // clean up list
376             } else {
377                 cursor.nodeRemoved(node);
378             }
379         }
380     }
381 
382     /**
383      * Creates a list iterator for the sublist.
384      *
385      * @param subList  the sublist to get an iterator for
386      * @param fromIndex  the index to start from, relative to the sublist
387      * @return the list iterator for the sublist
388      */
389     @Override
390     protected ListIterator<E> createSubListListIterator(final LinkedSubList<E> subList, final int fromIndex) {
391         final SubCursor<E> cursor = new SubCursor<>(subList, fromIndex);
392         registerCursor(cursor);
393         return cursor;
394     }
395 
396     /**
397      * Returns a {@link Cursor} for iterating through the elements of this list.
398      * <p>
399      * A {@code Cursor} is a {@code ListIterator} with an additional
400      * {@code close()} method. Calling this method immediately discards the
401      * references to the cursor. If it is not called, then the garbage collector
402      * will still remove the reference as it is held via a {@code WeakReference}.
403      * <p>
404      * The cursor enables iteration and list changes to occur in any order without
405      * invalidating the iterator (from one thread). When elements are added to the
406      * list, an event is fired to all active cursors enabling them to adjust to the
407      * change in the list.
408      * <p>
409      * When the "current" (i.e., last returned by {@link ListIterator#next}
410      * or {@link ListIterator#previous}) element of the list is removed,
411      * the cursor automatically adjusts to the change (invalidating the
412      * last returned value such that it cannot be removed).
413      * <p>
414      * The {@link #listIterator()} method returns the same as this method, and can
415      * be cast to a {@code Cursor} if the {@code close} method is required.
416      *
417      * @return a new cursor iterator
418      */
419     public CursorableLinkedList.Cursor<E> cursor() {
420         return cursor(0);
421     }
422 
423     /**
424      * Returns a {@link Cursor} for iterating through the elements of this list
425      * starting from a specified index.
426      * <p>
427      * A {@code Cursor} is a {@code ListIterator} with an additional
428      * {@code close()} method. Calling this method immediately discards the
429      * references to the cursor. If it is not called, then the garbage collector
430      * will still remove the reference as it is held via a {@code WeakReference}.
431      * <p>
432      * The cursor enables iteration and list changes to occur in any order without
433      * invalidating the iterator (from one thread). When elements are added to the
434      * list, an event is fired to all active cursors enabling them to adjust to the
435      * change in the list.
436      * <p>
437      * When the "current" (i.e., last returned by {@link ListIterator#next}
438      * or {@link ListIterator#previous}) element of the list is removed,
439      * the cursor automatically adjusts to the change (invalidating the
440      * last returned value such that it cannot be removed).
441      * <p>
442      * The {@link #listIterator(int)} method returns the same as this method, and can
443      * be cast to a {@code Cursor} if the {@code close} method is required.
444      *
445      * @param fromIndex  the index to start from
446      * @return a new cursor iterator
447      * @throws IndexOutOfBoundsException if the index is out of range
448      *      (index &lt; 0 || index &gt; size()).
449      */
450     public CursorableLinkedList.Cursor<E> cursor(final int fromIndex) {
451         final Cursor<E> cursor = new Cursor<>(this, fromIndex);
452         registerCursor(cursor);
453         return cursor;
454     }
455 
456     /**
457      * The equivalent of a default constructor called
458      * by any constructor and by {@code readObject}.
459      */
460     @Override
461     protected void init() {
462         super.init();
463         cursors = new ArrayList<>();
464     }
465 
466     /**
467      * Returns an iterator that does <strong>not</strong> support concurrent modification.
468      * <p>
469      * If the underlying list is modified while iterating using this iterator
470      * a ConcurrentModificationException will occur.
471      * The cursor behavior is available via {@link #listIterator()}.
472      *
473      * @return a new iterator that does <strong>not</strong> support concurrent modification
474      */
475     @Override
476     public Iterator<E> iterator() {
477         return super.listIterator(0);
478     }
479 
480     /**
481      * Returns a cursor iterator that allows changes to the underlying list in parallel.
482      * <p>
483      * The cursor enables iteration and list changes to occur in any order without
484      * invalidating the iterator (from one thread). When elements are added to the
485      * list, an event is fired to all active cursors enabling them to adjust to the
486      * change in the list.
487      * <p>
488      * When the "current" (i.e., last returned by {@link ListIterator#next}
489      * or {@link ListIterator#previous}) element of the list is removed,
490      * the cursor automatically adjusts to the change (invalidating the
491      * last returned value such that it cannot be removed).
492      *
493      * @return a new cursor iterator
494      */
495     @Override
496     public ListIterator<E> listIterator() {
497         return cursor(0);
498     }
499 
500     /**
501      * Returns a cursor iterator that allows changes to the underlying list in parallel.
502      * <p>
503      * The cursor enables iteration and list changes to occur in any order without
504      * invalidating the iterator (from one thread). When elements are added to the
505      * list, an event is fired to all active cursors enabling them to adjust to the
506      * change in the list.
507      * <p>
508      * When the "current" (i.e., last returned by {@link ListIterator#next}
509      * or {@link ListIterator#previous}) element of the list is removed,
510      * the cursor automatically adjusts to the change (invalidating the
511      * last returned value such that it cannot be removed).
512      *
513      * @param fromIndex  the index to start from
514      * @return a new cursor iterator
515      */
516     @Override
517     public ListIterator<E> listIterator(final int fromIndex) {
518         return cursor(fromIndex);
519     }
520 
521     /**
522      * Deserializes the data held in this object to the stream specified.
523      *
524      * @param in  the input stream
525      * @throws IOException if an error occurs while reading from the stream
526      * @throws ClassNotFoundException if an object read from the stream cannot be loaded
527      */
528     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
529         in.defaultReadObject();
530         doReadObject(in);
531     }
532 
533     /**
534      * Registers a cursor to be notified of changes to this list.
535      *
536      * @param cursor  the cursor to register
537      */
538     protected void registerCursor(final Cursor<E> cursor) {
539         // We take this opportunity to clean the cursors list
540         // of WeakReference objects to garbage-collected cursors.
541         cursors.removeIf(ref -> ref.get() == null);
542         cursors.add(new WeakReference<>(cursor));
543     }
544 
545     /**
546      * Removes all nodes by iteration.
547      */
548     @Override
549     protected void removeAllNodes() {
550         if (!isEmpty()) {
551             // superclass implementation would break all the iterators
552             final Iterator<E> it = iterator();
553             while (it.hasNext()) {
554                 it.next();
555                 it.remove();
556             }
557         }
558     }
559 
560     /**
561      * Removes the specified node from the list.
562      *
563      * @param node  the node to remove
564      * @throws NullPointerException if {@code node} is null
565      */
566     @Override
567     protected void removeNode(final Node<E> node) {
568         super.removeNode(node);
569         broadcastNodeRemoved(node);
570     }
571 
572     /**
573      * Deregisters a cursor from the list to be notified of changes.
574      *
575      * @param cursor  the cursor to deregister
576      */
577     protected void unregisterCursor(final Cursor<E> cursor) {
578         for (final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator(); it.hasNext();) {
579             final WeakReference<Cursor<E>> ref = it.next();
580             final Cursor<E> cur = ref.get();
581             if (cur == null) {
582                 // some other unrelated cursor object has been
583                 // garbage-collected; let's take the opportunity to
584                 // clean up the cursors list anyway.
585                 it.remove();
586             } else if (cur == cursor) {
587                 ref.clear();
588                 it.remove();
589                 break;
590             }
591         }
592     }
593 
594     /**
595      * Updates the node with a new value.
596      * This implementation sets the value on the node.
597      * Subclasses can override this to record the change.
598      *
599      * @param node  node to update
600      * @param value  new value of the node
601      */
602     @Override
603     protected void updateNode(final Node<E> node, final E value) {
604         super.updateNode(node, value);
605         broadcastNodeChanged(node);
606     }
607 
608     /**
609      * Serializes this object to an ObjectOutputStream.
610      *
611      * @param out the target ObjectOutputStream.
612      * @throws IOException thrown when an I/O errors occur writing to the target stream.
613      */
614     private void writeObject(final ObjectOutputStream out) throws IOException {
615         out.defaultWriteObject();
616         doWriteObject(out);
617     }
618 
619 }