KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > internal > databinding > provisional > observable > value > ComputedValue


1 /*******************************************************************************
2  * Copyright (c) 2005, 2006 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  *******************************************************************************/

11 package org.eclipse.jface.internal.databinding.provisional.observable.value;
12
13 import org.eclipse.jface.internal.databinding.provisional.observable.IChangeListener;
14 import org.eclipse.jface.internal.databinding.provisional.observable.IObservable;
15 import org.eclipse.jface.internal.databinding.provisional.observable.IStaleListener;
16 import org.eclipse.jface.internal.databinding.provisional.observable.ObservableTracker;
17
18 /**
19  * A Lazily calculated value that automatically computes and registers listeners
20  * on its dependencies as long as all of its dependencies are IObservable
21  * objects
22  *
23  * @since 1.0
24  */

25 public abstract class ComputedValue extends AbstractObservableValue {
26
27     private boolean dirty = true;
28
29     private boolean stale = false;
30
31     private Object JavaDoc cachedValue = null;
32
33     /**
34      * Dependencies list. This is a collection that contains no duplicates. It
35      * is normally an ArrayList to conserve memory, but if it ever grows above a
36      * certain number of elements, a HashSet is substited to conserve runtime.
37      */

38     private IObservable[] dependencies = new IObservable[0];
39
40     /**
41      *
42      */

43     public ComputedValue() {
44         this(Object JavaDoc.class);
45     }
46
47     /**
48      * @param valueType
49      */

50     public ComputedValue(Object JavaDoc valueType) {
51         this.valueType = valueType;
52     }
53
54     /**
55      * Inner class that implements interfaces that we don't want to expose as
56      * public API. Each interface could have been implemented using a separate
57      * anonymous class, but we combine them here to reduce the memory overhead
58      * and number of classes.
59      *
60      * <p>
61      * The Runnable calls computeValue and stores the result in cachedValue.
62      * </p>
63      *
64      * <p>
65      * The IUpdatableListener stores each updatable in the dependencies list.
66      * This is registered as the listener when calling ObservableTracker, to
67      * detect every updatable that is used by computeValue.
68      * </p>
69      *
70      * <p>
71      * The IChangeListener is attached to every dependency.
72      * </p>
73      *
74      */

75     private class PrivateInterface implements Runnable JavaDoc, IChangeListener,
76             IStaleListener {
77         public void run() {
78             cachedValue = calculate();
79         }
80
81         public void handleStale(IObservable source) {
82             if (!dirty && !stale) {
83                 stale = true;
84                 fireStale();
85             }
86         }
87
88         public void handleChange(IObservable source) {
89             makeDirty();
90         }
91     }
92
93     private PrivateInterface privateInterface = new PrivateInterface();
94
95     private Object JavaDoc valueType;
96
97     public final Object JavaDoc doGetValue() {
98         if (dirty) {
99             // This line will do the following:
100
// - Run the computeValue method
101
// - While doing so, add any updatable that is touched to the
102
// dependencies list
103
IObservable[] newDependencies = ObservableTracker.runAndMonitor(
104                     privateInterface, privateInterface, null);
105
106             stale = false;
107             for (int i = 0; i < newDependencies.length; i++) {
108                 IObservable observable = newDependencies[i];
109                 // Add a change listener to the new dependency.
110
if (observable.isStale()) {
111                     stale = true;
112                 } else {
113                     observable.addStaleListener(privateInterface);
114                 }
115             }
116
117             dependencies = newDependencies;
118
119             dirty = false;
120         }
121
122         return cachedValue;
123     }
124
125     /**
126      * Subclasses must override this method to provide the object's value.
127      *
128      * @return the object's value
129      */

130     protected abstract Object JavaDoc calculate();
131
132     protected final void makeDirty() {
133         if (!dirty) {
134             dirty = true;
135
136             // Stop listening for dependency changes.
137
for (int i = 0; i < dependencies.length; i++) {
138                 IObservable observable = dependencies[i];
139
140                 observable.removeChangeListener(privateInterface);
141                 observable.removeStaleListener(privateInterface);
142             }
143
144             // copy the old value
145
final Object JavaDoc oldValue = cachedValue;
146             // Fire the "dirty" event. This implementation recomputes the new
147
// value lazily.
148
fireValueChange(new ValueDiff() {
149
150                 public Object JavaDoc getOldValue() {
151                     return oldValue;
152                 }
153
154                 public Object JavaDoc getNewValue() {
155                     return getValue();
156                 }
157             });
158         }
159     }
160
161     public boolean isStale() {
162         // we need to recompute, otherwise staleness wouldn't mean anything
163
getValue();
164         return stale;
165     }
166
167     public Object JavaDoc getValueType() {
168         return valueType;
169     }
170 }
171
Popular Tags