View Javadoc

1   package cz.cuni.amis.pogamut.defcon.communication.messages.infos;
2   
3   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldObjectUpdateResult;
4   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldObjectUpdateResult.Result;
5   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldObjectUpdateResult.WorldObjectUpdateResult;
6   import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldObjectUpdatedEvent;
7   import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObject;
8   import cz.cuni.amis.pogamut.base.communication.worldview.object.WorldObjectId;
9   import cz.cuni.amis.pogamut.defcon.communication.messages.Updatable;
10  import cz.cuni.amis.utils.exception.PogamutException;
11  
12  import java.lang.reflect.Field;
13  
14  import java.util.ArrayList;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  
19  import javabot.PogamutJBotSupport;
20  
21  
22  /**
23   * Generic updater for arbitrary objects. All that you need is to annotate some fields with
24   * {@link Updatable} annotation.<p></p>
25   *  <p>Follows the philosophy of {@link IWorldObjectUpdateEvent}.</p>
26   *
27   * @author Jimmy
28   */
29  public class DefConObjectUpdate implements IWorldObjectUpdatedEvent {
30      /**
31       * Maps of existing updaters (cache).
32       */
33      private static Map<Class<?>, Updater> updaters = new HashMap<Class<?>, Updater>();
34  
35      /**
36       * TODO: FIX!
37       */
38      private double time = 0;
39  
40      /**
41       * Source object of the update.
42       */
43      private DefConObject source;
44  
45  /**
46       * Creates new object update with 'source' as object that contains updates.
47       *
48       * @param source
49       */
50      public DefConObjectUpdate(DefConObject source) {
51          this.source = source;
52      }
53  
54      /**
55       * Returns updater for class 'cls' from cache or creates new one.
56       *
57       * @param cls
58       *
59       * @return
60       */
61      public synchronized static Updater getUpdater(Class<?> cls) {
62          Updater updater = updaters.get(cls);
63  
64          if (updater != null) {
65              return updater;
66          }
67  
68          updater = new Updater(cls);
69          updaters.put(cls, updater);
70  
71          return updater;
72      }
73  
74      /**
75       * Returns source of the updates.
76       *
77       * @return
78       */
79      public DefConObject getSource() {
80          return this.source;
81      }
82  
83      /**
84       * Returns ID of the object that should be updated.
85       *
86       * @return ID
87       */
88      @Override
89      public WorldObjectId getId() {
90          return (source == null) ? null : source.getId();
91      }
92  
93      /**
94       * Updates object 'obj' with the source event.
95       *
96       * @param obj object that should be updated
97       *
98       * @return result of the object update
99       */
100     @Override
101     public IWorldObjectUpdateResult update(IWorldObject obj) {
102         if (source == null) {
103             return null;
104         }
105 
106         if (obj == null) {
107             return new WorldObjectUpdateResult(Result.CREATED, source);
108         }
109 
110         if (!obj.getClass().equals(source.getClass())) {
111             throw new PogamutException("Can't update object of class " + obj.getClass().getName() +
112                 " with information from object of class " + source.getClass().getName(), this);
113         }
114 
115         if (source.getDestroyed()) {
116             return new WorldObjectUpdateResult(Result.DESTROYED, obj);
117         }
118 
119         getUpdater(obj.getClass()).update(obj, source);
120 
121         return new WorldObjectUpdateResult(Result.UPDATED, obj);
122     }
123 
124     /**
125      * Returns the time of the last update of the object.
126      *
127      * @return time
128      */
129     public double getLastSeenTime() {
130         return time;
131     }
132 
133     /**
134      * Sets the last-seen-time of the object.
135      *
136      * @param time
137      */
138     protected void setLastSeenTime(double time) {
139         this.time = time;
140     }
141 
142     /**
143      * DOCUMENT ME!
144      *
145      * @return DOCUMENT ME!
146      */
147     @Override
148     public long getSimTime() {
149         return (long) time;
150     }
151 
152     /**
153      * Updater class that is using Java Reflection API to get the list of fields that has
154      * to be updated over some class.
155      *
156      * @author Jimmy
157      */
158     private static class Updater {
159         /**
160          * Class that the updater handles
161          */
162         private Class<?> cls;
163 
164         /**
165          * Fields of 'cls' that contains {@link Updatable} annotation.
166          */
167         private List<Field> fields = new ArrayList<Field>();
168 
169 /**
170          * Creates updater class for 'cls'
171          *
172          * @param cls
173          */
174         public Updater(Class<?> cls) {
175             this.cls = cls;
176 
177             do {
178                 for (Field field : cls.getDeclaredFields()) {
179                     if (field.isAnnotationPresent(Updatable.class)) {
180                         fields.add(field);
181                     }
182                 }
183             } while ((cls = cls.getSuperclass()) != null);
184         }
185 
186         /**
187          * Returns class that updater handles.
188          *
189          * @return
190          */
191         public Class getUpdaterClass() {
192             return cls;
193         }
194 
195         /**
196          * Updates 'oldObj' with {@link Updatable} fields of 'newObj'. Both object must
197          * be of class 'cls' (returned by getUpdaterClass()).
198          *
199          * @param oldObj
200          * @param newObj
201          */
202         public void update(Object oldObj, Object newObj) {
203             if (!oldObj.getClass().equals(cls) || !newObj.getClass().equals(cls)) {
204                 throw new PogamutException("Can't update object of class " +
205                     oldObj.getClass().getName() + " with information from object of class " +
206                     newObj.getClass().getName() + " because they bot must be of class " +
207                     cls.getName(), this);
208             }
209 
210             for (Field field : fields) {
211                 try {
212                     field.setAccessible(true);
213 
214                     Object value = field.get(newObj);
215 
216                     if (value == null) {
217                         continue;
218                     }
219 
220                     try {
221                         // PogamutJBotSupport.writeToConsole("updating field " + field.toString() + " of " + oldObj.toString());
222                         field.set(oldObj, value);
223                     } catch (Exception e) {
224                         throw new PogamutException("Could not set value " + value +
225                             " to the field '" + field.getName() + "' of the object " + newObj +
226                             " because: " + e.getMessage(), e, this);
227                     }
228                 } catch (Exception e) {
229                     throw new PogamutException("Could not obtain field '" + field.getName() +
230                         "' from object " + newObj + " because: " + e.getMessage(), e, this);
231                 } finally {
232                     field.setAccessible(false);
233                 }
234             }
235         }
236     }
237 }