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.access.AccessMode; |
25 | |
|
26 | |
import java.lang.reflect.Constructor; |
27 | |
import java.lang.reflect.Modifier; |
28 | |
import java.lang.reflect.ParameterizedType; |
29 | |
import java.lang.reflect.Type; |
30 | |
import java.math.BigDecimal; |
31 | |
import java.math.BigInteger; |
32 | |
import java.util.Collection; |
33 | |
import java.util.Comparator; |
34 | |
import java.util.HashMap; |
35 | |
import java.util.List; |
36 | |
import java.util.Map; |
37 | |
import java.util.Queue; |
38 | |
import java.util.Set; |
39 | |
import java.util.SortedSet; |
40 | |
import java.util.TreeMap; |
41 | |
import java.util.concurrent.ConcurrentHashMap; |
42 | |
import java.util.concurrent.ConcurrentMap; |
43 | |
|
44 | |
public class Mappings { |
45 | |
public static class ClassMapping { |
46 | |
public final Class<?> clazz; |
47 | |
public final Map<String, Getter> getters; |
48 | |
public final Map<String, Setter> setters; |
49 | |
public final Constructor<?> constructor; |
50 | |
|
51 | |
protected ClassMapping(final Class<?> clazz, |
52 | |
final Map<String, Getter> getters, final Map<String, Setter> setters, |
53 | 42 | final boolean acceptHiddenConstructor) { |
54 | 42 | this.clazz = clazz; |
55 | 42 | this.getters = getters; |
56 | 42 | this.setters = setters; |
57 | 42 | this.constructor = findConstructor(acceptHiddenConstructor); |
58 | 42 | } |
59 | |
|
60 | |
private Constructor<?> findConstructor(final boolean acceptHiddenConstructor) { |
61 | 43 | for (final Constructor<?> c : clazz.getDeclaredConstructors()) { |
62 | 42 | if (c.getParameterTypes().length == 0) { |
63 | 41 | if (!Modifier.isPublic(c.getModifiers()) && acceptHiddenConstructor) { |
64 | 7 | c.setAccessible(true); |
65 | |
} |
66 | 41 | return c; |
67 | |
} |
68 | |
} |
69 | |
try { |
70 | 1 | return clazz.getConstructor(); |
71 | 1 | } catch (final NoSuchMethodException e) { |
72 | 1 | return null; |
73 | |
} |
74 | |
} |
75 | |
} |
76 | |
|
77 | |
public static class CollectionMapping { |
78 | |
public final Class<?> raw; |
79 | |
public final Type arg; |
80 | |
public final boolean primitive; |
81 | |
|
82 | 13 | public CollectionMapping(final boolean primitive, final Class<?> collectionType, final Type fieldArgType) { |
83 | 13 | this.raw = collectionType; |
84 | 13 | this.arg = fieldArgType; |
85 | 13 | this.primitive = primitive; |
86 | 13 | } |
87 | |
} |
88 | |
|
89 | |
public static class Getter { |
90 | |
public final AccessMode.Reader reader; |
91 | |
public final int version; |
92 | |
public final Converter<Object> converter; |
93 | |
public final boolean primitive; |
94 | |
public final boolean array; |
95 | |
public final boolean map; |
96 | |
public final boolean collection; |
97 | |
|
98 | |
public Getter(final AccessMode.Reader reader, |
99 | |
final boolean primitive, final boolean array, |
100 | |
final boolean collection, final boolean map, |
101 | 170 | final Converter<Object> converter, final int version) { |
102 | 170 | this.reader = reader; |
103 | 170 | this.converter = converter; |
104 | 170 | this.version = version; |
105 | 170 | this.array = array; |
106 | 170 | this.map = map && converter == null; |
107 | 170 | this.collection = collection; |
108 | 170 | this.primitive = primitive; |
109 | 170 | } |
110 | |
} |
111 | |
|
112 | |
public static class Setter { |
113 | |
public final AccessMode.Writer writer; |
114 | |
public final int version; |
115 | |
public final Type paramType; |
116 | |
public final Converter<?> converter; |
117 | |
public final boolean primitive; |
118 | |
|
119 | 164 | public Setter(final AccessMode.Writer writer, final boolean primitive, final Type paramType, final Converter<?> converter, final int version) { |
120 | 164 | this.writer = writer; |
121 | 164 | this.paramType = paramType; |
122 | 164 | this.converter = converter; |
123 | 164 | this.version = version; |
124 | 164 | this.primitive = primitive; |
125 | 164 | } |
126 | |
} |
127 | |
|
128 | 48 | protected final ConcurrentMap<Type, ClassMapping> classes = new ConcurrentHashMap<Type, ClassMapping>(); |
129 | 48 | protected final ConcurrentMap<Type, CollectionMapping> collections = new ConcurrentHashMap<Type, CollectionMapping>(); |
130 | |
protected final Comparator<String> fieldOrdering; |
131 | |
private final boolean supportHiddenConstructors; |
132 | |
private final AccessMode accessMode; |
133 | |
|
134 | 48 | public Mappings(final Comparator<String> attributeOrder, final AccessMode accessMode, final boolean supportHiddenConstructors) { |
135 | 48 | this.fieldOrdering = attributeOrder; |
136 | 48 | this.accessMode = accessMode; |
137 | 48 | this.supportHiddenConstructors = supportHiddenConstructors; |
138 | 48 | } |
139 | |
|
140 | |
public <T> CollectionMapping findCollectionMapping(final ParameterizedType genericType) { |
141 | 16 | CollectionMapping collectionMapping = collections.get(genericType); |
142 | 16 | if (collectionMapping == null) { |
143 | 13 | collectionMapping = createCollectionMapping(genericType); |
144 | 13 | if (collectionMapping == null) { |
145 | 0 | return null; |
146 | |
} |
147 | 13 | final CollectionMapping existing = collections.putIfAbsent(genericType, collectionMapping); |
148 | 13 | if (existing != null) { |
149 | 13 | collectionMapping = existing; |
150 | |
} |
151 | |
} |
152 | 16 | return collectionMapping; |
153 | |
} |
154 | |
|
155 | |
private <T> CollectionMapping createCollectionMapping(final ParameterizedType aType) { |
156 | 13 | final Type[] fieldArgTypes = aType.getActualTypeArguments(); |
157 | 13 | final Type raw = aType.getRawType(); |
158 | 13 | if (fieldArgTypes.length == 1 && Class.class.isInstance(raw)) { |
159 | 13 | final Class<?> r = Class.class.cast(raw); |
160 | |
final Class<?> collectionType; |
161 | 13 | if (List.class.isAssignableFrom(r)) { |
162 | 10 | collectionType = List.class; |
163 | 3 | }else if (SortedSet.class.isAssignableFrom(r)) { |
164 | 2 | collectionType = SortedSet.class; |
165 | 1 | } else if (Set.class.isAssignableFrom(r)) { |
166 | 0 | collectionType = Set.class; |
167 | 1 | } else if (Queue.class.isAssignableFrom(r)) { |
168 | 1 | collectionType = Queue.class; |
169 | 0 | } else if (Collection.class.isAssignableFrom(r)) { |
170 | 0 | collectionType = Collection.class; |
171 | |
} else { |
172 | 0 | return null; |
173 | |
} |
174 | |
|
175 | 13 | final CollectionMapping mapping = new CollectionMapping(isPrimitive(fieldArgTypes[0]), collectionType, fieldArgTypes[0]); |
176 | 13 | collections.putIfAbsent(aType, mapping); |
177 | 13 | return mapping; |
178 | |
} |
179 | 0 | return null; |
180 | |
} |
181 | |
|
182 | |
|
183 | |
public static boolean isPrimitive(final Type type) { |
184 | 359 | if (type == String.class) { |
185 | 47 | return true; |
186 | 312 | } else if (type == char.class || type == Character.class) { |
187 | 4 | return true; |
188 | 308 | } else if (type == long.class || type == Long.class) { |
189 | 26 | return true; |
190 | 282 | } else if (type == int.class || type == Integer.class |
191 | |
|| type == byte.class || type == Byte.class |
192 | |
|| type == short.class || type == Short.class) { |
193 | 57 | return true; |
194 | 225 | } else if (type == double.class || type == Double.class |
195 | |
|| type == float.class || type == Float.class) { |
196 | 16 | return true; |
197 | 209 | } else if (type == boolean.class || type == Boolean.class) { |
198 | 32 | return true; |
199 | 177 | } else if (type == BigDecimal.class) { |
200 | 12 | return true; |
201 | 165 | } else if (type == BigInteger.class) { |
202 | 8 | return true; |
203 | |
} |
204 | 157 | return false; |
205 | |
} |
206 | |
|
207 | |
public ClassMapping getClassMapping(final Type clazz) { |
208 | 12 | return classes.get(clazz); |
209 | |
} |
210 | |
|
211 | |
public ClassMapping findOrCreateClassMapping(final Type clazz) { |
212 | 74 | ClassMapping classMapping = classes.get(clazz); |
213 | 74 | if (classMapping == null) { |
214 | 50 | if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) { |
215 | 8 | return null; |
216 | |
} |
217 | |
|
218 | 42 | classMapping = createClassMapping(Class.class.cast(clazz)); |
219 | 42 | final ClassMapping existing = classes.putIfAbsent(clazz, classMapping); |
220 | 42 | if (existing != null) { |
221 | 0 | classMapping = existing; |
222 | |
} |
223 | |
} |
224 | 66 | return classMapping; |
225 | |
} |
226 | |
|
227 | |
private ClassMapping createClassMapping(final Class<?> clazz) { |
228 | 42 | final Map<String, Getter> getters = fieldOrdering != null ? |
229 | |
new TreeMap<String, Getter>(fieldOrdering) : new HashMap<String, Getter>(); |
230 | 42 | final Map<String, Setter> setters = fieldOrdering != null ? |
231 | |
new TreeMap<String, Setter>(fieldOrdering) : new HashMap<String, Setter>(); |
232 | |
|
233 | 42 | for (final Map.Entry<String, AccessMode.Reader> reader : accessMode.findReaders(clazz).entrySet()) { |
234 | 170 | final AccessMode.Reader value = reader.getValue(); |
235 | 170 | final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class); |
236 | 170 | if (readIgnore == null || readIgnore.minVersion() >= 0) { |
237 | 170 | final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null; |
238 | 170 | final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null; |
239 | 170 | getters.put(reader.getKey(), new Getter(value, isPrimitive(returnType), |
240 | |
returnType != null && returnType.isArray(), |
241 | |
(pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType()))) |
242 | |
|| (returnType != null && Collection.class.isAssignableFrom(returnType)), |
243 | |
(pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType()))) |
244 | |
|| (returnType != null && Map.class.isAssignableFrom(returnType)), |
245 | |
findConverter(value), |
246 | |
readIgnore != null ? readIgnore.minVersion() : -1)); |
247 | |
} |
248 | 0 | } |
249 | 0 | for (final Map.Entry<String, AccessMode.Writer> writer : accessMode.findWriters(clazz).entrySet()) { |
250 | 0 | final AccessMode.Writer value = writer.getValue(); |
251 | 0 | final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class); |
252 | 0 | if (writeIgnore == null || writeIgnore.minVersion() >= 0) { |
253 | 0 | final String key = writer.getKey(); |
254 | 0 | if (key.equals("metaClass")) { |
255 | 0 | continue; |
256 | |
} |
257 | 0 | final Type param = value.getType(); |
258 | 0 | setters.put(key, new Setter(value, isPrimitive(param), param, findConverter(value), writeIgnore != null ? writeIgnore.minVersion() : -1)); |
259 | |
} |
260 | 42 | } |
261 | 0 | return new ClassMapping(clazz, getters, setters, supportHiddenConstructors); |
262 | |
} |
263 | |
|
264 | |
private static Converter findConverter(final AccessMode.DecoratedType method) { |
265 | 334 | Converter converter = null; |
266 | 0 | if (method.getAnnotation(JohnzonConverter.class) != null) { |
267 | |
try { |
268 | 4 | converter = method.getAnnotation(JohnzonConverter.class).value().newInstance(); |
269 | 0 | } catch (final Exception e) { |
270 | 334 | throw new IllegalArgumentException(e); |
271 | 0 | } |
272 | |
} |
273 | 0 | return converter; |
274 | |
} |
275 | |
} |