KickJava   Java API By Example, From Geeks To Geeks.

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

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

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

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

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