1
2
3
4
5
6
7
8
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
238
239 this.fireEvent((EventListenerInterface) reference,
240 event);
241 } else
242 {
243
244
245 this.removeListener(entries[i], event.getType());
246 }
247 } catch (RemoteException remoteException)
248 {
249
250
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 }