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) 2006-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import java.util.*;
013
014/**
015 * Iterable list which filters undesirable elements.
016 * To be used instead of removing elements from an iterable list.
017 *
018 * @author Luis F. Canals
019 * @since december, 2007
020 */
021public class FilteredIterableList<T> extends AbstractSequentialList<T> {
022    private List<T> plainList;
023    private int size;
024    private boolean isEmpty;
025    private int lastIndex = 0;
026    private int lastGetIndex = -1;
027    private T lastGet = null;
028    private ListIterator<T> lastListIterator;
029
030    private final List<? extends T> internal;
031    private final Filter<T> filter;
032
033    private final Map<Integer, T> cached;
034
035    public FilteredIterableList(
036        final List<? extends T> list,
037        final Filter filter)
038    {
039        super();
040        this.plainList = null;
041        this.filter = filter;
042        this.internal = list;
043        this.isEmpty = ! this.listIterator(0).hasNext();
044        this.size = -1;
045        this.cached = new CacheMap<Integer, T>(4);
046    }
047
048    public T get(final int index) {
049        if (this.plainList != null) {
050            return this.plainList.get(index);
051        }
052
053        final T t = cached.get(index);
054        if (t != null) {
055            return cached.get(index);
056        } else {
057            if (index != this.lastGetIndex || index < 0) {
058                this.lastGet = super.get(index);
059                if (this.lastGet == null) {
060                    throw new IndexOutOfBoundsException();
061                }
062                this.lastGetIndex = index;
063            }
064            cached.put(index, this.lastGet);
065            return this.lastGet;
066        }
067    }
068
069    public ListIterator<T> listIterator(final int index) {
070        if (this.plainList == null) {
071            if (index == this.lastIndex + 1 && this.lastListIterator != null) {
072                if (this.lastListIterator.hasNext()) {
073                    this.lastIndex = index;
074                    return this.lastListIterator;
075                } else {
076                    throw new IndexOutOfBoundsException();
077                }
078            } else {
079                final Iterator<? extends T> it = internal.iterator();
080                T nextTmp = null;
081                while (it.hasNext()) {
082                    final T n = it.next();
083                    if (filter.accept(n)) {
084                        nextTmp = n;
085                        break;
086                    }
087                }
088                final T first = nextTmp;
089                this.lastListIterator = new ListIterator<T>() {
090                    private int idx = 0;
091                    private T nxt = first;
092                    private void postNext() {
093                        while (it.hasNext()) {
094                            final T n = it.next();
095                            if (filter.accept(n)) {
096                                nxt = n;
097                                return;
098                            }
099                        }
100                        nxt = null;
101                    }
102                    public boolean hasNext() {
103                        return nxt != null;
104                    }
105                    public T next() {
106                        idx++;
107                        final T n = nxt;
108                        cached.put(idx - 1, n);
109                        postNext();
110                        return n;
111                    }
112                    public int nextIndex() {
113                        return idx;
114                    }
115                    public void add(final T t) {
116                        throw new UnsupportedOperationException();
117                    }
118                    public void set(final T t) {
119                        throw new UnsupportedOperationException();
120                    }
121                    public boolean hasPrevious() {
122                        throw new UnsupportedOperationException();
123                    }
124                    public T previous() {
125                        throw new UnsupportedOperationException();
126                    }
127                    public int previousIndex() {
128                        throw new UnsupportedOperationException();
129                    }
130                    public void remove() {
131                        throw new UnsupportedOperationException();
132                    }
133                };
134                this.lastIndex = 0;
135            }
136
137            for (int i = this.lastIndex; i < index; i++) {
138                if (!this.lastListIterator.hasNext()) {
139                    throw new IndexOutOfBoundsException();
140                }
141                this.lastListIterator.next();
142            }
143            this.lastIndex = index;
144            return this.lastListIterator;
145        } else {
146            return plainList.listIterator(index);
147        }
148    }
149
150    public boolean isEmpty() {
151        return this.plainList != null ? this.plainList.isEmpty() : this.isEmpty;
152    }
153
154    public int size() {
155        if (this.size == -1) {
156            int s = this.lastIndex;
157            try {
158                final ListIterator<T> it = this.listIterator(this.lastIndex);
159                while (it.hasNext()) {
160                    s++;
161                    it.next();
162                }
163            } catch (IndexOutOfBoundsException ioobe) {
164                // Subyacent list is no more present...
165            }
166            this.size = s;
167        }
168        this.lastListIterator = null;
169        return this.size;
170    }
171
172    public Object[] toArray() {
173        ensurePlainList();
174        return this.plainList.toArray();
175    }
176
177    @Override
178    public <T> T[] toArray(T[] contents) {
179        ensurePlainList();
180        return this.plainList.toArray(contents);
181    }
182
183    private void ensurePlainList() {
184        if (this.plainList == null) {
185            final List<T> tmpPlainList = new ArrayList<T>();
186            for (final Iterator<T> it = this.listIterator(0); it.hasNext();) {
187                tmpPlainList.add(it.next());
188            }
189            this.plainList = tmpPlainList;
190        }
191    }
192
193    public int hashCode() {
194        return this.filter.hashCode();
195    }
196
197/*
198    public List<T> subList(final int start, final int end) {
199        return new AbstractList<T>() {
200            public T get(final int index) {
201                return FilteredIterableList.this.get(index-start);
202            }
203            public int size() {
204                return FilteredIterableList.this.size() - start;
205            }
206        };
207    }
208*/
209
210
211    //
212    // Inner classes ---------------------------------
213    //
214
215    /**
216     * Filter to determine which elements should be shown.
217     */
218    public static interface Filter<T> {
219        public boolean accept(final T element);
220    }
221}
222
223// End FilteredIterableList.java