| /******************************************************************************* |
| * Copyright (c) 2007, 2012 Oracle. 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: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.common.utility.internal.model.value; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import org.eclipse.jpt.common.utility.Filter; |
| import org.eclipse.jpt.common.utility.internal.CollectionTools; |
| import org.eclipse.jpt.common.utility.internal.iterables.FilteringIterable; |
| import org.eclipse.jpt.common.utility.internal.iterators.ReadOnlyIterator; |
| import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; |
| import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; |
| import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; |
| import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; |
| import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; |
| import org.eclipse.jpt.common.utility.model.value.ListValueModel; |
| |
| /** |
| * A <code>FilteringCollectionValueModel</code> wraps another |
| * {@link CollectionValueModel} and uses a {@link Filter} |
| * to determine which items in the collection are returned by calls |
| * to {@link #iterator()}. |
| * <p> |
| * The filter can be changed at any time; allowing the same |
| * adapter to be used with different filter criteria (e.g. when the user |
| * wants to view a list of <code>.java</code> files). |
| * <p> |
| * <strong>NB:</strong> If the objects in the "filtered" collection can change in such a way |
| * that they should be removed from the "filtered" collection, you will |
| * need to wrap the original collection in an {@link ItemAspectListValueModelAdapter}. |
| * For example, if the filter only "accepts" items whose names begin |
| * with "X" and the names of the items can change, you will need to |
| * wrap the original list of unfiltered items with an |
| * {@link ItemPropertyListValueModelAdapter} that listens for changes to each |
| * item's name and fires the appropriate event whenever an item's name |
| * changes. The event will cause this wrapper to re-filter the changed |
| * item and add or remove it from the "filtered" collection as appropriate. |
| */ |
| public class FilteringCollectionValueModel<E> |
| extends CollectionValueModelWrapper<E> |
| implements CollectionValueModel<E> |
| { |
| /** This filters the items in the nested collection. */ |
| private Filter<E> filter; |
| |
| /** Cache the items that were accepted by the filter */ |
| private final ArrayList<E> filteredItems = new ArrayList<E>(); |
| |
| |
| // ********** constructors ********** |
| |
| /** |
| * Construct a collection value model with the specified wrapped |
| * collection value model and a filter that simply accepts every object. |
| */ |
| public FilteringCollectionValueModel(CollectionValueModel<? extends E> collectionModel) { |
| this(collectionModel, Filter.Transparent.<E>instance()); |
| } |
| |
| /** |
| * Construct a collection value model with the specified wrapped |
| * collection value model and filter. |
| */ |
| public FilteringCollectionValueModel(CollectionValueModel<? extends E> collectionModel, Filter<E> filter) { |
| super(collectionModel); |
| this.filter = filter; |
| } |
| |
| /** |
| * Construct a collection value model with the specified wrapped |
| * list value model and a filter that simply accepts every object. |
| */ |
| public FilteringCollectionValueModel(ListValueModel<? extends E> listModel) { |
| this(new ListCollectionValueModelAdapter<E>(listModel)); |
| } |
| |
| /** |
| * Construct a collection value model with the specified wrapped |
| * list value model and filter. |
| */ |
| public FilteringCollectionValueModel(ListValueModel<? extends E> listModel, Filter<E> filter) { |
| this(new ListCollectionValueModelAdapter<E>(listModel), filter); |
| } |
| |
| |
| // ********** CollectionValueModel implementation ********** |
| |
| public Iterator<E> iterator() { |
| return new ReadOnlyIterator<E>(this.filteredItems); |
| } |
| |
| public int size() { |
| return this.filteredItems.size(); |
| } |
| |
| |
| // ********** CollectionValueModelWrapper overrides/implementation ********** |
| |
| @Override |
| protected void engageModel() { |
| super.engageModel(); |
| // sync our cache *after* we start listening to the nested collection, |
| // since its value might change when a listener is added |
| CollectionTools.addAll(this.filteredItems, this.filter(this.collectionHolder)); |
| } |
| |
| @Override |
| protected void disengageModel() { |
| super.disengageModel(); |
| // clear out the cache when we are not listening to the nested collection |
| this.filteredItems.clear(); |
| } |
| |
| @Override |
| protected void itemsAdded(CollectionAddEvent event) { |
| // filter the values before propagating the change event |
| this.addItemsToCollection(this.filter(this.getItems(event)), this.filteredItems, VALUES); |
| } |
| |
| @Override |
| protected void itemsRemoved(CollectionRemoveEvent event) { |
| // do *not* filter the values, because they may no longer be |
| // "accepted" and that might be why they were removed in the first place; |
| // anyway, any extraneous items are harmless |
| this.removeItemsFromCollection(event.getItems(), this.filteredItems, VALUES); |
| } |
| |
| @Override |
| protected void collectionCleared(CollectionClearEvent event) { |
| this.clearCollection(this.filteredItems, VALUES); |
| } |
| |
| @Override |
| protected void collectionChanged(CollectionChangeEvent event) { |
| this.rebuildFilteredItems(); |
| } |
| |
| |
| // ********** miscellaneous ********** |
| |
| /** |
| * Change the filter and rebuild the collection. |
| */ |
| public void setFilter(Filter<E> filter) { |
| this.filter = filter; |
| this.rebuildFilteredItems(); |
| } |
| |
| /** |
| * Return an iterable that filters the specified iterable. |
| */ |
| protected Iterable<E> filter(Iterable<? extends E> items) { |
| return new FilteringIterable<E>(items, this.filter); |
| } |
| |
| /** |
| * Synchronize our cache with the wrapped collection. |
| */ |
| protected void rebuildFilteredItems() { |
| this.filteredItems.clear(); |
| CollectionTools.addAll(this.filteredItems, this.filter(this.collectionHolder)); |
| this.fireCollectionChanged(VALUES, this.filteredItems); |
| } |
| |
| @Override |
| public void toString(StringBuilder sb) { |
| sb.append(this.filteredItems); |
| } |
| } |