### Eclipse Workspace Patch 1.0 #P org.eclipse.core.databinding Index: src/org/eclipse/core/databinding/util/IFilter.java =================================================================== RCS file: src/org/eclipse/core/databinding/util/IFilter.java diff -N src/org/eclipse/core/databinding/util/IFilter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/util/IFilter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.databinding.util; + +/** + * Interface for filters. Can accept or reject items. + * + * @since 1.0 + */ +public interface IFilter { + /** + * Determines if the given object passes this filter. + * + * @param toTest object to compare against the filter + * + * @return true iff the object is accepted by the filter. + */ + public boolean select(Object toTest); +} Index: src/org/eclipse/core/databinding/observable/set/FilteringSet.java =================================================================== RCS file: src/org/eclipse/core/databinding/observable/set/FilteringSet.java diff -N src/org/eclipse/core/databinding/observable/set/FilteringSet.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/observable/set/FilteringSet.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +package org.eclipse.core.databinding.observable.set; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.util.IFilter; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * @since 3.3 + * + */ +public abstract class FilteringSet extends ObservableSet { + + private IObservableSet set; + + private IFilter filter; + + private ISetChangeListener setChangeListener = new ISetChangeListener() { + public void handleSetChange(IObservableSet source, SetDiff diff) { + if (filter == null) { + fireSetChange(diff); + return; + } + Set removals = new HashSet(); + Set additions = new HashSet(); + for (Iterator it = diff.getRemovals().iterator(); it.hasNext();) { + Object removedElement = it.next(); + if (filter.select(removedElement)) { + wrappedSet.remove(removedElement); + removals.add(removedElement); + } + } + for (Iterator it = diff.getAdditions().iterator(); it.hasNext();) { + Object addedElement = it.next(); + if (filter.select(addedElement)) { + wrappedSet.add(addedElement); + additions.add(addedElement); + } + } + fireSetChange(Diffs.createSetDiff(additions, removals)); + } + }; + + /** + * @param set + */ + public FilteringSet(IObservableSet set) { + super(set.getRealm(), set, set.getElementType()); + this.set = set; + set.addSetChangeListener(setChangeListener); + } + + /** + * @param filter + * @param monitor + * @param incrementalEvents + */ + public void init(IFilter filter, IProgressMonitor monitor, + boolean incrementalEvents) { + Assert.isTrue(this.filter == null); + Assert.isTrue(filter != null); + wrappedSet = new HashSet(set); + this.filter = filter; + refilter(monitor, incrementalEvents, true); + } + + /** + * Clients should call this method to cause the elements to be refiltered + * after a change to the filter criteria. + * + * @param monitor + * progress monitor for reporting progress and cancellation + * @param incrementalEvents + * true if change events should be fired + * incrementally, false if just one combined + * change event at the end should be fired + * @param narrowing + * true if the result of the refilter will be a + * subset of the current set + * + */ + public void refilter(IProgressMonitor monitor, boolean incrementalEvents, + boolean narrowing) { + setStale(true); + monitor.beginTask("NON-NLSed: Filtering", set.size()); //$NON-NLS-1$ + Set additions = new HashSet(); + Set removals = new HashSet(); + // Delay firing of incremental events by one iteration so that we have + // one event left at the end. This allows us to call setStale(false) + // before firing the last event. + SetDiff nextDiff = null; + Iterator it = narrowing ? wrappedSet.iterator() : set.iterator(); + while (!monitor.isCanceled() && it.hasNext()) { + Object element = it.next(); + if (filter == null || filter.select(element)) { + if (!narrowing && wrappedSet.add(element)) { + if (incrementalEvents) { + if (nextDiff != null) { + fireSetChange(nextDiff); + } + nextDiff = Diffs.createSetDiff(Collections + .singleton(element), Collections.EMPTY_SET); + } else { + additions.add(element); + } + } + } else { + if (wrappedSet.remove(element)) { + if (incrementalEvents) { + if (nextDiff != null) { + fireSetChange(nextDiff); + } + nextDiff = Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(element)); + } else { + removals.add(element); + } + } + } + monitor.worked(1); + } + setStale(false); + if (incrementalEvents) { + if (nextDiff != null) { + fireSetChange(nextDiff); + } else { + // This is needed in order to notify clients that track + // staleness. + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.EMPTY_SET)); + } + } else { + SetDiff diff = Diffs.createSetDiff(additions, removals); + fireSetChange(diff); + } + monitor.done(); + } + +} Index: src/org/eclipse/core/databinding/observable/list/FilteringList.java =================================================================== RCS file: src/org/eclipse/core/databinding/observable/list/FilteringList.java diff -N src/org/eclipse/core/databinding/observable/list/FilteringList.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/databinding/observable/list/FilteringList.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,142 @@ +package org.eclipse.core.databinding.observable.list; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.value.IObservableValue; + +/** + * @since 1.0 + * + */ +public abstract class FilteringList extends ObservableList { + + private final IObservableList unfiltered; + + private final IObservableValue textObservable; + + private IListChangeListener listChangeListener = new IListChangeListener() { + public void handleListChange(IObservableList source, ListDiff diff) { + ListDiffEntry[] differences = diff.getDifferences(); + List listDiffEntries = new ArrayList(); + for (int i = 0; i < differences.length; i++) { + ListDiffEntry diffEntry = differences[i]; + Object element = diffEntry.getElement(); + if (diffEntry.isAddition()) { + if (select(element)) { + int index = internalAddElement(element, diffEntry + .getPosition()); + if (index >= 0) { + listDiffEntries.add(Diffs.createListDiffEntry( + index, true, element)); + } + } + } else { + if (select(element)) { + int index = internalRemoveElement(element, diffEntry + .getPosition()); + if (index >= 0) { + listDiffEntries.add(Diffs.createListDiffEntry( + index, false, element)); + } + } + } + } + fireListChange(Diffs + .createListDiff((ListDiffEntry[]) listDiffEntries + .toArray(new ListDiffEntry[listDiffEntries.size()]))); + } + }; + + protected FilteringList(IObservableList list, + IObservableValue textObservable) { + super(list.getRealm(), new ArrayList(), list.getElementType()); + this.unfiltered = list; + unfiltered.addListChangeListener(listChangeListener); + refilter(); + } + + protected void refilter() { + List listDiffEntries = new ArrayList(); + for (int i = 0; i < unfiltered.size(); i++) { + Object element = unfiltered.get(i); + if (select(element)) { + int newIndex = internalAddElement(element, i); + if (newIndex >= 0) { + listDiffEntries.add(Diffs.createListDiffEntry(newIndex, + true, element)); + } + } else { + int oldIndex = internalRemoveElement(element, i); + if (oldIndex >= 0) { + listDiffEntries.add(Diffs.createListDiffEntry(oldIndex, + false, element)); + } + } + } + fireListChange(Diffs.createListDiff((ListDiffEntry[]) listDiffEntries + .toArray(new ListDiffEntry[listDiffEntries.size()]))); + } + + private int internalRemoveElement(Object element, int childIndex) { + int location = Arrays.binarySearch(filteredIndices, childIndex); + if (location >= 0) { + wrappedList.remove(location); + if (location == 0) { + if (filteredIndices.length == 1) { + filteredIndices = new int[0]; + } else { + int[] next = new int[filteredIndices.length - 1]; + System.arraycopy(filteredIndices, 1, next, 0, next.length); + filteredIndices = next; + } + } else if (location == (filteredIndices.length - 1)) { + int[] next = new int[filteredIndices.length - 1]; + System.arraycopy(filteredIndices, 0, next, 0, location); + filteredIndices = next; + } else { + int[] next = new int[filteredIndices.length - 1]; + System.arraycopy(filteredIndices, 0, next, 0, location); + System.arraycopy(filteredIndices, location + 1, next, location, + next.length - location); + filteredIndices = next; + } + } + return location; + } + + private int internalAddElement(Object element, int index) { + int location = Arrays.binarySearch(filteredIndices, index); + if (location >= 0) { + return -1; + } + location = 0 - (location + 1); + wrappedList.add(location, element); + int[] next = new int[filteredIndices.length + 1]; + if (location == 0) { + next[0] = index; + System.arraycopy(filteredIndices, 0, next, 1, + filteredIndices.length); + } else if (location == filteredIndices.length) { + next[filteredIndices.length] = index; + System.arraycopy(filteredIndices, 0, next, 0, + filteredIndices.length); + } else { + System.arraycopy(filteredIndices, 0, next, 0, location); + next[location] = index; + System.arraycopy(filteredIndices, location, next, location + 1, + filteredIndices.length - location); + } + filteredIndices = next; + return location; + } + + protected boolean select(Object element) { + return stringMatcher.match(getText(element)); + } + + protected abstract String getText(Object element); + +}