root / branches / Mobile_Compatible_Hito_1 / libFMap / src-data / org / gvsig / data / ComplexObservable.java @ 21606
History | View | Annotate | Download (8.82 KB)
1 |
package org.gvsig.data; |
---|---|
2 |
|
3 |
import java.lang.ref.Reference; |
4 |
import java.lang.ref.WeakReference; |
5 |
import java.util.ArrayList; |
6 |
import java.util.Iterator; |
7 |
import java.util.Vector; |
8 |
|
9 |
|
10 |
public class ComplexObservable implements IObservable{ |
11 |
|
12 |
private boolean propageNotifications=true; |
13 |
private boolean inComplex=false; |
14 |
private ComplexNotification complexNotification= null; |
15 |
|
16 |
private boolean changed = false; |
17 |
private Vector obs; |
18 |
|
19 |
/** Construct an Observable with zero Observers. */
|
20 |
|
21 |
public ComplexObservable() {
|
22 |
obs = new Vector(); |
23 |
this.inComplex=false; |
24 |
} |
25 |
|
26 |
/**
|
27 |
* Adds an observer to the set of observers for this object, provided
|
28 |
* that it is not the same as some observer already in the set.
|
29 |
* The order in which notifications will be delivered to multiple
|
30 |
* observers is not specified. See the class comment.
|
31 |
*
|
32 |
* @param o an observer to be added.
|
33 |
* @throws NullPointerException if the parameter o is null.
|
34 |
*/
|
35 |
public synchronized void addObserver(IObserver o) { |
36 |
if (o == null) |
37 |
throw new NullPointerException(); |
38 |
if (!contains(o)) {
|
39 |
obs.addElement(o); |
40 |
} |
41 |
} |
42 |
|
43 |
public synchronized void addObserver(Reference ref) { |
44 |
if (ref == null || ref.get() == null) |
45 |
throw new NullPointerException(); |
46 |
IObserver o = (IObserver)ref.get(); |
47 |
if (!contains(o)) {
|
48 |
obs.addElement(ref); |
49 |
} |
50 |
} |
51 |
|
52 |
private boolean contains(IObserver o){ |
53 |
if (obs.contains(o)){
|
54 |
return true; |
55 |
} |
56 |
Iterator iter = obs.iterator();
|
57 |
Object obj;
|
58 |
Reference ref1;
|
59 |
while (iter.hasNext()){
|
60 |
obj = iter.next(); |
61 |
if (obj instanceof Reference){ |
62 |
ref1 = (Reference)obj;
|
63 |
if (o.equals(ref1.get())){
|
64 |
return true; |
65 |
} |
66 |
} |
67 |
} |
68 |
return false; |
69 |
} |
70 |
|
71 |
/**
|
72 |
* Deletes an observer from the set of observers of this object.
|
73 |
* Passing <CODE>null</CODE> to this method will have no effect.
|
74 |
* @param o the observer to be deleted.
|
75 |
*/
|
76 |
public synchronized void deleteObserver(IObserver o) { |
77 |
if (!obs.removeElement(o)){
|
78 |
this.deleteObserverReferenced(o);
|
79 |
} |
80 |
} |
81 |
|
82 |
private void deleteObserverReferenced(IObserver o){ |
83 |
Iterator iter = obs.iterator();
|
84 |
Object obj;
|
85 |
ArrayList toRemove = new ArrayList(); |
86 |
Reference ref1;
|
87 |
while (iter.hasNext()){
|
88 |
obj = iter.next(); |
89 |
if (obj instanceof Reference){ |
90 |
ref1 = (Reference)obj;
|
91 |
if (ref1.get() == null || o.equals(ref1.get())){ |
92 |
toRemove.add(obj); |
93 |
} |
94 |
} |
95 |
} |
96 |
iter = toRemove.iterator(); |
97 |
while (iter.hasNext()){
|
98 |
obs.remove(iter.next()); |
99 |
} |
100 |
} |
101 |
|
102 |
public synchronized void deleteObserver(Reference ref) { |
103 |
IObserver o = (IObserver)ref.get(); |
104 |
obs.removeElement(ref); |
105 |
if (o == null) |
106 |
return;
|
107 |
deleteObserverReferenced(o); |
108 |
} |
109 |
|
110 |
/**
|
111 |
* If this object has changed, as indicated by the
|
112 |
* <code>hasChanged</code> method, then notify all of its observers
|
113 |
* and then call the <code>clearChanged</code> method to
|
114 |
* indicate that this object has no longer changed.
|
115 |
* <p>
|
116 |
* Each observer has its <code>update</code> method called with two
|
117 |
* arguments: this observable object and <code>null</code>. In other
|
118 |
* words, this method is equivalent to:
|
119 |
* <blockquote><tt>
|
120 |
* notifyObservers(null)</tt></blockquote>
|
121 |
*
|
122 |
* @see java.util.Observable#clearChanged()
|
123 |
* @see java.util.Observable#hasChanged()
|
124 |
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
|
125 |
*/
|
126 |
public void notifyObservers() { |
127 |
throw new UnsupportedOperationException("Notify requires an notification Object"); |
128 |
} |
129 |
|
130 |
/**
|
131 |
* If this object has changed, as indicated by the
|
132 |
* <code>hasChanged</code> method, then notify all of its observers
|
133 |
* and then call the <code>clearChanged</code> method to indicate
|
134 |
* that this object has no longer changed.
|
135 |
* <p>
|
136 |
* Each observer has its <code>update</code> method called with two
|
137 |
* arguments: this observable object and the <code>arg</code> argument.
|
138 |
*
|
139 |
* @param arg any object.
|
140 |
* @see java.util.Observable#clearChanged()
|
141 |
* @see java.util.Observable#hasChanged()
|
142 |
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
|
143 |
*/
|
144 |
public void notifyObservers(Object arg) { |
145 |
if (!this.inComplex){ |
146 |
this.setChanged();
|
147 |
notify(arg); |
148 |
}else{
|
149 |
complexNotification.addNotification(arg); |
150 |
} |
151 |
|
152 |
} |
153 |
|
154 |
/**
|
155 |
* Clears the observer list so that this object no longer has any observers.
|
156 |
*/
|
157 |
public synchronized void deleteObservers() { |
158 |
obs.removeAllElements(); |
159 |
} |
160 |
|
161 |
/**
|
162 |
* Marks this <tt>Observable</tt> object as having been changed; the
|
163 |
* <tt>hasChanged</tt> method will now return <tt>true</tt>.
|
164 |
*/
|
165 |
protected synchronized void setChanged() { |
166 |
changed = true;
|
167 |
} |
168 |
|
169 |
/**
|
170 |
* Indicates that this object has no longer changed, or that it has
|
171 |
* already notified all of its observers of its most recent change,
|
172 |
* so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
|
173 |
* This method is called automatically by the
|
174 |
* <code>notifyObservers</code> methods.
|
175 |
*
|
176 |
* @see java.util.Observable#notifyObservers()
|
177 |
* @see java.util.Observable#notifyObservers(java.lang.Object)
|
178 |
*/
|
179 |
protected synchronized void clearChanged() { |
180 |
changed = false;
|
181 |
} |
182 |
|
183 |
/**
|
184 |
* Tests if this object has changed.
|
185 |
*
|
186 |
* @return <code>true</code> if and only if the <code>setChanged</code>
|
187 |
* method has been called more recently than the
|
188 |
* <code>clearChanged</code> method on this object;
|
189 |
* <code>false</code> otherwise.
|
190 |
* @see java.util.Observable#clearChanged()
|
191 |
* @see java.util.Observable#setChanged()
|
192 |
*/
|
193 |
public synchronized boolean hasChanged() { |
194 |
return changed;
|
195 |
} |
196 |
|
197 |
/**
|
198 |
* Returns the number of observers of this <tt>Observable</tt> object.
|
199 |
*
|
200 |
* @return the number of observers of this object.
|
201 |
*/
|
202 |
public synchronized int countObservers() { |
203 |
clearDeadReferences(); |
204 |
return obs.size();
|
205 |
} |
206 |
|
207 |
public void enableNotifications(){ |
208 |
clearDeadReferences(); |
209 |
this.propageNotifications =true; |
210 |
} |
211 |
|
212 |
public void diableNotifications(){ |
213 |
this.propageNotifications =false; |
214 |
} |
215 |
|
216 |
public boolean isEnabledNotifications(){ |
217 |
return this.propageNotifications; |
218 |
} |
219 |
|
220 |
public boolean inComplex(){ |
221 |
return this.inComplex; |
222 |
} |
223 |
|
224 |
public void beginComplexNotification(){ |
225 |
clearDeadReferences(); |
226 |
this.clearChanged();
|
227 |
inComplex=true;
|
228 |
complexNotification=new ComplexNotification();
|
229 |
} |
230 |
|
231 |
public void endComplexNotification(){ |
232 |
inComplex=false;
|
233 |
this.setChanged();
|
234 |
|
235 |
Iterator iter=complexNotification.getIterator();
|
236 |
while(iter.hasNext()){
|
237 |
notify(iter.next()); |
238 |
} |
239 |
} |
240 |
|
241 |
protected synchronized void clearDeadReferences(){ |
242 |
Iterator iter = obs.iterator();
|
243 |
Object obj;
|
244 |
ArrayList toRemove = new ArrayList(); |
245 |
Reference ref1;
|
246 |
while (iter.hasNext()){
|
247 |
obj = iter.next(); |
248 |
if (obj instanceof Reference){ |
249 |
ref1 = (Reference)obj;
|
250 |
if (ref1.get() == null){ |
251 |
toRemove.add(obj); |
252 |
} |
253 |
} |
254 |
} |
255 |
iter = toRemove.iterator(); |
256 |
while (iter.hasNext()){
|
257 |
obs.remove(iter.next()); |
258 |
} |
259 |
|
260 |
} |
261 |
|
262 |
private void notify(Object object) { |
263 |
/*
|
264 |
* a temporary array buffer, used as a snapshot of the state of
|
265 |
* current Observers.
|
266 |
*/
|
267 |
Object[] arrLocal; |
268 |
|
269 |
synchronized (this) { |
270 |
/* We don't want the Observer doing callbacks into
|
271 |
* arbitrary code while holding its own Monitor.
|
272 |
* The code where we extract each Observable from
|
273 |
* the Vector and store the state of the Observer
|
274 |
* needs synchronization, but notifying observers
|
275 |
* does not (should not). The worst result of any
|
276 |
* potential race-condition here is that:
|
277 |
* 1) a newly-added Observer will miss a
|
278 |
* notification in progress
|
279 |
* 2) a recently unregistered Observer will be
|
280 |
* wrongly notified when it doesn't care
|
281 |
*/
|
282 |
if (!changed)
|
283 |
return;
|
284 |
arrLocal = obs.toArray(); |
285 |
clearChanged(); |
286 |
} |
287 |
|
288 |
Object obj;
|
289 |
IObserver observer; |
290 |
ArrayList toRemove = new ArrayList(); |
291 |
for (int i = arrLocal.length-1; i>=0; i--){ |
292 |
obj = arrLocal[i]; |
293 |
if (obj instanceof Reference){ |
294 |
observer = (IObserver)((Reference)obj).get();
|
295 |
if (observer == null){ |
296 |
toRemove.add(obj); |
297 |
continue;
|
298 |
} |
299 |
} else {
|
300 |
observer = (IObserver)obj; |
301 |
} |
302 |
observer.update(this, object);
|
303 |
} |
304 |
|
305 |
Iterator iter = toRemove.iterator();
|
306 |
while (iter.hasNext()){
|
307 |
obs.remove(iter.next()); |
308 |
} |
309 |
|
310 |
} |
311 |
} |