001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2008-2009 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import mondrian.olap.Util;
013
014import java.util.*;
015
016/**
017 * Iterator over union of several {@link Iterable} collections.
018 *
019 * <p>Try, for instance, using the {@link #over} helper method:</p>
020 *
021 * <blockquote>
022 * <code>
023 * List&lt;String&gt; names;<br/>
024 * List&lt;String&gt; addresses;<br/>
025 * for (Sstring s : UnionIterator.over(names, addresses)) {
026 * &nbsp;&nbsp;print(s);<br/>
027 * }
028 * </code>
029 * </blockquote>
030 *
031 * @author jhyde
032 * @since Apr 28, 2008
033 */
034public class UnionIterator<T> implements Iterator<T> {
035    private final Iterator<Iterable<? extends T>> iterableIterator;
036    private Iterator<? extends T> iterator;
037
038    /**
039     * Creates a UnionIterator.
040     *
041     * @param iterables Array of iterables
042     */
043    public UnionIterator(Iterable<? extends T>... iterables) {
044        List<Iterable<? extends T>> list;
045        if (Util.Retrowoven) {
046            // Retroweaver has its own version of Iterable, but
047            // Collection doesn't implement it. Solve the problem by
048            // creating an explicit Iterable wrapper.
049            list = new ArrayList<Iterable<? extends T>>(iterables.length);
050            for (Iterable<? extends T> iterable : iterables) {
051                //noinspection unchecked
052                list.add(new MyIterable(iterable));
053            }
054        } else {
055            list = Arrays.asList(iterables);
056        }
057        this.iterableIterator = list.iterator();
058        moveToNext();
059    }
060
061    /**
062     * Creates a UnionIterator over a list of collections.
063     *
064     * @param iterables Array of collections
065     */
066    public UnionIterator(Collection<? extends T>... iterables) {
067        List<Iterable<? extends T>> list =
068            new ArrayList<Iterable<? extends T>>(iterables.length);
069        for (Iterable<? extends T> iterable : iterables) {
070            //noinspection unchecked
071            list.add(new MyIterable(iterable));
072        }
073        this.iterableIterator = list.iterator();
074        moveToNext();
075    }
076
077    public boolean hasNext() {
078        return iterator.hasNext();
079    }
080
081    public T next() {
082        final T t = iterator.next();
083        if (!iterator.hasNext()) {
084            moveToNext();
085        }
086        return t;
087    }
088
089    /**
090     * Moves to the next iterator that has at least one element.
091     * Called after finishing an iterator, or at the start.
092     */
093    private void moveToNext() {
094        do {
095            if (iterableIterator.hasNext()) {
096                iterator = iterableIterator.next().iterator();
097            } else {
098                iterator = Collections.<T>emptyList().iterator();
099                break;
100            }
101        } while (!iterator.hasNext());
102    }
103
104    public void remove() {
105        iterator.remove();
106    }
107
108    /**
109     * Returns the union of a list of iterables.
110     *
111     * <p>You can use it like this:
112     * <blockquote><pre>
113     * Iterable&lt;String&gt; iter1;
114     * Iterable&lt;String&gt; iter2;
115     * for (String s : union(iter1, iter2)) {
116     *   print(s);
117     * }</pre></blockquote>
118     *
119     * @param iterables Array of one or more iterables
120     * @return iterable over the union of the iterables
121     */
122    public static <T> Iterable<T> over(
123        final Iterable<? extends T>... iterables)
124    {
125        return new Iterable<T>() {
126            public Iterator<T> iterator() {
127                return new UnionIterator<T>(iterables);
128            }
129        };
130    }
131
132    /**
133     * Returns the union of a list of collections.
134     *
135     * <p>This method exists for code that will be retrowoven to run on JDK 1.4.
136     * Retroweaver has its own version of the {@link Iterable} interface, which
137     * is problematic since the {@link java.util.Collection} classes don't
138     * implement it. This method solves some of these problems by working in
139     * terms of collections; retroweaver deals with these correctly.
140     *
141     * @see #over(Iterable[])
142     *
143     * @param collections Array of one or more collections
144     * @return iterable over the union of the collections
145     */
146    public static <T> Iterable<T> over(
147        final Collection<? extends T>... collections)
148    {
149        return new Iterable<T>() {
150            public Iterator<T> iterator() {
151                return new UnionIterator<T>(collections);
152            }
153        };
154    }
155
156    private static class MyIterable<T> implements Iterable {
157        private final Iterable<T> iterable;
158
159        /**
160         * Creates a MyIterable.
161         *
162         * @param iterable Iterable
163         */
164        public MyIterable(Iterable<T> iterable) {
165            this.iterable = iterable;
166        }
167
168        public Iterator<T> iterator() {
169            return iterable.iterator();
170        }
171    }
172}
173
174// End UnionIterator.java