View Javadoc

1   package cz.cuni.amis.pogamut.base.communication.worldview.impl;
2   
3   import java.util.Collection;
4   import java.util.Collections;
5   import java.util.HashMap;
6   import java.util.Map;
7   import java.util.Queue;
8   import java.util.concurrent.ConcurrentLinkedQueue;
9   import java.util.logging.Level;
10  
11  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
12  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEvent;
13  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
14  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
15  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
16  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.WorldObjectId;
18  import cz.cuni.amis.pogamut.base.component.IComponent;
19  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
20  import cz.cuni.amis.pogamut.base.component.controller.ComponentControlHelper;
21  import cz.cuni.amis.pogamut.base.component.controller.ComponentController;
22  import cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies;
23  import cz.cuni.amis.pogamut.base.component.controller.IComponentControlHelper;
24  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
25  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
26  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
27  import cz.cuni.amis.utils.ClassUtils;
28  import cz.cuni.amis.utils.exception.PogamutException;
29  import cz.cuni.amis.utils.listener.IListener;
30  import cz.cuni.amis.utils.listener.Listeners;
31  import cz.cuni.amis.utils.listener.ListenersMap;
32  import cz.cuni.amis.utils.maps.HashMapMap;
33  import cz.cuni.amis.utils.maps.LazyMap;
34  import cz.cuni.amis.utils.token.Token;
35  import cz.cuni.amis.utils.token.Tokens;
36  
37  /**
38   * Abstract world view is implementing some of the tedious things every WorldView will surely
39   * implement -> maps for holding the references to all world objects either according to their
40   * id and type (class). It also implements a map of listeners for events that may
41   * be generated in the world.
42   * <p><p>
43   * For raising new IWorldEvent in descendants call protected method raiseEvent(IWorldEvent event), that
44   * is preventing recursion.
45   * <p><p>
46   * Note that there is a big advantage in how the listeners are called and how objects are stored.<p>
47   * The event notifying method (raiseEvent()) is respecting 
48   * the class/interface hierarchy thus informing listeners hooked on all
49   * levels of the hierarchy.
50   * <p><p>
51   * The items are stored according the the class/interface hierarchy as well!
52   * <p><p>
53   * <b>Example:</b>You have interface ItemEvent (extends IWorldObjectEvent) and it's implementation WeaponEvent and HealthEvent. Perheps
54   * you want to listen for all events on WeaponEvent, so you will create IWorldEventListener&lt;WeaponEvent&gt;.
55   * But hey - you may want to listen on both WeaponEvent and HealthEvent (and perheps any ItemEvent there is),
56   * that's easy - just create IWorldEventListener&lt;ItemEvent&gt; and you will receive both WeaponEvent and HealthEvent.
57   * That's because during event handling we're probing the event class ancestors / interfaces and informing
58   * all listeners on all class-hierarchy levels. 
59   * <p><p>
60   * Ultimately you may create IWorldEventListener&lt;IWorldEvent&gt; to
61   * catch all events the world view produces (be careful because it may cause serious performance hit if you
62   * process these events slowly).
63   * <p><p>
64   * Same goes for storing the items under it's class in the 'worldObjects'.
65   * 
66   * @author Jimmy
67   */
68  @AgentScoped
69  @SuppressWarnings("unchecked")
70  public abstract class AbstractWorldView implements IWorldView {
71  	
72  	public static final Token COMPONENT_ID = Tokens.get("WorldView");
73  	
74  	/**
75  	 * Class that notifies listeners about the world view event.
76  	 * @author Jimmy
77  	 */
78  	private static class ListenerNotifier<T> implements Listeners.ListenerNotifier<IListener> {
79  
80  		/**
81  		 * Event that is being processed.
82  		 */
83  		private T event = null;
84  		
85  		@Override
86  		public T getEvent() {
87  			return event;
88  		}
89  		
90  		public void setEvent(T event) {
91  			this.event = event;			
92  		}
93  
94  		/**
95  		 * Method that is used to notify the listener.
96  		 */
97  		@Override
98  		public void notify(IListener listener) {
99  			listener.notify(event);
100 		}
101 		
102 	}
103 	
104 	/**
105 	 * Notifier object - preallocated, this will raise events on the listeners.
106 	 */
107 	private ListenerNotifier notifier = new ListenerNotifier();
108 	
109 	/**
110 	 * Unsynchronized map that holds all the objects according to their type
111 	 * in the maps.
112 	 * <p><p>
113 	 * Due to nature of generics we can't typed this field, it holds maps of objects
114 	 * according to their classes. 
115 	 * <p>
116 	 * Map &lt; Class, Map &lt; IWorldObjectId, IWorldObject of Class &gt; &gt;
117 	 */
118 	private HashMapMap<Class, WorldObjectId, IWorldObject> worldObjects = 
119 		new HashMapMap<Class, WorldObjectId, IWorldObject>();
120 	
121 	/**
122 	 * Synchronized version of world objects.
123 	 */
124 	private Map<Class, Map<WorldObjectId, IWorldObject>> syncWorldObjects = 
125 		Collections.synchronizedMap(worldObjects);
126 			
127 	/**
128 	 * Synchronized map of all the world objects that are present in the worldview.
129 	 */
130 	private Map<WorldObjectId, IWorldObject> knownObjects =
131 		Collections.synchronizedMap(new HashMap<WorldObjectId, IWorldObject>());
132 		
133 	/**
134 	 * Level A Listeners
135 	 * <p><p>
136 	 * Map of the event listeners, key is the event class where the listener is hooked to.
137 	 */
138 	private ListenersMap<Class> eventListeners = new ListenersMap<Class>();
139 	
140 	/**
141 	 * Level B Listeners
142 	 * <p><p>
143 	 * Map of the object class to the object listeners.
144 	 */
145 	private ListenersMap<Class> objectsListeners = new ListenersMap<Class>();
146 	
147 	/**
148 	 * Level C listeners
149 	 * <p><p>
150 	 * Map of event listeners on some object class.
151 	 * <p><p>
152 	 * First key is eventClass, second key is objectClass.
153 	 */
154 	private Map<Class, ListenersMap<Class>> objectEventListeners = 
155 		Collections.synchronizedMap(
156 			new LazyMap<Class, ListenersMap<Class>>(){
157 
158 				@Override
159 				protected ListenersMap<Class> create(Class key) {
160 					ListenersMap<Class> listeners = new ListenersMap<Class>();
161 					listeners.setLog(log, "LevelC-" + key.getSimpleName());
162 					return listeners;
163 				}
164 			}
165 		);
166 		
167 	/**
168 	 * Level D Listeners
169 	 * <p><p>
170 	 * Listeners listening on all events on a specific object.
171 	 */
172 	private ListenersMap<WorldObjectId> specificObjectListeners = new ListenersMap<WorldObjectId>();
173 	
174 	/**
175 	 * Level E Listeners
176 	 * <p><p>
177 	 * Listeners listening for some specific event on some specific object.
178 	 * <p><p>
179 	 * Map of (IWorldObjectId, class of IWorldEvent or desc.).  
180 	 */
181 	private Map<WorldObjectId, ListenersMap<Class>> specificObjectEventListeners = 
182 		Collections.synchronizedMap(
183 			new LazyMap<WorldObjectId, ListenersMap<Class>>() {
184 
185 				@Override
186 				protected ListenersMap<Class> create(WorldObjectId key) {
187 					ListenersMap<Class> listeners = new ListenersMap<Class>();
188 					listeners.setLog(log, "LevelE-" + key.getStringId());
189 					return listeners;
190 				}
191 			}
192 		);
193 		
194 	    
195 	/**
196 	 * Flag that is telling us whether there is an event being processed or not.
197 	 * <p><p>
198 	 * It is managed only by raiseEvent() method - DO NOT MODIFY OUTSIDE IT!
199 	 */
200 	private boolean raiseEventProcessing = false;
201 	
202 	/**
203 	 * List of events we have to process.
204 	 * <p><p>
205 	 * It is managed only by raiseEvent() method - DO NOT MODIFY THIS OUTSIDE THAT METHOD!
206 	 */
207 	private Queue<IWorldEvent> raiseEventsList = new ConcurrentLinkedQueue<IWorldEvent>();
208 		
209 	protected LogCategory log;
210 
211 	protected IComponentBus eventBus;
212 
213 	protected ComponentController<IComponent> controller;
214 
215 	public AbstractWorldView(ComponentDependencies dependencies, IComponentBus bus, IAgentLogger logger) {
216 		this.log = logger.getCategory(getComponentId().getToken());		
217 		this.eventBus = bus;
218 		this.controller = new ComponentController(this, control, this.eventBus, this.log, dependencies);
219 
220 		this.eventListeners.setLog(log, "LevelA");
221 		this.objectsListeners.setLog(log, "LevelB");
222 		this.specificObjectListeners.setLog(log, "LevelD");
223 	}
224 	
225 	//
226 	//
227 	// WORLD VIEW CONTROL
228 	//
229 	//
230 	
231     protected IComponentControlHelper control = new ComponentControlHelper() {
232 		
233     	@Override
234 		public void startPaused() {
235     		AbstractWorldView.this.start(true);
236 		}
237     	
238     	@Override
239 		public void start() throws PogamutException {
240     		AbstractWorldView.this.start(false);
241 		}
242     	
243     	@Override
244 		public void prePause() throws PogamutException {
245     		AbstractWorldView.this.prePause();
246 		}
247     	
248     	@Override
249 		public void pause() throws PogamutException {
250     		AbstractWorldView.this.pause();
251     	}
252     	
253     	@Override
254 		public void resume() throws PogamutException {
255     		AbstractWorldView.this.resume();
256     	}
257     	
258     	@Override
259 		public void preStop() throws PogamutException {
260     		AbstractWorldView.this.preStop();
261 		}
262     	
263     	@Override
264 		public void stop() throws PogamutException {
265     		AbstractWorldView.this.stop();
266 		}
267 		
268     	@Override
269 		public void kill() {
270     		AbstractWorldView.this.kill();
271 		}
272 		
273 		@Override
274 		public void reset() {
275 			AbstractWorldView.this.reset();
276 		}
277 		
278 	};
279 	
280 	/**
281 	 * Cleans up internal data structures, called from start/stop/kill/reset methods.
282 	 * <p><p>
283 	 * If you override this method, do not forget to call super.cleanUp().
284 	 */
285 	protected void cleanUp() {
286 		synchronized(worldObjects) {
287 			worldObjects.clear();
288 		}
289 		synchronized(knownObjects) {
290 			knownObjects.clear();
291 		}
292 		synchronized(raiseEventsList) {	
293 			raiseEventsList.clear();
294 		}
295 	}
296 	
297 	/**
298 	 * Starts the world view. 
299 	 * <p><p>
300 	 * If you override this method, do not forget to call super.start().
301 	 */
302 	protected void start(boolean startPaused) {
303 		cleanUp();
304 	}
305 	
306 	/**
307 	 * Pre-pauses the world view.
308 	 * <p><p>
309 	 * If you override this method, do not forget to call super.preStop().
310 	 */
311 	protected void prePause() {
312 	}
313 	
314 	/**
315 	 * Pauses the world view. 
316 	 * <p><p>
317 	 * If you override this method, do not forget to call super.start().
318 	 */
319 	protected void pause() {
320 	}
321 	
322 	/**
323 	 * Resumes the world view. 
324 	 * <p><p>
325 	 * If you override this method, do not forget to call super.start().
326 	 */
327 	protected void resume() {
328 	}
329 	
330 	/**
331 	 * Pre-stops the world view.
332 	 * <p><p>
333 	 * If you override this method, do not forget to call super.preStop().
334 	 */
335 	protected void preStop() {
336 	}
337 	
338 	/**
339 	 * Stops the world view.
340 	 * <p><p>
341 	 * If you override this method, do not forget to call super.stop().
342 	 */
343 	protected void stop() {
344 		cleanUp();
345 	}
346 	
347 	/**
348 	 * Kills the world view.
349 	 * <p><p>
350 	 * If you override this method, do not forget to call super.stop().
351 	 */
352 	protected void kill() {
353 		cleanUp();
354 	}
355 
356 	/**
357 	 * Resets the world view so it is start()able again.
358 	 * <p><p>
359 	 * If you override this method, do not forget to call super.reset().
360 	 */
361 	protected void reset() {
362 		cleanUp();
363 	}
364 	
365 	protected boolean isRunning() {
366 		return controller.isRunning();
367 	}
368 	
369 	protected boolean isPaused() {
370 		return controller.isPaused();
371 	}
372 	
373 	@Override
374 	public Token getComponentId() {
375 		return COMPONENT_ID;
376 	}
377 	
378 	public LogCategory getLog() {
379 		return log;
380 	}
381 	
382 	//
383 	//
384 	// COMPONENTS
385 	//
386 	//
387 	
388 	@Override
389 	public IComponentBus getEventBus() {
390 		return eventBus;
391 	}
392 	
393 	//
394 	//
395 	// EVENT LISTENERS
396 	// 
397 	//
398 	
399 	@Override
400 	public void addEventListener(Class<?> event, IWorldEventListener<?> listener) {
401 		eventListeners.add(event, listener);
402 	}
403 	
404 	@Override
405 	public void addObjectListener(Class<?> objectClass, IWorldObjectEventListener<?, ?> listener) {
406 		objectsListeners.add(objectClass, listener);
407 	}
408 
409 	@Override
410 	public void addObjectListener(Class<?> objectClass, Class<?> eventClass, IWorldObjectEventListener<?,?> listener) {
411 		ListenersMap<Class> listeners = objectEventListeners.get(eventClass);
412 		listeners.add(objectClass, listener);
413 	}
414 	
415 	@Override
416 	public void addObjectListener(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
417 		specificObjectListeners.add(objectId, listener);
418 	}
419 	
420 	@Override
421 	public void addObjectListener(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
422 		ListenersMap<Class> listeners = specificObjectEventListeners.get(objectId);
423 		listeners.add(eventClass, listener);
424 	}
425 	
426 	@Override
427 	public boolean isListening(Class<?> eventClass, IWorldEventListener<?> listener) {
428 		return eventListeners.isListening(eventClass, listener);
429 	}
430 	
431 	@Override
432 	public boolean isListening(Class<?> objectClass, IWorldObjectEventListener<?, ?> listener) {
433 		return objectsListeners.isListening(listener);
434 	}
435 
436 
437 	@Override
438 	public boolean isListening(Class<?> objectClass, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
439 		if (objectEventListeners.containsKey(objectClass)) { // be careful not to create unnecessary ListenersMap
440 			return objectEventListeners.get(eventClass).isListening(objectClass, listener);
441 		} else {
442 			return false;
443 		}
444 	}
445 
446 	@Override
447 	public boolean isListening(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
448 		return specificObjectListeners.isListening(objectId, listener);
449 	}
450 
451 	@Override
452 	public boolean isListening(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
453 		if (specificObjectEventListeners.containsKey(objectId)) { // be careful not to create unnecessary ListenersMap
454 			return specificObjectEventListeners.get(objectId).isListening(eventClass, listener);
455 		} else {
456 			return false;
457 		}
458 		
459 	}
460 	
461 	@Override
462 	public boolean isListening(IWorldEventListener<?> listener) {
463 		if (eventListeners.isListening(listener) ||
464 			specificObjectListeners.isListening(listener)) {
465 			return true;
466 		}
467 		synchronized(objectEventListeners) {
468 			for (ListenersMap<Class> listeners : objectEventListeners.values()) {
469 				if (listeners.isListening(listener)) return true;
470 			}
471 		}
472 		synchronized(specificObjectEventListeners) {
473 			for (ListenersMap<Class> listeners : specificObjectEventListeners.values()) {
474 				if (listeners.isListening(listener)) return true;
475 			}
476 		}
477 		return false;
478 	}
479 
480 	@Override
481 	public void removeEventListener(Class<?> eventClass, IWorldEventListener<?> listener) {
482 		eventListeners.remove(eventClass, listener);
483 	}
484 
485 	@Override
486 	public void removeObjectListener(Class<?> objectClass,	IWorldObjectEventListener<?, ?> listener) {
487 		objectsListeners.remove(objectClass, listener);
488 	}
489 	
490 	@Override
491 	public void removeObjectListener(Class<?> objectClass, Class<?> eventClass,	IWorldObjectEventListener<?, ?> listener) {
492 		if (objectEventListeners.containsKey(eventClass)) { // be careful not to create unnecessary ListenersMap
493 			objectEventListeners.get(eventClass).remove(objectClass, listener);
494 		}
495 	}
496 
497 	@Override
498 	public void removeObjectListener(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
499 		specificObjectListeners.remove(objectId, listener);
500 	}
501 
502 	@Override
503 	public void removeObjectListener(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
504 		if (specificObjectEventListeners.containsKey(objectId)) { // be careful not to create unnecessary ListenersMap
505 			specificObjectEventListeners.get(objectId).remove(eventClass, listener);
506 		}
507 	}
508 	
509 		
510 	@Override
511 	public void removeListener(IWorldEventListener<?> listener) {
512 		eventListeners.remove(listener);
513 		synchronized(objectEventListeners) {
514 			for (ListenersMap<Class> listeners : objectEventListeners.values()) {
515 				listeners.remove(listener);
516 			}
517 		}
518 		specificObjectListeners.remove(listener);
519 		specificObjectEventListeners.remove(listener);
520 	}
521 	
522 	//
523 	//
524 	// OBJECTS PUBLIC METHOD
525 	//
526 	//
527 	
528 	@Override
529 	public Map<Class, Map<WorldObjectId, IWorldObject>> getAll() {
530 		return syncWorldObjects;
531 	}
532 			
533 	@Override
534 	public <T extends IWorldObject> Map<WorldObjectId, T> getAll(Class<T> type) {	
535 		return (Map<WorldObjectId, T>) syncWorldObjects.get(type);
536 	}
537 
538     @Override
539     public <T extends IWorldObject> T getSingle(Class<T> cls) {
540         Collection<T> col = getAll(cls).values();
541         if(col.size() > 1) throw new IllegalArgumentException("There must be at most one object of given class (" + cls.getName() + ") to use this method. But there were more instances (" + col.size() + ").");
542         if(col.size() < 1) return null;
543         synchronized(col) {
544         	return col.iterator().next();
545         }
546     }
547 
548 	@Override
549 	public IWorldObject get(WorldObjectId objectId) {
550 		return knownObjects.get(objectId);
551 	}
552 
553         @Override
554         public <T extends IWorldObject> T get(WorldObjectId id, Class<T> clazz) {
555             IWorldObject obj = get(id);
556             if(obj == null){
557                 return null;
558             }
559             else if(clazz.isAssignableFrom(obj.getClass())){
560                 return (T)obj;
561             } else {
562                 throw new ClassCastException("Object with id " + id + " is not of class " + clazz);
563             }
564         }
565         
566         
567 
568 	@Override
569 	public Map<WorldObjectId, IWorldObject> get() {
570 		return knownObjects;
571 	}
572 
573 	//
574 	//
575 	// PROTECTED (HELPED) METHODS
576 	//
577 	//
578 	
579 	/**
580 	 * Method that adds a new world object to the object maps. It will be called from
581 	 * the descendant whenever new object is encountered for the first time.
582 	 * <p><p>
583 	 * Synchronized!
584 	 * 
585 	 * @param worldObject
586 	 */
587 	protected synchronized void addWorldObject(IWorldObject worldObject) {
588 		knownObjects.put(worldObject.getId(), worldObject);
589 		for (Class cls : ClassUtils.getSubclasses(worldObject.getClass())) {
590 			syncWorldObjects.get(cls).put(worldObject.getId(), worldObject);
591 		}		
592 	}
593 	
594 	/**
595 	 * Removes world object from the world view - this will be called from the descendants
596 	 * of the AbstractWorldView whenever world object should disappear from the world view 
597 	 * (was destroyed in the world).
598 	 * <p><p>
599 	 * Synchronized!
600 	 * 
601 	 * @param worldObject
602 	 */
603 	protected synchronized void removeWorldObject(IWorldObject worldObject) {
604 		knownObjects.remove(worldObject.getId());
605 		for (Class<?> cls : ClassUtils.getSubclasses(worldObject.getClass())) {
606 			syncWorldObjects.get(cls).remove(worldObject.getId());
607 		}			
608 	}
609 	
610 	/**
611 	 * Process new IWorldEvent - notify all the listeners about it. Forbids recursion.
612 	 * <p><p>
613 	 * Use in the descendants to process new IWorldChangeEvent.
614 	 * <p><p>
615 	 * Does not catch any exceptions!
616 	 * <p><p>
617 	 * Synchronized!
618 	 * 
619 	 * @param event
620 	 */
621 	protected synchronized void raiseEvent(IWorldEvent event) {
622 		// method is synchronized - only one thread inside at given time
623 		
624 		// is this method recursively called? 
625 		if (raiseEventProcessing) {
626 			// yes it is -> that means the previous event has not been
627 			// processed! ... store this event and allows the previous one
628 			// to be fully processed (e.g. postpone raising this event)
629 			raiseEventsList.add(event);
630 			return;
631 		} else {
632 			// no it is not ... so raise the flag that we're inside the method
633 			raiseEventProcessing = true;
634 		}
635 		// process event
636 				
637 		innerRaiseEvent(event);
638 		
639 		// check the events list size, do we have more events to process?
640 		while(raiseEventsList.size() != 0) {
641 			// yes we do -> do it!
642 			innerRaiseEvent(raiseEventsList.poll());			
643 		}
644 		// all events has been processed, drop the flag that we're inside the method
645 		raiseEventProcessing = false;			
646 	}
647 		
648 	//
649 	//
650 	// EVENT PROCESSING
651 	//
652 	//
653 		
654 	/**
655 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
656 	 * @param event
657 	 */
658 	private void notifyLevelAListeners(IWorldEvent event) {
659 		Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
660 		notifier.setEvent(event);
661 		for (Class eventClass : eventClasses) {
662 			eventListeners.notify(eventClass, notifier);
663 		}
664 	}
665 	
666 	/**
667 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
668 	 * @param event
669 	 */
670 	private void notifyLevelBListeners(IWorldObjectEvent event) {
671 		IWorldObject object = event.getObject();
672 		Collection<Class> objectClasses = ClassUtils.getSubclasses(object.getClass());
673 		notifier.setEvent(event);
674 		for (Class objectClass : objectClasses) {
675 			objectsListeners.notify(objectClass, notifier);
676 		}
677 	}
678 
679 	
680 	/**
681 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
682 	 * @param event
683 	 */
684 	private void notifyLevelCListeners(IWorldObjectEvent event) {
685 		Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
686 		Collection<Class> objectClasses = ClassUtils.getSubclasses(event.getObject().getClass());
687 		notifier.setEvent(event);
688 		for (Class eventClass : eventClasses) {
689 			ListenersMap<Class> listeners = objectEventListeners.get(eventClass);
690 			if (listeners == null) continue;
691 			if (!listeners.hasListeners()) continue;
692 			for (Class objectClass : objectClasses) {
693 				listeners.notify(objectClass, notifier);			
694 			}
695 		}
696 	}
697 	
698 	/**
699 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
700 	 * @param event
701 	 */
702 	private void notifyLevelDListeners(IWorldObjectEvent event) {
703 		notifier.setEvent(event);
704 		specificObjectListeners.notify(event.getId(), notifier);
705 	}
706 	
707 	/**
708 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
709 	 * @param event
710 	 */
711 	private void notifyLevelEListeners(IWorldObjectEvent event) {
712 		notifier.setEvent(event);
713 		WorldObjectId objectId = event.getId();
714 		ListenersMap<Class> listeners = specificObjectEventListeners.get(objectId);
715 		if (listeners.hasListeners()) {
716 			Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
717 			notifier.setEvent(event);
718 			for (Class eventClass : eventClasses) {
719 				listeners.notify(eventClass, notifier);			
720 			}
721 		}
722 	}
723 	
724 	/**
725 	 * Process new IWorldEvent - DO NOT CALL SEPARATELY - must be called only from raiseEvent(),
726 	 * that forbids recursion of its calls.
727 	 * <p><p>
728 	 * Contains the sequence in which the listeners are informed about the event.
729 	 * @param event
730 	 */
731 	private void innerRaiseEvent(IWorldEvent event) {
732 		if (log.isLoggable(Level.FINEST)) log.finest("notifying " + event);
733 		notifyLevelAListeners(event);		
734 		if (event instanceof IWorldObjectEvent) {
735 			// now we may notify other listeners as well
736 			IWorldObjectEvent objectEvent = (IWorldObjectEvent)event;
737 			notifyLevelBListeners(objectEvent);
738 			notifyLevelCListeners(objectEvent);
739 			notifyLevelDListeners(objectEvent);
740 			notifyLevelEListeners(objectEvent);
741 		}
742 	}
743 
744 	@Override
745 	public String toString() {
746 		if (this == null) return "AbstractWorldView-instantiating";
747 		return getClass().getSimpleName();
748 	}
749 	
750 }