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