comparison org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/UnionSet.d @ 95:6208d4f6a277

Added trees for databinding.beans and observable
author Frank Benoit <benoit@tionex.de>
date Tue, 21 Apr 2009 10:55:51 +0200
parents
children b74ac5dfcc06
comparison
equal deleted inserted replaced
94:1d37a7813832 95:6208d4f6a277
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 package org.eclipse.core.databinding.observable.set;
14
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.Set;
21
22 import org.eclipse.core.databinding.observable.Diffs;
23 import org.eclipse.core.databinding.observable.Realm;
24 import org.eclipse.core.internal.databinding.observable.IStalenessConsumer;
25 import org.eclipse.core.internal.databinding.observable.StalenessTracker;
26
27 /**
28 * Represents a set consisting of the union of elements from one or more other
29 * sets. This object does not need to be explicitly disposed. If nobody is
30 * listening to the UnionSet, the set will remove its listeners.
31 *
32 * <p>
33 * This class is thread safe. All state accessing methods must be invoked from
34 * the {@link Realm#isCurrent() current realm}. Methods for adding and removing
35 * listeners may be invoked from any thread.
36 * </p>
37 *
38 * @since 1.0
39 */
40 public final class UnionSet : ObservableSet {
41
42 /**
43 * child sets
44 */
45 private IObservableSet[] childSets;
46
47 private bool stale = false;
48
49 /**
50 * Map of elements onto Integer reference counts. This map is constructed
51 * when the first listener is added to the union set. Null if nobody is
52 * listening to the UnionSet.
53 */
54 private HashMap refCounts = null;
55
56 private StalenessTracker stalenessTracker;
57
58 /**
59 * @param childSets
60 */
61 public this(IObservableSet[] childSets) {
62 super(childSets[0].getRealm(), null, childSets[0].getElementType());
63 System.arraycopy(childSets, 0, this.childSets = new IObservableSet[childSets.length], 0, childSets.length);
64 this.stalenessTracker = new StalenessTracker(childSets,
65 stalenessConsumer);
66 }
67
68 private ISetChangeListener childSetChangeListener = new class() ISetChangeListener {
69 public void handleSetChange(SetChangeEvent event) {
70 processAddsAndRemoves(event.diff.getAdditions(), event.diff.getRemovals());
71 }
72 };
73
74 private IStalenessConsumer stalenessConsumer = new class() IStalenessConsumer {
75 public void setStale(bool stale) {
76 bool oldStale = this.outer.stale;
77 this.outer.stale = stale;
78 if (stale && !oldStale) {
79 fireStale();
80 }
81 }
82 };
83
84 public bool isStale() {
85 getterCalled();
86 if (refCounts !is null) {
87 return stale;
88 }
89
90 for (int i = 0; i < childSets.length; i++) {
91 IObservableSet childSet = childSets[i];
92
93 if (childSet.isStale()) {
94 return true;
95 }
96 }
97 return false;
98 }
99
100 private void processAddsAndRemoves(Set adds, Set removes) {
101 Set addsToFire = new HashSet();
102 Set removesToFire = new HashSet();
103
104 for (Iterator iter = adds.iterator(); iter.hasNext();) {
105 Object added = iter.next();
106
107 Integer refCount = cast(Integer) refCounts.get(added);
108 if (refCount is null) {
109 refCounts.put(added, new Integer(1));
110 addsToFire.add(added);
111 } else {
112 int refs = refCount.intValue();
113 refCount = new Integer(refs + 1);
114 refCounts.put(added, refCount);
115 }
116 }
117
118 for (Iterator iter = removes.iterator(); iter.hasNext();) {
119 Object removed = iter.next();
120
121 Integer refCount = cast(Integer) refCounts.get(removed);
122 if (refCount !is null) {
123 int refs = refCount.intValue();
124 if (refs <= 1) {
125 removesToFire.add(removed);
126 refCounts.remove(removed);
127 } else {
128 refCount = new Integer(refCount.intValue() - 1);
129 refCounts.put(removed, refCount);
130 }
131 }
132 }
133
134 // just in case the removes overlapped with the adds
135 addsToFire.removeAll(removesToFire);
136
137 if (addsToFire.size() > 0 || removesToFire.size() > 0) {
138 fireSetChange(Diffs.createSetDiff(addsToFire, removesToFire));
139 }
140 }
141
142 protected void firstListenerAdded() {
143 super.firstListenerAdded();
144
145 refCounts = new HashMap();
146 for (int i = 0; i < childSets.length; i++) {
147 IObservableSet next = childSets[i];
148 next.addSetChangeListener(childSetChangeListener);
149 incrementRefCounts(next);
150 }
151 stalenessTracker = new StalenessTracker(childSets, stalenessConsumer);
152 setWrappedSet(refCounts.keySet());
153 }
154
155 protected void lastListenerRemoved() {
156 super.lastListenerRemoved();
157
158 for (int i = 0; i < childSets.length; i++) {
159 IObservableSet next = childSets[i];
160
161 next.removeSetChangeListener(childSetChangeListener);
162 stalenessTracker.removeObservable(next);
163 }
164 refCounts = null;
165 stalenessTracker = null;
166 setWrappedSet(null);
167 }
168
169 private ArrayList incrementRefCounts(Collection added) {
170 ArrayList adds = new ArrayList();
171
172 for (Iterator iter = added.iterator(); iter.hasNext();) {
173 Object next = iter.next();
174
175 Integer refCount = cast(Integer) refCounts.get(next);
176 if (refCount is null) {
177 adds.add(next);
178 refCount = new Integer(1);
179 refCounts.put(next, refCount);
180 } else {
181 refCount = new Integer(refCount.intValue() + 1);
182 refCounts.put(next, refCount);
183 }
184 }
185 return adds;
186 }
187
188 protected void getterCalled() {
189 super.getterCalled();
190 if (refCounts is null) {
191 // no listeners, recompute
192 setWrappedSet(computeElements());
193 }
194 }
195
196 private Set computeElements() {
197 // If there is no cached value, compute the union from scratch
198 if (refCounts is null) {
199 Set result = new HashSet();
200 for (int i = 0; i < childSets.length; i++) {
201 result.addAll(childSets[i]);
202 }
203 return result;
204 }
205
206 // Else there is a cached value. Return it.
207 return refCounts.keySet();
208 }
209
210 }