Average Density: 0.03
  1 package nl.tudelft.jpacman.level;
  2 
  3 import java.util.ArrayList;
  4 import java.util.HashMap;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 import nl.tudelft.jpacman.board.Unit;
  9 
 10 /**
 11  * A map of possible collisions and their handlers.
 12  *
 13  * @author Michael de Jong
 14  * @author Jeroen Roosen 
 15  */
 16 public class CollisionInteractionMap implements CollisionMap {
 17 
 18     /**
 19      * The collection of collision handlers.
 20      */
 21     private final Map<Class<? extends Unit>,
 22         Map<Class<? extends Unit>, CollisionHandler<?, ?>>> handlers;
 23 
 24     /**
 25      * Creates a new, empty collision map.
 26      */
 27     public CollisionInteractionMap() {
 28         this.handlers = new HashMap<>();
 29     }
 30 
 31     /**
 32      * Adds a two-way collision interaction to this collection, i.e. the
 33      * collision handler will be used for both C1 versus C2 and C2 versus C1.
 34      *
 35      * @param <C1>
 36      *            The collider type.
 37      * @param <C2>
 38      *            The collidee (unit that was moved into) type.
 39      *
 40      * @param collider
 41      *            The collider type.
 42      * @param collidee
 43      *            The collidee type.
 44      * @param handler
 45      *            The handler that handles the collision.
 46      */
 47     public <C1 extends Unit, C2 extends Unit> void onCollision(
 48         Class<C1> collider, Class<C2> collidee, CollisionHandler<C1, C2> handler) {
 49         onCollision(collider, collidee, true, handler);
 50     }
 51 
 52     /**
 53      * Adds a collision interaction to this collection.
 54      *
 55      * @param <C1>
 56      *            The collider type.
 57      * @param <C2>
 58      *            The collidee (unit that was moved into) type.
 59      *
 60      * @param collider
 61      *            The collider type.
 62      * @param collidee
 63      *            The collidee type.
 64      * @param symetric
 65      *            <code>true</code> if this collision is used for both
 66      *            C1 against C2 and vice versa;
 67      *            <code>false</code> if only for C1 against C2.
 68      * @param handler
 69      *            The handler that handles the collision.
 70      */
 71     public <C1 extends Unit, C2 extends Unit> void onCollision(
 72         Class<C1> collider, Class<C2> collidee, boolean symetric,
 73         CollisionHandler<C1, C2> handler) {
 74         addHandler(collider, collidee, handler);
 75         if (symetric) {
 76             addHandler(collidee, collider, new InverseCollisionHandler<>(handler));
 77         }
 78     }
 79 
 80     /**
 81      * Adds the collision interaction..
 82      *
 83      * @param collider
 84      *            The collider type.
 85      * @param collidee
 86      *            The collidee type.
 87      * @param handler
 88      *            The handler that handles the collision.
 89      */
 90     private void addHandler(Class<? extends Unit> collider,
 91                             Class<? extends Unit> collidee, CollisionHandler<?, ?> handler) {
 92         if (!handlers.containsKey(collider)) {
 93             handlers.put(collider, new HashMap<>());
 94         }
 95 
 96         Map<Class<? extends Unit>, CollisionHandler<?, ?>> map = handlers.get(collider);
 97         map.put(collidee, handler);
 98     }
 99 
100     /**
101      * Handles the collision between two colliding parties, if a suitable
102      * collision handler is listed.
103      *
104      * @param <C1>
105      *            The collider type.
106      * @param <C2>
107      *            The collidee (unit that was moved into) type.
108      *
109      * @param collider
110      *            The collider.
111      * @param collidee
112      *            The collidee.
113      */
114     @SuppressWarnings("unchecked")
115     @Override
116     public <C1 extends Unit, C2 extends Unit> void collide(C1 collider,
117                                                            C2 collidee) {
118         Class<? extends Unit> colliderKey = getMostSpecificClass(handlers, collider.getClass());
119         if (colliderKey == null) {
120             return;
121         }
122 
123         Map<Class<? extends Unit>, CollisionHandler<?, ?>> map = handlers.get(colliderKey);
124         Class<? extends Unit> collideeKey = getMostSpecificClass(map, collidee.getClass());
125         if (collideeKey == null) {
126             return;
127         }
128 
129         CollisionHandler<C1, C2> collisionHandler = (CollisionHandler<C1, C2>) map.get(collideeKey);
130         if (collisionHandler == null) {
131             return;
132         }
133 
134         collisionHandler.handleCollision(collider, collidee);
135     }
136 
137     /**
138      * Figures out the most specific class that is listed in the map. I.e. if A
139      * extends B and B is listed while requesting A, then B will be returned.
140      *
141      * @param map
142      *            The map with the key collection to find a matching class in.
143      * @param key
144      *            The class to search the most suitable key for.
145      * @return The most specific class from the key collection.
146      */
147     private Class<? extends Unit> getMostSpecificClass(
148         Map<Class<? extends Unit>, ?> map, Class<? extends Unit> key) {
149         List<Class<? extends Unit>> collideeInheritance = getInheritance(key);
150         for (Class<? extends Unit> pointer : collideeInheritance) {
151             if (map.containsKey(pointer)) {
152                 return pointer;
153             }
154         }
155         return null;
156     }
157 
158     /**
159      * Returns a list of all classes and interfaces the class inherits.
160      *
161      * @param clazz
162      *            The class to create a list of super classes and interfaces
163      *            for.
164      * @return A list of all classes and interfaces the class inherits.
165      */
166     @SuppressWarnings("unchecked")
167     private List<Class<? extends Unit>> getInheritance(
168         Class<? extends Unit> clazz) {
169         List<Class<? extends Unit>> found = new ArrayList<>();
170         found.add(clazz);
171 
172         int index = 0;
173         while (found.size() > index) {
174             Class<?> current = found.get(index);
175             Class<?> superClass = current.getSuperclass();
176             if (superClass != null && Unit.class.isAssignableFrom(superClass)) {
177                 found.add((Class<? extends Unit>) superClass);
178             }
179             for (Class<?> classInterface : current.getInterfaces()) {
180                 if (Unit.class.isAssignableFrom(classInterface)) {
181                     found.add((Class<? extends Unit>) classInterface);
182                 }
183             }
184             index++;
185         }
186 
187         return found;
188     }
189 
190     /**
191      * Handles the collision between two colliding parties.
192      *
193      * @author Michael de Jong
194      *
195      * @param <C1>
196      *            The collider type.
197      * @param <C2>
198      *            The collidee type.
199      */
200     public interface CollisionHandler<C1 extends Unit, C2 extends Unit> {
201 
202         /**
203          * Handles the collision between two colliding parties.
204          *
205          * @param collider
206          *            The collider.
207          * @param collidee
208          *            The collidee.
209          */
210         void handleCollision(C1 collider, C2 collidee);
211     }
212 
213     /**
214      * An symmetrical copy of a collision hander.
215      *
216      * @author Michael de Jong
217      *
218      * @param <C1>
219      *            The collider type.
220      * @param <C2>
221      *            The collidee type.
222      */
223     private static class InverseCollisionHandler<C1 extends Unit, C2 extends Unit>
224         implements CollisionHandler<C1, C2> {
225 
226         /**
227          * The handler of this collision.
228          */
229         private final CollisionHandler<C2, C1> handler;
230 
231         /**
232          * Creates a new collision handler.
233          *
234          * @param handler
235          *            The symmetric handler for this collision.
236          */
237         InverseCollisionHandler(CollisionHandler<C2, C1> handler) {
238             this.handler = handler;
239         }
240 
241         /**
242          * Handles this collision by flipping the collider and collidee, making
243          * it compatible with the initial collision.
244          */
245         @Override
246         public void handleCollision(C1 collider, C2 collidee) {
247             handler.handleCollision(collidee, collider);
248         }
249     }
250 
251 }