View Javadoc

1   package cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation;
2   
3   import java.lang.reflect.Constructor;
4   import java.lang.reflect.Method;
5   import java.lang.reflect.Modifier;
6   import java.util.ArrayList;
7   import java.util.List;
8   import java.util.Map;
9   import java.util.logging.Level;
10  import java.util.logging.Logger;
11  
12  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
13  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEvent;
14  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
15  import cz.cuni.amis.pogamut.base.communication.worldview.listener.IListenerRegistrator;
16  import cz.cuni.amis.pogamut.base.communication.worldview.listener.ListenerLevel;
17  import cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation.exception.ListenerMethodParametersException;
18  import cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation.exception.MissingConstructorException;
19  import cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation.exception.MoreThanOneListenerLevelAnnotationException;
20  import cz.cuni.amis.pogamut.base.communication.worldview.listener.exception.ListenersAlreadyRegisteredException;
21  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
22  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
23  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
24  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
25  import cz.cuni.amis.pogamut.base.communication.worldview.object.WorldObjectId;
26  import cz.cuni.amis.utils.ClassUtils;
27  import cz.cuni.amis.utils.Lazy;
28  import cz.cuni.amis.utils.NullCheck;
29  import cz.cuni.amis.utils.exception.PogamutException;
30  import cz.cuni.amis.utils.maps.LazyMap;
31  
32  /**
33   * The registrator that is driven by annotations on the class it introspects.
34   * <p><p>
35   * WARNING: the inheritance does not work here! Only the top class object is introspected.
36   * 
37   * 
38   * @author Jimmy
39   */
40  /**
41   * @author Jimmy
42   *
43   */
44  public class AnnotationListenerRegistrator implements IListenerRegistrator{
45  
46  	/**
47  	 * Tries to instantiate ID class of 'idClass' with 'id' as an identifier. It first looks for constructor having only one string parameter
48  	 * and then for static method "idClass get(String)".
49  	 * @param method
50  	 * @param idClass
51  	 * @param id
52  	 * @return
53  	 */
54  	public static WorldObjectId getId(Method method, Class idClass, String id) {
55  		try {
56  			Object newId = null;
57  			
58  			// try constructor
59  			Constructor constructor = null;
60  			
61  			try {
62  				constructor = idClass.getConstructor(String.class);
63  			} catch (Exception e) {
64  			}
65  			if (constructor != null) {
66  				newId = idClass.getConstructor(String.class).newInstance(id);
67  			} else {
68  				Method getMethod = null;
69  				
70  				try {
71  					getMethod = idClass.getMethod("get", String.class);
72  				} catch (Exception e) {					
73  				}
74  				
75  				if (getMethod != null && isStaticMethod(getMethod) && getMethod.getReturnType().isAssignableFrom(idClass)) {
76  					newId = getMethod.invoke(idClass, id);
77  				} else {
78  					throw new IllegalArgumentException("Creation of ID for the annotation on the method " + ClassUtils.getMethodSignature(method) + " has failed. Can't create object ID, id class " + idClass + " for id '" + id + "', as id class has neither constructor with String as parameter, nor static get(String) method for obtaining ids.");
79  				}
80  			}
81  			
82  			if (newId == null) {
83  				throw new IllegalArgumentException("Creation of ID for the annotation on the method " + ClassUtils.getMethodSignature(method) + " has failed. Failed to instantiate ID '" + id + "' using id class " + idClass +", result id is NULL! Have you specified correct idClass?");
84  			}
85  			
86  			if (!newId.getClass().isAssignableFrom(idClass)) {
87  				throw new IllegalArgumentException("Creation of ID for the annotation on the method " + ClassUtils.getMethodSignature(method) + " has failed. Failed to instantiate CORRECT ID '" + id + "' using id class " + idClass +". Result id is of incompatible class " + newId.getClass() + ". Have you specified correct idClass?");
88  			}
89  			
90  			if (!WorldObjectId.class.isAssignableFrom(newId.getClass())) {
91  				throw new IllegalArgumentException("Creation of ID for the annotation on the method " + ClassUtils.getMethodSignature(method) + " has failed. Failed to instantiate CORRECT ID '" + id + "' using id class " + idClass +". Result id is of incompatible class " + newId.getClass() + " as it does not extends WorldObjectId class. Have you specified correct idClass?");
92  			}
93  			
94  			return (WorldObjectId) newId;
95  			
96  		} catch (Exception e) {
97  			throw new IllegalArgumentException("Creation of ID for the annotation on the method " + ClassUtils.getMethodSignature(method) + " has failed. Failed to instantiate CORRECT ID '" + id + "' using id class " + idClass +". Exception occured during reflection.", e);
98  		}
99  	}
100 	
101 	/**
102 	 * Returns a new {@link WorldObjectId} for the given 'annotation'.
103 	 * @param method
104 	 * @param annotation
105 	 * @return id instance
106 	 */
107 	public static WorldObjectId getId(Method method, ObjectListener annotation) {
108 		NullCheck.check(method, "method");
109 		NullCheck.check(annotation, "annotation");
110 		if (annotation.idClass() == null) {				
111 			throw new IllegalArgumentException(ClassUtils.getMethodSignature(method) + "-@ObjectListener.idClass == null, specify class of the id!");
112 		}
113 		if (annotation.objectId() == null) {
114 			throw new IllegalArgumentException(ClassUtils.getMethodSignature(method) + "-@ObjectListener.idClass == null, specify class of the id!");
115 		}
116 		return getId(method, annotation.idClass(), annotation.objectId());
117 	}
118 	
119 	/**
120 	 * Tells whether 'method' is static.
121 	 * @param method
122 	 * @return
123 	 */
124 	public static boolean isStaticMethod(Method method) {
125 		return Modifier.isStatic(method.getModifiers());
126 	}
127 	
128 	/**
129 	 * Returns a new {@link WorldObjectId} for the given 'annotation'.
130 	 * @param method
131 	 * @param annotation
132 	 * @return id instance
133 	 */
134 	public static WorldObjectId getId(Method method, ObjectEventListener annotation) {
135 		NullCheck.check(method, "method");
136 		NullCheck.check(annotation, "annotation");
137 		if (annotation.idClass() == null) {				
138 			throw new IllegalArgumentException(ClassUtils.getMethodSignature(method) + "-@ObjectEventListener.idClass == null, specify class of the id!");
139 		}
140 		if (annotation.objectId() == null) {
141 			throw new IllegalArgumentException(ClassUtils.getMethodSignature(method) + "-@ObjectEventListener.idClass == null, specify class of the id!");
142 		}
143 		return getId(method, annotation.idClass(), annotation.objectId());
144 	}
145 	
146 	/**
147 	 * Returns listener level that is gained from the method's annotation.
148 	 * @param method
149 	 * @return listener level of the method
150 	 */
151 	public static ListenerLevel getListenerLevel(Method method) {
152 		ListenerLevel level = null;
153 		if (method.isAnnotationPresent(EventListener.class)) {
154 			level = ListenerLevel.A;
155 		}
156 		if (method.isAnnotationPresent(ObjectClassListener.class)) {
157 			if (level != null) throw new MoreThanOneListenerLevelAnnotationException(method, AnnotationListenerRegistrator.class);
158 			level = ListenerLevel.B;
159 		}
160 		if (method.isAnnotationPresent(ObjectClassEventListener.class)) {
161 			if (level != null) throw new MoreThanOneListenerLevelAnnotationException(method, AnnotationListenerRegistrator.class);
162 			level = ListenerLevel.C;
163 		}
164 		if (method.isAnnotationPresent(ObjectListener.class)) {
165 			if (level != null) throw new MoreThanOneListenerLevelAnnotationException(method, AnnotationListenerRegistrator.class);
166 			level = ListenerLevel.D;
167 		}
168 		if (method.isAnnotationPresent(ObjectEventListener.class)) {
169 			if (level != null) throw new MoreThanOneListenerLevelAnnotationException(method, AnnotationListenerRegistrator.class);
170 			level = ListenerLevel.E;
171 		}
172 		return level;
173 	}
174 
175 	/**
176 	 * Level A listener that can be hooked to the world view (for more info 
177 	 * about listeners see {@link IWorldView}).
178 	 * 
179 	 * @author Jimmy
180 	 */
181 	private class LevelAListener implements IWorldEventListener {
182 
183 		Method method;
184 		
185 		public LevelAListener(Method method) {
186 			NullCheck.check(method, "method");
187 			this.method = method;
188 			
189 			// check annotation
190 			if (getAnnotation() == null) {
191 				throw new ListenerMethodParametersException(method, "There is no EventListener annotation on the method!", AnnotationListenerRegistrator.this);				
192 			}
193 			if (getEventClass() == null) {
194 				throw new ListenerMethodParametersException(method, "EventListener.eventClass can't be null!", AnnotationListenerRegistrator.this);
195 			}
196 			if (!(IWorldEvent.class.isAssignableFrom(getEventClass()))) {
197 				throw new ListenerMethodParametersException(method, "EventListener.eventClass == " + getEventClass() + " is not instance of IWorldEvent! Are you trying to use the listener for IWorldObjects? If so, see usage of ObjectClassEventListener or ObjectClassListener or ObjectEventListener or ObjectListener as used inside ResponsiveBot PogamutUT2004 example!", AnnotationListenerRegistrator.this);
198 			}
199 			
200 			// check the method signature
201 			if (method.getParameterTypes().length != 1 ||
202 			    !method.getParameterTypes()[0].isAssignableFrom(method.getAnnotation(EventListener.class).eventClass())) { 
203 				throw new ListenerMethodParametersException(method, method.getAnnotation(EventListener.class), AnnotationListenerRegistrator.this);
204 			}
205 			if (method.getParameterTypes()[0].isAssignableFrom(IWorldObject.class)) {
206 				throw new ListenerMethodParametersException(method, "EventListener can't be used to listen for OBJECTS! You must use ObjectClassEventListener or ObjectClassListener or ObjectEventListener or ObjectListener annotation for that! See ResponsiveBot PogamutUT2004 example!", AnnotationListenerRegistrator.this);
207 			}			 
208 		}
209 		
210 		public EventListener getAnnotation() {
211 			return method.getAnnotation(EventListener.class);
212 		}
213 		
214 		public Class getEventClass() {
215 			return getAnnotation().eventClass();
216 		}
217 		
218 		@Override
219 		public void notify(Object event) {
220 			try {
221 				method.setAccessible(true);
222 				method.invoke(obj, event);
223 				method.setAccessible(false);
224 			} catch (Exception e) {
225 				throw new PogamutException("Could not invoke LevelA listener " + ClassUtils.getMethodSignature(method) + " with parameter of class " + event.getClass() + ".", e, log, this);
226 			}
227 		}
228 		
229 	}
230 	
231 	private class LevelBListener implements IWorldObjectListener {
232 
233 		Method method;
234 		
235 		public LevelBListener(Method method) {
236 			NullCheck.check(method, "method");
237 			this.method = method; 
238 			
239 			// check the annotation
240 			if (getAnnotation() == null) {
241 				throw new ListenerMethodParametersException(method, "There is no ObjectClassListener annotation on the method!", AnnotationListenerRegistrator.this);				
242 			}
243 			if (getObjectClass() == null) {
244 				throw new ListenerMethodParametersException(method, "ObjectClassListener.objectClass can't be null!", AnnotationListenerRegistrator.this);
245 			}
246 			if (!IWorldObject.class.isAssignableFrom(getObjectClass())) {
247 				throw new ListenerMethodParametersException(method, "ObjectClassListener.objectClass == " + getObjectClass() + " is not instance of IWorldObject! Are you trying to use the listener for IWorldEvent? If so, use EventListener, see the example inside ResponsiveBot PogamutUT2004!", AnnotationListenerRegistrator.this);
248 			}
249 			
250 			// check the method signature
251 			if (method.getParameterTypes().length != 1 ||
252 			    !method.getParameterTypes()[0].isAssignableFrom(IWorldObjectEvent.class)) { 
253 				throw new ListenerMethodParametersException(method, method.getAnnotation(ObjectClassListener.class), AnnotationListenerRegistrator.this);
254 			}
255 			
256 		}
257 		
258 		public ObjectClassListener getAnnotation() {
259 			return method.getAnnotation(ObjectClassListener.class);
260 		}
261 		
262 		public Class getObjectClass() {
263 			return getAnnotation().objectClass();
264 		}
265 		
266 		@Override
267 		public void notify(Object event) {
268 			try {
269 				method.setAccessible(true);
270 				method.invoke(obj, event);
271 				method.setAccessible(false);
272 			} catch (Exception e) {
273 				throw new PogamutException("Could not invoke LevelB listener " + ClassUtils.getMethodSignature(method) + " with parameter of class " + event.getClass() + ".", e, log, this);
274 			}
275 		}
276 		
277 	}
278 	
279 	private class LevelCListener implements IWorldObjectEventListener {
280 
281 		Method method;
282 		
283 		public LevelCListener(Method method) {
284 			NullCheck.check(method, "method");
285 			this.method = method; 
286 			
287 			// check the annotation
288 			if (getAnnotation() == null) {
289 				throw new ListenerMethodParametersException(method, "There is no ObjectClassEventListener annotation on the method!", AnnotationListenerRegistrator.this);				
290 			}
291 			if (getEventClass() == null) {
292 				throw new ListenerMethodParametersException(method, "ObjectClassEventListener.eventClass can't be null!", AnnotationListenerRegistrator.this);
293 			}
294 			if (!IWorldObjectEvent.class.isAssignableFrom(getEventClass())) {
295 				throw new ListenerMethodParametersException(method, "ObjectClassEventListener.eventClass == " + getEventClass() + " is not instance of IWorldObjectEvent! Are you trying to use the listener for IWorldEvent? If so, use EventListener, see the example inside ResponsiveBot PogamutUT2004!", AnnotationListenerRegistrator.this);	
296 			}
297 			if (getObjectClass() == null) {
298 				throw new ListenerMethodParametersException(method, "ObjectClassEventListener.objectClass can't be null!", AnnotationListenerRegistrator.this);
299 			}
300 			if (!IWorldObject.class.isAssignableFrom(getObjectClass())) {
301 				throw new ListenerMethodParametersException(method, "ObjectClassEventListener.objectClass == " + getObjectClass() + " is not instance of IWorldObject! Are you trying to use the listener for IWorldEvent? If so, use EventListener, see the example inside ResponsiveBot PogamutUT2004!", AnnotationListenerRegistrator.this);
302 			}
303 			
304 			// check the method signature
305 			if (method.getParameterTypes().length != 1 ||
306 			    !method.getParameterTypes()[0].isAssignableFrom(method.getAnnotation(ObjectClassEventListener.class).eventClass())) { 
307 				throw new ListenerMethodParametersException(method, method.getAnnotation(ObjectClassEventListener.class), AnnotationListenerRegistrator.this);
308 			}		
309 		}
310 		
311 		public ObjectClassEventListener getAnnotation() {
312 			return method.getAnnotation(ObjectClassEventListener.class);
313 		}
314 		
315 		public Class getEventClass() {
316 			return getAnnotation().eventClass();
317 		}
318 		
319 		public Class getObjectClass() {
320 			return getAnnotation().objectClass();
321 		}
322 		
323 		@Override
324 		public void notify(Object event) {
325 			try {
326 				method.setAccessible(true);
327 				method.invoke(obj, event);
328 				method.setAccessible(false);
329 			} catch (Exception e) {
330 				throw new PogamutException("Could not invoke LevelC listener " + ClassUtils.getMethodSignature(method) + " with parameter of class " + event.getClass() + ".", e, log, this);
331 			}
332 		}
333 		
334 	}
335 	
336 	private class LevelDListener implements IWorldObjectListener {
337 
338 		Method method;
339 		
340 		WorldObjectId objectId;
341 		
342 		public LevelDListener(Method method) {
343 			NullCheck.check(method, "method");
344 			this.method = method; 
345 			
346 			// check the annotation
347 			if (getAnnotation() == null) {
348 				throw new ListenerMethodParametersException(method, "There is no ObjectListener annotation on the method!", AnnotationListenerRegistrator.this);				
349 			}
350 			
351 			// check the method signature
352 			if (method.getParameterTypes().length != 1 ||
353 			    !method.getParameterTypes()[0].isAssignableFrom(IWorldObjectEvent.class)) { 
354 				throw new ListenerMethodParametersException(method, method.getAnnotation(ObjectListener.class), AnnotationListenerRegistrator.this);
355 			}
356 			
357 			objectId = getId(method, getAnnotation());
358 		}
359 		
360 		public ObjectListener getAnnotation() {
361 			return method.getAnnotation(ObjectListener.class);
362 		}
363 		
364 		public WorldObjectId getObjectId() {
365 			return objectId;
366 		}
367 		
368 		@Override
369 		public void notify(Object event) {
370 			try {
371 				method.setAccessible(true);
372 				method.invoke(obj, event);
373 				method.setAccessible(false);
374 			} catch (Exception e) {
375 				throw new PogamutException("Could not invoke LevelD listener " + ClassUtils.getMethodSignature(method) + " with parameter of class " + event.getClass() + ".", e, log, this);
376 			}
377 		}
378 		
379 	}
380 	
381 	private class LevelEListener implements IWorldObjectEventListener {
382 
383 		Method method;
384 		
385 		WorldObjectId objectId;
386 		
387 		public LevelEListener(Method method) {
388 			NullCheck.check(method, "method");
389 			this.method = method; 
390 			
391 			// check the annotation
392 			if (getAnnotation() == null) {
393 				throw new ListenerMethodParametersException(method, "There is no ObjectListener annotation on the method!", AnnotationListenerRegistrator.this);				
394 			}
395 			if (getEventClass() == null) {
396 				throw new ListenerMethodParametersException(method, "ObjectEventListener.eventClass can't be null!", AnnotationListenerRegistrator.this);
397 			}
398 			if (!IWorldObjectEvent.class.isAssignableFrom(getEventClass())) {
399 				throw new ListenerMethodParametersException(method, "ObjectEventListener.eventClass == " + getEventClass() + " is not instance of IWorldObjectEvent! Are you trying to use the listener for IWorldEvent? If so, use EventListener, see the example inside ResponsiveBot PogamutUT2004!", AnnotationListenerRegistrator.this);	
400 			}
401 			
402 			// check the method signature
403 			if (method.getParameterTypes().length != 1 ||
404 			    !method.getParameterTypes()[0].isAssignableFrom(method.getAnnotation(ObjectEventListener.class).eventClass())) { 
405 				throw new ListenerMethodParametersException(method, method.getAnnotation(ObjectEventListener.class), AnnotationListenerRegistrator.this);
406 			}
407 			
408 			objectId = getId(method, getAnnotation());
409 		}
410 		
411 		public ObjectEventListener getAnnotation() {
412 			return method.getAnnotation(ObjectEventListener.class);
413 		}
414 		
415 		public WorldObjectId getObjectId() {
416 			return objectId;
417 		}
418 		
419 		public Class getEventClass() {
420 			return getAnnotation().eventClass();
421 		}
422 		
423 		@Override
424 		public void notify(Object event) {
425 			try {
426 				method.setAccessible(true);
427 				method.invoke(obj, event);
428 				method.setAccessible(false);
429 			} catch (Exception e) {
430 				throw new PogamutException("Could not invoke LevelE listener " + ClassUtils.getMethodSignature(method) + " with parameter of class " + event.getClass() + ".", e, log, this);
431 			}
432 		}
433 		
434 	}
435 	
436 	private IWorldView worldView;
437 	private Object obj;
438 	private boolean listenersRegistered = false;
439 	
440 	private Lazy<List<Method>> methods = new Lazy<List<Method>>() {
441 
442 		@Override
443 		protected List<Method> create() {
444 			return probeMethods();
445 		}
446 		
447 	};
448 	
449 	private Map<ListenerLevel, List<IWorldEventListener>> listeners = new LazyMap<ListenerLevel, List<IWorldEventListener>>() {
450 
451 		@Override
452 		protected List<IWorldEventListener> create(ListenerLevel key) {
453 			return new ArrayList<IWorldEventListener>();
454 		}
455 		
456 	};
457 	
458 	private Logger log;
459 	
460 	public AnnotationListenerRegistrator(Object obj, IWorldView worldView, Logger log) {
461 		this.worldView = worldView;
462 		NullCheck.check(this.worldView, "worldView");
463 		this.obj = obj;
464 		NullCheck.check(this.obj, "obj");
465 		this.log = log;
466 		NullCheck.check(this.log, "log");
467 	}
468 	
469 	/**
470 	 * Check the {@link AnnotationListenerRegistrator#obj} methods for the listener annotations. 
471 	 * @return
472 	 */
473 	private List<Method> probeMethods() {
474 		List<Method> methods = new ArrayList<Method>();
475 		for (Method method : obj.getClass().getDeclaredMethods()) {
476 			if (getListenerLevel(method) != null) methods.add(method);
477 		}
478 		return methods;
479 	}
480 	
481 	/**
482 	 * Introspect all object's methods and register various listeners based on
483 	 * {@link EventListener}, etc... annotations.
484 	 * 
485 	 * @param obj
486 	 */
487 	@Override
488 	public synchronized void addListeners() throws ListenersAlreadyRegisteredException {
489 		if (listenersRegistered) throw new ListenersAlreadyRegisteredException(this);
490 		if (log.isLoggable(Level.FINER)) log.finer(obj + " -> " + worldView + ": Registering listeners.");
491 		for (Method method : this.methods.getVal()) {
492 			switch(getListenerLevel(method)){
493 			case A:
494 				if (log.isLoggable(Level.FINE)) log.fine(obj + " -> " + worldView + ": Registering level A listener for " + ClassUtils.getMethodSignature(method));
495 				LevelAListener listenerA = new LevelAListener(method);
496 				worldView.addEventListener(listenerA.getEventClass(), listenerA);
497 				listeners.get(ListenerLevel.A).add(listenerA);
498 				break;
499 			case B:
500 				if (log.isLoggable(Level.FINE)) log.fine(obj + " -> " + worldView + ": Registering level B listener for " + ClassUtils.getMethodSignature(method));
501 				LevelBListener listenerB = new LevelBListener(method);
502 				worldView.addObjectListener(listenerB.getObjectClass(), listenerB);
503 				listeners.get(ListenerLevel.B).add(listenerB);
504 				break;
505 			case C:
506 				if (log.isLoggable(Level.FINE)) log.fine(obj + " -> " + worldView + ": Registering level C listener for " + ClassUtils.getMethodSignature(method));
507 				LevelCListener listenerC = new LevelCListener(method);
508 				worldView.addObjectListener(listenerC.getObjectClass(), listenerC.getEventClass(), listenerC);
509 				listeners.get(ListenerLevel.C).add(listenerC);
510 				break;
511 			case D:
512 				if (log.isLoggable(Level.FINE)) log.fine(obj + " -> " + worldView + ": Registering level D listener for " + ClassUtils.getMethodSignature(method));
513 				LevelDListener listenerD = new LevelDListener(method);
514 				worldView.addObjectListener(listenerD.getObjectId(), listenerD);
515 				listeners.get(ListenerLevel.D).add(listenerD);
516 				break;
517 			case E:
518 				if (log.isLoggable(Level.FINE)) log.fine(obj + " -> " + worldView + ": Registering level E listener for " + ClassUtils.getMethodSignature(method));
519 				LevelEListener listenerE = new LevelEListener(method);
520 				worldView.addObjectListener(listenerE.getObjectId(), listenerE.getEventClass(), listenerE);
521 				listeners.get(ListenerLevel.E).add(listenerE);
522 				break;
523 			}	
524 		}
525 		if (log.isLoggable(Level.INFO)) log.info(obj + " -> " + worldView + ": Registered " + listeners.size() + " listeners.");
526 	}
527 	
528 	public int getListenersCount() {
529 		return listeners.get(ListenerLevel.A).size() + listeners.get(ListenerLevel.B).size() + listeners.get(ListenerLevel.C).size() + listeners.get(ListenerLevel.D).size() + listeners.get(ListenerLevel.E).size();
530 	}
531 
532 	@Override
533 	public synchronized void removeListeners() {
534 		if (!listenersRegistered) return;
535 		if (log.isLoggable(Level.FINER)) log.finer(obj + " -> " + worldView + ": Removing " + getListenersCount()  + " listeners.");
536 		for (IWorldEventListener l : listeners.get(ListenerLevel.A)) {
537 			LevelAListener listenerA = (LevelAListener) l;
538 			worldView.removeEventListener(listenerA.getEventClass(), listenerA);
539 		}
540 		for (IWorldEventListener l : listeners.get(ListenerLevel.B)) {
541 			LevelBListener listenerB = (LevelBListener) l;
542 			worldView.removeObjectListener(listenerB.getObjectClass(), listenerB);
543 		}
544 		for (IWorldEventListener l : listeners.get(ListenerLevel.C)) {
545 			LevelCListener listenerC = (LevelCListener) l;
546 			worldView.removeObjectListener(listenerC.getObjectClass(), listenerC.getEventClass(), listenerC);
547 		}
548 		for (IWorldEventListener l : listeners.get(ListenerLevel.D)) {
549 			LevelDListener listenerD = (LevelDListener) l;
550 			worldView.removeObjectListener(listenerD.getObjectId(), listenerD);
551 		}
552 		for (IWorldEventListener l : listeners.get(ListenerLevel.E)) {
553 			LevelEListener listenerE = (LevelEListener) l;
554 			worldView.removeObjectListener(listenerE.getObjectId(), listenerE.getEventClass(), listenerE);
555 		}
556 		if (log.isLoggable(Level.INFO)) log.info(obj + " -> " + worldView + ": Listeners removed.");
557 	}
558 
559 	public Logger getLog() {
560 		return log;
561 	}
562 	
563 }