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