View Javadoc

1   package cz.cuni.amis.pogamut.base.communication.worldview.object;
2   
3   import java.util.Map;
4   import java.util.concurrent.ExecutionException;
5   import java.util.concurrent.Future;
6   import java.util.concurrent.TimeUnit;
7   
8   import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
9   import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectDestroyedEvent;
10  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectFirstEncounteredEvent;
11  import cz.cuni.amis.pogamut.base.component.bus.event.BusAwareCountDownLatch;
12  import cz.cuni.amis.utils.exception.PogamutException;
13  
14  /**
15   * Use this if you want to wait for first appearance of some IWorldObject with known string ID.
16   * 
17   * @author ik
18   */
19  public class WorldObjectFuture<T extends IWorldObject> implements Future<T> {
20  	
21  	public static class WorldObjectFutureException extends PogamutException {
22  
23  		public WorldObjectFutureException(String message, Object origin) {
24  			super(message, origin);
25  		}
26  		
27  	}
28  
29      private final IWorldView worldView;
30      private final Class<T> objectClass;
31      
32      private T worldObject = null;
33      private BusAwareCountDownLatch latch;
34      private IWorldObjectListener<T> listener = null;
35  	private boolean cancelled = false;
36  	
37      /**
38       * Creates new instance of future that waits for first appearance of WorldObject
39       * of given class.
40       * @param worldView WorldView where the object will appear
41       * @param objectClass class of the object that will appear
42       */
43      @SuppressWarnings("unchecked")
44      public WorldObjectFuture(final IWorldView worldView, final Class<T> objectClass) {
45      	this.worldView = worldView;
46      	this.objectClass = objectClass;
47      	Map<WorldObjectId, T> objects = worldView.getAll(objectClass);
48      	switch (objects.size()) {
49      	case 0:
50      		latch = new BusAwareCountDownLatch(1, worldView.getEventBus(), worldView);
51      		worldView.addObjectListener(
52                  objectClass,
53                  IWorldObjectEvent.class,
54                  listener = new IWorldObjectListener<T>() {
55  	                @Override
56  	                public void notify(IWorldObjectEvent<T> event) {
57  	                	if (event instanceof WorldObjectDestroyedEvent) return;
58  	                    worldObject = event.getObject();
59  	                    worldView.removeObjectListener(objectClass, IWorldObjectEvent.class, this);
60  	                    customObjectEncounteredHook(worldObject);
61  	                    latch.countDown();
62  	                }
63                  }
64              );
65      		objects = worldView.getAll(objectClass);
66      		switch (objects.size()) {
67          		case 0:
68          			break;
69          		case 1:        			
70          			worldView.removeObjectListener(objectClass, IWorldObjectEvent.class, listener);
71          			worldObject = objects.values().iterator().next();
72          			latch.countDown();
73          			break;
74          		case 2:       
75          			if (worldObject != null) return;
76          			worldView.removeObjectListener(objectClass, IWorldObjectEvent.class, listener);
77          			latch.countDown();
78              		throw new WorldObjectFutureException("There are already " + objects.size() + " objects in world view of class " + objectClass.getSimpleName() + ".", this);
79      		}
80      		break;
81      	case 1:
82      		latch = new BusAwareCountDownLatch(0, worldView.getEventBus(), worldView);
83      		worldObject = objects.values().iterator().next();
84      		customObjectEncounteredHook(worldObject);
85      		break;
86      	default:
87      		throw new WorldObjectFutureException("There are already " + objects.size() + " objects in world view of class " + objectClass.getSimpleName() + ".", this);
88      	}    	
89      }
90  
91      /**
92       * Creates new instance of future that waits for first appearance of WorldObject
93       * of given id.
94       * @param worldView WorldView where the object will appear
95       * @param id string id of the object that will appear
96       * @param objectClass class of the object that will appear
97       */
98      @SuppressWarnings("unchecked")
99      public WorldObjectFuture(final IWorldView worldView, final String id, final Class<T> objectClass) {
100     	this.worldView = worldView;
101     	this.objectClass = objectClass;
102     	
103     	IWorldObject o = worldView.get(WorldObjectId.get(id));
104     	if (o != null) {
105     		this.worldObject = (T) o;
106     		latch = new BusAwareCountDownLatch(0, worldView.getEventBus(), worldView);
107     	} else {    	
108     		latch = new BusAwareCountDownLatch(1, worldView.getEventBus(), worldView);
109 	        worldView.addObjectListener(
110 	            objectClass,
111 	            IWorldObjectEvent.class,
112 	            listener = new IWorldObjectListener<T>() {
113 	
114 		            @Override
115 		            public void notify(IWorldObjectEvent<T> event) {
116 		            	if (event instanceof WorldObjectDestroyedEvent) return;
117 		                if (event.getObject().getId().getStringId().equals(id)) {		                	
118 		                    worldObject = event.getObject();
119 		                    worldView.removeObjectListener(objectClass, IWorldObjectEvent.class, this);
120 		                    latch.countDown();
121                                     customObjectEncounteredHook(worldObject);
122 		                }
123 		            }
124 	            }
125 	        );
126 	        
127 	        o = worldView.get(WorldObjectId.get(id));
128 	        if (o != null) {
129 	        	worldView.removeObjectListener(objectClass, WorldObjectFirstEncounteredEvent.class, listener);
130     			latch.countDown();
131 	        }
132     	}
133     }
134 
135     /**
136      * Utility method for custom handling of the first-encounter event.
137      * @param obj
138      */
139     protected void customObjectEncounteredHook(T obj) {
140     }
141 
142     @Override
143     public synchronized boolean cancel(boolean mayInterruptIfRunning) {
144         if (latch != null) latch.countDown();
145         cancelled  = true;
146         worldView.removeObjectListener(objectClass, IWorldObjectEvent.class, listener);
147         return true;
148     }
149 
150     @Override
151     public synchronized boolean isCancelled() {
152         return cancelled;
153     }
154 
155     @Override
156     public boolean isDone() {
157         return worldObject != null;
158     }
159 
160     /**
161      * @return the object that was awaited
162      * @throws InterruptedException
163      * @throws ExecutionException
164      */
165     @Override
166     public T get() {
167         latch.await();
168         return worldObject;
169     }
170 
171     @Override
172     public T get(long timeout, TimeUnit unit) {
173         latch.await(timeout, unit);
174         return worldObject;
175     }
176 }