Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 0a92b48e96937f0fb3c4256548cece644f5a9b92 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*******************************************************************************
 * 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);
	}
}

Back to the top