View Javadoc

1   /*
2    * @(#)EventProducer.java April 4, 2003 Copyright (c) 2002-2005 Delft University
3    * of Technology Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights
4    * reserved. This software is proprietary information of Delft University of
5    * Technology The code is published under the Lesser General Public License
6    */
7   package nl.tudelft.simulation.event;
8   
9   import java.io.IOException;
10  import java.io.ObjectOutputStream;
11  import java.io.Serializable;
12  import java.lang.reflect.Field;
13  import java.rmi.RemoteException;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.Collections;
17  import java.util.HashMap;
18  import java.util.Iterator;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Set;
22  
23  import nl.tudelft.simulation.event.ref.Reference;
24  import nl.tudelft.simulation.event.ref.StrongReference;
25  import nl.tudelft.simulation.event.ref.WeakReference;
26  
27  /***
28   * The EventProducer forms the reference implementation of the
29   * EventProducerInterface. Objects extending this class are provided all the
30   * functionalities for registration and event firering.
31   * <p>
32   * (c) copyright 2002-2005 <a href="http://www.simulation.tudelft.nl">Delft
33   * University of Technology </a>, the Netherlands.
34   * <p>
35   * See for project information <a
36   * href="http://www.simulation.tudelft.nl/dsol/event">www.simulation.tudelft.nl/event
37   * </a> <br>
38   * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
39   * General Public License (LGPL) </a>, no warranty
40   * 
41   * @author <a href="http://www.peter-jacobs.com">Peter Jacobs </a>
42   * @version $Revision: 1.8 $ $Date: 2005/08/04 12:08:32 $
43   * @since 1.5
44   */
45  public abstract class EventProducer implements EventProducerInterface,
46  		Serializable {
47  	/*** listeners is the collection of interested listeners */
48  	protected Map<EventType, Reference<EventListenerInterface>[]> listeners = Collections
49  			.synchronizedMap(new EventListenerMap<EventType, Reference<EventListenerInterface>[]>());
50  
51  	/***
52  	 * the semaphore used to lock on while performing thread sensitive
53  	 * operations.
54  	 */
55  	private transient Object semaphore = new Object();
56  
57  	/*** the cache to prevent continuous reflection */
58  	private transient EventType[] cache = null;
59  
60  	/***
61  	 * checks whether no duplicate short values are assigned to the producer. An
62  	 * eventproducer produces events of a certain eventType. This eventType
63  	 * functions as a marker for registration. If the eventProducer defines two
64  	 * eventTypes with an equal value, the marker function is lost. This method
65  	 * checks for this particular problem.
66  	 * 
67  	 * @return returns whether every eventType in this class is unique.
68  	 */
69  	private boolean checkEventType() {
70  		EventType[] events = this.getEventTypes();
71  		for (int i = 0; i < events.length; i++) {
72  			for (int j = 0; j < events.length; j++) {
73  				if (i != j && events[i].equals(events[j])) {
74  					return false;
75  				}
76  			}
77  		}
78  		return true;
79  	}
80  
81  	/***
82  	 * constructs a new EventProducer and checks for double values in events
83  	 */
84  	public EventProducer() {
85  		super();
86  		if (!this.checkEventType()) {
87  			throw new RuntimeException("EventProducer failed: "
88  					+ "more events have the same short value");
89  		}
90  	}
91  
92  	/***
93  	 * adds the listener as weak reference to the listener.
94  	 * 
95  	 * @see nl.tudelft.simulation.event.EventProducerInterface
96  	 *      #addListener(EventListenerInterface, EventType)
97  	 */
98  	public synchronized boolean addListener(
99  			final EventListenerInterface listener, final EventType eventType) {
100 		return this.addListener(listener, eventType,
101 				EventProducerInterface.FIRST_POSITION);
102 	}
103 
104 	/***
105 	 * @see nl.tudelft.simulation.event.EventProducerInterface
106 	 *      #addListener(nl.tudelft.simulation.event.EventListenerInterface,
107 	 *      nl.tudelft.simulation.event.EventType, boolean)
108 	 */
109 	public synchronized boolean addListener(
110 			final EventListenerInterface listener, final EventType eventType,
111 			final boolean weak) {
112 		return this.addListener(listener, eventType,
113 				EventProducerInterface.FIRST_POSITION, weak);
114 	}
115 
116 	/***
117 	 * adds the listener as weak reference to the listener.
118 	 * 
119 	 * @see nl.tudelft.simulation.event.EventProducerInterface
120 	 *      #addListener(nl.tudelft.simulation.event.EventListenerInterface,
121 	 *      nl.tudelft.simulation.event.EventType, short)
122 	 */
123 	public synchronized boolean addListener(
124 			final EventListenerInterface listener, final EventType eventType,
125 			final short position) {
126 		if (listener == null || position < EventProducerInterface.LAST_POSITION) {
127 			return false;
128 		}
129 		return this.addListener(listener, eventType, position, false);
130 	}
131 
132 	/***
133 	 * @see nl.tudelft.simulation.event.EventProducerInterface
134 	 *      #addListener(nl.tudelft.simulation.event.EventListenerInterface,
135 	 *      nl.tudelft.simulation.event.EventType, short, boolean)
136 	 */
137 	@SuppressWarnings("unchecked")
138 	public synchronized boolean addListener(
139 			final EventListenerInterface listener, final EventType eventType,
140 			final short position, final boolean weak) {
141 		if (listener == null || position < EventProducerInterface.LAST_POSITION) {
142 			return false;
143 		}
144 		synchronized (this.semaphore) {
145 			Reference<EventListenerInterface> reference = null;
146 			if (!weak) {
147 				reference = new StrongReference<EventListenerInterface>(
148 						listener);
149 			} else {
150 				reference = new WeakReference<EventListenerInterface>(listener);
151 			}
152 			if (this.listeners.containsKey(eventType)) {
153 				Reference<EventListenerInterface>[] entries = this.listeners
154 						.get(eventType);
155 				for (int i = 0; i < entries.length; i++) {
156 					if (listener.equals(entries[i].get())) {
157 						return false;
158 					}
159 				}
160 				List<Reference<EventListenerInterface>> entriesArray = new ArrayList<Reference<EventListenerInterface>>(
161 						Arrays.asList(entries));
162 				if (position == EventProducerInterface.LAST_POSITION) {
163 					entriesArray.add(reference);
164 				} else {
165 					entriesArray.add(position, reference);
166 				}
167 				this.listeners.put(eventType, entriesArray
168 						.toArray(new Reference[entriesArray.size()]));
169 			} else {
170 				Reference[] entries = { reference };
171 				this.listeners.put(eventType, entries);
172 			}
173 		}
174 		return true;
175 	}
176 
177 	/***
178 	 * fires the event to the listener. This method is a hook method. The
179 	 * default implementation simply invokes the notify on the listener. In
180 	 * specific cases (filtering, storing, queuing, this method can be
181 	 * overwritten.
182 	 * 
183 	 * @param listener
184 	 *            the listener for this event
185 	 * @param event
186 	 *            the event to fire
187 	 * @return the event
188 	 * @throws RemoteException
189 	 *             on network failure.
190 	 */
191 	protected synchronized EventInterface fireEvent(
192 			final EventListenerInterface listener, final EventInterface event)
193 			throws RemoteException {
194 		listener.notify(event);
195 		return event;
196 	}
197 
198 	/***
199 	 * fires an event to subscribed listeners.
200 	 * 
201 	 * @param event
202 	 *            the event.
203 	 * @return the event.
204 	 */
205 	protected synchronized EventInterface fireEvent(final EventInterface event) {
206 		if (this.listeners.containsKey(event.getType())) {
207 			synchronized (this.semaphore) {
208 				Reference<EventListenerInterface>[] entries = this.listeners
209 						.get(event.getType());
210 				for (int i = 0; i < entries.length; i++) {
211 					EventListenerInterface listener = entries[i].get();
212 					try {
213 						if (listener != null) {
214 							// The garbage collection has not cleaned the
215 							// referent
216 							this.fireEvent(listener, event);
217 						} else {
218 							// The garbage collection cleaned the referent;
219 							// there is no need to keep the subscription
220 							this.removeListener(entries[i], event.getType());
221 						}
222 					} catch (RemoteException remoteException) {
223 						// A network failure prevented the delivery,
224 						// subscription is removed.
225 						this.removeListener(entries[i], event.getType());
226 					}
227 				}
228 			}
229 		}
230 		return event;
231 	}
232 
233 	/***
234 	 * fires a byte value to subscribed listeners subscribed to eventType.
235 	 * 
236 	 * @param eventType
237 	 *            the eventType of the event.
238 	 * @param value
239 	 *            the value of the event.
240 	 * @return the byte value.
241 	 */
242 	protected synchronized byte fireEvent(final EventType eventType,
243 			final byte value) {
244 		this.fireEvent(eventType, new Byte(value));
245 		return value;
246 	}
247 
248 	/***
249 	 * fires a boolean value to subscribed listeners subscribed to eventType.
250 	 * 
251 	 * @param eventType
252 	 *            the eventType of the event.
253 	 * @param value
254 	 *            the value of the event.
255 	 * @return the byte value.
256 	 */
257 	protected synchronized boolean fireEvent(final EventType eventType,
258 			final boolean value) {
259 		this.fireEvent(eventType, new Boolean(value));
260 		return value;
261 	}
262 
263 	/***
264 	 * fires a byte value to subscribed listeners subscribed to eventType. A
265 	 * timed event is fired.
266 	 * 
267 	 * @param eventType
268 	 *            the eventType of the event.
269 	 * @param value
270 	 *            the value of the event.
271 	 * @param time
272 	 *            a timestamp for the event.
273 	 * @return the byte value.
274 	 */
275 	protected synchronized byte fireEvent(final EventType eventType,
276 			final byte value, final double time) {
277 		this.fireEvent(eventType, new Byte(value), time);
278 		return value;
279 	}
280 
281 	/***
282 	 * fires a boolean value to subscribed listeners subscribed to eventType. A
283 	 * timed event is fired.
284 	 * 
285 	 * @param eventType
286 	 *            the eventType of the event.
287 	 * @param value
288 	 *            the value of the event.
289 	 * @param time
290 	 *            a timestamp for the event.
291 	 * @return the byte value.
292 	 */
293 	protected synchronized boolean fireEvent(final EventType eventType,
294 			final boolean value, final double time) {
295 		this.fireEvent(eventType, new Boolean(value), time);
296 		return value;
297 	}
298 
299 	/***
300 	 * fires a double value to subscribed listeners subscribed to eventType.
301 	 * 
302 	 * @param eventType
303 	 *            the eventType of the event.
304 	 * @param value
305 	 *            the value of the event.
306 	 * @return the double value.
307 	 */
308 	protected synchronized double fireEvent(final EventType eventType,
309 			final double value) {
310 		this.fireEvent(eventType, new Double(value));
311 		return value;
312 	}
313 
314 	/***
315 	 * fires a double value to subscribed listeners subscribed to eventType. A
316 	 * timed event is fired.
317 	 * 
318 	 * @param eventType
319 	 *            the eventType of the event.
320 	 * @param value
321 	 *            the value of the event.
322 	 * @param time
323 	 *            a timestamp for the event.
324 	 * @return the double value.
325 	 */
326 	protected synchronized double fireEvent(final EventType eventType,
327 			final double value, final double time) {
328 		this.fireEvent(eventType, new Double(value), time);
329 		return value;
330 	}
331 
332 	/***
333 	 * fires an integer value to subscribed listeners subscribed to eventType.
334 	 * 
335 	 * @param eventType
336 	 *            the eventType of the event.
337 	 * @param value
338 	 *            the value of the event.
339 	 * @return the integer value.
340 	 */
341 	protected synchronized int fireEvent(final EventType eventType,
342 			final int value) {
343 		this.fireEvent(eventType, new Integer(value));
344 		return value;
345 	}
346 
347 	/***
348 	 * fires an integer value to subscribed listeners subscribed to eventType. A
349 	 * timed event is fired.
350 	 * 
351 	 * @param eventType
352 	 *            the eventType of the event.
353 	 * @param value
354 	 *            the value of the event.
355 	 * @param time
356 	 *            a timestamp for the event.
357 	 * @return the integer value.
358 	 */
359 	protected synchronized int fireEvent(final EventType eventType,
360 			final int value, final double time) {
361 		this.fireEvent(eventType, new Integer(value), time);
362 		return value;
363 	}
364 
365 	/***
366 	 * fires a long value to subscribed listeners subscribed to eventType.
367 	 * 
368 	 * @param eventType
369 	 *            the eventType of the event.
370 	 * @param value
371 	 *            the value of the event.
372 	 * @return the long value.
373 	 */
374 	protected synchronized long fireEvent(final EventType eventType,
375 			final long value) {
376 		this.fireEvent(eventType, new Long(value));
377 		return value;
378 	}
379 
380 	/***
381 	 * fires a long value to subscribed listeners subscribed to eventType. A
382 	 * timed event is fired.
383 	 * 
384 	 * @param eventType
385 	 *            the eventType of the event.
386 	 * @param value
387 	 *            the value of the event.
388 	 * @param time
389 	 *            a timestamp for the event.
390 	 * @return the long value.
391 	 */
392 	protected synchronized long fireEvent(final EventType eventType,
393 			final long value, final double time) {
394 		this.fireEvent(eventType, new Long(value), time);
395 		return value;
396 	}
397 
398 	/***
399 	 * fires a value to subscribed listeners subscribed to eventType.
400 	 * 
401 	 * @param eventType
402 	 *            the eventType of the event.
403 	 * @param value
404 	 *            the value of the event.
405 	 * @return the Serializable value.
406 	 */
407 	protected synchronized Object fireEvent(final EventType eventType,
408 			final Object value) {
409 		this.fireEvent(new Event(eventType, this, value));
410 		return value;
411 	}
412 
413 	/***
414 	 * fires a Serializable value to subscribed listeners subscribed to
415 	 * eventType. A timed event is fired.
416 	 * 
417 	 * @param eventType
418 	 *            the eventType of the event.
419 	 * @param value
420 	 *            the value of the event.
421 	 * @param time
422 	 *            a timestamp for the event.
423 	 * @return the Serializable value.
424 	 */
425 	protected synchronized Object fireEvent(final EventType eventType,
426 			final Object value, final double time) {
427 		this.fireEvent(new TimedEvent(eventType, this, value, time));
428 		return value;
429 	}
430 
431 	/***
432 	 * fires a short value to subscribed listeners subscribed to eventType.
433 	 * 
434 	 * @param eventType
435 	 *            the eventType of the event.
436 	 * @param value
437 	 *            the value of the event.
438 	 * @return the short value.
439 	 */
440 	protected synchronized short fireEvent(final EventType eventType,
441 			final short value) {
442 		this.fireEvent(eventType, new Short(value));
443 		return value;
444 	}
445 
446 	/***
447 	 * fires a short value to subscribed listeners subscribed to eventType. A
448 	 * timed event is fired.
449 	 * 
450 	 * @param eventType
451 	 *            the eventType of the event.
452 	 * @param value
453 	 *            the value of the event.
454 	 * @param time
455 	 *            a timestamp for the event.
456 	 * @return the short value.
457 	 */
458 	protected synchronized short fireEvent(final EventType eventType,
459 			final short value, final double time) {
460 		this.fireEvent(eventType, new Short(value), time);
461 		return value;
462 	}
463 
464 	/***
465 	 * @see nl.tudelft.simulation.event.EventProducerInterface#getEventTypes()
466 	 */
467 	public synchronized EventType[] getEventTypes() {
468 		if (this.cache != null) {
469 			return this.cache;
470 		}
471 		List<Field> fieldList = new ArrayList<Field>();
472 		Class<?> clazz = this.getClass();
473 		while (clazz != null) {
474 			Field[] declaredFields = clazz.getDeclaredFields();
475 			for (int i = 0; i < declaredFields.length; i++) {
476 				fieldList.add(declaredFields[i]);
477 			}
478 			clazz = clazz.getSuperclass();
479 		}
480 		Field[] fields = fieldList.toArray(new Field[fieldList.size()]);
481 		Map<String, Object> result = new HashMap<String, Object>();
482 		for (int i = 0; i < fields.length; i++) {
483 			if (fields[i].getType().equals(EventType.class)) {
484 				fields[i].setAccessible(true);
485 				try {
486 					if (!result.containsKey(fields[i].getName())) {
487 						result.put(fields[i].getName(), fields[i].get(this));
488 					}
489 				} catch (Exception exception) {
490 					exception.printStackTrace();
491 				}
492 			}
493 		}
494 		this.cache = result.values().toArray(new EventType[result.size()]);
495 		return this.cache;
496 	}
497 
498 	/***
499 	 * removes all the listeners from the producer.
500 	 * 
501 	 * @return int the amount of removed listeners.
502 	 */
503 	protected synchronized int removeAllListeners() {
504 		int result = this.listeners.size();
505 		this.listeners = null;
506 		this.listeners = Collections
507 				.synchronizedMap(new EventListenerMap<EventType, Reference<EventListenerInterface>[]>());
508 		return result;
509 	}
510 
511 	/***
512 	 * removes all the listeners of a class.
513 	 * 
514 	 * @param ofClass
515 	 *            the class or superclass.
516 	 * @return the number of listeners which were removed.
517 	 */
518 	protected synchronized int removeAllListeners(final Class ofClass) {
519 		Map<EventType, Reference<EventListenerInterface>[]> temp = new HashMap<EventType, Reference<EventListenerInterface>[]>(
520 				this.listeners);
521 		int result = 0;
522 		synchronized (this.semaphore) {
523 			Set<EventType> keys = temp.keySet();
524 			for (Iterator<EventType> i = keys.iterator(); i.hasNext();) {
525 				EventType type = i.next();
526 				List<Reference<EventListenerInterface>> list = Arrays
527 						.asList(this.listeners.get(type));
528 				for (Iterator<Reference<EventListenerInterface>> ii = list
529 						.iterator(); ii.hasNext();) {
530 					Reference<EventListenerInterface> listener = ii.next();
531 					if (listener.getClass().isAssignableFrom(ofClass)) {
532 						this.removeListener(listener.get(), type);
533 						result++;
534 					}
535 				}
536 			}
537 		}
538 		return result;
539 	}
540 
541 	/***
542 	 * @see nl.tudelft.simulation.event.EventProducerInterface
543 	 *      #removeListener(EventListenerInterface, EventType)
544 	 */
545 	@SuppressWarnings("unchecked")
546 	public synchronized boolean removeListener(
547 			final EventListenerInterface listener, final EventType eventType) {
548 		if (!this.listeners.containsKey(eventType)) {
549 			return false;
550 		}
551 		boolean result = false;
552 		synchronized (this.semaphore) {
553 			Reference<EventListenerInterface>[] entries = this.listeners
554 					.get(eventType);
555 			List<Reference<EventListenerInterface>> list = new ArrayList<Reference<EventListenerInterface>>(
556 					Arrays.asList(entries));
557 			for (Iterator<Reference<EventListenerInterface>> i = list
558 					.iterator(); i.hasNext();) {
559 				Reference<EventListenerInterface> reference = i.next();
560 				EventListenerInterface entrie = reference.get();
561 				if (entrie == null) {
562 					i.remove();
563 				} else {
564 					if (listener.equals(entrie)) {
565 						i.remove();
566 						result = true;
567 					}
568 				}
569 			}
570 			this.listeners.put(eventType, list.toArray(new Reference[list
571 					.size()]));
572 			if (list.size() == 0) {
573 				this.listeners.remove(eventType);
574 			}
575 		}
576 		return result;
577 	}
578 
579 	/***
580 	 * removes a reference from the subscription list
581 	 * 
582 	 * @param reference
583 	 *            the reference to remove
584 	 * @param eventType
585 	 *            the eventType for which reference must be removed
586 	 * @return success whenever the reference is removes; otherwise returns
587 	 *         false.
588 	 */
589 	@SuppressWarnings("unchecked")
590 	private synchronized boolean removeListener(
591 			final Reference<EventListenerInterface> reference,
592 			final EventType eventType) {
593 		boolean success = false;
594 		Reference<EventListenerInterface>[] entries = this.listeners.get(eventType);
595 		List<Reference<EventListenerInterface>> list = new ArrayList<Reference<EventListenerInterface>>(
596 				Arrays.asList(entries));
597 		for (Iterator<Reference<EventListenerInterface>> i = list.iterator(); i
598 				.hasNext();) {
599 			if (i.next().equals(reference)) {
600 				i.remove();
601 				success = true;
602 			}
603 		}
604 		Reference<EventListenerInterface>[] toArray = (Reference<EventListenerInterface>[]) list
605 				.toArray(new Reference[list.size()]);
606 		this.listeners.put(eventType, toArray);
607 		if (list.size() == 0) {
608 			this.listeners.remove(eventType);
609 		}
610 		return success;
611 	}
612 
613 	/***
614 	 * writes a serializable method to stream
615 	 * 
616 	 * @param out
617 	 *            the outputstream
618 	 * @throws IOException
619 	 *             on IOException
620 	 */
621 	private synchronized void writeObject(final ObjectOutputStream out)
622 			throws IOException {
623 		out.defaultWriteObject();
624 	}
625 
626 	/***
627 	 * reads a serializable method from stream
628 	 * 
629 	 * @param in
630 	 *            the inputstream
631 	 * @throws IOException
632 	 *             on IOException
633 	 */
634 	private synchronized void readObject(final java.io.ObjectInputStream in)
635 			throws IOException, ClassNotFoundException {
636 		in.defaultReadObject();
637 		this.semaphore = new Object();
638 	}
639 }