1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.johnzon.mapper.reflection; |
20 | |
|
21 | |
import org.apache.johnzon.mapper.Converter; |
22 | |
import org.apache.johnzon.mapper.JohnzonConverter; |
23 | |
import org.apache.johnzon.mapper.JohnzonIgnore; |
24 | |
import org.apache.johnzon.mapper.JohnzonVirtualObject; |
25 | |
import org.apache.johnzon.mapper.JohnzonVirtualObjects; |
26 | |
import org.apache.johnzon.mapper.access.AccessMode; |
27 | |
|
28 | |
import java.beans.ConstructorProperties; |
29 | |
import java.lang.annotation.Annotation; |
30 | |
import java.lang.reflect.Array; |
31 | |
import java.lang.reflect.Constructor; |
32 | |
import java.lang.reflect.Modifier; |
33 | |
import java.lang.reflect.ParameterizedType; |
34 | |
import java.lang.reflect.Type; |
35 | |
import java.math.BigDecimal; |
36 | |
import java.math.BigInteger; |
37 | |
import java.util.Collection; |
38 | |
import java.util.Comparator; |
39 | |
import java.util.HashMap; |
40 | |
import java.util.HashSet; |
41 | |
import java.util.LinkedHashMap; |
42 | |
import java.util.LinkedList; |
43 | |
import java.util.List; |
44 | |
import java.util.Map; |
45 | |
import java.util.Queue; |
46 | |
import java.util.Set; |
47 | |
import java.util.SortedSet; |
48 | |
import java.util.TreeMap; |
49 | |
import java.util.concurrent.ConcurrentHashMap; |
50 | |
import java.util.concurrent.ConcurrentMap; |
51 | |
|
52 | |
import static java.util.Arrays.asList; |
53 | |
|
54 | 1 | public class Mappings { |
55 | |
public static class ClassMapping { |
56 | |
public final Class<?> clazz; |
57 | |
public final Map<String, Getter> getters; |
58 | |
public final Map<String, Setter> setters; |
59 | |
public final Constructor<?> constructor; |
60 | |
public final boolean constructorHasArguments; |
61 | |
public final String[] constructorParameters; |
62 | |
public final Converter<?>[] constructorParameterConverters; |
63 | |
public final Type[] constructorParameterTypes; |
64 | |
|
65 | |
protected ClassMapping(final Class<?> clazz, |
66 | |
final Map<String, Getter> getters, final Map<String, Setter> setters, |
67 | 51 | final boolean acceptHiddenConstructor, final boolean useConstructor) { |
68 | 51 | this.clazz = clazz; |
69 | 51 | this.getters = getters; |
70 | 51 | this.setters = setters; |
71 | 51 | this.constructor = findConstructor(acceptHiddenConstructor, useConstructor); |
72 | |
|
73 | 51 | this.constructorHasArguments = this.constructor != null && this.constructor.getGenericParameterTypes().length > 0; |
74 | 51 | if (this.constructorHasArguments) { |
75 | 1 | this.constructorParameterTypes = this.constructor.getGenericParameterTypes(); |
76 | |
|
77 | 1 | this.constructorParameters = new String[this.constructor.getGenericParameterTypes().length]; |
78 | 1 | final ConstructorProperties constructorProperties = this.constructor.getAnnotation(ConstructorProperties.class); |
79 | 1 | System.arraycopy(constructorProperties.value(), 0, this.constructorParameters, 0, this.constructorParameters.length); |
80 | |
|
81 | 1 | this.constructorParameterConverters = new Converter<?>[this.constructor.getGenericParameterTypes().length]; |
82 | 4 | for (int i = 0; i < this.constructorParameters.length; i++) { |
83 | 4 | for (final Annotation a : this.constructor.getParameterAnnotations()[i]) { |
84 | 1 | if (a.annotationType() == JohnzonConverter.class) { |
85 | |
try { |
86 | 1 | this.constructorParameterConverters[i] = JohnzonConverter.class.cast(a).value().newInstance(); |
87 | 0 | } catch (final Exception e) { |
88 | 0 | throw new IllegalArgumentException(e); |
89 | 1 | } |
90 | |
} |
91 | |
} |
92 | |
} |
93 | 1 | } else { |
94 | 50 | this.constructorParameterTypes = null; |
95 | 50 | this.constructorParameters = null; |
96 | 50 | this.constructorParameterConverters = null; |
97 | |
} |
98 | 51 | } |
99 | |
|
100 | |
private Constructor<?> findConstructor(final boolean acceptHiddenConstructor, final boolean useConstructor) { |
101 | 51 | Constructor<?> found = null; |
102 | 52 | for (final Constructor<?> c : clazz.getDeclaredConstructors()) { |
103 | 51 | if (c.getParameterTypes().length == 0) { |
104 | 49 | if (!Modifier.isPublic(c.getModifiers()) && acceptHiddenConstructor) { |
105 | 7 | c.setAccessible(true); |
106 | |
} |
107 | 49 | found = c; |
108 | 49 | if (!useConstructor) { |
109 | 49 | break; |
110 | |
} |
111 | 2 | } else if (c.getAnnotation(ConstructorProperties.class) != null) { |
112 | 1 | found = c; |
113 | 1 | break; |
114 | |
} |
115 | |
} |
116 | 51 | if (found != null) { |
117 | 50 | return found; |
118 | |
} |
119 | |
try { |
120 | 1 | return clazz.getConstructor(); |
121 | 1 | } catch (final NoSuchMethodException e) { |
122 | 1 | return null; |
123 | |
} |
124 | |
} |
125 | |
} |
126 | |
|
127 | |
public static class CollectionMapping { |
128 | |
public final Class<?> raw; |
129 | |
public final Type arg; |
130 | |
public final boolean primitive; |
131 | |
|
132 | 18 | public CollectionMapping(final boolean primitive, final Class<?> collectionType, final Type fieldArgType) { |
133 | 18 | this.raw = collectionType; |
134 | 18 | this.arg = fieldArgType; |
135 | 18 | this.primitive = primitive; |
136 | 18 | } |
137 | |
} |
138 | |
|
139 | |
public static class Getter { |
140 | |
public final AccessMode.Reader reader; |
141 | |
public final int version; |
142 | |
public final Converter<Object> converter; |
143 | |
public final boolean primitive; |
144 | |
public final boolean array; |
145 | |
public final boolean map; |
146 | |
public final boolean collection; |
147 | |
|
148 | |
public Getter(final AccessMode.Reader reader, |
149 | |
final boolean primitive, final boolean array, |
150 | |
final boolean collection, final boolean map, |
151 | 189 | final Converter<Object> converter, final int version) { |
152 | 189 | this.reader = reader; |
153 | 189 | this.converter = converter; |
154 | 189 | this.version = version; |
155 | 189 | this.array = array; |
156 | 189 | this.map = map && converter == null; |
157 | 189 | this.collection = collection; |
158 | 189 | this.primitive = primitive; |
159 | 189 | } |
160 | |
} |
161 | |
|
162 | |
public static class Setter { |
163 | |
public final AccessMode.Writer writer; |
164 | |
public final int version; |
165 | |
public final Type paramType; |
166 | |
public final Converter<?> converter; |
167 | |
public final boolean primitive; |
168 | |
public final boolean array; |
169 | |
|
170 | |
public Setter(final AccessMode.Writer writer, final boolean primitive, final boolean array, |
171 | 183 | final Type paramType, final Converter<?> converter, final int version) { |
172 | 183 | this.writer = writer; |
173 | 183 | this.paramType = paramType; |
174 | 183 | this.converter = converter; |
175 | 183 | this.version = version; |
176 | 183 | this.primitive = primitive; |
177 | 183 | this.array = array; |
178 | 183 | } |
179 | |
} |
180 | |
|
181 | 0 | private static final JohnzonParameterizedType VIRTUAL_TYPE = new JohnzonParameterizedType(Map.class, String.class, Object.class); |
182 | |
|
183 | 63 | protected final ConcurrentMap<Type, ClassMapping> classes = new ConcurrentHashMap<Type, ClassMapping>(); |
184 | 63 | protected final ConcurrentMap<Type, CollectionMapping> collections = new ConcurrentHashMap<Type, CollectionMapping>(); |
185 | |
protected final Comparator<String> fieldOrdering; |
186 | |
private final boolean supportHiddenConstructors; |
187 | |
private final boolean supportConstructors; |
188 | |
private final AccessMode accessMode; |
189 | |
private final int version; |
190 | |
|
191 | |
public Mappings(final Comparator<String> attributeOrder, final AccessMode accessMode, |
192 | |
final boolean supportHiddenConstructors, final boolean supportConstructors, |
193 | 63 | final int version) { |
194 | 63 | this.fieldOrdering = attributeOrder; |
195 | 63 | this.accessMode = accessMode; |
196 | 63 | this.supportHiddenConstructors = supportHiddenConstructors; |
197 | 63 | this.supportConstructors = supportConstructors; |
198 | 63 | this.version = version; |
199 | 63 | } |
200 | |
|
201 | |
public <T> CollectionMapping findCollectionMapping(final ParameterizedType genericType) { |
202 | 23 | CollectionMapping collectionMapping = collections.get(genericType); |
203 | 23 | if (collectionMapping == null) { |
204 | 18 | collectionMapping = createCollectionMapping(genericType); |
205 | 18 | if (collectionMapping == null) { |
206 | 0 | return null; |
207 | |
} |
208 | 18 | final CollectionMapping existing = collections.putIfAbsent(genericType, collectionMapping); |
209 | 18 | if (existing != null) { |
210 | 18 | collectionMapping = existing; |
211 | |
} |
212 | |
} |
213 | 23 | return collectionMapping; |
214 | |
} |
215 | |
|
216 | |
private <T> CollectionMapping createCollectionMapping(final ParameterizedType aType) { |
217 | 18 | final Type[] fieldArgTypes = aType.getActualTypeArguments(); |
218 | 18 | final Type raw = aType.getRawType(); |
219 | 18 | if (fieldArgTypes.length == 1 && Class.class.isInstance(raw)) { |
220 | 18 | final Class<?> r = Class.class.cast(raw); |
221 | |
final Class<?> collectionType; |
222 | 18 | if (List.class.isAssignableFrom(r)) { |
223 | 13 | collectionType = List.class; |
224 | 5 | }else if (SortedSet.class.isAssignableFrom(r)) { |
225 | 2 | collectionType = SortedSet.class; |
226 | 3 | } else if (Set.class.isAssignableFrom(r)) { |
227 | 0 | collectionType = Set.class; |
228 | 3 | } else if (Queue.class.isAssignableFrom(r)) { |
229 | 1 | collectionType = Queue.class; |
230 | 2 | } else if (Collection.class.isAssignableFrom(r)) { |
231 | 2 | collectionType = Collection.class; |
232 | |
} else { |
233 | 0 | return null; |
234 | |
} |
235 | |
|
236 | 18 | final CollectionMapping mapping = new CollectionMapping(isPrimitive(fieldArgTypes[0]), collectionType, fieldArgTypes[0]); |
237 | 18 | collections.putIfAbsent(aType, mapping); |
238 | 18 | return mapping; |
239 | |
} |
240 | 0 | return null; |
241 | |
} |
242 | |
|
243 | |
|
244 | |
public static boolean isPrimitive(final Type type) { |
245 | 402 | if (type == String.class) { |
246 | 58 | return true; |
247 | 344 | } else if (type == char.class || type == Character.class) { |
248 | 4 | return true; |
249 | 340 | } else if (type == long.class || type == Long.class) { |
250 | 24 | return true; |
251 | 316 | } else if (type == int.class || type == Integer.class |
252 | |
|| type == byte.class || type == Byte.class |
253 | |
|| type == short.class || type == Short.class) { |
254 | 73 | return true; |
255 | 243 | } else if (type == double.class || type == Double.class |
256 | |
|| type == float.class || type == Float.class) { |
257 | 14 | return true; |
258 | 229 | } else if (type == boolean.class || type == Boolean.class) { |
259 | 30 | return true; |
260 | 199 | } else if (type == BigDecimal.class) { |
261 | 10 | return true; |
262 | 189 | } else if (type == BigInteger.class) { |
263 | 6 | return true; |
264 | |
} |
265 | 183 | return false; |
266 | |
} |
267 | |
|
268 | |
public ClassMapping getClassMapping(final Type clazz) { |
269 | 16 | return classes.get(clazz); |
270 | |
} |
271 | |
|
272 | |
public ClassMapping findOrCreateClassMapping(final Type clazz) { |
273 | 91 | ClassMapping classMapping = classes.get(clazz); |
274 | 91 | if (classMapping == null) { |
275 | 62 | if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) { |
276 | 11 | return null; |
277 | |
} |
278 | |
|
279 | 51 | classMapping = createClassMapping(Class.class.cast(clazz)); |
280 | 51 | final ClassMapping existing = classes.putIfAbsent(clazz, classMapping); |
281 | 51 | if (existing != null) { |
282 | 0 | classMapping = existing; |
283 | |
} |
284 | |
} |
285 | 80 | return classMapping; |
286 | |
} |
287 | |
|
288 | |
private ClassMapping createClassMapping(final Class<?> clazz) { |
289 | 51 | final Map<String, Getter> getters = newOrderedMap(); |
290 | 51 | final Map<String, Setter> setters = newOrderedMap(); |
291 | |
|
292 | 51 | final Map<String, AccessMode.Reader> readers = accessMode.findReaders(clazz); |
293 | 51 | final Map<String, AccessMode.Writer> writers = accessMode.findWriters(clazz); |
294 | |
|
295 | 51 | final Collection<String> virtualFields = new HashSet<String>(); |
296 | |
{ |
297 | 51 | final JohnzonVirtualObjects virtualObjects = clazz.getAnnotation(JohnzonVirtualObjects.class); |
298 | 51 | if (virtualObjects != null) { |
299 | 3 | for (final JohnzonVirtualObject virtualObject : virtualObjects.value()) { |
300 | 2 | handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers); |
301 | |
} |
302 | |
} |
303 | |
|
304 | 51 | final JohnzonVirtualObject virtualObject = clazz.getAnnotation(JohnzonVirtualObject.class); |
305 | 51 | if (virtualObject != null) { |
306 | 0 | handleVirtualObject(virtualFields, virtualObject, getters, setters, readers, writers); |
307 | |
} |
308 | |
} |
309 | |
|
310 | 51 | for (final Map.Entry<String, AccessMode.Reader> reader : readers.entrySet()) { |
311 | 187 | final String key = reader.getKey(); |
312 | 187 | if (virtualFields.contains(key)) { |
313 | 3 | continue; |
314 | |
} |
315 | 184 | addGetterIfNeeded(getters, key, reader.getValue()); |
316 | 184 | } |
317 | |
|
318 | 51 | for (final Map.Entry<String, AccessMode.Writer> writer : writers.entrySet()) { |
319 | 181 | final String key = writer.getKey(); |
320 | 181 | if (virtualFields.contains(key)) { |
321 | 3 | continue; |
322 | |
} |
323 | 178 | addSetterIfNeeded(setters, key, writer.getValue()); |
324 | 178 | } |
325 | 51 | return new ClassMapping(clazz, getters, setters, supportHiddenConstructors, supportConstructors); |
326 | |
} |
327 | |
|
328 | |
private <T> Map<String, T> newOrderedMap() { |
329 | 106 | return fieldOrdering != null ? new TreeMap<String, T>(fieldOrdering) : new HashMap<String, T>(); |
330 | |
} |
331 | |
|
332 | |
private void addSetterIfNeeded(final Map<String, Setter> setters, |
333 | |
final String key, |
334 | |
final AccessMode.Writer value) { |
335 | 181 | final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class); |
336 | 181 | if (writeIgnore == null || writeIgnore.minVersion() >= 0) { |
337 | 181 | if (key.equals("metaClass")) { |
338 | 0 | return; |
339 | |
} |
340 | 181 | final Type param = value.getType(); |
341 | 181 | final Class<?> returnType = Class.class.isInstance(param) ? Class.class.cast(param) : null; |
342 | 181 | final Setter setter = new Setter( |
343 | |
value, isPrimitive(param), returnType != null && returnType.isArray(), param, |
344 | |
findConverter(value), writeIgnore != null ? writeIgnore.minVersion() : -1); |
345 | 181 | setters.put(key, setter); |
346 | |
} |
347 | 181 | } |
348 | |
|
349 | |
private void addGetterIfNeeded(final Map<String, Getter> getters, |
350 | |
final String key, |
351 | |
final AccessMode.Reader value) { |
352 | 187 | final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class); |
353 | 187 | if (readIgnore == null || readIgnore.minVersion() >= 0) { |
354 | 187 | final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null; |
355 | 187 | final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null; |
356 | 187 | final Getter getter = new Getter(value, isPrimitive(returnType), |
357 | |
returnType != null && returnType.isArray(), |
358 | |
(pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType()))) |
359 | |
|| (returnType != null && Collection.class.isAssignableFrom(returnType)), |
360 | |
(pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType()))) |
361 | |
|| (returnType != null && Map.class.isAssignableFrom(returnType)), |
362 | |
findConverter(value), |
363 | |
readIgnore != null ? readIgnore.minVersion() : -1); |
364 | 187 | getters.put(key, getter); |
365 | |
} |
366 | 187 | } |
367 | |
|
368 | |
|
369 | |
private void handleVirtualObject(final Collection<String> virtualFields, |
370 | |
final JohnzonVirtualObject o, |
371 | |
final Map<String, Getter> getters, |
372 | |
final Map<String, Setter> setters, |
373 | |
final Map<String, AccessMode.Reader> readers, |
374 | |
final Map<String, AccessMode.Writer> writers) { |
375 | 2 | final String[] path = o.path(); |
376 | 2 | if (path.length < 1) { |
377 | 0 | throw new IllegalArgumentException("@JohnzonVirtualObject need a path"); |
378 | |
} |
379 | |
|
380 | |
|
381 | 5 | for (final JohnzonVirtualObject.Field f : o.fields()) { |
382 | 3 | virtualFields.add(f.value()); |
383 | |
} |
384 | |
|
385 | |
|
386 | 2 | final Map<String, Getter> objectGetters = newOrderedMap(); |
387 | 2 | final Map<String, Setter> objectSetters = newOrderedMap(); |
388 | |
|
389 | 5 | for (final JohnzonVirtualObject.Field f : o.fields()) { |
390 | 3 | final String name = f.value(); |
391 | 3 | if (f.read()) { |
392 | 3 | final AccessMode.Reader reader = readers.get(name); |
393 | 3 | if (reader != null) { |
394 | 3 | addGetterIfNeeded(objectGetters, name, reader); |
395 | |
} |
396 | |
} |
397 | 3 | if (f.write()) { |
398 | 3 | final AccessMode.Writer writer = writers.get(name); |
399 | 3 | if (writer != null) { |
400 | 3 | addSetterIfNeeded(objectSetters, name, writer); |
401 | |
} |
402 | |
} |
403 | |
} |
404 | |
|
405 | 2 | final String key = path[0]; |
406 | |
|
407 | 2 | final Getter getter = getters.get(key); |
408 | 2 | final MapBuilderReader newReader = new MapBuilderReader(objectGetters, path, version); |
409 | 2 | getters.put(key, new Getter(getter == null ? newReader : new CompositeReader(getter.reader, newReader), false, false, false, true, null, -1)); |
410 | |
|
411 | 0 | final Setter newSetter = setters.get(key); |
412 | 0 | final MapUnwrapperWriter newWriter = new MapUnwrapperWriter(objectSetters, path); |
413 | 0 | setters.put(key, new Setter(newSetter == null ? newWriter : new CompositeWriter(newSetter.writer, newWriter), false, false, VIRTUAL_TYPE, null, -1)); |
414 | 0 | } |
415 | |
|
416 | |
private static Converter findConverter(final AccessMode.DecoratedType method) { |
417 | 368 | Converter converter = null; |
418 | 0 | if (method.getAnnotation(JohnzonConverter.class) != null) { |
419 | |
try { |
420 | 4 | converter = method.getAnnotation(JohnzonConverter.class).value().newInstance(); |
421 | 0 | } catch (final Exception e) { |
422 | 368 | throw new IllegalArgumentException(e); |
423 | 0 | } |
424 | |
} |
425 | 0 | return converter; |
426 | |
} |
427 | |
|
428 | |
private static class MapBuilderReader implements AccessMode.Reader { |
429 | |
private final Map<String, Getter> getters; |
430 | |
private final Map<String, Object> template; |
431 | |
private final String[] paths; |
432 | |
private final int version; |
433 | |
|
434 | 2 | public MapBuilderReader(final Map<String, Getter> objectGetters, final String[] paths, final int version) { |
435 | 2 | this.getters = objectGetters; |
436 | 2 | this.paths = paths; |
437 | 2 | this.template = new LinkedHashMap<String, Object>(); |
438 | 2 | this.version = version; |
439 | |
|
440 | 2 | Map<String, Object> last = this.template; |
441 | 3 | for (int i = 1; i < paths.length; i++) { |
442 | 1 | final Map<String, Object> newLast = new LinkedHashMap<String, Object>(); |
443 | 1 | last.put(paths[i], newLast); |
444 | 1 | last = newLast; |
445 | |
} |
446 | 2 | } |
447 | |
|
448 | |
@Override |
449 | |
public Object read(final Object instance) { |
450 | 2 | final Map<String, Object> map = new LinkedHashMap<String, Object>(template); |
451 | 2 | Map<String, Object> nested = map; |
452 | 3 | for (int i = 1; i < paths.length; i++) { |
453 | 1 | nested = Map.class.cast(nested.get(paths[i])); |
454 | |
} |
455 | 2 | for (final Map.Entry<String, Getter> g : getters.entrySet()) { |
456 | 3 | final Mappings.Getter getter = g.getValue(); |
457 | 3 | final Object value = getter.reader.read(instance); |
458 | 3 | final Object val = value == null || getter.converter == null ? value : getter.converter.toString(value); |
459 | 3 | if (val == null) { |
460 | 0 | continue; |
461 | |
} |
462 | 3 | if (getter.version >= 0 && version >= getter.version) { |
463 | 0 | continue; |
464 | |
} |
465 | |
|
466 | 3 | nested.put(g.getKey(), val); |
467 | 3 | } |
468 | 2 | return map; |
469 | |
} |
470 | |
|
471 | |
@Override |
472 | |
public Type getType() { |
473 | 0 | return VIRTUAL_TYPE; |
474 | |
} |
475 | |
|
476 | |
@Override |
477 | |
public <T extends Annotation> T getAnnotation(final Class<T> clazz) { |
478 | 0 | throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields"); |
479 | |
} |
480 | |
} |
481 | |
|
482 | |
private static class MapUnwrapperWriter implements AccessMode.Writer { |
483 | |
private final Map<String, Setter> writers; |
484 | |
private final Map<String, Class<?>> componentTypes; |
485 | |
private final String[] paths; |
486 | |
|
487 | 2 | public MapUnwrapperWriter(final Map<String, Setter> writers, final String[] paths) { |
488 | 2 | this.writers = writers; |
489 | 2 | this.paths = paths; |
490 | 2 | this.componentTypes = new HashMap<String, Class<?>>(); |
491 | |
|
492 | 2 | for (final Map.Entry<String, Setter> setter : writers.entrySet()) { |
493 | 3 | if (setter.getValue().array) { |
494 | 1 | componentTypes.put(setter.getKey(), Class.class.cast(setter.getValue().paramType).getComponentType()); |
495 | |
} |
496 | 3 | } |
497 | 2 | } |
498 | |
|
499 | |
@Override |
500 | |
public void write(final Object instance, final Object value) { |
501 | 2 | Map<String, Object> nested = null; |
502 | 5 | for (final String path : paths) { |
503 | 3 | nested = Map.class.cast(nested == null ? value : nested.get(path)); |
504 | 3 | if (nested == null) { |
505 | 0 | return; |
506 | |
} |
507 | |
} |
508 | |
|
509 | 2 | for (final Map.Entry<String, Setter> setter : writers.entrySet()) { |
510 | 3 | final Setter setterValue = setter.getValue(); |
511 | 3 | final String key = setter.getKey(); |
512 | 3 | final Object rawValue = nested.get(key); |
513 | 3 | Object val = value == null || setterValue.converter == null ? |
514 | |
rawValue : Converter.class.cast(setterValue.converter).toString(rawValue); |
515 | 3 | if (val == null) { |
516 | 0 | continue; |
517 | |
} |
518 | |
|
519 | 3 | if (setterValue.array && Collection.class.isInstance(val)) { |
520 | 1 | final Collection<?> collection = Collection.class.cast(val); |
521 | 1 | final Object[] array = (Object[]) Array.newInstance(componentTypes.get(key), collection.size()); |
522 | 1 | val = collection.toArray(array); |
523 | |
} |
524 | |
|
525 | 3 | final AccessMode.Writer setterMethod = setterValue.writer; |
526 | 3 | setterMethod.write(instance, val); |
527 | 3 | } |
528 | 2 | } |
529 | |
|
530 | |
@Override |
531 | |
public Type getType() { |
532 | 0 | return VIRTUAL_TYPE; |
533 | |
} |
534 | |
|
535 | |
@Override |
536 | |
public <T extends Annotation> T getAnnotation(final Class<T> clazz) { |
537 | 0 | throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields"); |
538 | |
} |
539 | |
} |
540 | |
|
541 | |
private static class CompositeReader implements AccessMode.Reader { |
542 | |
private final AccessMode.Reader[] delegates; |
543 | |
|
544 | 1 | public CompositeReader(final AccessMode.Reader... delegates) { |
545 | 1 | final Collection<AccessMode.Reader> all = new LinkedList<AccessMode.Reader>(); |
546 | 3 | for (final AccessMode.Reader r : delegates) { |
547 | 2 | if (CompositeReader.class.isInstance(r)) { |
548 | 0 | all.addAll(asList(CompositeReader.class.cast(r).delegates)); |
549 | |
} else { |
550 | 2 | all.add(r); |
551 | |
} |
552 | |
} |
553 | 1 | this.delegates = all.toArray(new AccessMode.Reader[all.size()]); |
554 | 1 | } |
555 | |
|
556 | |
@Override |
557 | |
public Object read(final Object instance) { |
558 | 1 | final Map<String, Object> map = new LinkedHashMap<String, Object>(); |
559 | 3 | for (final AccessMode.Reader reader : delegates) { |
560 | 2 | final Map<String, Object> readerMap = (Map<String, Object>) reader.read(instance); |
561 | 2 | for (final Map.Entry<String, Object> entry :readerMap.entrySet()) { |
562 | 2 | final Object o = map.get(entry.getKey()); |
563 | 2 | if (o == null) { |
564 | 2 | map.put(entry.getKey(), entry.getValue()); |
565 | 0 | } else if (Map.class.isInstance(o)) { |
566 | |
|
567 | |
} else { |
568 | 0 | throw new IllegalStateException(entry.getKey() + " is ambiguous"); |
569 | |
} |
570 | 2 | } |
571 | |
} |
572 | 1 | return map; |
573 | |
} |
574 | |
|
575 | |
@Override |
576 | |
public Type getType() { |
577 | 0 | return VIRTUAL_TYPE; |
578 | |
} |
579 | |
|
580 | |
@Override |
581 | |
public <T extends Annotation> T getAnnotation(final Class<T> clazz) { |
582 | 0 | throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields"); |
583 | |
} |
584 | |
} |
585 | |
|
586 | |
private static class CompositeWriter implements AccessMode.Writer { |
587 | |
private final AccessMode.Writer[] delegates; |
588 | |
|
589 | 1 | public CompositeWriter(final AccessMode.Writer... writers) { |
590 | 1 | final Collection<AccessMode.Writer> all = new LinkedList<AccessMode.Writer>(); |
591 | 3 | for (final AccessMode.Writer r : writers) { |
592 | 2 | if (CompositeWriter.class.isInstance(r)) { |
593 | 0 | all.addAll(asList(CompositeWriter.class.cast(r).delegates)); |
594 | |
} else { |
595 | 2 | all.add(r); |
596 | |
} |
597 | |
} |
598 | 1 | this.delegates = all.toArray(new AccessMode.Writer[all.size()]); |
599 | 1 | } |
600 | |
|
601 | |
@Override |
602 | |
public void write(final Object instance, final Object value) { |
603 | 3 | for (final AccessMode.Writer w : delegates) { |
604 | 2 | w.write(instance, value); |
605 | |
} |
606 | 1 | } |
607 | |
|
608 | |
@Override |
609 | |
public Type getType() { |
610 | 0 | return VIRTUAL_TYPE; |
611 | |
} |
612 | |
|
613 | |
@Override |
614 | |
public <T extends Annotation> T getAnnotation(final Class<T> clazz) { |
615 | 0 | throw new UnsupportedOperationException("getAnnotation shouldn't get called for virtual fields"); |
616 | |
} |
617 | |
} |
618 | |
} |