Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libTools / src / org / gvsig / tools / observer / impl / BaseWeakReferencingObservable.java @ 24267

History | View | Annotate | Download (10.9 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 IVER T.I. S.A.   {{Task}}
26
 */
27
package org.gvsig.tools.observer.impl;
28

    
29
import java.lang.ref.Reference;
30
import java.lang.ref.WeakReference;
31
import java.util.ArrayList;
32
import java.util.Iterator;
33
import java.util.List;
34

    
35
import org.gvsig.tools.observer.ComplexWeakReferencingObservable;
36
import org.gvsig.tools.observer.WeakReferencingObservable;
37
import org.gvsig.tools.observer.Observer;
38

    
39
/**
40
 * Implementation for observers based on the use of Reference, instead of direct
41
 * Observer references.
42
 * 
43
 * This way an Observer may be Garbagge collected if its unique reference is the
44
 * one registered into the Observable.
45
 * 
46
 * @author <a href="mailto:josemanuel.vivo@iver.es">Jos? Manuel Viv?</a>
47
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
48
 */
49
public class BaseWeakReferencingObservable implements ComplexWeakReferencingObservable {
50

    
51
    private boolean propageNotifications = true;
52
    private boolean inComplex = false;
53
    private ComplexNotification complexNotification = null;
54

    
55
    private boolean changed = false;
56
    private List obs = new ArrayList();
57

    
58
    /**
59
     * Adds an observer to the set of observers for this object, provided that
60
     * it is not the same as some observer already in the set. The order in
61
     * which notifications will be delivered to multiple observers is not
62
     * specified. See the class comment.
63
     * 
64
     * @param observer
65
     *            an observer to be added.
66
     * @throws NullPointerException
67
     *             if the parameter o is null.
68
     */
69
    public void addObserver(Observer observer) {
70
        addObserver(new WeakReference(observer));
71
    }
72

    
73
    public void addObserver(Reference ref) {
74
        if (ref == null || ref.get() == null) {
75
            throw new NullPointerException();
76
        }
77
        Observer observer = (Observer) ref.get();
78
        synchronized (obs) {
79
            if (!contains(observer)) {
80
                obs.add(ref);
81
            }
82
        }
83
    }
84

    
85
    public void addObservers(BaseWeakReferencingObservable observable) {
86
        observable.clearDeadReferences();
87
        Iterator iter = observable.obs.iterator();
88
        while (iter.hasNext()) {
89
            addObserver((Reference) iter.next());
90
        }
91

    
92
    }
93

    
94
    /**
95
     * Deletes an observer from the set of observers of this object. Passing
96
     * <CODE>null</CODE> to this method will have no effect.
97
     * 
98
     * @param observer
99
     *            the observer to be deleted.
100
     */
101
    public void deleteObserver(Observer observer) {
102
        deleteObserverReferenced(observer);
103
    }
104

    
105
    public void deleteObserver(Reference ref) {
106
        synchronized (obs) {
107
            obs.remove(ref);
108
        }
109
        deleteObserverReferenced((Observer) ref.get());
110
    }
111

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

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

    
155
    }
156

    
157
    /**
158
     * Clears the observer list so that this object no longer has any observers.
159
     */
160
    public void deleteObservers() {
161
        synchronized (obs) {
162
            obs.clear();
163
        }
164
    }
165

    
166
    /**
167
     * Returns the number of observers of this object.
168
     * 
169
     * @return the number of observers of this object.
170
     */
171
    public int countObservers() {
172
        clearDeadReferences();
173
        synchronized (obs) {
174
            return obs.size();
175
        }
176
    }
177

    
178
    public void enableNotifications() {
179
        clearDeadReferences();
180
        propageNotifications = true;
181
    }
182

    
183
    public void disableNotifications() {
184
        propageNotifications = false;
185
    }
186

    
187
    public boolean isEnabledNotifications() {
188
        return propageNotifications;
189
    }
190

    
191
    public boolean inComplex() {
192
        return inComplex;
193
    }
194

    
195
    public void beginComplexNotification() {
196
        clearDeadReferences();
197
        clearChanged();
198
        inComplex = true;
199
        complexNotification = new ComplexNotification();
200
        complexNotification.clear();
201
    }
202

    
203
    public void endComplexNotification() {
204
        inComplex = false;
205
        setChanged();
206

    
207
        Iterator iter = complexNotification.getIterator();
208
        while (iter.hasNext()) {
209
            notify(this, iter.next());
210
        }
211
        complexNotification = null;
212
    }
213

    
214
    protected void notify(WeakReferencingObservable observable, Object object) {
215
        if (!isEnabledNotifications()) {
216
            return;
217
        }
218

    
219
        /*
220
         * a temporary array buffer, used as a snapshot of the state of current
221
         * Observers.
222
         */
223
        Object[] arrLocal;
224

    
225
        synchronized (obs) {
226
            /*
227
             * We don't want the Observer doing callbacks into arbitrary code
228
             * while holding its own Monitor. The code where we extract each
229
             * DefaultObservable from the Vector and store the state of the
230
             * Observer needs synchronization, but notifying observers does not
231
             * (should not). The worst result of any potential race-condition
232
             * here is that: 1) a newly-added Observer will miss a notification
233
             * in progress 2) a recently unregistered Observer will be wrongly
234
             * notified when it doesn't care
235
             */
236
            if (!changed) {
237
                return;
238
            }
239
            arrLocal = obs.toArray();
240
            clearChanged();
241
        }
242

    
243
        // Go through all the registered observers and call update on them.
244
        // If any of the Reference is null, remove it from the list.
245
        for (int i = arrLocal.length - 1; i >= 0; i--) {
246
            Observer observer = (Observer) ((Reference) arrLocal[i]).get();
247
            if (observer == null) {
248
                obs.remove(arrLocal[i]);
249
            } else {
250
                observer.update(observable, object);
251
            }
252
        }
253
    }
254

    
255
    /**
256
     * Tests if this object has changed.
257
     * 
258
     * @return <code>true</code> if and only if the <code>setChanged</code>
259
     *         method has been called more recently than the
260
     *         <code>clearChanged</code> method on this object;
261
     *         <code>false</code> otherwise.
262
     * @see java.util.Observable#clearChanged()
263
     * @see java.util.Observable#setChanged()
264
     */
265
    protected boolean hasChanged() {
266
        return changed;
267
    }
268

    
269
    /**
270
     * Marks this <tt>DefaultObservable</tt> object as having been changed; the
271
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
272
     */
273
    protected void setChanged() {
274
        changed = true;
275
    }
276

    
277
    /**
278
     * Indicates that this object has no longer changed, or that it has already
279
     * notified all of its observers of its most recent change, so that the
280
     * <tt>hasChanged</tt> method will now return <tt>false</tt>. This method is
281
     * called automatically by the <code>notifyObservers</code> methods.
282
     * 
283
     * @see java.util.Observable#notifyObservers()
284
     * @see java.util.Observable#notifyObservers(java.lang.Object)
285
     */
286
    protected void clearChanged() {
287
        changed = false;
288
    }
289

    
290
    private void clearDeadReferences() {
291
        synchronized (obs) {
292
            Iterator iter = obs.iterator();
293
            while (iter.hasNext()) {
294
                Object obj = iter.next();
295
                if (obj instanceof Reference && ((Reference) obj).get() == null) {
296
                    iter.remove();
297
                }
298
            }
299
        }
300
    }
301

    
302
    private boolean contains(Observer observer) {
303
        synchronized (obs) {
304
            Iterator iter = obs.iterator();
305
            while (iter.hasNext()) {
306
                Object obj = iter.next();
307
                if (obj instanceof Reference) {
308
                    Object value = ((Reference) obj).get();
309
                    if (observer.equals(value)) {
310
                        return true;
311
                    }
312
                }
313
            }
314
        }
315
        return false;
316
    }
317

    
318
    private void deleteObserverReferenced(Observer observer) {
319
        if (observer == null) {
320
            return;
321
        }
322
        synchronized (obs) {
323
            Iterator iter = obs.iterator();
324
            while (iter.hasNext()) {
325
                Object obj = iter.next();
326
                if (obj instanceof Reference) {
327
                    Object value = ((Reference) obj).get();
328
                    if (value == null || observer.equals(value)) {
329
                        iter.remove();
330
                    }
331
                }
332
            }
333
        }
334
    }
335
}