001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.iterators; 018 019import java.util.Iterator; 020 021/** 022 * An LazyIteratorChain is an Iterator that wraps a number of Iterators in a lazy manner. 023 * <p> 024 * This class makes multiple iterators look like one to the caller. When any 025 * method from the Iterator interface is called, the LazyIteratorChain will delegate 026 * to a single underlying Iterator. The LazyIteratorChain will invoke the Iterators 027 * in sequence until all Iterators are exhausted. 028 * <p> 029 * The Iterators are provided by {@link #nextIterator(int)} which has to be overridden by 030 * sub-classes and allows to lazily create the Iterators as they are accessed: 031 * <pre> 032 * return new LazyIteratorChain<String>() { 033 * protected Iterator<String> nextIterator(int count) { 034 * return count == 1 ? Arrays.asList("foo", "bar").iterator() : null; 035 * } 036 * }; 037 * </pre> 038 * <p> 039 * Once the inner Iterator's {@link Iterator#hasNext()} method returns false, 040 * {@link #nextIterator(int)} will be called to obtain another iterator, and so on 041 * until {@link #nextIterator(int)} returns null, indicating that the chain is exhausted. 042 * <p> 043 * NOTE: The LazyIteratorChain may contain no iterators. In this case the class will 044 * function as an empty iterator. 045 * 046 * @since 4.0 047 * @version $Id: LazyIteratorChain.html 972421 2015-11-14 20:00:04Z tn $ 048 */ 049public abstract class LazyIteratorChain<E> implements Iterator<E> { 050 051 /** The number of times {@link #nextIterator()} was already called. */ 052 private int callCounter = 0; 053 054 /** Indicates that the Iterator chain has been exhausted. */ 055 private boolean chainExhausted = false; 056 057 /** The current iterator. */ 058 private Iterator<? extends E> currentIterator = null; 059 060 /** 061 * The "last used" Iterator is the Iterator upon which next() or hasNext() 062 * was most recently called used for the remove() operation only. 063 */ 064 private Iterator<? extends E> lastUsedIterator = null; 065 066 //----------------------------------------------------------------------- 067 068 /** 069 * Gets the next iterator after the previous one has been exhausted. 070 * <p> 071 * This method <b>MUST</b> return null when there are no more iterators. 072 * 073 * @param count the number of time this method has been called (starts with 1) 074 * @return the next iterator, or null if there are no more. 075 */ 076 protected abstract Iterator<? extends E> nextIterator(int count); 077 078 /** 079 * Updates the current iterator field to ensure that the current Iterator 080 * is not exhausted. 081 */ 082 private void updateCurrentIterator() { 083 if (callCounter == 0) { 084 currentIterator = nextIterator(++callCounter); 085 if (currentIterator == null) { 086 currentIterator = EmptyIterator.<E>emptyIterator(); 087 chainExhausted = true; 088 } 089 // set last used iterator here, in case the user calls remove 090 // before calling hasNext() or next() (although they shouldn't) 091 lastUsedIterator = currentIterator; 092 } 093 094 while (currentIterator.hasNext() == false && !chainExhausted) { 095 final Iterator<? extends E> nextIterator = nextIterator(++callCounter); 096 if (nextIterator != null) { 097 currentIterator = nextIterator; 098 } else { 099 chainExhausted = true; 100 } 101 } 102 } 103 104 //----------------------------------------------------------------------- 105 106 /** 107 * Return true if any Iterator in the chain has a remaining element. 108 * 109 * @return true if elements remain 110 */ 111 public boolean hasNext() { 112 updateCurrentIterator(); 113 lastUsedIterator = currentIterator; 114 115 return currentIterator.hasNext(); 116 } 117 118 /** 119 * Returns the next element of the current Iterator 120 * 121 * @return element from the current Iterator 122 * @throws java.util.NoSuchElementException if all the Iterators are exhausted 123 */ 124 public E next() { 125 updateCurrentIterator(); 126 lastUsedIterator = currentIterator; 127 128 return currentIterator.next(); 129 } 130 131 /** 132 * Removes from the underlying collection the last element returned by the Iterator. 133 * <p> 134 * As with next() and hasNext(), this method calls remove() on the underlying Iterator. 135 * Therefore, this method may throw an UnsupportedOperationException if the underlying 136 * Iterator does not support this method. 137 * 138 * @throws UnsupportedOperationException if the remove operator is not 139 * supported by the underlying Iterator 140 * @throws IllegalStateException if the next method has not yet been called, 141 * or the remove method has already been called after the last call to the next method. 142 */ 143 public void remove() { 144 if (currentIterator == null) { 145 updateCurrentIterator(); 146 } 147 lastUsedIterator.remove(); 148 } 149 150}