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 23170 jmvivo
/* 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 24178 cordinyana
package org.gvsig.tools.observer.impl;
28 23063 jmvivo
29
import java.lang.ref.Reference;
30
import java.lang.ref.WeakReference;
31
import java.util.ArrayList;
32
import java.util.Iterator;
33 24178 cordinyana
import java.util.List;
34 23063 jmvivo
35 24267 jjdelcerro
import org.gvsig.tools.observer.ComplexWeakReferencingObservable;
36 27337 jmvivo
import org.gvsig.tools.observer.Observer;
37 24267 jjdelcerro
import org.gvsig.tools.observer.WeakReferencingObservable;
38 23063 jmvivo
39 24178 cordinyana
/**
40
 * Implementation for observers based on the use of Reference, instead of direct
41
 * Observer references.
42 27337 jmvivo
 *
43 24178 cordinyana
 * This way an Observer may be Garbagge collected if its unique reference is the
44
 * one registered into the Observable.
45 27337 jmvivo
 *
46 24178 cordinyana
 * @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 24267 jjdelcerro
public class BaseWeakReferencingObservable implements ComplexWeakReferencingObservable {
50 23063 jmvivo
51 24178 cordinyana
    private boolean propageNotifications = true;
52
    private boolean inComplex = false;
53
    private ComplexNotification complexNotification = null;
54 23063 jmvivo
55 24178 cordinyana
    private boolean changed = false;
56
    private List obs = new ArrayList();
57 23063 jmvivo
58
    /**
59 24178 cordinyana
     * 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 27337 jmvivo
     *
64 24178 cordinyana
     * @param observer
65
     *            an observer to be added.
66
     * @throws NullPointerException
67
     *             if the parameter o is null.
68 23063 jmvivo
     */
69 24178 cordinyana
    public void addObserver(Observer observer) {
70
        addObserver(new WeakReference(observer));
71 23063 jmvivo
    }
72
73 24178 cordinyana
    public void addObserver(Reference ref) {
74 23170 jmvivo
        if (ref == null || ref.get() == null) {
75 24178 cordinyana
            throw new NullPointerException();
76
        }
77
        Observer observer = (Observer) ref.get();
78
        synchronized (obs) {
79
            if (!contains(observer)) {
80
                obs.add(ref);
81
            }
82
        }
83 23063 jmvivo
    }
84
85 24267 jjdelcerro
    public void addObservers(BaseWeakReferencingObservable observable) {
86 24178 cordinyana
        observable.clearDeadReferences();
87
        Iterator iter = observable.obs.iterator();
88
        while (iter.hasNext()) {
89
            addObserver((Reference) iter.next());
90
        }
91 23063 jmvivo
92
    }
93
94
    /**
95 24178 cordinyana
     * Deletes an observer from the set of observers of this object. Passing
96
     * <CODE>null</CODE> to this method will have no effect.
97 27337 jmvivo
     *
98 24178 cordinyana
     * @param observer
99
     *            the observer to be deleted.
100 23063 jmvivo
     */
101 24178 cordinyana
    public void deleteObserver(Observer observer) {
102
        deleteObserverReferenced(observer);
103 23063 jmvivo
    }
104
105 24178 cordinyana
    public void deleteObserver(Reference ref) {
106
        synchronized (obs) {
107
            obs.remove(ref);
108 23063 jmvivo
        }
109 24178 cordinyana
        deleteObserverReferenced((Observer) ref.get());
110 23063 jmvivo
    }
111
112
    /**
113 24178 cordinyana
     * 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 23063 jmvivo
     * <p>
118
     * Each observer has its <code>update</code> method called with two
119 24178 cordinyana
     * 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 27337 jmvivo
     *
124 24178 cordinyana
     * @see java.util.Observable#clearChanged()
125
     * @see java.util.Observable#hasChanged()
126
     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
127 23063 jmvivo
     */
128
    public void notifyObservers() {
129 24178 cordinyana
        notifyObservers(null);
130 23063 jmvivo
    }
131
132
    /**
133 24178 cordinyana
     * 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 23063 jmvivo
     * <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 27337 jmvivo
     *
141 24178 cordinyana
     * @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 23063 jmvivo
     */
147 24178 cordinyana
    public void notifyObservers(Object arg) {
148
        if (!inComplex()) {
149
            setChanged();
150
            notify(this, arg);
151
        } else {
152
            complexNotification.addNotification(arg);
153
        }
154 23063 jmvivo
155
    }
156
157
    /**
158
     * Clears the observer list so that this object no longer has any observers.
159
     */
160 24178 cordinyana
    public void deleteObservers() {
161
        synchronized (obs) {
162
            obs.clear();
163
        }
164 23063 jmvivo
    }
165
166
    /**
167 24178 cordinyana
     * Returns the number of observers of this object.
168 27337 jmvivo
     *
169 24178 cordinyana
     * @return the number of observers of this object.
170 23063 jmvivo
     */
171 24178 cordinyana
    public int countObservers() {
172
        clearDeadReferences();
173
        synchronized (obs) {
174
            return obs.size();
175
        }
176 23063 jmvivo
    }
177
178 24178 cordinyana
    public void enableNotifications() {
179
        clearDeadReferences();
180
        propageNotifications = true;
181 23063 jmvivo
    }
182
183 24178 cordinyana
    public void disableNotifications() {
184
        propageNotifications = false;
185 23063 jmvivo
    }
186
187 24178 cordinyana
    public boolean isEnabledNotifications() {
188
        return propageNotifications;
189 23063 jmvivo
    }
190
191 24178 cordinyana
    public boolean inComplex() {
192
        return inComplex;
193
    }
194 23063 jmvivo
195 24178 cordinyana
    public void beginComplexNotification() {
196
        clearDeadReferences();
197
        clearChanged();
198
        inComplex = true;
199
        complexNotification = new ComplexNotification();
200
        complexNotification.clear();
201
    }
202 23063 jmvivo
203 24178 cordinyana
    public void endComplexNotification() {
204
        inComplex = false;
205 23063 jmvivo
206 27337 jmvivo
207 24178 cordinyana
        Iterator iter = complexNotification.getIterator();
208
        while (iter.hasNext()) {
209 27337 jmvivo
                setChanged();
210 24178 cordinyana
            notify(this, iter.next());
211
        }
212
        complexNotification = null;
213
    }
214 23063 jmvivo
215 24267 jjdelcerro
    protected void notify(WeakReferencingObservable observable, Object object) {
216 24178 cordinyana
        if (!isEnabledNotifications()) {
217
            return;
218 23063 jmvivo
        }
219
220 24178 cordinyana
        /*
221
         * a temporary array buffer, used as a snapshot of the state of current
222
         * Observers.
223 23063 jmvivo
         */
224
        Object[] arrLocal;
225
226 24178 cordinyana
        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 23063 jmvivo
            arrLocal = obs.toArray();
241
            clearChanged();
242
        }
243
244 24178 cordinyana
        // 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 23063 jmvivo
        }
254 24178 cordinyana
    }
255 23063 jmvivo
256 24178 cordinyana
    /**
257
     * Tests if this object has changed.
258 27337 jmvivo
     *
259 24178 cordinyana
     * @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 27337 jmvivo
     *
284 24178 cordinyana
     * @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 23063 jmvivo
        }
301 24178 cordinyana
    }
302 23063 jmvivo
303 24178 cordinyana
    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
}