/* ComodifiableIterator.java

	Purpose:
		
	Description:
		
	History:
		Fri Dec  2 11:53:25 TST 2011, Created by tomyeh

Copyright (C) 2011 Potix Corporation. All Rights Reserved.

*/
package org.zkoss.util;

import java.util.Iterator;
import java.util.Collection;
import java.util.List;
import java.util.LinkedList;

/**
 * A co-modifiable iterator. Used internally.
 * @author tomyeh
 */
/*package*/ class ComodifiableIterator<F, T> implements Iterator<T> {
	private final List<F> _visited = new LinkedList<F>();
	private List<F> _lastVisited;
	private final Collection<F> _col;
	private Iterator<F> _it;
	private F _next;
	private Converter<F, T> _converter;
	private boolean _nextAvail;

	@SuppressWarnings("unchecked")
	/*package*/ ComodifiableIterator(Collection<F> col, Converter<F, T> converter) {
		_col = col;
		_it = col.iterator();
		_converter = converter != null ? converter: _identityConverter;
	}
	public boolean hasNext() {
		//Note: we cannot just check hasNext() since it does not throw
		//ConcurrentModificationException, so if _col is cleared, hasNext()
		//might still return true! Moreover, hasNext() might return false
		//while there is more
		if (_nextAvail)
			return true;
		while (!_col.isEmpty()) { //isEmpty is reliable and empty is a common case
			final F o;
			try {
				o = _it.next();
			} catch (java.util.NoSuchElementException ex) {
				return false;
			} catch (java.util.ConcurrentModificationException ex) {
				_lastVisited = new LinkedList<F>(_visited); //make a copy
				_it = _col.iterator();
				continue; //do it again
			}
			if (!removeFromLastVisited(o)) { //not visited before
				_visited.add(o);
				_next = o;
				return _nextAvail = true;
			}
		}
		return false;
	}
	public T next() {
		if (_nextAvail) {
			_nextAvail = false;
			return _converter.convert(_next);
		}
		for (;;) {
			final F o;
			try {
				o = _it.next();
			} catch (java.util.ConcurrentModificationException ex) {
				_lastVisited = new LinkedList<F>(_visited); //make a copy
				_it = _col.iterator();
				continue; //do it again
			}
			if (!removeFromLastVisited(o)) { //not visited before
				_visited.add(o);
				return _converter.convert(o);
			}
		}
	}
	private boolean removeFromLastVisited(F o) {
		if (_lastVisited != null)
			for (Iterator<F> it = _lastVisited.iterator(); it.hasNext();) {
				if (it.next() == o) { //not equals (more restricted)
					it.remove();
					return true;
				}
			}
		return false;
	}
	public void remove() {
		_it.remove();
		_nextAvail = false;
	}

	private static final Converter _identityConverter = new Converter() {
		public Object convert(Object o) {
			return o;
		}
	};
}
