1/*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions, and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32// ObservableHandler.cpp
33
34#include "ObservableHandler.h"
35
36#include <Debug.h>
37#include <Looper.h>
38
39__USE_CORTEX_NAMESPACE
40
41// ---------------------------------------------------------------- //
42// *** deletion
43// ---------------------------------------------------------------- //
44
45// clients must call release() rather than deleting,
46// to ensure that all observers are notified of the
47// object's demise.  if the object has already been
48// released, return an error.
49
50status_t ObservableHandler::release() {
51	if(m_released)
52		return B_NOT_ALLOWED;
53
54//	PRINT((
55//		"ObservableHandler::release(): %ld targets\n", CountTargets()));
56
57	if(!LockLooper()) {
58		ASSERT(!"failed to lock looper");
59	}
60
61	m_released = true;
62
63	if(CountTargets()) {
64		// notify
65		notifyRelease();
66		UnlockLooper();
67	}
68	else {
69		releaseComplete();
70		UnlockLooper();
71		delete this;
72	}
73
74	return B_OK;
75}
76
77// ---------------------------------------------------------------- //
78// *** ctor/dtor
79// ---------------------------------------------------------------- //
80
81ObservableHandler::~ObservableHandler() {
82	if(CountTargets()) {
83		PRINT((
84			"*** ~ObservableHandler() '%s': %" B_PRId32 " observers remain\n",
85			Name(), CountTargets()));
86	}
87}
88
89ObservableHandler::ObservableHandler(
90	const char*							name) :
91	BHandler(name),
92	m_released(false) {}
93
94ObservableHandler::ObservableHandler(
95	BMessage*								archive) :
96	BHandler(archive),
97	m_released(false) {}
98
99// ---------------------------------------------------------------- //
100// *** accessors
101// ---------------------------------------------------------------- //
102
103// return true if release() has been called, false otherwise.
104bool ObservableHandler::isReleased() const {
105	return m_released;
106}
107
108// ---------------------------------------------------------------- //
109// *** hooks
110// ---------------------------------------------------------------- //
111
112// sends M_OBSERVER_ADDED to the newly-added observer
113void ObservableHandler::observerAdded(
114	const BMessenger&				observer) {
115
116	BMessage m(M_OBSERVER_ADDED);
117	m.AddMessenger("target", BMessenger(this));
118	observer.SendMessage(&m);
119}
120
121// sends M_OBSERVER_REMOVED to the newly-removed observer
122void ObservableHandler::observerRemoved(
123	const BMessenger&				observer) {
124
125	BMessage m(M_OBSERVER_REMOVED);
126	m.AddMessenger("target", BMessenger(this));
127	observer.SendMessage(&m);
128}
129
130// ---------------------------------------------------------------- //
131// *** internal operations
132// ---------------------------------------------------------------- //
133
134// call to send the given message to all observers.
135// Responsibility for deletion of the message remains with
136// the caller.
137
138status_t ObservableHandler::notify(
139	BMessage*								message) {
140#if DEBUG
141	BLooper* l = Looper();
142	ASSERT(l);
143	ASSERT(l->IsLocked());
144#endif
145
146	return Invoke(message);
147}
148
149// sends M_RELEASE_OBSERVABLE
150void ObservableHandler::notifyRelease() {
151	BMessage m(M_RELEASE_OBSERVABLE);
152	m.AddMessenger("target", BMessenger(this));
153	notify(&m);
154}
155
156// ---------------------------------------------------------------- //
157// *** BHandler
158// ---------------------------------------------------------------- //
159
160void ObservableHandler::MessageReceived(
161	BMessage*								message) {
162
163//	PRINT((
164//		"### ObservableHandler::MessageReceived()\n"));
165//	message->PrintToStream();
166
167	switch(message->what) {
168		case M_ADD_OBSERVER:
169			_handleAddObserver(message);
170			break;
171
172		case M_REMOVE_OBSERVER:
173			_handleRemoveObserver(message);
174			break;
175
176		case M_KILL_OBSERVABLE:
177			// +++++ this should be an optional feature
178			releaseComplete();
179			delete this; // BOOM!
180			break;
181
182		default:
183			_inherited::MessageReceived(message);
184	}
185}
186
187// ---------------------------------------------------------------- //
188// *** BArchivable
189// ---------------------------------------------------------------- //
190
191status_t ObservableHandler::Archive(
192	BMessage*								archive,
193	bool										deep) const {
194
195#if DEBUG
196	BLooper* l = Looper();
197	ASSERT(l);
198	ASSERT(l->IsLocked());
199#endif
200	if(m_released)
201		return B_NOT_ALLOWED; // can't archive a dead object
202
203	return _inherited::Archive(archive, deep);
204}
205
206// ---------------------------------------------------------------- //
207// implementation
208// ---------------------------------------------------------------- //
209
210void ObservableHandler::_handleAddObserver(
211	BMessage*								message) {
212
213#if DEBUG
214	BLooper* l = Looper();
215	ASSERT(l);
216	ASSERT(l->IsLocked());
217#endif
218	BMessage reply;
219
220	BMessenger observer;
221	status_t err = message->FindMessenger(
222		"observer", &observer);
223	if(err < B_OK) {
224		PRINT((
225			"* ObservableHandler::_handleAddObserver(): no observer specified!\n"));
226		// send reply? +++++
227		return;
228	}
229
230	if(m_released) {
231		// already quitting
232		reply.what = M_BAD_TARGET;
233		reply.AddMessenger("target", BMessenger(this));
234		reply.AddMessenger("observer", observer);
235		message->SendReply(&reply);
236
237		return;
238	}
239	else if(IndexOfTarget(observer.Target(0)) != -1) {
240		// observer already added
241		reply.what = M_BAD_OBSERVER;
242		reply.AddMessenger("target", BMessenger(this));
243		reply.AddMessenger("observer", observer);
244		message->SendReply(&reply);
245
246		return;
247	}
248
249	// valid observer given
250
251	// add it
252	err = AddTarget(observer.Target(0));
253	ASSERT(err == B_OK);
254
255	// call hook
256	observerAdded(observer);
257}
258
259void ObservableHandler::_handleRemoveObserver(
260	BMessage*								message) {
261
262#if DEBUG
263	BLooper* l = Looper();
264	ASSERT(l);
265	ASSERT(l->IsLocked());
266#endif
267	BMessage reply;
268
269	BMessenger observer;
270	status_t err = message->FindMessenger(
271		"observer", &observer);
272	if(err < B_OK) {
273		PRINT((
274			"* ObservableHandler::_handleRemoveObserver(): no observer specified!\n"));
275		// send reply? +++++
276		return;
277	}
278
279	int32 index = IndexOfTarget(observer.Target(0));
280	if(index == -1) {
281		reply.what = M_BAD_OBSERVER;
282
283		reply.AddMessenger("target", BMessenger(this));
284		reply.AddMessenger("observer", observer);
285		message->SendReply(&reply);
286		return;
287	}
288
289	// valid observer given; remove it & call notification hook
290	RemoveTarget(index);
291	observerRemoved(observer);
292
293	// time to shut down?
294	if(m_released && !CountTargets()) {
295		releaseComplete();
296		delete this; // BOOM!
297	}
298}
299
300
301// END -- ObservableHandler.cpp --
302