(no commit message)
[utils] / support / general / src / main / java / org / wamblee / observer / Observable.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.observer;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.TreeMap;
22
23 /**
24  * Implements subscription and notification logic for an observer pattern. This
25  * class is thread safe.
26  */
27 public class Observable<ObservableType, Event> {
28     /**
29      * Observable.
30      */
31     private ObservableType observable;
32
33     /**
34      * Used to notify observers.
35      */
36     private ObserverNotifier<ObservableType, Event> notifier;
37
38     /**
39      * Map of subscription to observer.
40      */
41     private Map<Long, Observer<ObservableType, Event>> observers;
42
43     /**
44      * Counter for subscriptions. Holds the next subscription.
45      */
46     private long counter;
47
48     /**
49      * Constructs the observable.
50      * 
51      * @param aObservable
52      *            Observable this instance is used for.
53      * @param aNotifier
54      *            Object used for implementing notification of listeners.
55      */
56     public Observable(ObservableType aObservable,
57         ObserverNotifier<ObservableType, Event> aNotifier) {
58         observable = aObservable;
59         notifier = aNotifier;
60         observers = new TreeMap<Long, Observer<ObservableType, Event>>();
61         counter = 0;
62     }
63
64     /**
65      * Subscribe an obvers.
66      * 
67      * @param aObserver
68      *            Observer to subscribe.
69      * @return Event Event to send.
70      */
71     public synchronized long subscribe(Observer<ObservableType, Event> aObserver) {
72         long subscription = counter++; // integer rage is so large it will
73         // never roll over.
74
75         observers.put(subscription, aObserver);
76
77         return subscription;
78     }
79
80     /**
81      * Unsubscribe an observer.
82      * 
83      * @param aSubscription
84      *            Subscription which is used
85      * @throws IllegalArgumentException
86      *             In case the subscription is not known.
87      */
88     public synchronized void unsubscribe(long aSubscription) {
89         Object obj = observers.remove(aSubscription);
90
91         if (obj == null) {
92             throw new IllegalArgumentException("Subscription '" +
93                 aSubscription + "'");
94         }
95     }
96
97     /**
98      * Gets the number of subscribed observers.
99      * 
100      * @return Number of subscribed observers.
101      */
102     public int getObserverCount() {
103         return observers.size();
104     }
105
106     /**
107      * Notifies all subscribed observers.
108      * 
109      * @param aEvent
110      *            Event to send.
111      */
112     public void send(Event aEvent) {
113         // Make sure we do the notification while not holding the lock to avoid
114         // potential deadlock
115         // situations.
116         List<Observer<ObservableType, Event>> myObservers = new ArrayList<Observer<ObservableType, Event>>();
117
118         synchronized (this) {
119             myObservers.addAll(observers.values());
120         }
121
122         for (Observer<ObservableType, Event> observer : myObservers) {
123             notifier.update(observer, observable, aEvent);
124         }
125     }
126 }