Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libTools / src / org / gvsig / tools / observer / impl / BaseWeakReferencingObservable.java @ 27337

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.Observer;
37
import org.gvsig.tools.observer.WeakReferencingObservable;
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

    
206

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

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

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

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

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

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

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

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

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

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

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