KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > databinding > observable > value > ComputedValue


1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 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 package org.eclipse.core.databinding.observable.value;
14
15 import org.eclipse.core.databinding.observable.ChangeEvent;
16 import org.eclipse.core.databinding.observable.IChangeListener;
17 import org.eclipse.core.databinding.observable.IObservable;
18 import org.eclipse.core.databinding.observable.IStaleListener;
19 import org.eclipse.core.databinding.observable.ObservableTracker;
20 import org.eclipse.core.databinding.observable.Realm;
21 import org.eclipse.core.databinding.observable.StaleEvent;
22
23 /**
24  * A Lazily calculated value that automatically computes and registers listeners
25  * on its dependencies as long as all of its dependencies are IObservable
26  * objects
27  * <p>
28  * This class is thread safe. All state accessing methods must be invoked from
29  * the {@link Realm#isCurrent() current realm}. Methods for adding and removing
30  * listeners may be invoked from any thread.
31  * </p>
32  * @since 1.0
33  */

34 public abstract class ComputedValue extends AbstractObservableValue {
35
36     private boolean dirty = true;
37
38     private boolean stale = false;
39
40     private Object JavaDoc cachedValue = null;
41
42     /**
43      * Dependencies list. This is a collection that contains no duplicates. It
44      * is normally an ArrayList to conserve memory, but if it ever grows above a
45      * certain number of elements, a HashSet is substited to conserve runtime.
46      */

47     private IObservable[] dependencies = new IObservable[0];
48
49     /**
50      *
51      */

52     public ComputedValue() {
53         this(Realm.getDefault(), null);
54     }
55
56     /**
57      * @param valueType can be <code>null</code>
58      */

59     public ComputedValue(Object JavaDoc valueType) {
60         this(Realm.getDefault(), valueType);
61     }
62
63     /**
64      * @param realm
65      *
66      */

67     public ComputedValue(Realm realm) {
68         this(realm, null);
69     }
70     
71     /**
72      * @param realm
73      * @param valueType
74      */

75     public ComputedValue(Realm realm, Object JavaDoc valueType) {
76         super(realm);
77         this.valueType = valueType;
78     }
79     
80     /**
81      * Inner class that implements interfaces that we don't want to expose as
82      * public API. Each interface could have been implemented using a separate
83      * anonymous class, but we combine them here to reduce the memory overhead
84      * and number of classes.
85      *
86      * <p>
87      * The Runnable calls computeValue and stores the result in cachedValue.
88      * </p>
89      *
90      * <p>
91      * The IChangeListener stores each observable in the dependencies list.
92      * This is registered as the listener when calling ObservableTracker, to
93      * detect every observable that is used by computeValue.
94      * </p>
95      *
96      * <p>
97      * The IChangeListener is attached to every dependency.
98      * </p>
99      *
100      */

101     private class PrivateInterface implements Runnable JavaDoc, IChangeListener,
102             IStaleListener {
103         public void run() {
104             cachedValue = calculate();
105         }
106
107         public void handleStale(StaleEvent event) {
108             if (!dirty && !stale) {
109                 stale = true;
110                 fireStale();
111             }
112         }
113
114         public void handleChange(ChangeEvent event) {
115             makeDirty();
116         }
117     }
118
119     private PrivateInterface privateInterface = new PrivateInterface();
120
121     private Object JavaDoc valueType;
122
123     protected final Object JavaDoc doGetValue() {
124         if (dirty) {
125             // This line will do the following:
126
// - Run the calculate method
127
// - While doing so, add any observable that is touched to the
128
// dependencies list
129
IObservable[] newDependencies = ObservableTracker.runAndMonitor(
130                     privateInterface, privateInterface, null);
131
132             stale = false;
133             for (int i = 0; i < newDependencies.length; i++) {
134                 IObservable observable = newDependencies[i];
135                 // Add a change listener to the new dependency.
136
if (observable.isStale()) {
137                     stale = true;
138                 } else {
139                     observable.addStaleListener(privateInterface);
140                 }
141             }
142
143             dependencies = newDependencies;
144
145             dirty = false;
146         }
147
148         return cachedValue;
149     }
150
151     /**
152      * Subclasses must override this method to provide the object's value.
153      *
154      * @return the object's value
155      */

156     protected abstract Object JavaDoc calculate();
157
158     protected final void makeDirty() {
159         if (!dirty) {
160             dirty = true;
161
162             stopListening();
163
164             // copy the old value
165
final Object JavaDoc oldValue = cachedValue;
166             // Fire the "dirty" event. This implementation recomputes the new
167
// value lazily.
168
fireValueChange(new ValueDiff() {
169
170                 public Object JavaDoc getOldValue() {
171                     return oldValue;
172                 }
173
174                 public Object JavaDoc getNewValue() {
175                     return getValue();
176                 }
177             });
178         }
179     }
180
181     /**
182      *
183      */

184     private void stopListening() {
185         // Stop listening for dependency changes.
186
for (int i = 0; i < dependencies.length; i++) {
187             IObservable observable = dependencies[i];
188
189             observable.removeChangeListener(privateInterface);
190             observable.removeStaleListener(privateInterface);
191         }
192     }
193
194     public boolean isStale() {
195         // we need to recompute, otherwise staleness wouldn't mean anything
196
getValue();
197         return stale;
198     }
199
200     public Object JavaDoc getValueType() {
201         return valueType;
202     }
203
204     public synchronized void addChangeListener(IChangeListener listener) {
205         super.addChangeListener(listener);
206         // If somebody is listening, we need to make sure we attach our own
207
// listeners
208
getValue();
209     }
210
211     public synchronized void addValueChangeListener(IValueChangeListener listener) {
212         super.addValueChangeListener(listener);
213         // If somebody is listening, we need to make sure we attach our own
214
// listeners
215
getValue();
216     }
217     
218     public synchronized void dispose() {
219         super.dispose();
220         stopListening();
221     }
222
223 }
224
Popular Tags