View Javadoc

1   package cz.cuni.amis.pogamut.multi.communication.worldview.impl;
2   
3   import java.lang.ref.WeakReference;
4   import java.util.Collection;
5   import java.util.Collections;
6   import java.util.HashMap;
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  
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.object.IWorldObject;
16  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
18  import cz.cuni.amis.pogamut.base.communication.worldview.object.WorldObjectId;
19  import cz.cuni.amis.pogamut.base.component.IComponent;
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.component.lifecyclebus.ILifecycleBus;
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.pogamut.multi.agent.ITeamedAgentId;
28  import cz.cuni.amis.pogamut.multi.communication.worldview.ILocalWorldView;
29  import cz.cuni.amis.pogamut.multi.communication.worldview.ISharedWorldView;
30  import cz.cuni.amis.pogamut.multi.communication.worldview.object.ICompositeWorldObject;
31  import cz.cuni.amis.pogamut.multi.communication.worldview.object.ILocalWorldObject;
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.utils.exception.TimeKeyNotLockedException;
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.AbstractLazyMap;
43  import cz.cuni.amis.utils.maps.HashMapSet;
44  import cz.cuni.amis.utils.maps.LazyMap;
45  import cz.cuni.amis.utils.maps.WeakHashMapMap;
46  import cz.cuni.amis.utils.token.Token;
47  import cz.cuni.amis.utils.token.Tokens;
48  
49  /**
50   * WorldView responsible for single agent.
51   * The LocalWorldView always knows which timeKey is considered actual to its agent and makes sure, that the objects stay consistent
52   * for that timeKey and that newer objects are stored for further use.
53   * Abstract worldView handles data management and listener handling.
54   * @author srlok
55   *
56   */
57  @SuppressWarnings({"rawtypes", "unchecked"})
58  public abstract class AbstractLocalWorldView implements ILocalWorldView {
59  	
60  	public static final Token COMPONENT_ID = Tokens.get("AbstractLocalWorldView");
61  	
62  	//sharedWorldView encompassing the entire game
63  	protected ISharedWorldView sharedWorldView;
64  	//agentId of the agent associated with this worldView
65  	protected ITeamedAgentId agentId;
66  	
67  	// OBJECT MAPS //
68  	//local objects//
69  	
70  	//objects belonging to current time-key
71  	protected Map<WorldObjectId, ILocalWorldObject> actLocalWorldObjects =
72  				Collections.synchronizedMap( new HashMap<WorldObjectId, ILocalWorldObject>() );
73  	
74  	//map used to determine the keySet of maps returned when getAll(class) is called.
75  	protected HashMapSet<Class, WorldObjectId> classMap = 
76  				new HashMapSet<Class,WorldObjectId>();
77  
78  	protected Map<Class, Set<WorldObjectId> > syncClassMap = 
79  				Collections.synchronizedMap( classMap );
80  	
81  	//saved different TimeKey-versions of localObjects
82  	WeakHashMapMap<TimeKey, WorldObjectId, ILocalWorldObject> localWorldObjects = 
83  		new WeakHashMapMap<TimeKey, WorldObjectId, ILocalWorldObject>();
84  	
85  	//composite objects //
86  	// ??? //
87  	/**
88  	 * 
89  	 * The map contains: <Class<?>, ? extends Map< WorldObjectId, ? extends ICompositeWorldObject>>
90  	 * But due to generic quirks, we must keep it untemplated.
91  	 */
92  	HashMap compositeClassMap = 
93  		new HashMap<Class<?>, LazyCompositeObjectMap<ICompositeWorldObject>>();
94  	
95  	/**
96  	 * This map acts as a cache for already-created compositeObjects, the weak reference in this map
97  	 * makes sure that objects no longer needed are removed by the garbage collector whenever it is needed.
98  	 */
99  	WeakHashMapMap<TimeKey, WorldObjectId, ICompositeWorldObject> cachedCompositeWorldObjects = 
100 		new WeakHashMapMap<TimeKey, WorldObjectId, ICompositeWorldObject>();
101 	
102 	//TIME KEYS//
103 	TimeKey currentTimeKey;
104 	
105 	
106 	//LISTENERS//
107 	
108 	private static class ListenerNotifier<T> implements Listeners.ListenerNotifier<IListener> {
109 
110 		/**
111 		 * Event that is being processed.
112 		 */
113 		private T event = null;
114 		
115 		@Override
116 		public T getEvent() {
117 			return event;
118 		}
119 		
120 		public void setEvent(T event) {
121 			this.event = event;			
122 		}
123 
124 		/**
125 		 * Method that is used to notify the listener.
126 		 */
127 		@Override
128 		public void notify(IListener listener) {
129 			listener.notify(event);
130 		}
131 		
132 	}
133 	
134 	/**
135 	 * Notifier object - preallocated, this will raise events on the listeners.
136 	 */
137 	private ListenerNotifier notifier = new ListenerNotifier();
138 
139 	
140 	/**
141 	 * Level A Listeners
142 	 * <p><p>
143 	 * Map of the event listeners, key is the event class where the listener is hooked to.
144 	 */
145 	private ListenersMap<Class> eventListeners = new ListenersMap<Class>();
146 	
147 	/**
148 	 * Level B Listeners
149 	 * <p><p>
150 	 * Map of the object class to the object listeners.
151 	 */
152 	private ListenersMap<Class> objectsListeners = new ListenersMap<Class>();
153 	
154 	/**
155 	 * Level C listeners
156 	 * <p><p>
157 	 * Map of event listeners on some object class.
158 	 * <p><p>
159 	 * First key is eventClass, second key is objectClass.
160 	 */
161 	private Map<Class, ListenersMap<Class>> objectEventListeners = 
162 		Collections.synchronizedMap(
163 			new LazyMap<Class, ListenersMap<Class>>(){
164 
165 				@Override
166 				protected ListenersMap<Class> create(Class key) {
167 					ListenersMap<Class> listeners = new ListenersMap<Class>();
168 					listeners.setLog(log, "LevelC-" + key.getSimpleName());
169 					return listeners;
170 				}
171 			}
172 		);
173 		
174 	/**
175 	 * Level D Listeners
176 	 * <p><p>
177 	 * Listeners listening on all events on a specific object.
178 	 */
179 	private ListenersMap<WorldObjectId> specificObjectListeners = new ListenersMap<WorldObjectId>();
180 	
181 	/**
182 	 * Level E Listeners
183 	 * <p><p>
184 	 * Listeners listening for some specific event on some specific object.
185 	 * <p><p>
186 	 * Map of (IWorldObjectId, class of IWorldEvent or desc.).  
187 	 */
188 	private Map<WorldObjectId, ListenersMap<Class>> specificObjectEventListeners = 
189 		Collections.synchronizedMap(
190 			new LazyMap<WorldObjectId, ListenersMap<Class>>() {
191 
192 				@Override
193 				protected ListenersMap<Class> create(WorldObjectId key) {
194 					ListenersMap<Class> listeners = new ListenersMap<Class>();
195 					listeners.setLog(log, "LevelE-" + key.getStringId());
196 					return listeners;
197 				}
198 			}
199 		);
200 		
201 	    
202 	/**
203 	 * Flag that is telling us whether there is an event being processed or not.
204 	 * <p><p>
205 	 * It is managed only by raiseEvent() method - DO NOT MODIFY OUTSIDE IT!
206 	 */
207 	private boolean raiseEventProcessing = false;
208 	
209 	/**
210 	 * List of events we have to process.
211 	 * <p><p>
212 	 * It is managed only by raiseEvent() method - DO NOT MODIFY THIS OUTSIDE THAT METHOD!
213 	 */
214 	private Queue<IWorldEvent> raiseEventsList = new ConcurrentLinkedQueue<IWorldEvent>();
215 		
216 	protected LogCategory log;
217 
218 	protected ILifecycleBus eventBus;
219 
220 	protected ComponentController<IComponent> controller;
221 	
222 
223 	public AbstractLocalWorldView(ComponentDependencies dependencies, ILifecycleBus bus, IAgentLogger logger, ISharedWorldView sharedWV, ITeamedAgentId agentId) {
224 		this.log = logger.getCategory(getComponentId().getToken());		
225 		this.agentId = agentId;
226 		this.eventBus = bus;
227 		this.controller = new ComponentController(this, control, this.eventBus, this.log, dependencies);
228 		this.sharedWorldView = sharedWV;
229 		sharedWorldView.registerLocalWorldView(this, bus);
230 		
231 		this.eventListeners.setLog(log, "LevelA");
232 		this.objectsListeners.setLog(log, "LevelB");
233 		this.specificObjectListeners.setLog(log, "LevelD");
234 		
235 	}
236 	
237 
238 	//
239 	//
240 	// WORLD VIEW CONTROL
241 	//
242 	//
243 	
244     protected IComponentControlHelper control = new ComponentControlHelper() {
245 		
246     	@Override
247 		public void start() throws PogamutException {
248     		log.finest("LocalWorldView [ " + agentId + " ] Starting...");
249     		AbstractLocalWorldView.this.start();
250 		}
251     	
252     	@Override
253 		public void prePause() throws PogamutException {
254     		AbstractLocalWorldView.this.prePause();
255 		}
256     	
257     	@Override
258 		public void pause() throws PogamutException {
259     		AbstractLocalWorldView.this.pause();
260     	}
261     	
262     	@Override
263 		public void resume() throws PogamutException {
264     		AbstractLocalWorldView.this.resume();
265     	}
266     	
267     	@Override
268 		public void preStop() throws PogamutException {
269     		AbstractLocalWorldView.this.preStop();
270 		}
271     	
272     	@Override
273 		public void stop() throws PogamutException {
274     		AbstractLocalWorldView.this.stop();
275 		}
276 		
277     	@Override
278 		public void kill() {
279     		AbstractLocalWorldView.this.kill();
280 		}
281 		
282 		@Override
283 		public void reset() {
284 			AbstractLocalWorldView.this.reset();
285 		}
286 		
287 	};
288 	
289 	/**
290 	 * Cleans up internal data structures, called from start/stop/kill/reset methods.
291 	 * <p><p>
292 	 * If you override this method, do not forget to call super.cleanUp().
293 	 */
294 	protected void cleanUp() {
295 		synchronized( this.actLocalWorldObjects ) {
296 			actLocalWorldObjects.clear();
297 		}
298 		synchronized(classMap) {
299 			classMap.clear();
300 		}
301 		synchronized(raiseEventsList) {	
302 			raiseEventsList.clear();
303 		}
304 	}
305 	
306 	/**
307 	 * Starts the world view. 
308 	 * <p><p>
309 	 * If you override this method, do not forget to call super.start().
310 	 */
311 	protected void start() {
312 		cleanUp();
313 	}
314 	
315 	/**
316 	 * Pre-pauses the world view.
317 	 * <p><p>
318 	 * If you override this method, do not forget to call super.preStop().
319 	 */
320 	protected void prePause() {
321 	}
322 	
323 	/**
324 	 * Pauses the world view. 
325 	 * <p><p>
326 	 * If you override this method, do not forget to call super.start().
327 	 */
328 	protected void pause() {
329 	}
330 	
331 	/**
332 	 * Resumes the world view. 
333 	 * <p><p>
334 	 * If you override this method, do not forget to call super.start().
335 	 */
336 	protected void resume() {
337 	}
338 	
339 	/**
340 	 * Pre-stops the world view.
341 	 * <p><p>
342 	 * If you override this method, do not forget to call super.preStop().
343 	 */
344 	protected void preStop() {
345 	}
346 	
347 	/**
348 	 * Stops the world view.
349 	 * <p><p>
350 	 * If you override this method, do not forget to call super.stop().
351 	 */
352 	protected void stop() {
353 		cleanUp();
354 	}
355 	
356 	/**
357 	 * Kills the world view.
358 	 * <p><p>
359 	 * If you override this method, do not forget to call super.stop().
360 	 */
361 	protected void kill() {
362 		cleanUp();
363 	}
364 
365 	/**
366 	 * Resets the world view so it is start()able again.
367 	 * <p><p>
368 	 * If you override this method, do not forget to call super.reset().
369 	 */
370 	protected void reset() {
371 		cleanUp();
372 	}
373 	
374 	protected boolean isRunning() {
375 		return controller.isRunning();
376 	}
377 	
378 	protected boolean isPaused() {
379 		return controller.isPaused();
380 	}
381 	
382 	@Override
383 	public Token getComponentId() {
384 		return COMPONENT_ID;
385 	}
386 	
387 	
388 	
389 	/**
390 	 * <p>Method used for returning the appropriate CompositeObject type made from the three provided object parts.
391 	 * All parts must share the same WorldObjectId and must belong to the same CompositeObject class. Also, none of the parts
392 	 * may be null. </p>
393 	 * <p>The method should return a proxy-type CompositeObject, meaning that the object will only wrap all objectParts and no data
394 	 * will actually be copied.</p>
395 	 * <p>This method must be <i>overriden</i> by a object-awareWorldView which has information about all the object classes in the world and
396 	 * is able to call the correct constructors</p>
397 	 * @param localObject local part of the object
398 	 * @param sharedObject shared part of the object
399 	 * @param staticObject static part of the object
400 	 * @return Proxy-type compositeObject.
401 	 */
402 	protected abstract ICompositeWorldObject createCompositeObject(ILocalWorldObject localObject, ISharedWorldObject sharedObject, IStaticWorldObject staticObject);
403 	
404 	
405 	/**
406 	 * Returns agentId of the agent associated with this WorldView.
407 	 * @return teamed AgentId 
408 	 */
409 	public ITeamedAgentId getAgentId()
410 	{
411 		return this.agentId;
412 	}
413 	
414 	  //////////////////
415 	 //OBJECT METHODS//
416 	//////////////////
417 	
418 	/**
419 	 * This is a class for lazy maps holding CompositeWorldObjects . These maps will be returned by all getAll methods,
420 	 * the map ensures, that the CompositeObject creation from its parts is delayed as long as possible (it will only be invoked,
421 	 * when the object itself is requested).
422 	 * <br>
423 	 * If you need to iterate over the map, iterate over the keySet, so you will preserve the lazy-behavior.
424 	 */
425 	protected class LazyCompositeObjectMap<T extends ICompositeWorldObject> extends AbstractLazyMap<WorldObjectId,T>
426 	{
427 		
428 		private long currentTime;
429 		private WeakReference<TimeKey> timeKey;
430 		
431 		public LazyCompositeObjectMap( long time)
432 		{
433 			super();
434 			this.currentTime = time;
435 			this.timeKey = new WeakReference<TimeKey>(TimeKey.get(currentTime));
436 		}
437 		
438 		LazyCompositeObjectMap(Set<WorldObjectId> keySet, long time)
439 		{
440 			super(keySet);
441 			this.currentTime = time;
442 			this.timeKey = new WeakReference<TimeKey>(TimeKey.get(currentTime));
443 		}
444 		
445 		public boolean setTimeKey( long newTime)
446 		{
447 			if (currentTimeKey.equals(newTime))
448 			{
449 				return false;
450 			}
451 			super.clearCache();
452 			this.currentTime = newTime;		
453 			this.timeKey = new WeakReference<TimeKey>(TimeKey.get(currentTime));
454 			return true;
455 		}
456 		
457 		@Override
458 		protected T create(Object key) {
459 			return (T)AbstractLocalWorldView.this.get((WorldObjectId)key, this.timeKey.get());
460 		}
461 		
462 	}
463 	
464 	
465 	/////////////////
466 	//LOCAL objects//
467 	/////////////////
468 	
469 	@Override
470 	public ILocalWorldObject getLocal(WorldObjectId objectId) {
471 		return getLocal(objectId, getCurrentTimeKey() );
472 	}
473 
474 	/**
475 	 * Returns the LocalObject associated with the provided TimeKey.
476 	 * Returns the local part of requested WorldObject. A local part for every object contains properties subjective to only one agent.
477 	 * (like isVisible). 
478 	 * @param objectId
479 	 * @param time
480 	 * @return
481 	 */
482 	protected ILocalWorldObject getLocal(WorldObjectId objectId, TimeKey time) {
483 		ILocalWorldObject value = localWorldObjects.get( time, objectId );
484 		if ( value != null) { return value; }
485 		return actLocalWorldObjects.get( objectId );
486 	}
487 
488 	
489 	// COMPOSITE objects //
490 	
491 	/**
492 	 * Returns the object actual to the specified TimeKey.
493 	 * Returns the CompositeWorldObject for the requested Id. Composite objects behave like old WorldObjects, they contain all object fields
494 	 * and the data is guaranteed to be consistent (meaning all fields are actual for the same timeKey).
495 	 * Any object returned by this method should be compatible with any non-multi method (ie. a method expecting just IWorldObject).
496 	 * @param objectId
497 	 * @param time
498 	 * @return
499 	 * @throws
500 	 */
501 	protected ICompositeWorldObject get(WorldObjectId objectId, TimeKey time) {
502 		ICompositeWorldObject obj = cachedCompositeWorldObjects.get( time, objectId);
503 		if ( obj != null )
504 		{
505 			return obj;
506 		}		
507 		else
508 		{
509 		obj = createCompositeObject(
510 				this.getLocal( objectId, time ),
511 				sharedWorldView.getShared(agentId.getTeamId(), objectId, time),
512 				sharedWorldView.getStatic(objectId));
513 				cachedCompositeWorldObjects.put(time,objectId,obj);
514 		}
515 		return obj;
516 	}
517 
518 	@Override
519 	public ICompositeWorldObject get(WorldObjectId objectId) {
520 		return this.get(objectId, getCurrentTimeKey() );
521 	}
522 
523         @Override
524         public <T extends ICompositeWorldObject> T get(WorldObjectId objectId, Class<T> clazz) {
525             IWorldObject obj = get(objectId);
526             if(obj == null){
527                 return null;
528             }
529             else if(clazz.isAssignableFrom(obj.getClass())){
530                 return (T)obj;
531             } else {
532                 throw new ClassCastException("Object with id " + objectId + " is not of class " + clazz);
533             }
534         }                
535 
536 	/**
537 	 * Returns all objects sorted according to class. All of the classMaps are lazy-implemented, so the CompositeObject will only be created
538 	 * on get method. If you need to iterate over the map, but you don't need the actual objects, iterate over the keySet.
539 	 * Do not hold reference to this map! Always use new getAll() call when you need this map again or you risk that some classMaps will get
540 	 * new timeKeys. By calling getAll(Class, time != thisTime).
541 	 * @param time
542 	 * @return
543 	 */
544 	protected Map<Class, Map<WorldObjectId, ICompositeWorldObject>> getAll(TimeKey time) {
545 		for ( Object oC : compositeClassMap.keySet() )
546 		{
547 			Class c = (Class)oC;
548 			((LazyCompositeObjectMap)(compositeClassMap.get(c))).setTimeKey( time.getTime() );
549 		}
550 		return compositeClassMap;
551 	}
552 
553 	//TODO maybe ensuring this doesn't happen???
554 	@Override
555 	/***
556 	 * Careful, do not store this map, always use a new getAll() call since the map can get invalidated when timeKeys change
557 	 * the map returned from this method, will overwrite the sub-map returned from this map.
558 	 */
559 	public Map<Class, Map<WorldObjectId, ICompositeWorldObject>> getAll() {
560 		return getAll(getCurrentTimeKey());
561 	}
562 
563 	/**
564 	 * Returns a lazy-implemented classMap containing CompositeWorldObjects current to the specified TimeKey.
565 	 * <p> WARNING : do not store this map, always use the getAll() method or copy the map if you really need it stored somewhere. 
566 	 * The TimeKey can be overriden by calling another getAll( Class==Class, time!=time) .
567 	 * @param <T>
568 	 * @param type
569 	 * @param time
570 	 * @return
571 	 */
572 	protected <T extends IWorldObject> Map<WorldObjectId, T> getAll(Class<T> type,
573 			TimeKey time) 
574 	{
575 		LazyCompositeObjectMap map = (LazyCompositeObjectMap) this.compositeClassMap.get(type);
576 		if ( map == null )
577 		{
578 			map = new LazyCompositeObjectMap(time.getTime());
579 			compositeClassMap.put(type, map);
580 			return map;
581 		}
582 		
583 		map.setTimeKey(time.getTime());
584 		return map;
585 	}
586 
587 	@Override
588 	public <T extends IWorldObject> Map<WorldObjectId, T> getAll(Class<T> type) {
589 		return getAll(type, getCurrentTimeKey());
590 	}
591 
592 	@Override
593 	public Map<WorldObjectId, ICompositeWorldObject> get() {
594 		return get(getCurrentTimeKey());
595 	}
596 
597 	/**
598 	 * Returns a map of all CompositeWorldObjects in the world. <br>
599 	 * The map is lazy, the Composite objects are created only when a get() method is called.
600 	 * If you need to iterate over this map and you don't need all of the values
601 	 * iterate over the keySet. 
602 	 * <br>
603 	 * The map will contain objects current to the provided TimeKey.
604 	 * @param time
605 	 * @return
606 	 */
607 	protected Map<WorldObjectId, ICompositeWorldObject> get(TimeKey time)
608 	{
609 		LazyCompositeObjectMap objects = new LazyCompositeObjectMap(this.actLocalWorldObjects.keySet(),time.getTime());
610 		return objects;
611 	}
612 	
613 	//SINGLE//
614 	
615 	@Override
616 	public <T extends IWorldObject> T getSingle(Class<T> cls) {
617 		return this.getSingle(cls, this.getCurrentTimeKey());
618 	}
619 	
620 	
621 	protected <T extends IWorldObject> T getSingle(Class<T> cls, TimeKey time) {
622 		Collection<T> vals = getAll(cls, time).values();
623 		if (vals.size() == 1)
624 		{
625 			return vals.iterator().next();
626 		}
627 		throw new IllegalArgumentException();
628 	}
629 	
630 	//protected object methods//
631 	
632 	/**
633 	 * Returns the most recent instance of object with the specified id.
634 	 * 
635 	 * @param id id of the object
636 	 * 
637 	 * @return most recent object instance
638 	 */
639 	protected ILocalWorldObject getMostRecentLocalWorldObject( WorldObjectId id )
640 	{
641 		return this.actLocalWorldObjects.get( id );
642 	}
643 	
644 	/**
645 	 * Adds this object as old for all heldTimeKeys that currently hold no old copy of this object.
646 	 */
647 	protected synchronized void addOldLocalWorldObject( ILocalWorldObject obj, long eventTime)
648 	{
649 		
650 		for (Long time : TimeKeyManager.get().getHeldKeys() )
651 		{
652 			if ( time < eventTime )
653 			{				
654 				synchronized( localWorldObjects )
655 				{
656 					TimeKey timeKey = TimeKey.get(time);
657 					if ( localWorldObjects.get( timeKey, obj.getId() ) == null)
658 					{
659 						//log.fine("Adding shadowCopy : [Et:"+eventTime+";St:"+timeKey.getTime()+"] ; " + obj.toString());
660 						localWorldObjects.put( timeKey , obj.getId(),obj);
661 					}
662 				}
663 			}
664 		}
665 	}
666 	
667 	/**
668 	 * Helper method for adding a new object into all object maps. (used in CREATED events).
669 	 * @param obj
670 	 */
671 	protected synchronized void addLocalWorldObject( ILocalWorldObject obj)
672 	{
673 		actLocalWorldObjects.put(obj.getId(), obj);
674 		for ( Class cls : ClassUtils.getSubclasses(obj.getCompositeClass()) )
675 		{
676 			//????
677 			LazyCompositeObjectMap map = (LazyCompositeObjectMap)compositeClassMap.get(cls);
678 			if ( map == null)
679 			{
680 				map = new LazyCompositeObjectMap( currentTimeKey.getTime() );
681 				compositeClassMap.put(cls, map);
682 			}
683 			map.keySet().add(obj.getId());	
684 			syncClassMap.get(cls).add( obj.getId() );
685 		}
686 		
687 	}
688 	
689 	/**
690 	 * Helper method to remove a localWorldObject from all corresponding object maps (used in DESTROYED events).
691 	 * @param obj
692 	 */
693 	protected synchronized void removeLocalWorldObject( ILocalWorldObject obj)
694 	{
695 		actLocalWorldObjects.remove(obj.getId());
696 		for ( Class cls : ClassUtils.getSubclasses(obj.getCompositeClass()) )
697 		{
698 			LazyCompositeObjectMap map = (LazyCompositeObjectMap)compositeClassMap.get(cls);
699 			if ( map != null )
700 			{
701 				map.keySet().remove(obj.getId());
702 			}
703 			syncClassMap.get(cls).remove(obj.getId());
704 		}
705 		
706 	}
707 	
708 	////////////
709 	//TIME-KEY//
710 	////////////
711 	
712 	/**
713 	 * Method for initializing the first timeKey, returns false if the
714 	 * TimeKey is already set.s
715 	 * @param key
716 	 * @return
717 	 */
718 	@Override
719 	public boolean setInitialTime( TimeKey key )
720 	{
721 		if ( currentTimeKey == null )
722 		{
723 			currentTimeKey = key;
724 			return true;
725 		}
726 		return false;
727 	}
728 	
729 	@Override
730 	public boolean setCurrentTime( TimeKey key )
731 	{
732 		currentTimeKey = key;
733 		return true;
734 	}
735 	
736 	@Override
737 	public TimeKey getCurrentTimeKey() {
738 		return this.currentTimeKey;
739 	}
740 
741 	@Override
742 	public void lockTime(long time) {
743 		TimeKeyManager.get().lock(time);
744 	}
745 
746 	@Override
747 	public void unlockTime(long time) {
748 		try {
749 			TimeKeyManager.get().unlock(time);
750 		} catch (TimeKeyNotLockedException e) {
751 			log.warning("Trying to unlock timeKey : " + time + " which is not locked. ");
752 		}
753 	}
754 
755 	
756 	/////////////
757 	//LISTENERS//
758 	////////////
759 	
760 	@Override
761 	public void addEventListener(Class<?> event, IWorldEventListener<?> listener) {
762 		eventListeners.add(event, listener);
763 	}
764 	
765 	@Override
766 	public void addObjectListener(Class<?> objectClass, IWorldObjectEventListener<?, ?> listener) {
767 		objectsListeners.add(objectClass, listener);
768 	}
769 
770 	@Override
771 	public void addObjectListener(Class<?> objectClass, Class<?> eventClass, IWorldObjectEventListener<?,?> listener) {
772 		ListenersMap<Class> listeners = objectEventListeners.get(eventClass);
773 		listeners.add(objectClass, listener);
774 	}
775 	
776 	@Override
777 	public void addObjectListener(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
778 		specificObjectListeners.add(objectId, listener);
779 	}
780 	
781 	@Override
782 	public void addObjectListener(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
783 		ListenersMap<Class> listeners = specificObjectEventListeners.get(objectId);
784 		listeners.add(eventClass, listener);
785 	}
786 	
787 	@Override
788 	public boolean isListening(Class<?> eventClass, IWorldEventListener<?> listener) {
789 		return eventListeners.isListening(eventClass, listener);
790 	}
791 	
792 	@Override
793 	public boolean isListening(Class<?> objectClass, IWorldObjectEventListener<?, ?> listener) {
794 		return objectsListeners.isListening(listener);
795 	}
796 
797 
798 	@Override
799 	public boolean isListening(Class<?> objectClass, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
800 		if (objectEventListeners.containsKey(objectClass)) { // be careful not to create unnecessary ListenersMap
801 			return objectEventListeners.get(eventClass).isListening(objectClass, listener);
802 		} else {
803 			return false;
804 		}
805 	}
806 
807 	@Override
808 	public boolean isListening(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
809 		return specificObjectListeners.isListening(objectId, listener);
810 	}
811 
812 	@Override
813 	public boolean isListening(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
814 		if (specificObjectEventListeners.containsKey(objectId)) { // be careful not to create unnecessary ListenersMap
815 			return specificObjectEventListeners.get(objectId).isListening(eventClass, listener);
816 		} else {
817 			return false;
818 		}
819 		
820 	}
821 	
822 	@Override
823 	public boolean isListening(IWorldEventListener<?> listener) {
824 		if (eventListeners.isListening(listener) ||
825 			specificObjectListeners.isListening(listener)) {
826 			return true;
827 		}
828 		synchronized(objectEventListeners) {
829 			for (ListenersMap<Class> listeners : objectEventListeners.values()) {
830 				if (listeners.isListening(listener)) return true;
831 			}
832 		}
833 		synchronized(specificObjectEventListeners) {
834 			for (ListenersMap<Class> listeners : specificObjectEventListeners.values()) {
835 				if (listeners.isListening(listener)) return true;
836 			}
837 		}
838 		return false;
839 	}
840 
841 	@Override
842 	public void removeEventListener(Class<?> eventClass, IWorldEventListener<?> listener) {
843 		eventListeners.remove(eventClass, listener);
844 	}
845 
846 	@Override
847 	public void removeObjectListener(Class<?> objectClass,	IWorldObjectEventListener<?, ?> listener) {
848 		objectsListeners.remove(objectClass, listener);
849 	}
850 	
851 	@Override
852 	public void removeObjectListener(Class<?> objectClass, Class<?> eventClass,	IWorldObjectEventListener<?, ?> listener) {
853 		if (objectEventListeners.containsKey(eventClass)) { // be careful not to create unnecessary ListenersMap
854 			objectEventListeners.get(eventClass).remove(objectClass, listener);
855 		}
856 	}
857 
858 	@Override
859 	public void removeObjectListener(WorldObjectId objectId, IWorldObjectEventListener<?, ?> listener) {
860 		specificObjectListeners.remove(objectId, listener);
861 	}
862 
863 	@Override
864 	public void removeObjectListener(WorldObjectId objectId, Class<?> eventClass, IWorldObjectEventListener<?, ?> listener) {
865 		if (specificObjectEventListeners.containsKey(objectId)) { // be careful not to create unnecessary ListenersMap
866 			specificObjectEventListeners.get(objectId).remove(eventClass, listener);
867 		}
868 	}
869 	
870 		
871 	@Override
872 	public void removeListener(IWorldEventListener<?> listener) {
873 		eventListeners.remove(listener);
874 		synchronized(objectEventListeners) {
875 			for (ListenersMap<Class> listeners : objectEventListeners.values()) {
876 				listeners.remove(listener);
877 			}
878 		}
879 		specificObjectListeners.remove(listener);
880 		specificObjectEventListeners.remove(listener);
881 	}
882 
883 	// EVENT PROCESSING
884 	
885 	
886 	/**
887 	 * Process new IWorldEvent - notify all the listeners about it. Forbids recursion.
888 	 * <p><p>
889 	 * Use in the descendants to process new IWorldChangeEvent.
890 	 * <p><p>
891 	 * Does not catch any exceptions!
892 	 * <p><p>
893 	 * Synchronized!
894 	 * 
895 	 * @param event
896 	 */
897 	protected synchronized void raiseEvent(IWorldEvent event) {
898 		// method is synchronized - only one thread inside at given time
899 		
900 		// is this method recursively called? 
901 		if (raiseEventProcessing) {
902 			// yes it is -> that means the previous event has not been
903 			// processed! ... store this event and allows the previous one
904 			// to be fully processed (e.g. postpone raising this event)
905 			raiseEventsList.add(event);
906 			return;
907 		} else {
908 			// no it is not ... so raise the flag that we're inside the method
909 			raiseEventProcessing = true;
910 		}
911 		// process event
912 				
913 		innerRaiseEvent(event);
914 		
915 		// check the events list size, do we have more events to process?
916 		while(raiseEventsList.size() != 0) {
917 			// yes we do -> do it!
918 			innerRaiseEvent(raiseEventsList.poll());			
919 		}
920 		// all events has been processed, drop the flag that we're inside the method
921 		raiseEventProcessing = false;			
922 	}
923 		
924 	//
925 	//
926 	// EVENT PROCESSING
927 	//
928 	//
929 		
930 	/**
931 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
932 	 * @param event
933 	 */
934 	private void notifyLevelAListeners(IWorldEvent event) {
935 		Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
936 		notifier.setEvent(event);
937 		for (Class eventClass : eventClasses) {
938 			eventListeners.notify(eventClass, notifier);
939 		}
940 	}
941 	
942 	/**
943 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
944 	 * @param event
945 	 */
946 	private void notifyLevelBListeners(IWorldObjectEvent event) {
947 		IWorldObject object = event.getObject();
948 		Collection<Class> objectClasses = ClassUtils.getSubclasses(object.getClass());
949 		notifier.setEvent(event);
950 		for (Class objectClass : objectClasses) {
951 			objectsListeners.notify(objectClass, notifier);
952 		}
953 	}
954 
955 	
956 	/**
957 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
958 	 * @param event
959 	 */
960 	private void notifyLevelCListeners(IWorldObjectEvent event) {
961 		Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
962 		Collection<Class> objectClasses = ClassUtils.getSubclasses(event.getObject().getClass());
963 		notifier.setEvent(event);
964 		for (Class eventClass : eventClasses) {
965 			ListenersMap<Class> listeners = objectEventListeners.get(eventClass);
966 			if (listeners == null) continue;
967 			if (!listeners.hasListeners()) continue;
968 			for (Class objectClass : objectClasses) {
969 				listeners.notify(objectClass, notifier);			
970 			}
971 		}
972 	}
973 	
974 	/**
975 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
976 	 * @param event
977 	 */
978 	private void notifyLevelDListeners(IWorldObjectEvent event) {
979 		notifier.setEvent(event);
980 		specificObjectListeners.notify(event.getId(), notifier);
981 	}
982 	
983 	/**
984 	 * Helper method used ONLY FROM innerRaiseEvent. DO NOT USE OUTSIDE THAT METHOD!
985 	 * @param event
986 	 */
987 	private void notifyLevelEListeners(IWorldObjectEvent event) {
988 		notifier.setEvent(event);
989 		WorldObjectId objectId = event.getId();
990 		ListenersMap<Class> listeners = specificObjectEventListeners.get(objectId);
991 		if (listeners.hasListeners()) {
992 			Collection<Class> eventClasses = ClassUtils.getSubclasses(event.getClass());
993 			notifier.setEvent(event);
994 			for (Class eventClass : eventClasses) {
995 				listeners.notify(eventClass, notifier);			
996 			}
997 		}
998 	}
999 	
1000 	/**
1001 	 * Process new IWorldEvent - DO NOT CALL SEPARATELY - must be called only from raiseEvent(),
1002 	 * that forbids recursion of its calls.
1003 	 * <p><p>
1004 	 * Contains the sequence in which the listeners are informed about the event.
1005 	 * @param event
1006 	 */
1007 	private void innerRaiseEvent(IWorldEvent event) {
1008 		if (log.isLoggable(Level.FINEST)) log.finest("notifying " + event);
1009 		notifyLevelAListeners(event);		
1010 		if (event instanceof IWorldObjectEvent) {
1011 			// now we may notify other listeners as well
1012 			IWorldObjectEvent objectEvent = (IWorldObjectEvent)event;
1013 			notifyLevelBListeners(objectEvent);
1014 			notifyLevelCListeners(objectEvent);
1015 			notifyLevelDListeners(objectEvent);
1016 			notifyLevelEListeners(objectEvent);
1017 		}
1018 	}
1019 
1020 	@Override
1021 	public ILifecycleBus getEventBus() {
1022 		return this.eventBus;
1023 	}
1024 
1025 }