1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections.iterators;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import org.apache.commons.collections.list.UnmodifiableList;
  22. /**
  23. * An IteratorChain is an Iterator that wraps a number of Iterators.
  24. * <p>
  25. * This class makes multiple iterators look like one to the caller
  26. * When any method from the Iterator interface is called, the IteratorChain
  27. * will delegate to a single underlying Iterator. The IteratorChain will
  28. * invoke the Iterators in sequence until all Iterators are exhausted.
  29. * <p>
  30. * Under many circumstances, linking Iterators together in this manner is
  31. * more efficient (and convenient) than reading out the contents of each
  32. * Iterator into a List and creating a new Iterator.
  33. * <p>
  34. * Calling a method that adds new Iterator<i>after a method in the Iterator
  35. * interface has been called</i> will result in an UnsupportedOperationException.
  36. * Subclasses should <i>take care</i> to not alter the underlying List of Iterators.
  37. * <p>
  38. * NOTE: As from version 3.0, the IteratorChain may contain no
  39. * iterators. In this case the class will function as an empty iterator.
  40. *
  41. * @since Commons Collections 2.1
  42. * @version $Revision: 1.13 $ $Date: 2004/05/26 21:58:02 $
  43. *
  44. * @author Morgan Delagrange
  45. * @author Stephen Colebourne
  46. */
  47. public class IteratorChain implements Iterator {
  48. /** The chain of iterators */
  49. protected final List iteratorChain = new ArrayList();
  50. /** The index of the current iterator */
  51. protected int currentIteratorIndex = 0;
  52. /** The current iterator */
  53. protected Iterator currentIterator = null;
  54. /**
  55. * The "last used" Iterator is the Iterator upon which
  56. * next() or hasNext() was most recently called
  57. * used for the remove() operation only
  58. */
  59. protected Iterator lastUsedIterator = null;
  60. /**
  61. * ComparatorChain is "locked" after the first time
  62. * compare(Object,Object) is called
  63. */
  64. protected boolean isLocked = false;
  65. //-----------------------------------------------------------------------
  66. /**
  67. * Construct an IteratorChain with no Iterators.
  68. * <p>
  69. * You will normally use {@link #addIterator(Iterator)} to add
  70. * some iterators after using this constructor.
  71. */
  72. public IteratorChain() {
  73. super();
  74. }
  75. /**
  76. * Construct an IteratorChain with a single Iterator.
  77. *
  78. * @param iterator first Iterator in the IteratorChain
  79. * @throws NullPointerException if the iterator is null
  80. */
  81. public IteratorChain(Iterator iterator) {
  82. super();
  83. addIterator(iterator);
  84. }
  85. /**
  86. * Constructs a new <code>IteratorChain</code> over the two
  87. * given iterators.
  88. *
  89. * @param a the first child iterator
  90. * @param b the second child iterator
  91. * @throws NullPointerException if either iterator is null
  92. */
  93. public IteratorChain(Iterator a, Iterator b) {
  94. super();
  95. addIterator(a);
  96. addIterator(b);
  97. }
  98. /**
  99. * Constructs a new <code>IteratorChain</code> over the array
  100. * of iterators.
  101. *
  102. * @param iterators the array of iterators
  103. * @throws NullPointerException if iterators array is or contains null
  104. */
  105. public IteratorChain(Iterator[] iterators) {
  106. super();
  107. for (int i = 0; i < iterators.length; i++) {
  108. addIterator(iterators[i]);
  109. }
  110. }
  111. /**
  112. * Constructs a new <code>IteratorChain</code> over the collection
  113. * of iterators.
  114. *
  115. * @param iterators the collection of iterators
  116. * @throws NullPointerException if iterators collection is or contains null
  117. * @throws ClassCastException if iterators collection doesn't contain an iterator
  118. */
  119. public IteratorChain(Collection iterators) {
  120. super();
  121. for (Iterator it = iterators.iterator(); it.hasNext();) {
  122. Iterator item = (Iterator) it.next();
  123. addIterator(item);
  124. }
  125. }
  126. //-----------------------------------------------------------------------
  127. /**
  128. * Add an Iterator to the end of the chain
  129. *
  130. * @param iterator Iterator to add
  131. * @throws IllegalStateException if I've already started iterating
  132. * @throws NullPointerException if the iterator is null
  133. */
  134. public void addIterator(Iterator iterator) {
  135. checkLocked();
  136. if (iterator == null) {
  137. throw new NullPointerException("Iterator must not be null");
  138. }
  139. iteratorChain.add(iterator);
  140. }
  141. /**
  142. * Set the Iterator at the given index
  143. *
  144. * @param index index of the Iterator to replace
  145. * @param iterator Iterator to place at the given index
  146. * @throws IndexOutOfBoundsException if index < 0 or index > size()
  147. * @throws IllegalStateException if I've already started iterating
  148. * @throws NullPointerException if the iterator is null
  149. */
  150. public void setIterator(int index, Iterator iterator) throws IndexOutOfBoundsException {
  151. checkLocked();
  152. if (iterator == null) {
  153. throw new NullPointerException("Iterator must not be null");
  154. }
  155. iteratorChain.set(index, iterator);
  156. }
  157. /**
  158. * Get the list of Iterators (unmodifiable)
  159. *
  160. * @return the unmodifiable list of iterators added
  161. */
  162. public List getIterators() {
  163. return UnmodifiableList.decorate(iteratorChain);
  164. }
  165. /**
  166. * Number of Iterators in the current IteratorChain.
  167. *
  168. * @return Iterator count
  169. */
  170. public int size() {
  171. return iteratorChain.size();
  172. }
  173. /**
  174. * Determine if modifications can still be made to the IteratorChain.
  175. * IteratorChains cannot be modified once they have executed a method
  176. * from the Iterator interface.
  177. *
  178. * @return true if IteratorChain cannot be modified, false if it can
  179. */
  180. public boolean isLocked() {
  181. return isLocked;
  182. }
  183. /**
  184. * Checks whether the iterator chain is now locked and in use.
  185. */
  186. private void checkLocked() {
  187. if (isLocked == true) {
  188. throw new UnsupportedOperationException("IteratorChain cannot be changed after the first use of a method from the Iterator interface");
  189. }
  190. }
  191. /**
  192. * Lock the chain so no more iterators can be added.
  193. * This must be called from all Iterator interface methods.
  194. */
  195. private void lockChain() {
  196. if (isLocked == false) {
  197. isLocked = true;
  198. }
  199. }
  200. /**
  201. * Updates the current iterator field to ensure that the current Iterator
  202. * is not exhausted
  203. */
  204. protected void updateCurrentIterator() {
  205. if (currentIterator == null) {
  206. if (iteratorChain.isEmpty()) {
  207. currentIterator = EmptyIterator.INSTANCE;
  208. } else {
  209. currentIterator = (Iterator) iteratorChain.get(0);
  210. }
  211. // set last used iterator here, in case the user calls remove
  212. // before calling hasNext() or next() (although they shouldn't)
  213. lastUsedIterator = currentIterator;
  214. }
  215. while (currentIterator.hasNext() == false && currentIteratorIndex < iteratorChain.size() - 1) {
  216. currentIteratorIndex++;
  217. currentIterator = (Iterator) iteratorChain.get(currentIteratorIndex);
  218. }
  219. }
  220. //-----------------------------------------------------------------------
  221. /**
  222. * Return true if any Iterator in the IteratorChain has a remaining element.
  223. *
  224. * @return true if elements remain
  225. */
  226. public boolean hasNext() {
  227. lockChain();
  228. updateCurrentIterator();
  229. lastUsedIterator = currentIterator;
  230. return currentIterator.hasNext();
  231. }
  232. /**
  233. * Returns the next Object of the current Iterator
  234. *
  235. * @return Object from the current Iterator
  236. * @throws java.util.NoSuchElementException if all the Iterators are exhausted
  237. */
  238. public Object next() {
  239. lockChain();
  240. updateCurrentIterator();
  241. lastUsedIterator = currentIterator;
  242. return currentIterator.next();
  243. }
  244. /**
  245. * Removes from the underlying collection the last element
  246. * returned by the Iterator. As with next() and hasNext(),
  247. * this method calls remove() on the underlying Iterator.
  248. * Therefore, this method may throw an
  249. * UnsupportedOperationException if the underlying
  250. * Iterator does not support this method.
  251. *
  252. * @throws UnsupportedOperationException
  253. * if the remove operator is not supported by the underlying Iterator
  254. * @throws IllegalStateException
  255. * if the next method has not yet been called, or the remove method has
  256. * already been called after the last call to the next method.
  257. */
  258. public void remove() {
  259. lockChain();
  260. updateCurrentIterator();
  261. lastUsedIterator.remove();
  262. }
  263. }