View Javadoc

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