/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2012, Open Source Geospatial Foundation (OSGeo) * (C) 2012, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.util.collection; import java.util.List; import java.util.Iterator; import java.util.ArrayList; import org.geotoolkit.lang.Decorator; import org.apache.sis.util.ArgumentChecks; /** * An iterator deferring to the iteration end any object which comply to some criterion. * This class needs: * <p> * <ul> * <li>An iterator to wrap (given at construction time)</li> * <li>An implementation of the {@link #isDeferred(Object) isDeferred(T)} method.</li> * </ul> * <p> * When the {@link #next()} method is invoked, {@code DeferringIterator} first delegates to * the {@code next()} method of the wrapped iterator, then gives the obtained element to the * {@link #isDeferred(Object) isDeferred} method. If {@code isDeferred} returns {@code true}, * then the element is returned immediately. Otherwise the element is enqueued and will be * returned in queue insertion order only after all non-deferred elements have been returned * by this iterator. * * @param <E> The type of elements to be returned by the iterator. * * @author Martin Desruisseaux (Geomatys) * @version 3.20 * * @since 3.20 * @module */ @Decorator(Iterator.class) public abstract class DeferringIterator<E> implements Iterator<E> { /** * The iterator given to the constructor, or the {@linkplain #fallbacks} iterator. */ private Iterator<E> iterator; /** * The object of classes not loaded by the desired class loader. * This list is initially null and is created only if needed. */ private List<E> fallbacks; /** * {@code true} if the {@linkplain #iterator} is iterating over the fallbacks. */ private boolean usingFallbacks; /** * Creates a new deferring iterator. * * @param iterator The iterator to wrap. */ protected DeferringIterator(final Iterator<E> iterator) { ArgumentChecks.ensureNonNull("iterator", iterator); this.iterator = iterator; } /** * Returns {@code true} if the given element should be deferred at the end of the iteration. * This method is invoked by the {@link #next()} method for all objects returned by the * wrapped iterator. If this method returns {@code true}, then the given element will be * returned immediately by the {@code next()} method. Otherwise the element is enqueued * and will be returned only after all non-deferred elements have been returned. * * @param element The object to test. May be {@code null} if the wrapped iterator can * returns null elements. * @return {@code true} if the given object should be deferred at the end of the iteration. */ protected abstract boolean isDeferred(E element); /** * Returns {@code true} if the iteration has more elements. */ @Override public boolean hasNext() { boolean hasNext = iterator.hasNext(); if (!hasNext && !usingFallbacks && fallbacks != null) { hasNext = fallbacks().hasNext(); } return hasNext; } /** * Returns the next element in the iteration. */ @Override public E next() { do { final E next = iterator.next(); if (usingFallbacks || !isDeferred(next)) { return next; } if (fallbacks == null) { fallbacks = new ArrayList<>(); } fallbacks.add(next); } while (iterator.hasNext()); return fallbacks().next(); } /** * Switches to the fallback iterator. This method is invoked when the iteration * using the iterator given at construction time is finished. */ private Iterator<E> fallbacks() { iterator = fallbacks.iterator(); fallbacks = null; usingFallbacks = true; return iterator; } /** * Removes the last element returned by the iterator. This method is supported only when * the following conditions hold: * <p> * <ul> * <li>The iterator is not iterating over the deferred elements</li> * <li>The wrapped iterator given to the constructor supports element removal.</li> * </ul> * * @throws UnsupportedOperationException If the iterator is iterating over deferred elements, * or if the wrapped iterator does not support this operation. */ @Override public void remove() throws UnsupportedOperationException { if (usingFallbacks) { throw new UnsupportedOperationException(); } iterator.remove(); } }