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) 2011-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.calc;
011
012import mondrian.calc.impl.*;
013import mondrian.olap.*;
014
015import java.util.*;
016
017/**
018* Utility methods for tuple collections and iterators.
019 *
020 * @see TupleList
021 * @see TupleIterator
022 *
023 * @author jhyde
024 */
025public final class TupleCollections {
026    private static final TupleList[] EMPTY_LISTS = {
027        new DelegatingTupleList(0, Collections.<List<Member>>emptyList()),
028        new UnaryTupleList(Collections.<Member>emptyList()),
029        new DelegatingTupleList(2, Collections.<List<Member>>emptyList()),
030        new DelegatingTupleList(3, Collections.<List<Member>>emptyList()),
031        new DelegatingTupleList(4, Collections.<List<Member>>emptyList())
032    };
033
034    // prevent instantiation
035    private TupleCollections() {
036    }
037
038    /**
039     * Creates a list of given arity.
040     *
041     * <p>If arity == 1, creates a {@link UnaryTupleList};
042     * if arity == 0, creates a {@link DelegatingTupleList};
043     * otherwise creates a {@link ArrayTupleList}.
044     *
045     * @see TupleList#cloneList(int)
046     * @see #createList(int, int)
047     *
048     * @param arity Arity
049     * @return Tuple list
050     */
051    public static TupleList createList(int arity) {
052        switch (arity) {
053        case 0:
054            return new DelegatingTupleList(0, new ArrayList<List<Member>>());
055        case 1:
056            return new UnaryTupleList();
057        default:
058            return new ArrayTupleList(arity);
059        }
060    }
061
062    /**
063     * Creates a list of given arity and initial capacity.
064     *
065     * <p>If arity == 1, creates a {@link UnaryTupleList};
066     * if arity == 0, creates a {@link DelegatingTupleList};
067     * otherwise creates a {@link ArrayTupleList}.
068     *
069     * @see TupleList#cloneList(int)
070     *
071     * @param arity Arity
072     * @param initialCapacity Initial capacity
073     * @return Tuple list
074     */
075    public static TupleList createList(int arity, int initialCapacity) {
076        switch (arity) {
077        case 0:
078            return new DelegatingTupleList(
079                0, new ArrayList<List<Member>>(initialCapacity));
080        case 1:
081            return new UnaryTupleList(new ArrayList<Member>(initialCapacity));
082        default:
083            return new ArrayTupleList(arity, initialCapacity);
084        }
085    }
086
087    /**
088     * Returns an empty TupleList of given arity.
089     *
090     * @param arity Number of members per tuple
091     * @return Empty tuple list
092     */
093    public static TupleList emptyList(int arity) {
094        return arity < EMPTY_LISTS.length
095            ? EMPTY_LISTS[arity]
096            : new DelegatingTupleList(
097                arity, Collections.<List<Member>>emptyList());
098    }
099
100    /**
101     * Creates an unmodifiable TupleList backed by a given list.
102     *
103     * @see Collections#unmodifiableList(java.util.List)
104     *
105     * @param  list the list for which an unmodifiable view is to be returned.
106     * @return an unmodifiable view of the specified list.
107     */
108    public static TupleList unmodifiableList(TupleList list) {
109        return list.getArity() == 1
110            ? new UnaryTupleList(
111                Collections.unmodifiableList(
112                    list.slice(0)))
113            : new DelegatingTupleList(
114                list.getArity(),
115                Collections.unmodifiableList(
116                    list));
117    }
118
119    /**
120     * Adapts a {@link TupleCursor} into a {@link TupleIterator}.
121     *
122     * <p>Since the latter is a more difficult API to implement, the wrapper
123     * has some extra state.
124     *
125     * <p>This method may be used to implement
126     * {@link mondrian.calc.TupleIterable#tupleIterator()} for a
127     * {@link TupleIterable} or {@link TupleList} that only has a
128     * {@code TupleCursor} implementation.
129     *
130     * @param cursor Cursor
131     * @return Tuple iterator view onto the cursor
132     */
133    public static TupleIterator iterator(final TupleCursor cursor) {
134        if (cursor instanceof TupleIterator) {
135            return (TupleIterator) cursor;
136        }
137        return new AbstractTupleIterator(cursor.getArity()) {
138            private int state = STATE_UNKNOWN;
139
140            private static final int STATE_UNKNOWN = 0;
141            private static final int STATE_HASNEXT = 1;
142            private static final int STATE_EOD = 2;
143
144            public List<Member> current() {
145                return cursor.current();
146            }
147
148            @Override
149            public boolean hasNext() {
150                switch (state) {
151                case STATE_UNKNOWN:
152                    if (cursor.forward()) {
153                        state = STATE_HASNEXT;
154                        return true;
155                    } else {
156                        state = STATE_EOD;
157                        return false;
158                    }
159                case STATE_EOD:
160                    return false;
161                case STATE_HASNEXT:
162                    return true;
163                default:
164                    throw new RuntimeException("unpexected state " + state);
165                }
166            }
167
168            @Override
169            public List<Member> next() {
170                switch (state) {
171                case STATE_UNKNOWN:
172                    if (cursor.forward()) {
173                        return cursor.current();
174                    }
175                    state = STATE_EOD;
176                    // fall through
177                case STATE_EOD:
178                    throw new NoSuchElementException();
179                case STATE_HASNEXT:
180                    state = STATE_UNKNOWN;
181                    return cursor.current();
182                default:
183                    throw new RuntimeException("unpexected state " + state);
184                }
185            }
186
187            public boolean forward() {
188                switch (state) {
189                case STATE_UNKNOWN:
190                    return cursor.forward();
191                case STATE_HASNEXT:
192                    state = STATE_UNKNOWN;
193                    return true;
194                case STATE_EOD:
195                    return false;
196                default:
197                    throw new RuntimeException("unpexected state " + state);
198                }
199            }
200
201            @Override
202            public void setContext(Evaluator evaluator) {
203                cursor.setContext(evaluator);
204            }
205
206            @Override
207            public void currentToArray(Member[] members, int offset) {
208                cursor.currentToArray(members, offset);
209            }
210
211            @Override
212            public Member member(int column) {
213                return cursor.member(column);
214            }
215        };
216    }
217
218    /**
219     * Creates a slice of a {@link TupleIterable}.
220     *
221     * <p>Can be used as an implementation for
222     * {@link mondrian.calc.TupleList#slice(int)}.
223     *
224     * @param tupleIterable Iterable
225     * @param column Which member of each tuple of project.
226     * @return Iterable that returns a given member of each tuple
227     */
228    public static Iterable<Member> slice(
229        final TupleIterable tupleIterable,
230        final int column)
231    {
232        if (column < 0 || column >= tupleIterable.getArity()) {
233            throw new IllegalArgumentException();
234        }
235        return new Iterable<Member>() {
236            public Iterator<Member> iterator() {
237                final TupleIterator tupleIterator =
238                    tupleIterable.tupleIterator();
239                return new Iterator<Member>() {
240                    public boolean hasNext() {
241                        return tupleIterator.hasNext();
242                    }
243
244                    public Member next() {
245                        if (!tupleIterator.forward()) {
246                            throw new NoSuchElementException();
247                        }
248                        return tupleIterator.member(column);
249                    }
250
251                    public void remove() {
252                        throw new UnsupportedOperationException();
253                    }
254                };
255            }
256        };
257    }
258
259    /**
260     * Converts a {@link mondrian.calc.TupleIterable} to an old-style iterable that
261     * creates an iterator over member arrays.
262     *
263     * @param tupleIterable Tuple iterable
264     * @return Iterable that creates an iterator over tuples represented as
265     *   member arrays
266     */
267    public static Iterable<Member[]> asMemberArrayIterable(
268        final TupleIterable tupleIterable)
269    {
270        return new Iterable<Member[]>() {
271            public Iterator<Member[]> iterator() {
272                return new Iterator<Member[]>() {
273                    final TupleIterator
274                        tupleIterator = tupleIterable.tupleIterator();
275                    public boolean hasNext() {
276                        return tupleIterator.hasNext();
277                    }
278
279                    public Member[] next() {
280                        final List<Member> next = tupleIterator.next();
281                        return next.toArray(new Member[next.size()]);
282                    }
283
284                    public void remove() {
285                        throw new UnsupportedOperationException();
286                    }
287                };
288            }
289        };
290    }
291
292    /**
293     * Converts a {@link mondrian.calc.TupleList} to an old-style list of member
294     * arrays.
295     *
296     * @param tupleList Tuple list
297     * @return List of member arrays
298     */
299    public static List<Member[]> asMemberArrayList(
300        final TupleList tupleList)
301    {
302        return new AbstractList<Member[]>() {
303            @Override
304            public Member[] get(int index) {
305                final List<Member> tuple = tupleList.get(index);
306                return tuple.toArray(new Member[tuple.size()]);
307            }
308
309            @Override
310            public int size() {
311                return tupleList.size();
312            }
313        };
314    }
315
316    /**
317     * Converts an old-style list (members or member arrays) to a
318     * {@link mondrian.calc.TupleList}.
319     *
320     * <p>Deduces the arity of the list from the first element, if the list
321     * is not empty. Otherwise assumes arity 1.
322     *
323     * <p>If the list happens to be a tuple list, returns unchanged.
324     *
325     * @param list Old-style list
326     * @return Tuple list
327     */
328    public static TupleList asTupleList(List list) {
329        if (list instanceof TupleList) {
330            return (TupleList) list;
331        }
332        if (list.isEmpty()) {
333            return TupleCollections.emptyList(1);
334        }
335        final Object o = list.get(0);
336        if (o instanceof Member) {
337            final List<Member> memberList = Util.cast(list);
338            return new UnaryTupleList(memberList);
339        } else {
340            final List<Member[]> memberArrayList = Util.cast(list);
341            return new DelegatingTupleList(
342                memberArrayList.get(0).length,
343                new AbstractList<List<Member>>() {
344                    public List<Member> get(int index) {
345                        return Arrays.asList(memberArrayList.get(index));
346                    }
347
348                    public int size() {
349                        return memberArrayList.size();
350                    }
351                }
352            );
353        }
354    }
355
356    /**
357     * Converts a {@link TupleIterable} into a {@link TupleList}.
358     *
359     * <p>If the iterable is already a list, returns the iterable. If it is not
360     * a list, the behavior depends on the {@code eager} parameter. With eager =
361     * true, creates a list and populates it with the contents of the
362     * iterable. With eager = false, wraps in an adapter that implements the
363     * list interface and materializes to a list the first time that an
364     * operation that is in TupleList but not TupleIterable -- for example,
365     * {@link TupleList#get} or {@link TupleList#size} -- is called.
366     *
367     * @param tupleIterable Iterable
368     * @param eager Whether to convert into a list now, as opposed to on first
369     *   use of a random-access method such as size or get.
370     * @return List
371     */
372    public static TupleList materialize(
373        TupleIterable tupleIterable,
374        boolean eager)
375    {
376        if (tupleIterable instanceof TupleList) {
377            return (TupleList) tupleIterable;
378        }
379        if (eager) {
380            TupleList tupleList = createList(tupleIterable.getArity());
381            TupleCursor tupleCursor = tupleIterable.tupleCursor();
382            while (tupleCursor.forward()) {
383                tupleList.addCurrent(tupleCursor);
384            }
385            return tupleList;
386        } else {
387            return new MaterializingTupleList(tupleIterable);
388        }
389    }
390
391    /**
392     * Implementation of {@link TupleList} that is based on a
393     * {@link TupleIterable} and materializes into a read-only list the first
394     * time that an method is called that requires a list.
395     */
396    private static class MaterializingTupleList
397        implements TupleList
398    {
399        private final TupleIterable tupleIterable;
400        TupleList tupleList;
401
402        public MaterializingTupleList(
403            TupleIterable tupleIterable)
404        {
405            this.tupleIterable = tupleIterable;
406        }
407
408        private TupleList materialize() {
409            if (tupleList == null) {
410                tupleList = TupleCollections.materialize(tupleIterable, true);
411            }
412            return tupleList;
413        }
414
415        // TupleIterable methods
416
417        public TupleIterator tupleIterator() {
418            if (tupleList == null) {
419                return tupleIterable.tupleIterator();
420            } else {
421                return tupleList.tupleIterator();
422            }
423        }
424
425        public TupleCursor tupleCursor() {
426            if (tupleList == null) {
427                return tupleIterable.tupleCursor();
428            } else {
429                return tupleList.tupleCursor();
430            }
431        }
432
433        public int getArity() {
434            return tupleIterable.getArity();
435        }
436
437        public Iterator<List<Member>> iterator() {
438            if (tupleList == null) {
439                return tupleIterable.iterator();
440            } else {
441                return tupleList.iterator();
442            }
443        }
444
445        public List<Member> slice(int column) {
446            // Note that TupleIterable has 'Iterable<Member> slice(int)'
447            // and TupleList has 'List<Member> slice(int)'.
448            // So, if this list is not materialized, we could return a slice of
449            // the un-materialized iterable. But it's not worth the complexity.
450            return materialize().slice(column);
451        }
452
453        public Member get(int slice, int index) {
454            return materialize().get(slice, index);
455        }
456
457        public TupleList cloneList(int capacity) {
458            return materialize().cloneList(capacity);
459        }
460
461        public void addTuple(Member... members) {
462            throw new UnsupportedOperationException();
463        }
464
465        public TupleList project(int[] destIndices) {
466            return materialize().project(destIndices);
467        }
468
469        public void addCurrent(TupleCursor tupleIter) {
470            materialize().addCurrent(tupleIter);
471        }
472
473        public TupleList subList(int fromIndex, int toIndex) {
474            return materialize().subList(fromIndex, toIndex);
475        }
476
477        public TupleList withPositionCallback(
478            PositionCallback positionCallback)
479        {
480            return materialize().withPositionCallback(positionCallback);
481        }
482
483        public TupleList fix() {
484            return materialize().fix();
485        }
486
487        public int size() {
488            return materialize().size();
489        }
490
491        public boolean isEmpty() {
492            return materialize().isEmpty();
493        }
494
495        public boolean contains(Object o) {
496            return materialize().contains(o);
497        }
498
499        public Object[] toArray() {
500            return materialize().toArray();
501        }
502
503        public <T> T[] toArray(T[] a) {
504            return materialize().toArray(a);
505        }
506
507        public boolean add(List<Member> members) {
508            throw new UnsupportedOperationException();
509        }
510
511        public boolean remove(Object o) {
512            throw new UnsupportedOperationException();
513        }
514
515        public boolean containsAll(Collection<?> c) {
516            return materialize().containsAll(c);
517        }
518
519        public boolean addAll(Collection<? extends List<Member>> c) {
520            throw new UnsupportedOperationException();
521        }
522
523        public boolean addAll(int index, Collection<? extends List<Member>> c) {
524            throw new UnsupportedOperationException();
525        }
526
527        public boolean removeAll(Collection<?> c) {
528            throw new UnsupportedOperationException();
529        }
530
531        public boolean retainAll(Collection<?> c) {
532            throw new UnsupportedOperationException();
533        }
534
535        public void clear() {
536            throw new UnsupportedOperationException();
537        }
538
539        public List<Member> get(int index) {
540            return materialize().get(index);
541        }
542
543        public List<Member> set(int index, List<Member> element) {
544            throw new UnsupportedOperationException();
545        }
546
547        public void add(int index, List<Member> element) {
548            throw new UnsupportedOperationException();
549        }
550
551        public List<Member> remove(int index) {
552            throw new UnsupportedOperationException();
553        }
554
555        public int indexOf(Object o) {
556            return materialize().indexOf(o);
557        }
558
559        public int lastIndexOf(Object o) {
560            return materialize().lastIndexOf(o);
561        }
562
563        public ListIterator<List<Member>> listIterator() {
564            return materialize().listIterator();
565        }
566
567        public ListIterator<List<Member>> listIterator(int index) {
568            return materialize().listIterator(index);
569        }
570    }
571}
572
573// End TupleCollections.java