/******************************************************************************* * Copyright (c) 2006, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation * ******************************************************************************/ package org.eclipse.persistence.jpa.jpql.utility.iterator; import java.io.Serializable; import java.util.List; import java.util.ListIterator; import org.eclipse.persistence.jpa.jpql.utility.CollectionTools; /** * A <code>CloneListIterator</code> iterates over a copy of a list, * allowing for concurrent access to the original list. * <p> * The original list passed to the <code>CloneListIterator</code>'s * constructor should be synchronized; otherwise you run the risk of * a corrupted list (e.g. {@link java.util.Vector}. * <p> * By default, a <code>CloneListIterator</code> does not support the * modification operations; this is because it does not have * access to the original list. But if the <code>CloneListIterator</code> * is supplied with a {@link Mutator} it will delegate the * modification operations to the {@link Mutator}. * Alternatively, a subclass can override the modification methods. * * @param <E> the type of elements returned by the iterator * * @see <a href="http://git.eclipse.org/c/dali/webtools.dali.git/tree/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterable/LiveCloneListIterable.java">LiveCloneListIterable</a> * @see <a href="http://git.eclipse.org/c/dali/webtools.dali.git/tree/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterable/SnapshotCloneListIterable.java">SnapshotCloneListIterable</a> */ public class CloneListIterator<E> implements ListIterator<E> { private int cursor; private final ListIterator<Object> listIterator; private final Mutator<E> mutator; private State state; /** * Construct a list iterator on a copy of the specified array. * The modification methods will not be supported, * unless a subclass overrides them. */ public CloneListIterator(E[] array) { this(array, Mutator.ReadOnly.<E>instance()); } /** * Construct a list iterator on a copy of the specified array. * Use the specified list mutator to modify the original list. */ public CloneListIterator(E[] array, Mutator<E> mutator) { this(mutator, array.clone()); } /** * Construct a list iterator on a copy of the specified list. * The modification methods will not be supported, * unless a subclass overrides them. */ public CloneListIterator(List<? extends E> list) { this(list, Mutator.ReadOnly.<E>instance()); } /** * Construct a list iterator on a copy of the specified list. * Use the specified list mutator to modify the original list. */ public CloneListIterator(List<? extends E> list, Mutator<E> mutator) { this(mutator, list.toArray()); } /** * Internal constructor used by subclasses. * Swap order of arguments to prevent collision with other constructor. * The passed in array will *not* be cloned. */ protected CloneListIterator(Mutator<E> mutator, Object... array) { super(); // build a copy of the list and keep it in sync with original (if the mutator allows changes) // that way the nested list iterator will maintain some of our state this.listIterator = CollectionTools.list(array).listIterator(); this.mutator = mutator; this.cursor = 0; this.state = State.UNKNOWN; } public void add(E o) { // allow the nested iterator to throw an exception before we modify the original list this.listIterator.add(o); this.add(this.cursor, o); this.cursor++; } /** * Add the specified element to the original list. * <p> * This method can be overridden by a subclass as an * alternative to building a {@link Mutator}. */ protected void add(int index, E o) { this.mutator.add(index, o); } public boolean hasNext() { return this.listIterator.hasNext(); } public boolean hasPrevious() { return this.listIterator.hasPrevious(); } /** * The list passed in during construction held elements of type <code>E</code>, * so this cast is not a problem. We need this cast because * all the elements of the original collection were copied into * an object array (<code>Object[]</code>). */ @SuppressWarnings("unchecked") protected E nestedNext() { return (E) this.listIterator.next(); } /** * The list passed in during construction held elements of type <code>E</code>, * so this cast is not a problem. We need this cast because * all the elements of the original collection were copied into * an object array (<code>Object[]</code>). */ @SuppressWarnings("unchecked") protected E nestedPrevious() { return (E) this.listIterator.previous(); } public E next() { // allow the nested iterator to throw an exception before we modify the index E next = this.nestedNext(); this.cursor++; this.state = State.NEXT; return next; } public int nextIndex() { return this.listIterator.nextIndex(); } public E previous() { // allow the nested iterator to throw an exception before we modify the index E previous = this.nestedPrevious(); this.cursor--; this.state = State.PREVIOUS; return previous; } public int previousIndex() { return this.listIterator.previousIndex(); } public void remove() { // allow the nested iterator to throw an exception before we modify the original list this.listIterator.remove(); if (this.state == State.PREVIOUS) { this.remove(this.cursor); } else { this.cursor--; this.remove(this.cursor); } } /** * Remove the specified element from the original list. * <p> * This method can be overridden by a subclass as an * alternative to building a {@link Mutator}. */ protected void remove(int index) { this.mutator.remove(index); } public void set(E o) { // allow the nested iterator to throw an exception before we modify the original list this.listIterator.set(o); if (this.state == State.PREVIOUS) { this.set(this.cursor, o); } else { this.set(this.cursor - 1, o); } } /** * Set the specified element in the original list. * <p> * This method can be overridden by a subclass as an * alternative to building a {@link Mutator}. */ protected void set(int index, E o) { this.mutator.set(index, o); } /** * Used by {@link CloneListIterator} to remove * elements from the original list; since the list iterator * does not have direct access to the original list. */ public interface Mutator<T> { /** * Add the specified object to the original list. */ void add(int index, T o); /** * Remove the specified object from the original list. */ void remove(int index); /** * Set the specified object in the original list. */ void set(int index, T o); final class ReadOnly<S> implements Mutator<S>, Serializable { @SuppressWarnings("rawtypes") public static final Mutator INSTANCE = new ReadOnly(); private static final long serialVersionUID = 1L; // ensure single instance private ReadOnly() { super(); } @SuppressWarnings("unchecked") public static <R> Mutator<R> instance() { return INSTANCE; } // add is not supported public void add(int index, Object o) { throw new UnsupportedOperationException(); } private Object readResolve() { // replace this object with the singleton return INSTANCE; } // remove is not supported public void remove(int index) { throw new UnsupportedOperationException(); } // set is not supported public void set(int index, Object o) { throw new UnsupportedOperationException(); } } } private enum State { NEXT, PREVIOUS, UNKNOWN } }