如何编写过滤的IObservableList

时间:2012-04-05 13:42:35

标签: java data-binding filter eclipse-databinding

从一些API调用中,我从Eclipse Databinding框架返回IObservableList<E>。我希望根据元素类型IObservableList<E>上定义的一些谓词从这个派生出另一个E。应根据原始列表中的更改动态更新派生列表。

我怎样才能最好地实现它?我考虑过继承DecoratingObservableList,但无法弄清楚如何使用它。

当然,我可以自己实现整个IObservableList界面,但我想知道是否没有其他实用程序类可以使用。

3 个答案:

答案 0 :(得分: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();}
}

答案 1 :(得分:3)

这是一个只读实现。

有几点需要注意:

  • 这取决于基于编辑的基本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();
    }
}

答案 2 :(得分:1)

您可能希望查看具有可过滤,可观察列表的GlazedLists的源代码。