Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libTools / src / org / gvsig / tools / observer / DefaultObservable.java @ 23063

History | View | Annotate | Download (9.46 KB)

1
package org.gvsig.tools.observer;
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
//TODO Actualizar Javadoc... sobretodo que se almacenan WeakRef en vez de referencias
10

    
11
public class DefaultObservable implements Observable{
12

    
13
        private boolean propageNotifications=true;
14
        private boolean inComplex=false;
15
        private ComplexNotification complexNotification= null;
16

    
17
        private boolean changed = false;
18
    private Vector obs;
19

    
20
    /** Construct an DefaultObservable with zero Observers. */
21

    
22
    public DefaultObservable() {
23
                obs = new Vector();
24
                this.inComplex=false;
25
    }
26

    
27
    /**
28
     * Adds an observer to the set of observers for this object, provided
29
     * that it is not the same as some observer already in the set.
30
     * The order in which notifications will be delivered to multiple
31
     * observers is not specified. See the class comment.
32
     *
33
     * @param   o   an observer to be added.
34
     * @throws NullPointerException   if the parameter o is null.
35
     */
36
    public synchronized void addObserver(Observer o) {
37
        this.addObserver(new WeakReference(o));
38

    
39
    }
40

    
41
    public synchronized void addObserver(Reference ref) {
42
        if (ref == null || ref.get() == null)
43
            throw new NullPointerException();
44
        Observer o = (Observer)ref.get();
45
                if (!contains(o)) {
46
                    obs.addElement(ref);
47
                }
48
    }
49

    
50
    public synchronized void addObservers(DefaultObservable o){
51
            Iterator iter = o.obs.iterator();
52
            o.clearDeadReferences();
53
            while (iter.hasNext()){
54
                    this.addObserver((Reference) iter.next());
55
            }
56

    
57
    }
58

    
59
    private boolean contains(Observer o){
60
            if (obs.contains(o)){
61
                    return true;
62
            }
63
        Iterator iter = obs.iterator();
64
        Object obj;
65
        Reference ref1;
66
        while (iter.hasNext()){
67
                obj = iter.next();
68
                if (obj instanceof Reference){
69
                        ref1 = (Reference)obj;
70
                        if (o.equals(ref1.get())){
71
                                return true;
72
                        }
73
                }
74
        }
75
        return false;
76
    }
77

    
78
    /**
79
     * Deletes an observer from the set of observers of this object.
80
     * Passing <CODE>null</CODE> to this method will have no effect.
81
     * @param   o   the observer to be deleted.
82
     */
83
    public synchronized void deleteObserver(Observer o) {
84
        if (!obs.removeElement(o)){
85
                this.deleteObserverReferenced(o);
86
        }
87
    }
88

    
89
    private void deleteObserverReferenced(Observer o){
90
        Iterator iter = obs.iterator();
91
        Object obj;
92
        ArrayList toRemove = new ArrayList();
93
        Reference ref1;
94
        while (iter.hasNext()){
95
                obj = iter.next();
96
                if (obj instanceof Reference){
97
                        ref1 = (Reference)obj;
98
                        if (ref1.get() == null || o.equals(ref1.get())){
99
                                toRemove.add(obj);
100
                        }
101
                }
102
        }
103
        iter = toRemove.iterator();
104
        while (iter.hasNext()){
105
                obs.remove(iter.next());
106
        }
107
    }
108

    
109
    public synchronized void deleteObserver(Reference ref) {
110
            Observer o = (Observer)ref.get();
111
        obs.removeElement(ref);
112
        if (o == null)
113
                return;
114
        deleteObserverReferenced(o);
115
    }
116

    
117
    /**
118
     * If this object has changed, as indicated by the
119
     * <code>hasChanged</code> method, then notify all of its observers
120
     * and then call the <code>clearChanged</code> method to
121
     * indicate that this object has no longer changed.
122
     * <p>
123
     * Each observer has its <code>update</code> method called with two
124
     * arguments: this observable object and <code>null</code>. In other
125
     * words, this method is equivalent to:
126
     * <blockquote><tt>
127
     * notifyObservers(null)</tt></blockquote>
128
     *
129
     * @see     java.util.Observable#clearChanged()
130
     * @see     java.util.Observable#hasChanged()
131
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
132
     */
133
    public void notifyObservers() {
134
            throw new UnsupportedOperationException("Notify requires an notification Object");
135
    }
136

    
137
    /**
138
     * If this object has changed, as indicated by the
139
     * <code>hasChanged</code> method, then notify all of its observers
140
     * and then call the <code>clearChanged</code> method to indicate
141
     * that this object has no longer changed.
142
     * <p>
143
     * Each observer has its <code>update</code> method called with two
144
     * arguments: this observable object and the <code>arg</code> argument.
145
     *
146
     * @param   arg   any object.
147
     * @see     java.util.Observable#clearChanged()
148
     * @see     java.util.Observable#hasChanged()
149
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
150
     */
151
    public void notifyObservers(Observable observable,Object arg) {
152
            if (!this.inComplex){
153
                        this.setChanged();
154
                        notify(observable,arg);
155
                }else{
156
                        complexNotification.addNotification(arg);
157
                }
158

    
159
    }
160

    
161
    /**
162
     * Clears the observer list so that this object no longer has any observers.
163
     */
164
    public synchronized void deleteObservers() {
165
            obs.removeAllElements();
166
    }
167

    
168
    /**
169
     * Marks this <tt>DefaultObservable</tt> object as having been changed; the
170
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
171
     */
172
    protected synchronized void setChanged() {
173
            changed = true;
174
    }
175

    
176
    /**
177
     * Indicates that this object has no longer changed, or that it has
178
     * already notified all of its observers of its most recent change,
179
     * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
180
     * This method is called automatically by the
181
     * <code>notifyObservers</code> methods.
182
     *
183
     * @see     java.util.Observable#notifyObservers()
184
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
185
     */
186
    protected synchronized void clearChanged() {
187
            changed = false;
188
    }
189

    
190
    /**
191
     * Tests if this object has changed.
192
     *
193
     * @return  <code>true</code> if and only if the <code>setChanged</code>
194
     *          method has been called more recently than the
195
     *          <code>clearChanged</code> method on this object;
196
     *          <code>false</code> otherwise.
197
     * @see     java.util.Observable#clearChanged()
198
     * @see     java.util.Observable#setChanged()
199
     */
200
    public synchronized boolean hasChanged() {
201
            return changed;
202
    }
203

    
204
    /**
205
     * Returns the number of observers of this <tt>DefaultObservable</tt> object.
206
     *
207
     * @return  the number of observers of this object.
208
     */
209
    public synchronized int countObservers() {
210
            clearDeadReferences();
211
            return obs.size();
212
    }
213

    
214
        public void enableNotifications(){
215
                clearDeadReferences();
216
                this.propageNotifications =true;
217
        }
218

    
219
        public void diableNotifications(){
220
                this.propageNotifications =false;
221
        }
222

    
223
        public boolean isEnabledNotifications(){
224
                return this.propageNotifications;
225
        }
226

    
227
        public boolean inComplex(){
228
                return this.inComplex;
229
        }
230

    
231
        public void beginComplexNotification(ComplexNotification complex){
232
                clearDeadReferences();
233
                this.clearChanged();
234
                inComplex=true;
235
                complexNotification=complex;
236
                complexNotification.clear();
237
        }
238

    
239
        public void endComplexNotification(Observable observable){
240
                inComplex=false;
241
                this.setChanged();
242

    
243
                Iterator iter=complexNotification.getIterator();
244
                while(iter.hasNext()){
245
                        notify(observable,iter.next());
246
                }
247
                complexNotification = null;
248
        }
249

    
250
        protected synchronized void clearDeadReferences(){
251
        Iterator iter = obs.iterator();
252
        Object obj;
253
        ArrayList toRemove = new ArrayList();
254
        Reference ref1;
255
        while (iter.hasNext()){
256
                obj = iter.next();
257
                if (obj instanceof Reference){
258
                        ref1 = (Reference)obj;
259
                        if (ref1.get() == null){
260
                                toRemove.add(obj);
261
                        }
262
                }
263
        }
264
        iter = toRemove.iterator();
265
        while (iter.hasNext()){
266
                obs.remove(iter.next());
267
        }
268

    
269
        }
270

    
271
        private void notify(Observable observable, Object object) {
272
                /*
273
         * a temporary array buffer, used as a snapshot of the state of
274
         * current Observers.
275
         */
276
        Object[] arrLocal;
277

    
278
        synchronized (this) {
279
            /* We don't want the Observer doing callbacks into
280
             * arbitrary code while holding its own Monitor.
281
             * The code where we extract each DefaultObservable from
282
             * the Vector and store the state of the Observer
283
             * needs synchronization, but notifying observers
284
             * does not (should not).  The worst result of any
285
             * potential race-condition here is that:
286
             * 1) a newly-added Observer will miss a
287
             *   notification in progress
288
             * 2) a recently unregistered Observer will be
289
             *   wrongly notified when it doesn't care
290
             */
291
            if (!changed)
292
                return;
293
            arrLocal = obs.toArray();
294
            clearChanged();
295
        }
296

    
297
                Object obj;
298
                Observer observer;
299
                ArrayList toRemove = new ArrayList();
300
        for (int i = arrLocal.length-1; i>=0; i--){
301
                obj = arrLocal[i];
302
                observer = (Observer)((Reference)obj).get();
303
                if (observer == null){
304
                        toRemove.add(obj);
305
                        continue;
306
                }
307
            observer.update(observable, object);
308
        }
309

    
310
        Iterator iter = toRemove.iterator();
311
        while (iter.hasNext()){
312
                obs.remove(iter.next());
313
        }
314

    
315
        }
316
}