78
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2006, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Matthew Hall - bug 208332
|
|
11 *******************************************************************************/
|
|
12
|
|
13 module org.eclipse.core.databinding.observable.set.UnionSet;
|
81
|
14 import org.eclipse.core.databinding.observable.set.ISetChangeListener;
|
|
15 import org.eclipse.core.databinding.observable.set.SetChangeEvent;
|
|
16 import org.eclipse.core.databinding.observable.set.IObservableSet;
|
85
|
17 import org.eclipse.core.databinding.observable.IObservable;
|
81
|
18 import org.eclipse.core.databinding.observable.set.ObservableSet;
|
78
|
19
|
|
20 import java.lang.all;
|
|
21
|
|
22 import java.util.ArrayList;
|
|
23 import java.util.Collection;
|
|
24 import java.util.HashMap;
|
|
25 import java.util.HashSet;
|
|
26 import java.util.Iterator;
|
|
27 import java.util.Set;
|
|
28
|
|
29 import org.eclipse.core.databinding.observable.Diffs;
|
|
30 import org.eclipse.core.databinding.observable.Realm;
|
|
31 import org.eclipse.core.internal.databinding.observable.IStalenessConsumer;
|
|
32 import org.eclipse.core.internal.databinding.observable.StalenessTracker;
|
|
33
|
|
34 /**
|
|
35 * Represents a set consisting of the union of elements from one or more other
|
|
36 * sets. This object does not need to be explicitly disposed. If nobody is
|
|
37 * listening to the UnionSet, the set will remove its listeners.
|
|
38 *
|
|
39 * <p>
|
|
40 * This class is thread safe. All state accessing methods must be invoked from
|
|
41 * the {@link Realm#isCurrent() current realm}. Methods for adding and removing
|
|
42 * listeners may be invoked from any thread.
|
|
43 * </p>
|
|
44 *
|
|
45 * @since 1.0
|
|
46 */
|
|
47 public final class UnionSet : ObservableSet {
|
|
48
|
|
49 /**
|
|
50 * child sets
|
|
51 */
|
|
52 private IObservableSet[] childSets;
|
|
53
|
|
54 private bool stale = false;
|
|
55
|
|
56 /**
|
|
57 * Map of elements onto Integer reference counts. This map is constructed
|
|
58 * when the first listener is added to the union set. Null if nobody is
|
|
59 * listening to the UnionSet.
|
|
60 */
|
|
61 private HashMap refCounts = null;
|
|
62
|
|
63 private StalenessTracker stalenessTracker;
|
|
64
|
|
65 /**
|
|
66 * @param childSets
|
|
67 */
|
|
68 public this(IObservableSet[] childSets) {
|
|
69 super(childSets[0].getRealm(), null, childSets[0].getElementType());
|
85
|
70 childSetChangeListener = new ChildSetChangeListener();
|
|
71 stalenessConsumer = new StalenessConsumer();
|
|
72 this.childSets = new IObservableSet[childSets.length];
|
|
73 for( int i = 0; i < childSets.length; i++ ){
|
|
74 this.childSets[i] = childSets[i];
|
|
75 }
|
|
76 this.stalenessTracker = new StalenessTracker(arraycast!(IObservable)(childSets),
|
78
|
77 stalenessConsumer);
|
|
78 }
|
|
79
|
85
|
80 private ISetChangeListener childSetChangeListener;
|
|
81 class ChildSetChangeListener : ISetChangeListener {
|
78
|
82 public void handleSetChange(SetChangeEvent event) {
|
|
83 processAddsAndRemoves(event.diff.getAdditions(), event.diff.getRemovals());
|
|
84 }
|
85
|
85 }
|
78
|
86
|
85
|
87 private IStalenessConsumer stalenessConsumer;
|
|
88 class StalenessConsumer : IStalenessConsumer {
|
78
|
89 public void setStale(bool stale) {
|
|
90 bool oldStale = this.outer.stale;
|
|
91 this.outer.stale = stale;
|
|
92 if (stale && !oldStale) {
|
|
93 fireStale();
|
|
94 }
|
|
95 }
|
85
|
96 }
|
78
|
97
|
|
98 public bool isStale() {
|
|
99 getterCalled();
|
|
100 if (refCounts !is null) {
|
|
101 return stale;
|
|
102 }
|
|
103
|
|
104 for (int i = 0; i < childSets.length; i++) {
|
|
105 IObservableSet childSet = childSets[i];
|
|
106
|
|
107 if (childSet.isStale()) {
|
|
108 return true;
|
|
109 }
|
|
110 }
|
|
111 return false;
|
|
112 }
|
|
113
|
|
114 private void processAddsAndRemoves(Set adds, Set removes) {
|
|
115 Set addsToFire = new HashSet();
|
|
116 Set removesToFire = new HashSet();
|
|
117
|
|
118 for (Iterator iter = adds.iterator(); iter.hasNext();) {
|
|
119 Object added = iter.next();
|
|
120
|
|
121 Integer refCount = cast(Integer) refCounts.get(added);
|
|
122 if (refCount is null) {
|
|
123 refCounts.put(added, new Integer(1));
|
|
124 addsToFire.add(added);
|
|
125 } else {
|
|
126 int refs = refCount.intValue();
|
|
127 refCount = new Integer(refs + 1);
|
|
128 refCounts.put(added, refCount);
|
|
129 }
|
|
130 }
|
|
131
|
|
132 for (Iterator iter = removes.iterator(); iter.hasNext();) {
|
|
133 Object removed = iter.next();
|
|
134
|
|
135 Integer refCount = cast(Integer) refCounts.get(removed);
|
|
136 if (refCount !is null) {
|
|
137 int refs = refCount.intValue();
|
|
138 if (refs <= 1) {
|
|
139 removesToFire.add(removed);
|
|
140 refCounts.remove(removed);
|
|
141 } else {
|
|
142 refCount = new Integer(refCount.intValue() - 1);
|
|
143 refCounts.put(removed, refCount);
|
|
144 }
|
|
145 }
|
|
146 }
|
|
147
|
|
148 // just in case the removes overlapped with the adds
|
|
149 addsToFire.removeAll(removesToFire);
|
|
150
|
|
151 if (addsToFire.size() > 0 || removesToFire.size() > 0) {
|
|
152 fireSetChange(Diffs.createSetDiff(addsToFire, removesToFire));
|
|
153 }
|
|
154 }
|
|
155
|
|
156 protected void firstListenerAdded() {
|
|
157 super.firstListenerAdded();
|
|
158
|
|
159 refCounts = new HashMap();
|
|
160 for (int i = 0; i < childSets.length; i++) {
|
|
161 IObservableSet next = childSets[i];
|
|
162 next.addSetChangeListener(childSetChangeListener);
|
|
163 incrementRefCounts(next);
|
|
164 }
|
85
|
165 stalenessTracker = new StalenessTracker(arraycast!(IObservable)(childSets), stalenessConsumer);
|
78
|
166 setWrappedSet(refCounts.keySet());
|
|
167 }
|
|
168
|
|
169 protected void lastListenerRemoved() {
|
|
170 super.lastListenerRemoved();
|
|
171
|
|
172 for (int i = 0; i < childSets.length; i++) {
|
|
173 IObservableSet next = childSets[i];
|
|
174
|
|
175 next.removeSetChangeListener(childSetChangeListener);
|
|
176 stalenessTracker.removeObservable(next);
|
|
177 }
|
|
178 refCounts = null;
|
|
179 stalenessTracker = null;
|
|
180 setWrappedSet(null);
|
|
181 }
|
|
182
|
|
183 private ArrayList incrementRefCounts(Collection added) {
|
|
184 ArrayList adds = new ArrayList();
|
|
185
|
|
186 for (Iterator iter = added.iterator(); iter.hasNext();) {
|
|
187 Object next = iter.next();
|
|
188
|
|
189 Integer refCount = cast(Integer) refCounts.get(next);
|
|
190 if (refCount is null) {
|
|
191 adds.add(next);
|
|
192 refCount = new Integer(1);
|
|
193 refCounts.put(next, refCount);
|
|
194 } else {
|
|
195 refCount = new Integer(refCount.intValue() + 1);
|
|
196 refCounts.put(next, refCount);
|
|
197 }
|
|
198 }
|
|
199 return adds;
|
|
200 }
|
|
201
|
|
202 protected void getterCalled() {
|
|
203 super.getterCalled();
|
|
204 if (refCounts is null) {
|
|
205 // no listeners, recompute
|
|
206 setWrappedSet(computeElements());
|
|
207 }
|
|
208 }
|
|
209
|
|
210 private Set computeElements() {
|
|
211 // If there is no cached value, compute the union from scratch
|
|
212 if (refCounts is null) {
|
|
213 Set result = new HashSet();
|
|
214 for (int i = 0; i < childSets.length; i++) {
|
|
215 result.addAll(childSets[i]);
|
|
216 }
|
|
217 return result;
|
|
218 }
|
|
219
|
|
220 // Else there is a cached value. Return it.
|
|
221 return refCounts.keySet();
|
|
222 }
|
|
223
|
|
224 }
|