Skip to main content
summaryrefslogblamecommitdiffstats
blob: 5493e0d5f1ee746d54baffb370865a6b4f9fc194 (plain) (tree)
1
2
                                                                                
                                                        



















                                                                                
                                                   



                                                                         

                                                          
                                                                   

                                                           


                                                                            



                                  
                                                        
                           
                            

                                         




                            








                                                                     
                                                          



                                                           








                                                                      


                                                                      
                                                                              
















                                                                               


                                                                                                              
                                                                               

                                       
                                           





                                                            
                                                   



                                                                                             
                                           
                              
                                        




                                                                                                     
                                           
                                                   







                                                 
                                                     


                                    
                                                         


                                      
                                                       



                                                                                             
                                                   
                              
                                            




                                                                                                     
                                         





                                                                                                     
                                         
                                                   









                                                     
                                                                                       

                                                                       
                                                   


                                      
                                                    


           
                                                                                       

                                                                       
                                                   


                                      
                                                        


           


                                                            
                                                     








                                                               
                                                     








                                                            
                                                     




                                            


                                          








                                                          
                                                      





















                                                                      
                                                     






















                                                                              
                                                                                          
                         




                                                                         




                 
/*******************************************************************************
 * Copyright (c) 2005, 2010 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0, which accompanies this distribution
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 * 
 * Contributors:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.utility.internal.iterators;

import java.util.List;
import java.util.ListIterator;
import org.eclipse.jpt.utility.internal.CollectionTools;
import org.eclipse.jpt.utility.internal.StringTools;

/**
 * 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 org.eclipse.jpt.utility.internal.iterables.LiveCloneListIterable
 * @see org.eclipse.jpt.utility.internal.iterables.SnapshotCloneListIterable
 */
public class CloneListIterator<E>
	implements ListIterator<E>
{
	private final ListIterator<Object> listIterator;
	private int cursor;
	private State state;
	private final Mutator<E> mutator;

	private enum State {
		UNKNOWN,
		PREVIOUS,
		NEXT
	}


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

	/**
	 * 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 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 list.
	 * Use the specified list mutator to modify the original list.
	 */
	public CloneListIterator(List<? extends E> list, Mutator<E> mutator) {
		this(mutator, list.toArray());
	}

	/**
	 * 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());
	}

	/**
	 * 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 synch 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;
	}


	// ********** ListIterator implementation **********

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

	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 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);
		}
	}

	public int nextIndex() {
		return this.listIterator.nextIndex();
	}

	public int previousIndex() {
		return this.listIterator.previousIndex();
	}

	public boolean hasPrevious() {
		return this.listIterator.hasPrevious();
	}

	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 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++;
	}

	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);
		}
	}


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

	/**
	 * 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();
	}

	/**
	 * 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);
	}

	/**
	 * 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);
	}

	/**
	 * 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);
	}


	// ********** overrides **********

	@Override
	public String toString() {
		return StringTools.buildToStringFor(this);
	}


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

	/**
	 * 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> {
			@SuppressWarnings("rawtypes")
			public static final Mutator INSTANCE = new ReadOnly();
			@SuppressWarnings("unchecked")
			public static <R> Mutator<R> instance() {
				return INSTANCE;
			}
			// ensure single instance
			private ReadOnly() {
				super();
			}
			// add is not supported
			public void add(int index, Object o) {
				throw new UnsupportedOperationException();
			}
			// 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();
			}
			@Override
			public String toString() {
				return "CloneListIterator.Mutator.ReadOnly"; //$NON-NLS-1$
			}
			private static final long serialVersionUID = 1L;
			private Object readResolve() {
				// replace this object with the singleton
				return INSTANCE;
			}
		}

	}

}

Back to the top