1 package cz.cuni.amis.pogamut.ut2004.communication.messages.custom;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Field;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
12 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
13 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ControlMessage;
14 import cz.cuni.amis.utils.ClassUtils;
15 import cz.cuni.amis.utils.Tuple3;
16
17
18
19
20
21
22
23
24
25 public class ControlMessageMapper<T extends ICustomControlMessage> {
26
27 public static interface ControlMessageGetter<T> {
28
29 public T get(ControlMessage msg);
30
31 }
32
33 @SuppressWarnings("unchecked")
34 public static final ControlMessageGetter<Integer>[] getterIntegers =
35 new ControlMessageGetter[] {
36
37 null,
38
39 new ControlMessageGetter<Integer>() {
40 @Override
41 public Integer get(ControlMessage msg) {
42 return msg.getPI1();
43 }
44 },
45
46 new ControlMessageGetter<Integer>() {
47 @Override
48 public Integer get(ControlMessage msg) {
49 return msg.getPI2();
50 }
51 },
52
53 new ControlMessageGetter<Integer>() {
54 @Override
55 public Integer get(ControlMessage msg) {
56 return msg.getPI3();
57 }
58 },
59 };
60
61 @SuppressWarnings("unchecked")
62 public static final ControlMessageGetter<Double>[] getterDoubles =
63 new ControlMessageGetter[] {
64
65 null,
66
67 new ControlMessageGetter<Double>() {
68 @Override
69 public Double get(ControlMessage msg) {
70 return msg.getPF1();
71 }
72 },
73
74 new ControlMessageGetter<Double>() {
75 @Override
76 public Double get(ControlMessage msg) {
77 return msg.getPF2();
78 }
79 },
80
81 new ControlMessageGetter<Double>() {
82 @Override
83 public Double get(ControlMessage msg) {
84 return msg.getPF3();
85 }
86 },
87 };
88
89 @SuppressWarnings("unchecked")
90 public static final ControlMessageGetter<Double>[] getterStrings =
91 new ControlMessageGetter[] {
92
93 null,
94
95 new ControlMessageGetter<String>() {
96 @Override
97 public String get(ControlMessage msg) {
98 return msg.getPS1();
99 }
100 },
101
102 new ControlMessageGetter<String>() {
103 @Override
104 public String get(ControlMessage msg) {
105 return msg.getPS2();
106 }
107 },
108
109 new ControlMessageGetter<String>() {
110 @Override
111 public String get(ControlMessage msg) {
112 return msg.getPS3();
113 }
114 },
115 };
116
117 @SuppressWarnings("unchecked")
118 public static final ControlMessageGetter<Double>[] getterBooleans =
119 new ControlMessageGetter[] {
120
121 null,
122
123 new ControlMessageGetter<Boolean>() {
124 @Override
125 public Boolean get(ControlMessage msg) {
126 return msg.isPB1();
127 }
128 },
129
130 new ControlMessageGetter<Boolean>() {
131 @Override
132 public Boolean get(ControlMessage msg) {
133 return msg.isPB2();
134 }
135 },
136
137 new ControlMessageGetter<Boolean>() {
138 @Override
139 public Boolean get(ControlMessage msg) {
140 return msg.isPB3();
141 }
142 },
143 };
144
145 private Class<T> descriptor;
146
147 private String type;
148
149 private Constructor<T> constructor;
150
151 @SuppressWarnings("rawtypes")
152 private List<Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>> fields = new ArrayList<Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>>();
153
154 private Field simTimeField;
155
156 @SuppressWarnings("rawtypes")
157 public ControlMessageMapper(Class<T> customControlMessageClass) {
158 Map<Integer, Field> integers = new HashMap<Integer, Field>();
159 Map<Integer, Field> doubles = new HashMap<Integer, Field>();
160 Map<Integer, Field> strings = new HashMap<Integer, Field>();
161 Map<Integer, Field> booleans = new HashMap<Integer, Field>();
162
163 this.descriptor = customControlMessageClass;
164
165 if (!customControlMessageClass.isAnnotationPresent(ControlMessageType.class)) {
166 throw new RuntimeException("Cannot create ControlMessageDeserializer for " + customControlMessageClass + " as it is not annotated with ControlMessageType!");
167 }
168 this.type = customControlMessageClass.getAnnotation(ControlMessageType.class).type();
169
170 try {
171 constructor = customControlMessageClass.getConstructor();
172 } catch (Exception e) {
173 throw new RuntimeException("Cannot create ControlMessageDeserializer as its parameter-less contructor is unavailable (either non-existent or Java security related).", e);
174 }
175 if (constructor == null) {
176 throw new RuntimeException("Cannot create ControlMessageDeserializer as its parameter-less contructor is unavailable (either non-existent or Java security related).");
177 }
178
179 Collection<Class> classes = ClassUtils.getSubclasses(customControlMessageClass);
180 for (Class cls : classes) {
181
182 if (cls.isAnnotation()) continue;
183 if (cls.isInterface()) continue;
184
185
186 for (Field field : cls.getDeclaredFields()) {
187
188 if (field.isAnnotationPresent(ControlMessageSimType.class)) {
189 if (!field.getType().equals(long.class)) {
190 throw new RuntimeException("Cannot create ControlMessageDeserializer as its @ControlMessageSimType field " + field.getDeclaringClass() + "." + field.getName() + " is not of type 'long' but '" + field.getType() + "', invalid.");
191 }
192 if (simTimeField != null) {
193 throw new RuntimeException("Cannot create ControlMessageDeserializer as its @ControlMessageSimType field declared twice, first " + simTimeField.getDeclaringClass() + "." + simTimeField.getName() + ", second " + field.getDeclaringClass() + "." + field.getDeclaringClass() + "." + field.getName() + ".");
194 }
195 simTimeField = field;
196 continue;
197 }
198
199 if (!field.isAnnotationPresent(ControlMessageField.class)) continue;
200
201 ControlMessageField info = field.getAnnotation(ControlMessageField.class);
202
203 if (info.index() < 0 || info.index() > 3) {
204 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " contains annotation ControlMessageParam(index=" + info.index() + "), unsupported. 1 <= index <= 3.");
205 }
206
207 Map<Integer, Field> map;
208
209 if (field.getType() == Integer.class) {
210 map = integers;
211 } else
212 if (field.getType() == Double.class) {
213 map = doubles;
214 } else
215 if (field.getType() == String.class) {
216 map = strings;
217 } else
218 if (field.getType() == Boolean.class) {
219 map = booleans;
220 } else
221 if (field.getType() == UnrealId.class) {
222 map = strings;
223 } else
224 if (field.getType() == Location.class) {
225 map = strings;
226 } else {
227 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " is of invalid type " + field.getType() + ", only Integer, Double, String, Boolean, UnrealId, Location is supported.");
228 }
229
230 if (map.containsKey(info.index())) {
231 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " is referencing index " + info.index() + " that has already been defined/taken by field " + map.get(info.index()).getName() + ".");
232 }
233
234 map.put(info.index(), field);
235
236 if (field.getType() == Integer.class) {
237 if (info.index() >= getterIntegers.length) {
238 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " has unexpected index " + info.index() + " for ControlMessage Integer field, I do not have ControlMessageGetter for that!");
239 }
240 fields.add(
241 new Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>(
242 field,
243 getterIntegers[info.index()],
244 ControlMessageTypeMapper.DIRECT_MAPPER
245 )
246 );
247 } else
248 if (field.getType() == Double.class) {
249 if (info.index() >= getterDoubles.length) {
250 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " has unexpected index " + info.index() + " for ControlMessage Double field, I do not have ControlMessageGetter for that!");
251 }
252 fields.add(
253 new Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>(
254 field,
255 getterDoubles[info.index()],
256 ControlMessageTypeMapper.DIRECT_MAPPER
257 )
258 );
259 } else
260 if (field.getType() == String.class) {
261 if (info.index() >= getterStrings.length) {
262 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " has unexpected index " + info.index() + " for ControlMessage String field, I do not have ControlMessageGetter for that!");
263 }
264 fields.add(
265 new Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>(
266 field,
267 getterStrings[info.index()],
268 ControlMessageTypeMapper.DIRECT_MAPPER
269 )
270 );
271 } else
272 if (field.getType() == Boolean.class) {
273 if (info.index() >= getterBooleans.length) {
274 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " has unexpected index " + info.index() + " for ControlMessage Boolean field, I do not have ControlMessageGetter for that!");
275 }
276 fields.add(
277 new Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>(
278 field,
279 getterBooleans[info.index()],
280 ControlMessageTypeMapper.DIRECT_MAPPER
281 )
282 );
283 } else
284 if (field.getType() == UnrealId.class) {
285 if (info.index() >= getterStrings.length) {
286 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " has unexpected index " + info.index() + " for ControlMessage String field, I do not have ControlMessageGetter for that!");
287 }
288 fields.add(
289 new Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>(
290 field,
291 getterStrings[info.index()],
292 ControlMessageTypeMapper.STRING_2_UNREAL_ID_MAPPER
293 )
294 );
295 } else
296 if (field.getType() == Location.class) {
297 if (info.index() >= getterStrings.length) {
298 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " has unexpected index " + info.index() + " for ControlMessage String field, I do not have ControlMessageGetter for that!");
299 }
300 fields.add(
301 new Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper>(
302 field,
303 getterStrings[info.index()],
304 ControlMessageTypeMapper.STRING_2_LOCATION_MAPPER
305 )
306 );
307 } else {
308 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as field " + field.getDeclaringClass() + "." + field.getName() + " is of invalid type " + field.getType() + ", only Integer, Double, String, Boolean, UnrealId, Location is supported.");
309 }
310 }
311 }
312
313 if (simTimeField == null) {
314 throw new RuntimeException("Cannot create CustomMessageDeserializer for " + customControlMessageClass + " as no long-typed field was annotated with @CustomMessageSimTime.");
315 }
316 }
317
318 public Class<T> getDescriptor() {
319 return descriptor;
320 }
321
322 public String getType() {
323 return type;
324 }
325
326 @SuppressWarnings("rawtypes")
327 public T deserialize(ControlMessage message) {
328 T result;
329
330 try {
331 result = constructor.newInstance();
332 } catch (Exception e) {
333 throw new RuntimeException("Failed to instantiate new " + descriptor + ".", e);
334 }
335
336 for (Tuple3<Field, ControlMessageGetter, ControlMessageTypeMapper> field : fields) {
337 Object baseValue = field.getSecond().get(message);
338 Object value = field.getThird().map(baseValue);
339 try {
340 field.getFirst().setAccessible(true);
341 field.getFirst().set(result, value);
342 } catch (Exception e) {
343 throw new RuntimeException("Failed to set " + descriptor + "." + field.getFirst().getName() + " with value '" + value + "' mapped from base value '" + baseValue + "'." , e);
344 }
345
346 }
347
348 try {
349 simTimeField.setAccessible(true);
350 simTimeField.set(result, message.getSimTime());
351 } catch (Exception e) {
352 throw new RuntimeException("Failed to set " + descriptor + "." + simTimeField.getName() + " with value '" + message.getSimTime() + "'", e);
353 }
354
355 return result;
356 }
357
358 }