Mercurial > projects > dwt2
comparison org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ComputedValue.d @ 78:0a55d2d5a946
Added file for databinding
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 14 Apr 2009 11:35:29 +0200 |
parents | |
children | 383ce7bd736b |
comparison
equal
deleted
inserted
replaced
76:f05e6e8b2f2d | 78:0a55d2d5a946 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2005, 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 * Brad Reynolds - bug 116920 | |
11 * Brad Reynolds - bug 147515 | |
12 *******************************************************************************/ | |
13 module org.eclipse.core.databinding.observable.value.ComputedValue; | |
14 | |
15 import java.lang.all; | |
16 | |
17 import org.eclipse.core.databinding.observable.ChangeEvent; | |
18 import org.eclipse.core.databinding.observable.IChangeListener; | |
19 import org.eclipse.core.databinding.observable.IObservable; | |
20 import org.eclipse.core.databinding.observable.IStaleListener; | |
21 import org.eclipse.core.databinding.observable.ObservableTracker; | |
22 import org.eclipse.core.databinding.observable.Realm; | |
23 import org.eclipse.core.databinding.observable.StaleEvent; | |
24 | |
25 /** | |
26 * A Lazily calculated value that automatically computes and registers listeners | |
27 * on its dependencies as long as all of its dependencies are IObservable | |
28 * objects | |
29 * <p> | |
30 * This class is thread safe. All state accessing methods must be invoked from | |
31 * the {@link Realm#isCurrent() current realm}. Methods for adding and removing | |
32 * listeners may be invoked from any thread. | |
33 * </p> | |
34 * | |
35 * @since 1.0 | |
36 */ | |
37 public abstract class ComputedValue : AbstractObservableValue { | |
38 | |
39 private bool dirty = true; | |
40 | |
41 private bool stale = false; | |
42 | |
43 private Object cachedValue = null; | |
44 | |
45 /** | |
46 * Array of observables this computed value depends on. This field has a | |
47 * value of <code>null</code> if we are not currently listening. | |
48 */ | |
49 private IObservable[] dependencies = null; | |
50 | |
51 /** | |
52 * | |
53 */ | |
54 public this() { | |
55 this(Realm.getDefault(), null); | |
56 } | |
57 | |
58 /** | |
59 * @param valueType | |
60 * can be <code>null</code> | |
61 */ | |
62 public this(Object valueType) { | |
63 this(Realm.getDefault(), valueType); | |
64 } | |
65 | |
66 /** | |
67 * @param realm | |
68 * | |
69 */ | |
70 public this(Realm realm) { | |
71 this(realm, null); | |
72 } | |
73 | |
74 /** | |
75 * @param realm | |
76 * @param valueType | |
77 */ | |
78 public this(Realm realm, Object valueType) { | |
79 super(realm); | |
80 this.valueType = valueType; | |
81 } | |
82 | |
83 /** | |
84 * Inner class that implements interfaces that we don't want to expose as | |
85 * public API. Each interface could have been implemented using a separate | |
86 * anonymous class, but we combine them here to reduce the memory overhead | |
87 * and number of classes. | |
88 * | |
89 * <p> | |
90 * The Runnable calls computeValue and stores the result in cachedValue. | |
91 * </p> | |
92 * | |
93 * <p> | |
94 * The IChangeListener stores each observable in the dependencies list. This | |
95 * is registered as the listener when calling ObservableTracker, to detect | |
96 * every observable that is used by computeValue. | |
97 * </p> | |
98 * | |
99 * <p> | |
100 * The IChangeListener is attached to every dependency. | |
101 * </p> | |
102 * | |
103 */ | |
104 private class PrivateInterface : Runnable, IChangeListener, | |
105 IStaleListener { | |
106 public void run() { | |
107 cachedValue = calculate(); | |
108 } | |
109 | |
110 public void handleStale(StaleEvent event) { | |
111 if (!dirty && !stale) { | |
112 stale = true; | |
113 fireStale(); | |
114 } | |
115 } | |
116 | |
117 public void handleChange(ChangeEvent event) { | |
118 makeDirty(); | |
119 } | |
120 } | |
121 | |
122 private PrivateInterface privateInterface = new PrivateInterface(); | |
123 | |
124 private Object valueType; | |
125 | |
126 protected final Object doGetValue() { | |
127 if (dirty) { | |
128 // This line will do the following: | |
129 // - Run the calculate method | |
130 // - While doing so, add any observable that is touched to the | |
131 // dependencies list | |
132 IObservable[] newDependencies = ObservableTracker.runAndMonitor( | |
133 privateInterface, privateInterface, null); | |
134 | |
135 stale = false; | |
136 for (int i = 0; i < newDependencies.length; i++) { | |
137 IObservable observable = newDependencies[i]; | |
138 // Add a change listener to the new dependency. | |
139 if (observable.isStale()) { | |
140 stale = true; | |
141 } else { | |
142 observable.addStaleListener(privateInterface); | |
143 } | |
144 } | |
145 | |
146 dependencies = newDependencies; | |
147 | |
148 dirty = false; | |
149 } | |
150 | |
151 return cachedValue; | |
152 } | |
153 | |
154 /** | |
155 * Subclasses must override this method to provide the object's value. | |
156 * | |
157 * @return the object's value | |
158 */ | |
159 protected abstract Object calculate(); | |
160 | |
161 protected final void makeDirty() { | |
162 if (!dirty) { | |
163 dirty = true; | |
164 | |
165 stopListening(); | |
166 | |
167 // copy the old value | |
168 final Object oldValue = cachedValue; | |
169 // Fire the "dirty" event. This implementation recomputes the new | |
170 // value lazily. | |
171 fireValueChange(new class() ValueDiff { | |
172 | |
173 public Object getOldValue() { | |
174 return oldValue; | |
175 } | |
176 | |
177 public Object getNewValue() { | |
178 return getValue(); | |
179 } | |
180 }); | |
181 } | |
182 } | |
183 | |
184 /** | |
185 * | |
186 */ | |
187 private void stopListening() { | |
188 // Stop listening for dependency changes. | |
189 if (dependencies !is null) { | |
190 for (int i = 0; i < dependencies.length; i++) { | |
191 IObservable observable = dependencies[i]; | |
192 | |
193 observable.removeChangeListener(privateInterface); | |
194 observable.removeStaleListener(privateInterface); | |
195 } | |
196 dependencies = null; | |
197 } | |
198 } | |
199 | |
200 public bool isStale() { | |
201 // we need to recompute, otherwise staleness wouldn't mean anything | |
202 getValue(); | |
203 return stale; | |
204 } | |
205 | |
206 public Object getValueType() { | |
207 return valueType; | |
208 } | |
209 | |
210 // this method exists here so that we can call it from the runnable below. | |
211 /** | |
212 * @since 1.1 | |
213 */ | |
214 protected bool hasListeners() { | |
215 return super.hasListeners(); | |
216 } | |
217 | |
218 public synchronized void addChangeListener(IChangeListener listener) { | |
219 super.addChangeListener(listener); | |
220 // If somebody is listening, we need to make sure we attach our own | |
221 // listeners | |
222 computeValueForListeners(); | |
223 } | |
224 | |
225 /** | |
226 * Some clients just add a listener and expect to get notified even if they | |
227 * never called getValue(), so we have to call getValue() ourselves here to | |
228 * be sure. Need to be careful about realms though, this method can be | |
229 * called outside of our realm. See also bug 198211. If a client calls this | |
230 * outside of our realm, they may receive change notifications before the | |
231 * runnable below has been executed. It is their job to figure out what to | |
232 * do with those notifications. | |
233 */ | |
234 private void computeValueForListeners() { | |
235 getRealm().exec(new class() Runnable { | |
236 public void run() { | |
237 if (dependencies is null) { | |
238 // We are not currently listening. | |
239 if (hasListeners()) { | |
240 // But someone is listening for changes. Call getValue() | |
241 // to make sure we start listening to the observables we | |
242 // depend on. | |
243 getValue(); | |
244 } | |
245 } | |
246 } | |
247 }); | |
248 } | |
249 | |
250 public synchronized void addValueChangeListener( | |
251 IValueChangeListener listener) { | |
252 super.addValueChangeListener(listener); | |
253 // If somebody is listening, we need to make sure we attach our own | |
254 // listeners | |
255 computeValueForListeners(); | |
256 } | |
257 | |
258 public synchronized void dispose() { | |
259 super.dispose(); | |
260 stopListening(); | |
261 } | |
262 | |
263 } |