View Javadoc

1   package cz.cuni.amis.pogamut.base3d.worldview;
2   
3   import java.util.Collections;
4   import java.util.HashMap;
5   import java.util.Map;
6   
7   import com.google.inject.Inject;
8   
9   import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
10  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
11  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
12  import cz.cuni.amis.pogamut.base.communication.worldview.object.WorldObjectId;
13  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
14  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
15  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
16  import cz.cuni.amis.pogamut.base3d.worldview.object.IViewable;
17  import cz.cuni.amis.utils.ClassUtils;
18  
19  /**
20   * Vision is taking care about the objects the bot might see (of all implementing IViewable interface).
21   * 
22   * @author Jimmy
23   *
24   */
25  @AgentScoped
26  public class Vision implements IWorldObjectListener<IViewable> {
27  	
28  	// TODO: (Jakub) hide WorldViewEventListener<IWorldObjectEvent> into
29  	//               inner private class
30  	
31  	/**
32  	 * Synchronized map that holds all the objects we may currently see according to their type
33  	 * in the maps.
34  	 * <p><p>
35  	 * Due to nature of generics we can't typed this field, it holds maps of objects
36  	 * according to their classes. 
37  	 * <p>
38  	 * Map &lt; Class, Map &lt; IWorldViewObjectId, IWorldObject of Class &gt; &gt;
39  	 */
40  	private Map seeObjects =
41  		Collections.synchronizedMap(
42  			new HashMap()
43  		);
44  	
45  	/**
46  	 * E.g. worldObjects but contains immutable (unmodifiable) version of map.
47  	 */
48  	private Map immutableSeeObjects =
49  		Collections.unmodifiableMap(
50  			seeObjects
51  		);
52  	
53  	/**
54  	 * Synchronized map of all the world objects that the agent can currently see.
55  	 */
56  	private Map<WorldObjectId, IWorldObject> seeObjectsId =
57  		Collections.synchronizedMap(
58  			new HashMap<WorldObjectId, IWorldObject>()
59  		);
60  	
61  	@Inject
62  	public Vision(IWorldView worldView, IAgentLogger logger) {
63  		// hook itself to listen on all events that are raised on viewable objects
64  		worldView.addObjectListener(IViewable.class, IWorldObjectEvent.class, this);		
65  	}
66  	
67  	@Override
68  	public void notify(IWorldObjectEvent<IViewable> event) {				
69  		IWorldObject obj = seeObjectsId.get(event.getId());
70  		if (obj != null) {
71  			if (((IViewable)obj).isVisible()) {
72  				if (seeObjects.get(event.getId()) == null) {
73  					addSeeObject(obj);
74  				}
75  			} else {
76  				if (seeObjects.get(event.getId()) != null) {
77  					removeSeeObject(obj);
78  				}
79  			}
80  		} else { 
81  			IViewable object = event.getObject();
82  			if (object.isVisible()) {
83  				if (seeObjects.get(event.getId()) == null) {
84  					addSeeObject(obj);
85  				}
86  			} else {
87  				if (seeObjects.get(event.getId()) != null) {
88  					removeSeeObject(obj);
89  				}
90  			}
91  		}
92  	}
93  	
94  	/**
95  	 * Used to introduce new object category into worldObjects and immutableWorldObjects.
96  	 * <p><p>
97  	 * It will create new synchronized Map&lt;IWorldViewObjectId, T&gt; in the worldObjects and it's immutable
98  	 * counterpart in immutableWorldObjects under key of 'cls'.
99  	 * <p><p>
100 	 * Returns modifiable version of created map.
101 	 * 
102 	 * @param <T>
103 	 * @param cls
104 	 * @return
105 	 */
106 	protected synchronized <T> Map<WorldObjectId, T> addNewObjectCategory(Class<T> cls) {
107 		Map<WorldObjectId, T> objects = Collections.synchronizedMap(new HashMap<WorldObjectId, T>());			
108 		seeObjects.put(cls, objects);
109 		immutableSeeObjects.put(cls, Collections.unmodifiableMap(objects));
110 		return objects;
111 	}
112 	
113 	/**
114 	 * Method that adds a new world object to the object maps. It will be called from
115 	 * the descendant whenever new object appears in the world view.
116 	 * @param worldObject
117 	 */
118 	protected void addSeeObject(IWorldObject seeObject) {
119 		seeObjectsId.put(seeObject.getId(), seeObject);
120 		for (Class cls : ClassUtils.getSubclasses(seeObject.getClass())) {
121 			Map objects;
122 			synchronized(seeObjects) {
123 				objects = (Map) seeObjects.get(cls);
124 				if (objects == null) objects = addNewObjectCategory(cls);
125 			}
126 			objects.put(seeObject.getId(), seeObject);
127 		}		
128 	}
129 	
130 	/**
131 	 * Returns world object of the given id or null if the object is not yet in the world view.
132 	 * @param objectId
133 	 * @return
134 	 */
135 	protected IWorldObject getSeeObject(WorldObjectId objectId) {
136 		return seeObjectsId.get(objectId);
137 	}
138 	
139 	/**
140 	 * Removes world object from the world view - this will be called from the descendants
141 	 * of the AbstractWorldView whenever world object should disappear from the world view.
142 	 * @param worldObject
143 	 */
144 	protected void removeSeeObject(IWorldObject worldObject) {
145 		seeObjectsId.remove(worldObject.getId());
146 		for (Class cls : ClassUtils.getSubclasses(worldObject.getClass())) {
147 			Map objects = (Map) seeObjects.get(cls);
148 			if (objects != null) {
149 				objects.remove(worldObject.getId());
150 			}		
151 		}			
152 	}
153 
154 	/**
155 	 * Returns map of all objects the agent can currently see. 
156 	 * <p><p>
157 	 * WARNING: returns immutable map!
158 	 * @return
159 	 */
160 	public Map<Class, Map<WorldObjectId, IWorldObject>> getSee() {
161 		return immutableSeeObjects;
162 	}
163 
164 	/**
165 	 * Returns map map of all objects of a certain type the agent can currently see.
166 	 * <p><p>
167 	 * WARNING: returns immutable map!
168 	 *  
169 	 * @param type
170 	 * @return
171 	 */
172 	public <T> Map<WorldObjectId, T> getSee(Class<T> type) {
173 		// WE HAVE TO SYNCHRONIZE on seeObjects NOT immutableSeeObjects,
174 		// because we're adding new category the world objects depends on that!
175 		// see addSeeObject()
176 		synchronized(seeObjects) {
177 			Map<WorldObjectId, T> objects = (Map<WorldObjectId, T>) immutableSeeObjects.get(type);
178 			if (objects == null) {
179 				addNewObjectCategory(type);
180 				return (Map<WorldObjectId, T>) immutableSeeObjects.get(type);
181 			} else {
182 				return objects;
183 			}
184 		}
185 	}
186 	
187 	/**
188 	 * If agents sees item of 'id' it returns it instances, otherwise it returns null.
189 	 * @param id
190 	 * @return
191 	 */
192 	public IWorldObject getSee(WorldObjectId id) {
193 		return seeObjectsId.get(id);
194 	}
195 	
196 }