Statistics
| Revision:

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
}