Statistics
| Revision:

gvsig-tools / org.gvsig.tools / library / trunk / org.gvsig.tools / org.gvsig.tools.lib / src / main / java / org / gvsig / tools / observer / impl / BaseWeakReferencingObservable.java @ 695

History | View | Annotate | Download (12.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.slf4j.Logger;
36
import org.slf4j.LoggerFactory;
37

    
38
import org.gvsig.tools.observer.ComplexObserver;
39
import org.gvsig.tools.observer.ComplexWeakReferencingObservable;
40
import org.gvsig.tools.observer.Observer;
41
import org.gvsig.tools.observer.WeakReferencingObservable;
42
import org.gvsig.tools.visitor.Visitor;
43

    
44
/**
45
 * Implementation for observers based on the use of Reference, instead of direct
46
 * Observer references.
47
 *
48
 * This way an Observer may be Garbagge collected if its unique reference is the
49
 * one registered into the Observable.
50
 *
51
 * @author <a href="mailto:josemanuel.vivo@iver.es">Jos? Manuel Viv?</a>
52
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
53
 */
54
public class BaseWeakReferencingObservable implements
55
                ComplexWeakReferencingObservable, Cloneable {
56
    private static final Logger LOG = 
57
        LoggerFactory.getLogger(BaseWeakReferencingObservable.class);
58

    
59
    private boolean propageNotifications = true;
60
    private DefaultComplexNotification complexNotification = null;
61

    
62
    private boolean changed = false;
63
    private List obs = new ArrayList();
64

    
65
        private int inComplexCount = 0;
66

    
67
    /**
68
     * Adds an observer to the set of observers for this object, provided that
69
     * it is not the same as some observer already in the set. The order in
70
     * which notifications will be delivered to multiple observers is not
71
     * specified. See the class comment.
72
     *
73
     * @param observer
74
     *            an observer to be added.
75
     * @throws NullPointerException
76
     *             if the parameter o is null.
77
     */
78
    public void addObserver(Observer observer) {
79
        addObserver(new WeakReference(observer));
80
    }
81

    
82
    public void addObserver(Reference ref) {
83
        if (ref == null || ref.get() == null) {
84
            throw new NullPointerException();
85
        }
86
        Observer observer = (Observer) ref.get();
87
        synchronized (obs) {
88
            if (!contains(observer)) {
89
                obs.add(ref);
90
            }
91
        }
92
    }
93

    
94
    public void addObservers(BaseWeakReferencingObservable observable) {
95
        observable.clearDeadReferences();
96
        Iterator iter = observable.obs.iterator();
97
        while (iter.hasNext()) {
98
            addObserver((Reference) iter.next());
99
        }
100

    
101
    }
102

    
103
    /**
104
     * Deletes an observer from the set of observers of this object. Passing
105
     * <CODE>null</CODE> to this method will have no effect.
106
     *
107
     * @param observer
108
     *            the observer to be deleted.
109
     */
110
    public void deleteObserver(Observer observer) {
111
        deleteObserverReferenced(observer);
112
    }
113

    
114
    public void deleteObserver(Reference ref) {
115
        synchronized (obs) {
116
            obs.remove(ref);
117
        }
118
        deleteObserverReferenced((Observer) ref.get());
119
    }
120

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

    
141
    /**
142
     * If this object has changed, as indicated by the <code>hasChanged</code>
143
     * method, then notify all of its observers and then call the
144
     * <code>clearChanged</code> method to indicate that this object has no
145
     * longer changed.
146
     * <p>
147
     * Each observer has its <code>update</code> method called with two
148
     * arguments: this observable object and the <code>arg</code> argument.                                        public void visit(Object obj)
149
                                                        throws VisitCanceledException, BaseException {
150
                                                // TODO Auto-generated method stub
151
                                                
152
                                        }
153
     *
154
     * @param arg
155
     *            any object.
156
     * @see java.util.Observable#clearChanged()
157
     * @see java.util.Observable#hasChanged()
158
     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
159
     */
160
    public void notifyObservers(Object arg) {
161
        if (!inComplex()) {
162
            setChanged();
163
            notify(this, arg);
164
        } else {
165
            complexNotification.addNotification(arg);
166
        }
167

    
168
    }
169

    
170
    /**
171
     * Clears the observer list so that this object no longer has any observers.
172
     */
173
    public void deleteObservers() {
174
        synchronized (obs) {
175
            obs.clear();
176
        }
177
    }
178

    
179
    /**
180
     * Returns the number of observers of this object.
181
     *
182
     * @return the number of observers of this object.
183
     */
184
    public int countObservers() {
185
        clearDeadReferences();
186
        synchronized (obs) {
187
            return obs.size();
188
        }
189
    }
190

    
191
    public void enableNotifications() {
192
        clearDeadReferences();
193
        propageNotifications = true;
194
    }
195

    
196
    public void disableNotifications() {
197
        propageNotifications = false;
198
    }
199

    
200
    public boolean isEnabledNotifications() {
201
        return propageNotifications;
202
    }
203

    
204
    public boolean inComplex() {
205
                return inComplexCount > 0;
206
    }
207

    
208
    public void beginComplexNotification() {
209
                if (inComplexCount == 0) {
210
                        clearDeadReferences();
211
                        clearChanged();
212
                        complexNotification = new DefaultComplexNotification();
213
                }
214
                inComplexCount++;
215
    }
216

    
217
    public void endComplexNotification() {
218
                if (inComplexCount > 0) {
219
                        if (inComplexCount == 1) {
220
                setChanged();
221
                notify(this, complexNotification);
222
                                complexNotification = null;
223
                        }
224
                        inComplexCount--;
225
                }
226
    }
227

    
228
        public Object clone() throws CloneNotSupportedException {
229
                BaseWeakReferencingObservable clone =
230
                                (BaseWeakReferencingObservable) super.clone();
231

    
232
                // Clone observers list
233
                if (countObservers() > 0) {
234
                        clone.setObservers(getObservers());
235
                }
236
                
237
                return clone;
238
        }
239

    
240
        private void setObservers(List observers) {
241
                this.obs = new ArrayList(observers);
242
        }
243

    
244
        private List getObservers() {
245
                return obs;
246
        }
247

    
248
    protected void notify(final WeakReferencingObservable observable,
249
        final Object notification) {
250
        if (!isEnabledNotifications()) {
251
            return;
252
        }
253

    
254
        /*
255
         * a temporary array buffer, used as a snapshot of the state of current
256
         * Observers.
257
         */
258
        Object[] arrLocal;
259

    
260
        synchronized (obs) {
261
            /*
262
             * We don't want the Observer doing callbacks into arbitrary code
263
             * while holding its own Monitor. The code where we extract each
264
             * DefaultObservable from the Vector and store the state of the
265
             * Observer needs synchronization, but notifying observers does not
266
             * (should not). The worst result of any potential race-condition
267
             * here is that: 1) a newly-added Observer will miss a notification
268
             * in progress 2) a recently unregistered Observer will be wrongly
269
             * notified when it doesn't care
270
             */
271
            if (!changed) {
272
                return;
273
            }
274
            arrLocal = obs.toArray();
275
            clearChanged();
276
        }
277

    
278
        // Go through all the registered observers and call update on them.
279
        // If any of the Reference is null, remove it from the list.
280
        for (int i = arrLocal.length - 1; i >= 0; i--) {
281
            final Observer observer =
282
                (Observer) ((Reference) arrLocal[i]).get();
283
            if (observer == null) {
284
                obs.remove(arrLocal[i]);
285
            } else {
286
                //If there is an error updating an observer, the exception 
287
                //has to be catched and next observer has to be updated. 
288
                //This exception is written in the log but would be fine to send 
289
                //a message to the user
290
                try{
291
                    // If the notification is complex, complex observers
292
                    // are able to handle it.
293
                    if (notification instanceof DefaultComplexNotification
294
                        && !(observer instanceof ComplexObserver)) {
295

    
296
                        ((DefaultComplexNotification) notification).accept(new Visitor() {
297

    
298
                            public void visit(Object simpleNotification) {
299
                                setChanged();
300
                                observer.update(observable, simpleNotification);
301
                            }
302
                        });
303
                    } else {
304
                        observer.update(observable, notification);
305
                    }
306
                }catch (Exception e){
307
                    LOG.error("Error notifying the observer", e);
308
                }
309
            }
310
        }
311
    }
312

    
313
    /**
314
     * Tests if this object has changed.
315
     *
316
     * @return <code>true</code> if and only if the <code>setChanged</code>
317
     *         method has been called more recently than the
318
     *         <code>clearChanged</code> method on this object;
319
     *         <code>false</code> otherwise.
320
     * @see java.util.Observable#clearChanged()
321
     * @see java.util.Observable#setChanged()
322
     */
323
    protected boolean hasChanged() {
324
        return changed;
325
    }
326

    
327
    /**
328
     * Marks this <tt>DefaultObservable</tt> object as having been changed; the
329
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
330
     */
331
    protected void setChanged() {
332
        changed = true;
333
    }
334

    
335
    /**
336
     * Indicates that this object has no longer changed, or that it has already
337
     * notified all of its observers of its most recent change, so that the
338
     * <tt>hasChanged</tt> method will now return <tt>false</tt>. This method is
339
     * called automatically by the <code>notifyObservers</code> methods.
340
     *
341
     * @see java.util.Observable#notifyObservers()
342
     * @see java.util.Observable#notifyObservers(java.lang.Object)
343
     */
344
    protected void clearChanged() {
345
        changed = false;
346
    }
347

    
348
    private void clearDeadReferences() {
349
        synchronized (obs) {
350
            Iterator iter = obs.iterator();
351
            while (iter.hasNext()) {
352
                Object obj = iter.next();
353
                if (obj instanceof Reference && ((Reference) obj).get() == null) {
354
                    iter.remove();
355
                }
356
            }
357
        }
358
    }
359

    
360
    private boolean contains(Observer observer) {
361
        synchronized (obs) {
362
            Iterator iter = obs.iterator();
363
            while (iter.hasNext()) {
364
                Object obj = iter.next();
365
                if (obj instanceof Reference) {
366
                    Object value = ((Reference) obj).get();
367
                    if (observer.equals(value)) {
368
                        return true;
369
                    }
370
                }
371
            }
372
        }
373
        return false;
374
    }
375

    
376
    private void deleteObserverReferenced(Observer observer) {
377
        if (observer == null) {
378
            return;
379
        }
380
        synchronized (obs) {
381
            Iterator iter = obs.iterator();
382
            while (iter.hasNext()) {
383
                Object obj = iter.next();
384
                if (obj instanceof Reference) {
385
                    Object value = ((Reference) obj).get();
386
                    if (value == null || observer.equals(value)) {
387
                        iter.remove();
388
                    }
389
                }
390
            }
391
        }
392
    }
393
}