KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > databinding > observable > ObservableTracker


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.core.databinding.observable;
12
13 import java.util.HashSet JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.Set JavaDoc;
16
17 import org.eclipse.core.internal.databinding.IdentityWrapper;
18 import org.eclipse.core.runtime.Assert;
19
20 /**
21  * This class makes it possible to monitor whenever an IObservable is read from.
22  * This can be used to automatically attach and remove listeners. How to use it:
23  *
24  * <p>
25  * If you are implementing an IObservable, invoke getterCalled(this) whenever a
26  * getter is called - that is, whenever your observable is read from. You only
27  * need to do this once per method call. If one getter delegates to another, the
28  * outer getter doesn't need to call the method since the inner one will.
29  * </p>
30  *
31  * <p>
32  * If you want to determine what observables were used in a particular block of
33  * code, call runAndMonitor(Runnable). This will execute the given runnable and
34  * return the set of observables that were read from.
35  * </p>
36  *
37  * <p>
38  * This can be used to automatically attach listeners. For example, imagine you
39  * have a block of code that updates some widget by reading from a bunch of
40  * observables. Whenever one of those observables changes, you want to re-run
41  * the code and cause the widget to be refreshed. You could do this in the
42  * traditional manner by attaching one listener to each observable and
43  * re-running your widget update code whenever one of them changes, but this
44  * code is repetitive and requires updating the listener code whenever you
45  * refactor the widget updating code.
46  * </p>
47  *
48  * <p>
49  * Alternatively, you could use a utility class that runs the code in a
50  * runAndMonitor block and automatically attach listeners to any observable used
51  * in updating the widget. The advantage of the latter approach is that it,
52  * eliminates the code for attaching and detaching listeners and will always
53  * stay in synch with changes to the widget update logic.
54  * </p>
55  *
56  * @since 1.0
57  */

58 public class ObservableTracker {
59
60     /**
61      * Threadlocal storage pointing to the current Set of IObservables, or null
62      * if none. Note that this is actually the top of a stack. Whenever a method
63      * changes the current value, it remembers the old value as a local variable
64      * and restores the old value when the method exits.
65      */

66     private static ThreadLocal JavaDoc currentChangeListener = new ThreadLocal JavaDoc();
67
68     private static ThreadLocal JavaDoc currentStaleListener = new ThreadLocal JavaDoc();
69
70     private static ThreadLocal JavaDoc currentObservableSet = new ThreadLocal JavaDoc();
71
72     /**
73      * Invokes the given runnable, and returns the set of IObservables that were
74      * read by the runnable. If the runnable calls this method recursively, the
75      * result will not contain IObservables that were used within the inner
76      * runnable.
77      *
78      * @param runnable
79      * runnable to execute
80      * @param changeListener
81      * listener to register with all accessed observables
82      * @param staleListener
83      * listener to register with all accessed observables, or
84      * <code>null</code> if no stale listener is to be registered
85      * @return an array of unique observable objects
86      */

87     public static IObservable[] runAndMonitor(Runnable JavaDoc runnable,
88             IChangeListener changeListener, IStaleListener staleListener) {
89         // Remember the previous value in the listener stack
90
Set JavaDoc lastObservableSet = (Set JavaDoc) currentObservableSet.get();
91         IChangeListener lastChangeListener = (IChangeListener) currentChangeListener
92                 .get();
93         IStaleListener lastStaleListener = (IStaleListener) currentStaleListener
94                 .get();
95
96         Set JavaDoc observableSet = new HashSet JavaDoc();
97         // Push the new listeners to the top of the stack
98
currentObservableSet.set(observableSet);
99         currentChangeListener.set(changeListener);
100         currentStaleListener.set(staleListener);
101         try {
102             runnable.run();
103         } finally {
104             // Pop the new listener off the top of the stack (by restoring the
105
// previous listener)
106
currentObservableSet.set(lastObservableSet);
107             currentChangeListener.set(lastChangeListener);
108             currentStaleListener.set(lastStaleListener);
109         }
110
111         int i = 0;
112         IObservable[] result = new IObservable[observableSet.size()];
113         for (Iterator JavaDoc it = observableSet.iterator(); it.hasNext();) {
114             IdentityWrapper wrapper = (IdentityWrapper) it.next();
115             result[i++] = (IObservable) wrapper.unwrap();
116         }
117
118         return result;
119     }
120
121     /**
122      * Notifies the ObservableTracker that an observable was read from. The
123      * JavaDoc for methods that invoke this method should include the following
124      * tag: "@TrackedGetter This method will notify ObservableTracker that the
125      * receiver has been read from". This lets callers know that they can rely
126      * on automatic updates from the object without explicitly attaching a
127      * listener.
128      *
129      * @param observable
130      */

131     public static void getterCalled(IObservable observable) {
132         Assert.isTrue(observable.getRealm().isCurrent());
133         Set JavaDoc lastObservableSet = (Set JavaDoc) currentObservableSet.get();
134         IChangeListener lastChangeListener = (IChangeListener) currentChangeListener
135                 .get();
136         IStaleListener lastStaleListener = (IStaleListener) currentStaleListener
137                 .get();
138
139         boolean added = false;
140         if (lastObservableSet != null) {
141             added = lastObservableSet.add(new IdentityWrapper(observable));
142         }
143
144         // If anyone is listening for observable usage...
145
if (added && lastChangeListener != null) {
146             observable.addChangeListener(lastChangeListener);
147         }
148         if (added && lastStaleListener != null) {
149             observable.addStaleListener(lastStaleListener);
150         }
151     }
152 }
153
Popular Tags