/*******************************************************************************
 * Copyright (c) 2006, 2014 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.tools.utility.iterator;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.persistence.jpa.jpql.utility.iterator.ArrayIterator;

/**
 * A <code>CloneIterator</code> iterates over a copy of a collection,
 * allowing for concurrent access to the original collection.
 * <p>
 * The original collection passed to the <code>CloneIterator</code>'s
 * constructor should be synchronized (e.g. {@link java.util.Vector});
 * otherwise you run the risk of a corrupted collection.
 * <p>
 * By default, a <code>CloneIterator</code> does not support the
 * {@link #remove()} operation; this is because it does not have
 * access to the original collection. But if the <code>CloneIterator</code>
 * is supplied with an {@link Remover} it will delegate the
 * {@link #remove()} operation to the {@link Remover}.
 * Alternatively, a subclass can override the {@link #remove(Object)}
 * method.
 *
 * @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/LiveCloneIterable.java">LiveCloneIterable</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/SnapshotCloneIterable.java">SnapshotCloneIterable</a>
 */
public class CloneIterator<E>
	implements Iterator<E>
{
	private final Iterator<Object> iterator;
	private E current;
	private final Remover<E> remover;
	private boolean removeAllowed;


	// ********** constructors **********

	/**
	 * Construct an iterator on a copy of the specified collection.
	 * The {@link #remove()} method will not be supported,
	 * unless a subclass overrides the {@link #remove(Object)}.
	 */
	public CloneIterator(Collection<? extends E> collection) {
		this(collection, Remover.ReadOnly.<E>instance());
	}

	/**
	 * Construct an iterator on a copy of the specified array.
	 * The {@link #remove()} method will not be supported,
	 * unless a subclass overrides the {@link #remove(Object)}.
	 */
	public CloneIterator(E[] array) {
		this(array, Remover.ReadOnly.<E>instance());
	}

	/**
	 * Construct an iterator on a copy of the specified collection.
	 * Use the specified remover to remove objects from the
	 * original collection.
	 */
	public CloneIterator(Collection<? extends E> collection, Remover<E> remover) {
		this(remover, collection.toArray());
	}

	/**
	 * Construct an iterator on a copy of the specified array.
	 * Use the specified remover to remove objects from the
	 * original array.
	 */
	public CloneIterator(E[] array, Remover<E> remover) {
		this(remover, array.clone());
	}

	/**
	 * Internal constructor used by subclasses.
	 * Swap order of arguments to prevent collision with other constructor.
	 * The passed in array will *not* be cloned.
	 */
	protected CloneIterator(Remover<E> remover, Object... array) {
		super();
		this.iterator = new ArrayIterator<Object>(array);
		this.current = null;
		this.remover = remover;
		this.removeAllowed = false;
	}


	// ********** Iterator implementation **********

	public boolean hasNext() {
		return this.iterator.hasNext();
	}

	public E next() {
		this.current = this.nestedNext();
		this.removeAllowed = true;
		return this.current;
	}

	public void remove() {
		if ( ! this.removeAllowed) {
			throw new IllegalStateException();
		}
		this.remove(this.current);
		this.removeAllowed = false;
	}


	// ********** internal methods **********

	/**
	 * The collection 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.iterator.next();
	}

	/**
	 * Remove the specified element from the original collection.
	 * <p>
	 * This method can be overridden by a subclass as an
	 * alternative to building a {@link Remover}.
	 */
	protected void remove(E e) {
		this.remover.remove(e);
	}


	//********** member interface **********

	/**
	 * Used by {@link CloneIterator} to remove
	 * elements from the original collection; since the iterator
	 * does not have direct access to the original collection.
	 */
	public interface Remover<T> {

		/**
		 * Remove the specified object from the original collection.
		 */
		void remove(T element);


		final class ReadOnly<S>
			implements Remover<S>, Serializable
		{
			@SuppressWarnings("rawtypes")
			public static final Remover INSTANCE = new ReadOnly();
			@SuppressWarnings("unchecked")
			public static <R> Remover<R> instance() {
				return INSTANCE;
			}
			// ensure single instance
			private ReadOnly() {
				super();
			}
			// remove is not supported
			public void remove(Object element) {
				throw new UnsupportedOperationException();
			}
			private static final long serialVersionUID = 1L;
			private Object readResolve() {
				// replace this object with the singleton
				return INSTANCE;
			}
		}
	}
}