View Javadoc

1   package cz.cuni.amis.pogamut.multi.communication.worldview.impl;
2   
3   import java.util.Collection;
4   import java.util.Collections;
5   import java.util.HashMap;
6   import java.util.LinkedList;
7   import java.util.Map;
8   import java.util.Queue;
9   import java.util.Set;
10  import java.util.concurrent.ConcurrentLinkedQueue;
11  import java.util.logging.Level;
12  import java.util.logging.Logger;
13  
14  import cz.cuni.amis.pogamut.base.agent.IAgentId;
15  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEvent;
16  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
18  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
19  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
20  import cz.cuni.amis.pogamut.base.communication.worldview.object.WorldObjectId;
21  import cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies;
22  import cz.cuni.amis.pogamut.base.component.controller.ISharedComponentControlHelper;
23  import cz.cuni.amis.pogamut.base.component.controller.ISharedComponentController;
24  import cz.cuni.amis.pogamut.base.component.controller.SharedComponentControlHelper;
25  import cz.cuni.amis.pogamut.base.component.controller.SharedComponentController;
26  import cz.cuni.amis.pogamut.base.component.lifecyclebus.ILifecycleBus;
27  import cz.cuni.amis.pogamut.multi.agent.ITeamId;
28  import cz.cuni.amis.pogamut.multi.agent.impl.TeamedAgentId;
29  import cz.cuni.amis.pogamut.multi.communication.worldview.ILocalWorldView;
30  import cz.cuni.amis.pogamut.multi.communication.worldview.ISharedWorldView;
31  import cz.cuni.amis.pogamut.multi.communication.worldview.object.ISharedProperty;
32  import cz.cuni.amis.pogamut.multi.communication.worldview.object.ISharedWorldObject;
33  import cz.cuni.amis.pogamut.multi.communication.worldview.object.IStaticWorldObject;
34  import cz.cuni.amis.pogamut.multi.communication.worldview.property.PropertyId;
35  import cz.cuni.amis.pogamut.multi.utils.timekey.TimeKey;
36  import cz.cuni.amis.pogamut.multi.utils.timekey.TimeKeyManager;
37  import cz.cuni.amis.utils.ClassUtils;
38  import cz.cuni.amis.utils.exception.PogamutException;
39  import cz.cuni.amis.utils.listener.IListener;
40  import cz.cuni.amis.utils.listener.Listeners;
41  import cz.cuni.amis.utils.listener.ListenersMap;
42  import cz.cuni.amis.utils.maps.HashTriMap;
43  import cz.cuni.amis.utils.maps.LazyMap;
44  import cz.cuni.amis.utils.maps.WeakHashQuadMap;
45  import cz.cuni.amis.utils.maps.WeakHashTriMap;
46  import cz.cuni.amis.utils.token.Token;
47  import cz.cuni.amis.utils.token.Tokens;
48  
49  @SuppressWarnings("unchecked")
50  public abstract class AbstractSharedWorldView implements ISharedWorldView {
51  
52  	public static final Token COMPONENT_ID = Tokens.get("AbstractSharedWorldView");
53  	
54  	/**
55  	 * LocalWorldViews registered with this sharedWorldView
56  	 */
57  	protected HashMap<TeamedAgentId, ILocalWorldView> localWorldViews = new HashMap<TeamedAgentId, ILocalWorldView>();
58  	
59  	/**
60  	 * Holds all sharedProperties in the world weakly-referenced by timeKey, this map is used for storing shadowCopies of properties.
61  	 */
62  	protected WeakHashQuadMap<TimeKey, ITeamId, WorldObjectId, PropertyId, ISharedProperty> sharedProperties =
63  		new WeakHashQuadMap<TimeKey, ITeamId, WorldObjectId, PropertyId, ISharedProperty>(8,8,32,8);
64  	
65  	/**
66  	 * Synchronized version of sharedProperties. If you iterate over this map, you must still synchronize manually however!
67  	 */
68  	protected Map<TimeKey, Map<ITeamId, Map<WorldObjectId, Map<PropertyId, ISharedProperty>>>> syncSharedProperties = 
69  		Collections.synchronizedMap( sharedProperties );
70  	
71  	/**
72  	 * SharedProperties currently considered most-recent.
73  	 */
74  	protected HashTriMap<ITeamId, WorldObjectId, PropertyId, ISharedProperty> currentSharedProperties = 
75  		new HashTriMap<ITeamId, WorldObjectId, PropertyId, ISharedProperty>(8,32,8);
76  	
77  	/**
78  	 * Synchronized version of currentSharedProperties. If you iterate over this map, you must still synchronize manually however!
79  	 */
80  	protected Map<ITeamId, Map<WorldObjectId, Map<PropertyId, ISharedProperty>>> syncCurrentSharedProperties =
81  		Collections.synchronizedMap( currentSharedProperties );
82  	
83  	/**
84  	 * Map of staticWorldObjects, these objects never change, so there is no need for shadow copies.
85  	 */
86  	protected Map<WorldObjectId, IStaticWorldObject> staticWorldObjects =
87  		Collections.synchronizedMap( new HashMap<WorldObjectId, IStaticWorldObject>(32) );
88  	
89  	/**
90  	 * Cached sharedWorldObjects.
91  	 */
92  	protected WeakHashTriMap<TimeKey, ITeamId, WorldObjectId, ISharedWorldObject> sharedWorldObjects = 
93  		new WeakHashTriMap<TimeKey, ITeamId, WorldObjectId, ISharedWorldObject>(8,8,32);
94  	
95  	/**
96  	 * Synchronized version of cached sharedWorldObjects.
97  	 */
98  	protected Map<TimeKey, Map<ITeamId, Map<WorldObjectId, ISharedWorldObject>>> syncSharedWorldObjects = 
99  		Collections.synchronizedMap( sharedWorldObjects );
100 	
101 	protected HashMap<WorldObjectId, Class> idClassMap = new HashMap<WorldObjectId, Class>();
102 	
103 	protected Map<WorldObjectId, Class> syncIdClassMap = Collections.synchronizedMap( idClassMap ); 
104 	
105 	//WORLDVIEW CONTROL//
106 	
107 	/**
108 	 * This method is  called when a new localWorldView is created and wants to use this sharedWorldView,
109 	 * the method registers the LocalWorldView with the sharedWorldView's sharedComponentBus and also
110 	 * internally stores the information about which WorldViews are registered to it.
111 	 * 
112 	 * @param localWV The local WorldView to to register.
113 	 * @param bus ILifecycleBus of the corresponding LocalWorldView
114 	 */
115 	@Override
116 	public void registerLocalWorldView( ILocalWorldView localWV, ILifecycleBus bus) {
117 		this.localWorldViews.put( (TeamedAgentId)localWV.getAgentId(), localWV);
118 		this.addComponentBus( localWV.getAgentId(), bus, new ComponentDependencies() );
119 	}
120 	
121 	//OBJECTS//
122 	
123 	/**
124 	 * Returns exactly the requested property
125 	 */
126 	protected ISharedProperty getSharedProperty(PropertyId id, ITeamId teamId, TimeKey time) {
127 		ISharedProperty result = syncSharedProperties.get(time).get(teamId).get(id.getWorldObjectId()).get(id);
128 		if ( result != null )
129 		{
130 			return result;
131 		}
132 		return currentSharedProperties.get(teamId, id.getWorldObjectId(), id);
133 	}
134 	
135 	/**
136 	 * Returns all shared properties belonging to the specified object.
137 	 * @param objectId
138 	 * @param teamId
139 	 * @param time
140 	 * @return
141 	 */
142 	protected Collection<ISharedProperty> getSharedProperties(WorldObjectId objectId, ITeamId teamId, TimeKey time)
143 	{
144 		LinkedList<ISharedProperty> lst = new LinkedList<ISharedProperty>();
145 		Set<PropertyId> set = syncCurrentSharedProperties.get(teamId).get(objectId).keySet();
146 		for ( PropertyId propId : set)
147 		{
148 			lst.add( getSharedProperty(propId, teamId, time) );
149 		}
150 		return lst;
151 	}
152 	
153 
154 	/**
155 	 * Creates a sharedWorldObject of the specified id.
156 	 * This method constructs the objects from sharedProperties so it must be overriden by a WorldView that is aware of sharedObjectTypes
157 	 * and can construct the correct objects.
158 	 * @param id
159 	 * @param teamId
160 	 * @param time
161 	 * @return
162 	 */
163 	protected abstract ISharedWorldObject createSharedObject(Class msgClass, WorldObjectId id, ITeamId teamId, TimeKey time);
164 
165 	@Override
166 	public ISharedWorldObject getShared(ITeamId teamId, WorldObjectId objectId, TimeKey time) {
167 		ISharedWorldObject value = sharedWorldObjects.get(time, teamId, objectId);
168 		
169 		if ( value != null ) // is object pre-cached
170 		{
171 			return value;
172 		}
173 	
174 		Class msgClass = syncIdClassMap.get(objectId);
175 		value =  createSharedObject(msgClass, objectId, teamId, time); //if not, create it
176 		if (value == null) {
177 			throw new PogamutException("SharedObject for objectId=" + objectId + ", teamId = " + teamId + ", time = " + time + ", could not have been created, createSharedObject(objectId, teamId, time) returned null!", this);
178 		}
179 		sharedWorldObjects.put(time, teamId, objectId, value); //cache the object
180 		return value;
181 	}
182 
183 	@Override
184 	public IStaticWorldObject getStatic(WorldObjectId id) {
185 		return staticWorldObjects.get(id);
186 	}
187 	
188 	//PROTECTED OBJECT METHODS//
189 	
190 	/**
191 	 * if the object already exists, no changes are made
192 	 */
193 	protected void addStaticWorldObject(IStaticWorldObject object) {
194 		synchronized(this.staticWorldObjects) {
195 			this.staticWorldObjects.put(object.getId(), object);
196 		}
197 	}
198 	
199 	protected void removeStaticWorldObject(WorldObjectId id) {
200 		synchronized(this.staticWorldObjects) {
201 			this.staticWorldObjects.remove(id);
202 		}
203 	}
204 	
205 	protected void removeStaticWorldObject(IStaticWorldObject object) {
206 		synchronized(this.staticWorldObjects) {
207 			this.staticWorldObjects.remove(object.getId());
208 		}
209 	}
210 	
211 	/**
212 	 * adds this shared property only for the specified team
213 	 * @param property
214 	 * @param teamId
215 	 */
216 	protected void addSharedProperty(ISharedProperty property, ITeamId teamId) {
217 		synchronized(syncCurrentSharedProperties) {
218 			syncCurrentSharedProperties.get(teamId).get(property.getObjectId()).put(property.getPropertyId(),property);
219 		}
220 	}
221 	
222 	/**
223 	 * adds this shared property for all teams
224 	 * @param property
225 	 */
226 	protected void addSharedProperty(ISharedProperty property) {
227 		synchronized(syncCurrentSharedProperties) {
228 			for ( ITeamId team : syncCurrentSharedProperties.keySet() ) {
229 				this.addSharedProperty(property, team);
230 			}
231 		}
232 	}
233 	
234 	protected void removeSharedProperty(ISharedProperty property, ITeamId teamId) {
235 		if (teamId == null) {
236 			removeSharedProperty(property);
237 			return;
238 		}
239 		synchronized(syncCurrentSharedProperties) {
240 			this.syncCurrentSharedProperties.get(teamId).get(property.getObjectId()).remove(property.getPropertyId());
241 		}
242 	}
243 	
244 	protected void removeSharedProperty(ISharedProperty property) {
245 		synchronized(syncCurrentSharedProperties) {
246 			for (ITeamId team : syncCurrentSharedProperties.keySet()) {
247 				this.removeSharedProperty(property, team);
248 			}
249 		}
250 	}
251 	
252 	/**
253 	 * adds all shared properties from this object for all teams
254 	 * @param object
255 	 */
256 	protected void addSharedWorldObject(ISharedWorldObject object) {
257 		synchronized(syncCurrentSharedProperties) {
258 			for ( ISharedProperty property : object.getProperties().values() ) {
259 				addSharedProperty( property );
260 			}
261 		}
262 	}
263 	
264 	/**
265 	 * Adds the provided sharedProperty for all currently held timeKeys. Any old properties inserted before will NOT be overriden, this method
266 	 * only adds new shadowCopies.
267 	 * @param property
268 	 * @param teamId
269 	 * @param eventTime time of the event causing the property update
270 	 */
271 	protected void addOldSharedProperty(ISharedProperty property, ITeamId teamId, long eventTime) {
272 		for (Long t : TimeKeyManager.get().getHeldKeys()) 
273 		{
274 			if (t < eventTime) 
275 			{
276 				TimeKey timeKey = TimeKey.get(t);
277 				Map<PropertyId, ISharedProperty> props = sharedProperties.get(timeKey, teamId, property.getObjectId());
278 				synchronized(props) 
279 				{
280 					ISharedProperty old = props.get(property.getPropertyId());
281 					if ( old == null) 
282 					{
283 						
284 						props.put(property.getPropertyId(), property);
285 					}
286 				}
287 			}
288 		}
289 	}
290 	
291 	
292 	//LISTENERS//
293 	
294 	private static class ListenerNotifier<T> implements Listeners.ListenerNotifier<IListener> {
295 
296 		/**
297 		 * Event that is being processed.
298 		 */
299 		private T event = null;
300 		
301 		@Override
302 		public T getEvent() {
303 			return event;
304 		}
305 		
306 		public void setEvent(T event) {
307 			this.event = event;			
308 		}
309 
310 		/**
311 		 * Method that is used to notify the listener.
312 		 */
313 		@Override
314 		public void notify(IListener listener) {
315 			listener.notify(event);
316 		}
317 		
318 	}
319 	
320 	/**
321 	 * Notifier object - preallocated, this will raise events on the listeners.
322 	 */
323 	private ListenerNotifier notifier = new ListenerNotifier();
324 
325 	
326 	/**
327 	 * Level A Listeners
328 	 * <p><p>
329 	 * Map of the event listeners, key is the event class where the listener is hooked to.
330 	 */
331 	private ListenersMap<Class> eventListeners = new ListenersMap<Class>();
332 	
333 	/**
334 	 * Level B Listeners
335 	 * <p><p>
336 	 * Map of the object class to the object listeners.
337 	 */
338 	private ListenersMap<Class> objectsListeners = new ListenersMap<Class>();
339 	
340 	/**
341 	 * Level C listeners
342 	 * <p><p>
343 	 * Map of event listeners on some object class.
344 	 * <p><p>
345 	 * First key is eventClass, second key is objectClass.
346 	 */
347 	private Map<Class, ListenersMap<Class>> objectEventListeners = 
348 		Collections.synchronizedMap(
349 			new LazyMap<Class, ListenersMap<Class>>(){
350 
351 				@Override
352 				protected ListenersMap<Class> create(Class key) {
353 					return new ListenersMap<Class>();
354 				}
355 			}
356 		);
357 		
358 	/**
359 	 * Level D Listeners
360 	 * <p><p>
361 	 * Listeners listening on all events on a specific object.
362 	 */
363 	private ListenersMap<WorldObjectId> specificObjectListeners = new ListenersMap<WorldObjectId>();
364 	
365 	/**
366 	 * Level E Listeners
367 	 * <p><p>
368 	 * Listeners listening for some specific event on some specific object.
369 	 * <p><p>
370 	 * Map of (IWorldObjectId, class of IWorldEvent or desc.).  
371 	 */
372 	private Map<WorldObjectId, ListenersMap<Class>> specificObjectEventListeners = 
373 		Collections.synchronizedMap(
374 			new LazyMap<WorldObjectId, ListenersMap<Class>>() {
375 
376 				@Override
377 				protected ListenersMap<Class> create(WorldObjectId key) {
378 					return new ListenersMap<Class>();
379 				}
380 			}
381 		);
382 		
383 	    
384 	/**
385 	 * Flag that is telling us whether there is an event being processed or not.
386 	 * <p><p>
387 	 * It is managed only by raiseEvent() method - DO NOT MODIFY OUTSIDE IT!
388 	 */
389 	private boolean raiseEventProcessing = false;
390 	
391 	/**
392 	 * List of events we have to process.
393 	 * <p><p>
394 	 * It is managed only by raiseEvent() method - DO NOT MODIFY THIS OUTSIDE THAT METHOD!
395 	 */
396 	private Queue<IWorldEvent> raiseEventsList = new ConcurrentLinkedQueue<IWorldEvent>();
397 		
398 	protected Logger log;
399 
400 	protected ISharedComponentController<ISharedWorldView> controller;
401 
402 	public AbstractSharedWorldView( Logger logger) {
403 		this.log = logger;
404 		this.controller = new SharedComponentController<ISharedWorldView>(this, control, logger);
405 		
406 		this.eventListeners.setLog(log, "LevelA");
407 		this.objectsListeners.setLog(log, "LevelB");
408 		this.specificObjectListeners.setLog(log, "LevelD");
409 	}
410 	
411 	//
412 	//
413 	// WORLD VIEW CONTROL
414 	//
415 	//
416 	
417 	@Override
418 	public void addComponentBus(IAgentId agentId, ILifecycleBus bus,
419 			ComponentDependencies dependencies) {
420 		this.controller.addComponentBus(agentId, bus, dependencies);
421 		
422 	}
423 
424 	@Override
425 	public void removeComponentBus(IAgentId agentId, ILifecycleBus bus) {
426 		this.controller.removeComponentBus(agentId, bus);		
427 	}
428 
429    
430 	
431     protected ISharedComponentControlHelper control = new SharedComponentControlHelper() {
432 		
433     	@Override
434 		public void start() throws PogamutException {
435     		AbstractSharedWorldView.this.start();
436 		}
437     	
438     	@Override
439 		public void prePause() throws PogamutException {
440     		AbstractSharedWorldView.this.prePause();
441 		}
442     	
443     	@Override
444 		public void pause() throws PogamutException {
445     		AbstractSharedWorldView.this.pause();
446     	}
447     	
448     	@Override
449 		public void resume() throws PogamutException {
450     		AbstractSharedWorldView.this.resume();
451     	}
452     	
453     	@Override
454 		public void preStop() throws PogamutException {
455     		AbstractSharedWorldView.this.preStop();
456 		}
457     	
458     	@Override
459 		public void stop() throws PogamutException {
460     		AbstractSharedWorldView.this.stop();
461 		}
462 		
463     	@Override
464 		public void kill() {
465     		AbstractSharedWorldView.this.kill();
466 		}
467 		
468 		@Override
469 		public void reset() {
470 			AbstractSharedWorldView.this.reset();
471 		}
472 		
473 	};
474 	
475 	/**
476 	 * Cleans up internal data structures, called from start/stop/kill/reset methods.
477 	 * <p><p>
478 	 * If you override this method, do not forget to call super.cleanUp().
479 	 */
480 	protected void cleanUp() {
481 		// TODO: srlok: should not we clear more stuff?
482 		synchronized( this.sharedProperties ) {
483 			sharedProperties.clear();
484 		}
485 		synchronized(this.staticWorldObjects) {
486 			staticWorldObjects.clear();
487 		}
488 		synchronized(this.sharedWorldObjects)
489 		{
490 			sharedWorldObjects.clear();
491 		}
492 		synchronized(raiseEventsList) {	
493 			raiseEventsList.clear();
494 		}
495 	}
496 	
497 	/**
498 	 * Starts the world view. 
499 	 * <p><p>
500 	 * If you override this method, do not forget to call super.start().
501 	 */
502 	protected void start() {
503 		cleanUp();
504 	}
505 	
506 	/**
507 	 * Pre-pauses the world view.
508 	 * <p><p>
509 	 * If you override this method, do not forget to call super.preStop().
510 	 */
511 	protected void prePause() {
512 	}
513 	
514 	/**
515 	 * Pauses the world view. 
516 	 * <p><p>
517 	 * If you override this method, do not forget to call super.start().
518 	 */
519 	protected void pause() {
520 	}
521 	
522 	/**
523 	 * Resumes the world view. 
524 	 * <p><p>
525 	 * If you override this method, do not forget to call super.start().
526 	 */
527 	protected void resume() {
528 	}
529 	
530 	/**
531 	 * Pre-stops the world view.
532 	 * <p><p>
533 	 * If you override this method, do not forget to call super.preStop().
534 	 */
535 	protected void preStop() {
536 	}
537 	
538 	/**
539 	 * Stops the world view.
540 	 * <p><p>
541 	 * If you override this method, do not forget to call super.stop().
542 	 */
543 	protected void stop() {
544 		cleanUp();
545 	}
546 	
547 	/**
548 	 * Kills the world view.
549 	 * <p><p>
550 	 * If you override this method, do not forget to call super.stop().
551 	 */
552 	protected void kill() {
553 		cleanUp();
554 	}
555 
556 	/**
557 	 * Resets the world view so it is start()able again.
558 	 * <p><p>
559 	 * If you override this method, do not forget to call super.reset().
560 	 */
561 	protected void reset() {
562 		cleanUp();
563 	}
564 	
565 	protected boolean isRunning() {
566 		return controller.isRunning();
567 	}
568 	
569 	protected boolean isPaused() {
570 		return controller.isPaused();
571 	}
572 	
573 	@Override
574 	public Token getComponentId() {
575 		return COMPONENT_ID;
576 	}
577 	
578 	/////////////
579 	//LISTENERS//
580 	////////////
581 	
582 	@Override
583 	public void addEventListener(Class<?> event, IWorldEventListener<?> listener) {
584 		eventListeners.add(event, listener);
585 	}
586 	
587 	@Override
588 	public void addObjectListener(Class<?> objectClass, IWorldObjectEventListener<?, ?> listener) {
589 		objectsListeners.add(objectClass, listener);
590 	}
591 
592 	@Override
593 	public void addObjectListener(Class<?> objectClass, Class<?> eventClass, IWorldObjectEventListener<?,?> listener) {
594 		ListenersMap<Class> listeners = objectEventListeners.get(eventClass);
595 		listeners.add(objectClass, listener);
596 	}
597 	
598 	@Override
599 	public void addObjectListener(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
600 		specificObjectListeners.add(objectId, listener);
601 	}
602 	
603 	@Override
604 	public void addObjectListener(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
605 		ListenersMap<Class> listeners = specificObjectEventListeners.get(objectId);
606 		listeners.add(eventClass, listener);
607 	}
608 	
609 	@Override
610 	public boolean isListening(Class<?> eventClass, IWorldEventListener<?> listener) {
611 		return eventListeners.isListening(eventClass, listener);
612 	}
613 	
614 	@Override
615 	public boolean isListening(Class<?> objectClass, IWorldObjectEventListener<?, ?> listener) {
616 		return objectsListeners.isListening(listener);
617 	}
618 
619 
620 	@Override
621 	public boolean isListening(Class<?> objectClass, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
622 		if (objectEventListeners.containsKey(objectClass)) { // be careful not to create unnecessary ListenersMap
623 			return objectEventListeners.get(eventClass).isListening(objectClass, listener);
624 		} else {
625 			return false;
626 		}
627 	}
628 
629 	@Override
630 	public boolean isListening(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
631 		return specificObjectListeners.isListening(objectId, listener);
632 	}
633 
634 	@Override
635 	public boolean isListening(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
636 		if (specificObjectEventListeners.containsKey(objectId)) { // be careful not to create unnecessary ListenersMap
637 			return specificObjectEventListeners.get(objectId).isListening(eventClass, listener);
638 		} else {
639 			return false;
640 		}
641 		
642 	}
643 	
644 	@Override
645 	public boolean isListening(IWorldEventListener<?> listener) {
646 		if (eventListeners.isListening(listener) ||
647 			specificObjectListeners.isListening(listener)) {
648 			return true;
649 		}
650 		synchronized(objectEventListeners) {
651 			for (ListenersMap<Class> listeners : objectEventListeners.values()) {
652 				if (listeners.isListening(listener)) return true;
653 			}
654 		}
655 		synchronized(specificObjectEventListeners) {
656 			for (ListenersMap<Class> listeners : specificObjectEventListeners.values()) {
657 				if (listeners.isListening(listener)) return true;
658 			}
659 		}
660 		return false;
661 	}
662 
663 	@Override
664 	public void removeEventListener(Class<?> eventClass, IWorldEventListener<?> listener) {
665 		eventListeners.remove(eventClass, listener);
666 	}
667 
668 	@Override
669 	public void removeObjectListener(Class<?> objectClass,	IWorldObjectEventListener<?, ?> listener) {
670 		objectsListeners.remove(objectClass, listener);
671 	}
672 	
673 	@Override
674 	public void removeObjectListener(Class<?> objectClass, Class<?> eventClass,	IWorldObjectEventListener<?, ?> listener) {
675 		if (objectEventListeners.containsKey(eventClass)) { // be careful not to create unnecessary ListenersMap
676 			objectEventListeners.get(eventClass).remove(objectClass, listener);
677 		}
678 	}
679 
680 	@Override
681 	public void removeObjectListener(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
682 		specificObjectListeners.remove(objectId, listener);
683 	}
684 
685 	@Override
686 	public void removeObjectListener(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
687 		if (specificObjectEventListeners.containsKey(objectId)) { // be careful not to create unnecessary ListenersMap
688 			specificObjectEventListeners.get(objectId).remove(eventClass, listener);
689 		}
690 	}
691 	
692 		
693 	@Override
694 	public void removeListener(IWorldEventListener<?> listener) {
695 		eventListeners.remove(listener);
696 		synchronized(objectEventListeners) {
697 			for (ListenersMap<Class> listeners : objectEventListeners.values()) {
698 				listeners.remove(listener);
699 			}
700 		}
701 		specificObjectListeners.remove(listener);
702 		specificObjectEventListeners.remove(listener);
703 	}
704 
705 	// EVENT PROCESSING
706 	
707 	
708 	/**
709 	 * Process new IWorldEvent - notify all the listeners about it. Forbids recursion.
710 	 * <p><p>
711 	 * Use in the descendants to process new IWorldChangeEvent.
712 	 * <p><p>
713 	 * Does not catch any exceptions!
714 	 * <p><p>
715 	 * Synchronized!
716 	 * 
717 	 * @param event
718 	 */
719 	protected synchronized void raiseEvent(IWorldEvent event) {
720 		// method is synchronized - only one thread inside at given time
721 		
722 		// is this method recursively called? 
723 		if (raiseEventProcessing) {
724 			// yes it is -> that means the previous event has not been
725 			// processed! ... store this event and allows the previous one
726 			// to be fully processed (e.g. postpone raising this event)
727 			raiseEventsList.add(event);
728 			return;
729 		} else {
730 			// no it is not ... so raise the flag that we're inside the method
731 			raiseEventProcessing = true;
732 		}
733 		// process event
734 				
735 		innerRaiseEvent(event);
736 		
737 		// check the events list size, do we have more events to process?
738 		while(raiseEventsList.size() != 0) {
739 			// yes we do -> do it!
740 			innerRaiseEvent(raiseEventsList.poll());			
741 		}
742 		// all events has been processed, drop the flag that we're inside the method
743 		raiseEventProcessing = false;			
744 	}
745 		
746 	//
747 	//
748 	// EVENT PROCESSING
749 	//
750 	//
751 		
752 	/**
753 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
754 	 * @param event
755 	 */
756 	private void notifyLevelAListeners(IWorldEvent event) {
757 		Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
758 		notifier.setEvent(event);
759 		for (Class eventClass : eventClasses) {
760 			eventListeners.notify(eventClass, notifier);
761 		}
762 	}
763 	
764 	/**
765 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
766 	 * @param event
767 	 */
768 	private void notifyLevelBListeners(IWorldObjectEvent event) {
769 		IWorldObject object = event.getObject();
770 		Collection<Class> objectClasses = ClassUtils.getSubclasses(object.getClass());
771 		notifier.setEvent(event);
772 		for (Class objectClass : objectClasses) {
773 			objectsListeners.notify(objectClass, notifier);
774 		}
775 	}
776 
777 	
778 	/**
779 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
780 	 * @param event
781 	 */
782 	private void notifyLevelCListeners(IWorldObjectEvent event) {
783 		Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
784 		Collection<Class> objectClasses = ClassUtils.getSubclasses(event.getObject().getClass());
785 		notifier.setEvent(event);
786 		for (Class eventClass : eventClasses) {
787 			ListenersMap<Class> listeners = objectEventListeners.get(eventClass);
788 			if (listeners == null) continue;
789 			if (!listeners.hasListeners()) continue;
790 			for (Class objectClass : objectClasses) {
791 				listeners.notify(objectClass, notifier);			
792 			}
793 		}
794 	}
795 	
796 	/**
797 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
798 	 * @param event
799 	 */
800 	private void notifyLevelDListeners(IWorldObjectEvent event) {
801 		notifier.setEvent(event);
802 		specificObjectListeners.notify(event.getId(), notifier);
803 	}
804 	
805 	/**
806 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
807 	 * @param event
808 	 */
809 	private void notifyLevelEListeners(IWorldObjectEvent event) {
810 		notifier.setEvent(event);
811 		WorldObjectId objectId = event.getId();
812 		ListenersMap<Class> listeners = specificObjectEventListeners.get(objectId);
813 		if (listeners.hasListeners()) {
814 			Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
815 			notifier.setEvent(event);
816 			for (Class eventClass : eventClasses) {
817 				listeners.notify(eventClass, notifier);			
818 			}
819 		}
820 	}
821 	
822 	/**
823 	 * Process new IWorldEvent - DO NOT CALL SEPARATELY - must be called only from raiseEvent(),
824 	 * that forbids recursion of its calls.
825 	 * <p><p>
826 	 * Contains the sequence in which the listeners are informed about the event.
827 	 * @param event
828 	 */
829 	private void innerRaiseEvent(IWorldEvent event) {
830 		if (log.isLoggable(Level.FINEST)) log.finest("notifying " + event);
831 		notifyLevelAListeners(event);		
832 		if (event instanceof IWorldObjectEvent) {
833 			// now we may notify other listeners as well
834 			IWorldObjectEvent objectEvent = (IWorldObjectEvent)event;
835 			notifyLevelBListeners(objectEvent);
836 			notifyLevelCListeners(objectEvent);
837 			notifyLevelDListeners(objectEvent);
838 			notifyLevelEListeners(objectEvent);
839 		}
840 	}
841 }