Как написать отфильтрованный IObservableList
От какого-то вызова API я получаю обратно IObservableList<E>
из среды привязки данных Eclipse. Я хотел бы получить другое IObservableList<E>
из этого согласно некоторому предикату, определенному на типе элемента E
, Производный список должен динамически обновляться в соответствии с изменениями в исходном списке.
Как я могу лучше всего реализовать это? Я рассмотрел подкласс DecoratingObservableList
, но не мог понять, как его использовать.
Конечно, я мог бы реализовать себя весь IObservableList
интерфейс, но мне было интересно, если бы не было других полезных классов вокруг, которые я мог бы использовать.
3 ответа
Я думаю, что расширение DecoratingObservableList
хорошее начало Я бы также предложил сконцентрироваться именно на предполагаемом использовании, а не на немедленной реализации всего API. Например, вам нужна запись в случайном порядке через set
? Если нет, то не беспокойтесь о его реализации. Это покрывает доступное только для чтения представление изменяемого ObservableList
, сопоставляя события изменения оформленного списка с соответствующими событиями изменения в отфильтрованном списке:
public class FilteredObservableList<E> extends DecoratingObservableList
{
private final IObservableList decorated;
private final Predicate pred;
private final List<E> filtered = new ArrayList();
public FilteredObservableList(
IObservableList decorated, Predicate pred, boolean disposeDecoratedOnDispose)
{
super(decorated, disposeDecoratedOnDispose);
this.decorated = decorated;
this.pred = pred;
for (Object o : decorated) filtered.add(pred.eval(o)? (E) o : null);
}
@Override protected void handleListChange(ListChangeEvent event) {
final List<ListDiffEntry> diffs = new ArrayList();
final List<Integer> mapping = new ArrayList();
int i = 0;
for (E e : filtered) mapping.add(e != null? i++ : i);
event.diff.accept(new ListDiffVisitor() {
@Override public void handleAdd(int index, Object element) {
final boolean passes = pred.eval(element);
filtered.add(index, passes? (E) element : null);
final Integer outInd = mapping.get(index);
mapping.add(index, outInd);
if (passes) {
diffs.add(new FilteredDiffEntry(outInd, true, element));
for (int i = index + 1; i < mapping.size(); i++)
mapping.set(i, mapping.get(i) + 1);
}
}
@Override public void handleRemove(int index, Object element) {
final boolean passes = filtered.get(index) != null;
filtered.remove(index);
final int outInd = mapping.get(index);
mapping.remove(index);
if (passes) {
diffs.add(new FilteredDiffEntry(outInd, false, element));
for (int i = index; i < mapping.size(); i++)
mapping.set(i, mapping.get(i)-1);
}
}
});
if (!diffs.isEmpty()) {
final ListDiffEntry[] difAry = diffs.toArray(new ListDiffEntry[diffs.size()]);
fireListChange(new ListDiff() {
@Override public ListDiffEntry[] getDifferences() { return difAry; }
});
}
}
public ListIterator<E> listIterator() {
getterCalled();
final Iterator<E> it = decorated.iterator();
return new ListIterator<E>() {
E next;
boolean nextReady;
public boolean hasNext() {
getterCalled();
if (nextReady) return true;
while (it.hasNext()) {
next = it.next();
if (next != null) { nextReady = true; break; }
}
return nextReady;
}
public E next() {
getterCalled();
if (hasNext()) { nextReady = false; return next; }
else throw new NoSuchElementException();
}
public void add(Object o) { throw new UnsupportedOperationException(); }
public boolean hasPrevious() { throw new UnsupportedOperationException(); }
public int nextIndex() { throw new UnsupportedOperationException(); }
public E previous() { throw new UnsupportedOperationException(); }
public int previousIndex() { throw new UnsupportedOperationException(); }
public void remove() { throw new UnsupportedOperationException(); }
public void set(Object o) { throw new UnsupportedOperationException(); }
};
}
public interface Predicate { boolean eval(Object o); }
private static final class FilteredDiffEntry extends ListDiffEntry {
private final int pos;
private final boolean isAdd;
private final Object el;
FilteredDiffEntry(int pos, boolean isAdd, Object el) {
this.pos = pos; this.isAdd = isAdd; this.el = el;
}
@Override public int getPosition() { return pos; }
@Override public boolean isAddition() { return isAdd; }
@Override public Object getElement() { return el; }
}
@Override public Object move(int _, int __) { throw new UnsupportedOperationException(); }
@Override public Object remove(int _) { throw new UnsupportedOperationException(); }
@Override public Object set(int _, Object __) { throw new UnsupportedOperationException(); }
@Override public void add(int _, Object __) { throw new UnsupportedOperationException(); }
@Override public boolean add(Object _) { throw new UnsupportedOperationException(); }
@Override public boolean addAll(Collection _) { throw new UnsupportedOperationException(); }
@Override public boolean addAll(int _, Collection __) {
throw new UnsupportedOperationException();
}
@Override public void clear() { throw new UnsupportedOperationException(); }
@Override public boolean remove(Object _) { throw new UnsupportedOperationException(); }
@Override public boolean removeAll(Collection _) { throw new UnsupportedOperationException();}
@Override public boolean retainAll(Collection _) { throw new UnsupportedOperationException();}
}
Вот реализация только для чтения.
Несколько предостережений:
- Это зависит от базовых событий запуска DecoratingObservableList при редактировании.
- Итератор не будет активным, если оформленный список изменится после времени создания итератора, он работает как
CopyOnWriteArrayList
сюда. Возможно, это должно броситьConcurrentModificationExceptions
там.
Если это должно быть доступно для записи, можете ли вы определить, как вы хотите сделать отображение индекса, и, возможно, указать, собираетесь ли вы разрешать неуникальные элементы в списке?
package filteredobservablelist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.core.databinding.observable.list.DecoratingObservableList;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
public class FilteredObservableList extends DecoratingObservableList {
private static final class FilteredListDiff extends ListDiff {
private final List<ListDiffEntry> filteredDiffs;
private FilteredListDiff(List<ListDiffEntry> filteredDiffs) {
this.filteredDiffs = filteredDiffs;
}
@Override
public ListDiffEntry[] getDifferences() {
return filteredDiffs.toArray(new ListDiffEntry[filteredDiffs.size()]);
}
}
public interface Predicate {
boolean evaluate(Object element);
}
private final Predicate predicate;
private List<Object> filteredList;
public FilteredObservableList(IObservableList decorated, boolean disposeDecoratedOnDispose, Predicate predicate) {
super(decorated, disposeDecoratedOnDispose);
this.predicate = predicate;
rebuildCache();
}
@Override
protected void handleListChange(final ListChangeEvent event) {
final List<ListDiffEntry> filteredDiffs = new ArrayList<ListDiffEntry>(event.diff.getDifferences().length);
for (ListDiffEntry element : event.diff.getDifferences()) {
if (predicate.evaluate(element.getElement())) {
filteredDiffs.add(element);
}
}
rebuildCache();
if (!filteredDiffs.isEmpty()) {
fireListChange(new FilteredListDiff(filteredDiffs));
}
}
private void rebuildCache() {
filteredList = new ArrayList<Object>();
for (Object element : getDecorated()) {
if (predicate.evaluate(element)) {
filteredList.add(element);
}
}
}
@Override
public boolean contains(Object o) {
return filteredList.contains(o);
}
@Override
public boolean containsAll(Collection c) {
return filteredList.containsAll(c);
}
@Override
public Object get(int index) {
getterCalled();
return filteredList.get(index);
}
@Override
public int indexOf(Object o) {
getterCalled();
return filteredList.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
getterCalled();
return filteredList.lastIndexOf(o);
}
@Override
public List subList(int fromIndex, int toIndex) {
getterCalled();
return this.filteredList.subList(fromIndex, toIndex);
}
@Override
public IObservableList getDecorated() {
return (IObservableList) super.getDecorated();
}
@Override
public Iterator iterator() {
return listIterator();
}
@Override
public ListIterator listIterator() {
return this.listIterator(0);
}
@Override
public ListIterator listIterator(int index) {
getterCalled();
final ListIterator iterator = filteredList.listIterator(index);
return new ListIterator() {
@Override
public boolean hasNext() {
getterCalled();
return iterator.hasNext();
}
@Override
public boolean hasPrevious() {
getterCalled();
return iterator.hasPrevious();
}
@Override
public Object next() {
getterCalled();
return iterator.next();
}
@Override
public int nextIndex() {
getterCalled();
return iterator.nextIndex();
}
@Override
public Object previous() {
getterCalled();
return iterator.previous();
}
@Override
public int previousIndex() {
getterCalled();
return iterator.previousIndex();
}
@Override
public void add(Object o) {
throw new UnsupportedOperationException();
}
@Override
public void set(Object o) {
throw new UnsupportedOperationException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Object move(int oldIndex, int newIndex) {
throw new UnsupportedOperationException();
}
@Override
public Object remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public Object set(int index, Object element) {
throw new UnsupportedOperationException();
}
@Override
public void add(int index, Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean add(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(int index, Collection c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection c) {
throw new UnsupportedOperationException();
}
}
Возможно, вы захотите взглянуть на исходный код GlazedLists, который имеет фильтруемые, наблюдаемые списки.