1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.beans;
17
18 import java.lang.reflect.Array;
19 import java.lang.reflect.GenericArrayType;
20 import java.lang.reflect.ParameterizedType;
21 import java.lang.reflect.Type;
22 import java.lang.reflect.TypeVariable;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31
32 import org.joda.beans.impl.direct.DirectBean;
33 import org.joda.beans.impl.flexi.FlexiBean;
34 import org.joda.convert.StringConvert;
35
36
37
38
39
40
41 public final class JodaBeanUtils {
42
43
44
45
46 private static final ConcurrentHashMap<Class<?>, MetaBean> metaBeans = new ConcurrentHashMap<Class<?>, MetaBean>();
47
48
49
50 private static final StringConvert converter = new StringConvert();
51
52
53
54
55 private JodaBeanUtils() {
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69 public static MetaBean metaBean(Class<?> cls) {
70 MetaBean meta = metaBeans.get(cls);
71 if (meta == null) {
72 throw new IllegalArgumentException("Unable to find meta-bean: " + cls.getName());
73 }
74 return meta;
75 }
76
77
78
79
80
81
82
83
84
85
86 public static void registerMetaBean(MetaBean metaBean) {
87 Class<? extends Bean> type = metaBean.beanType();
88 if (metaBeans.putIfAbsent(type, metaBean) != null) {
89 throw new IllegalArgumentException("Cannot register class twice: " + type.getName());
90 }
91 }
92
93
94
95
96
97
98
99
100
101
102 public static StringConvert stringConverter() {
103 return converter;
104 }
105
106
107
108
109
110
111
112
113
114 public static boolean equal(Object obj1, Object obj2) {
115 if (obj1 == obj2) {
116 return true;
117 }
118 if (obj1 == null || obj2 == null) {
119 return false;
120 }
121 if (obj1.getClass().isArray() && obj1.getClass() == obj2.getClass()) {
122 if (obj1 instanceof Object[] && obj2 instanceof Object[]) {
123 return Arrays.deepEquals((Object[]) obj1, (Object[]) obj2);
124 } else if (obj1 instanceof int[] && obj2 instanceof int[]) {
125 return Arrays.equals((int[]) obj1, (int[]) obj2);
126 } else if (obj1 instanceof long[] && obj2 instanceof long[]) {
127 return Arrays.equals((long[]) obj1, (long[]) obj2);
128 } else if (obj1 instanceof byte[] && obj2 instanceof byte[]) {
129 return Arrays.equals((byte[]) obj1, (byte[]) obj2);
130 } else if (obj1 instanceof double[] && obj2 instanceof double[]) {
131 return Arrays.equals((double[]) obj1, (double[]) obj2);
132 } else if (obj1 instanceof float[] && obj2 instanceof float[]) {
133 return Arrays.equals((float[]) obj1, (float[]) obj2);
134 } else if (obj1 instanceof char[] && obj2 instanceof char[]) {
135 return Arrays.equals((char[]) obj1, (char[]) obj2);
136 } else if (obj1 instanceof short[] && obj2 instanceof short[]) {
137 return Arrays.equals((short[]) obj1, (short[]) obj2);
138 } else if (obj1 instanceof boolean[] && obj2 instanceof boolean[]) {
139 return Arrays.equals((boolean[]) obj1, (boolean[]) obj2);
140 }
141 }
142 return obj1.equals(obj2);
143 }
144
145
146
147
148
149
150
151 public static int hashCode(Object obj) {
152 return obj == null ? 0 : obj.hashCode();
153 }
154
155
156
157
158
159
160
161 public static int hashCode(long value) {
162 return (int) (value ^ value >>> 32);
163 }
164
165
166
167
168
169
170
171 public static int hashCode(float value) {
172 return Float.floatToIntBits(value);
173 }
174
175
176
177
178
179
180
181 public static int hashCode(double value) {
182 return hashCode(Double.doubleToLongBits(value));
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 public static boolean propertiesEqual(Bean bean1, Bean bean2) {
203 Set<String> names = bean1.propertyNames();
204 if (names.equals(bean2.propertyNames()) == false) {
205 return false;
206 }
207 for (String name : names) {
208 Object value1 = bean1.property(name).get();
209 Object value2 = bean2.property(name).get();
210 if (equal(value1, value2) == false) {
211 return false;
212 }
213 }
214 return true;
215 }
216
217
218
219
220
221
222
223
224
225
226
227 public static int propertiesHashCode(Bean bean) {
228 int hash = 7;
229 Set<String> names = bean.propertyNames();
230 for (String name : names) {
231 Object value = bean.property(name).get();
232 hash += hashCode(value);
233 }
234 return hash;
235 }
236
237
238
239
240
241
242
243
244
245
246
247 public static String propertiesToString(Bean bean, String prefix) {
248 Set<String> names = bean.propertyNames();
249 StringBuilder buf = new StringBuilder((names.size()) * 32 + prefix.length());
250 if (prefix != null) {
251 buf.append(prefix);
252 }
253 buf.append('{');
254 if (names.size() > 0) {
255 for (String name : names) {
256 Object value = bean.property(name).get();
257 buf.append(name).append('=').append(value).append(',').append(' ');
258 }
259 buf.setLength(buf.length() - 2);
260 }
261 buf.append('}');
262 return buf.toString();
263 }
264
265
266 @SuppressWarnings("unchecked")
267 public static <T extends Bean> T clone(T original) {
268 BeanBuilder<? extends Bean> builder = original.metaBean().builder();
269 for (MetaProperty<?> mp : original.metaBean().metaPropertyIterable()) {
270 if (mp.readWrite().isWritable()) {
271 Object value = mp.get(original);
272 if (value instanceof Bean) {
273 value = clone((Bean) value);
274 }
275 builder.set(mp.name(), value);
276 }
277 }
278 return (T) builder.build();
279 }
280
281
282
283
284
285
286
287
288
289 public static void notNull(Object value, String propertyName) {
290 if (value == null) {
291 throw new IllegalArgumentException("Argument '" + propertyName + "' must not be null");
292 }
293 }
294
295
296
297
298
299
300
301
302 public static void notEmpty(String value, String propertyName) {
303 if (value == null || value.length() == 0) {
304 throw new IllegalArgumentException("Argument '" + propertyName + "' must not be empty");
305 }
306 }
307
308
309
310
311
312
313
314
315
316
317
318 public static Class<?> collectionType(Property<?> prop) {
319 return collectionType(prop.metaProperty(), prop.bean().getClass());
320 }
321
322
323
324
325
326
327
328
329
330
331
332 public static Class<?> collectionType(MetaProperty<?> prop, Class<?> targetClass) {
333 if (Collection.class.isAssignableFrom(prop.propertyType()) == false) {
334 throw new IllegalArgumentException("Property is not a Collection");
335 }
336 return extractType(targetClass, prop, 1, 0);
337 }
338
339
340
341
342
343
344
345
346 public static Class<?> mapKeyType(Property<?> prop) {
347 return mapKeyType(prop.metaProperty(), prop.bean().getClass());
348 }
349
350
351
352
353
354
355
356
357
358
359
360 public static Class<?> mapKeyType(MetaProperty<?> prop, Class<?> targetClass) {
361 if (Map.class.isAssignableFrom(prop.propertyType()) == false) {
362 throw new IllegalArgumentException("Property is not a Map");
363 }
364 return extractType(targetClass, prop, 2, 0);
365 }
366
367
368
369
370
371
372
373
374 public static Class<?> mapValueType(Property<?> prop) {
375 return mapValueType(prop.metaProperty(), prop.bean().getClass());
376 }
377
378
379
380
381
382
383
384
385
386
387
388 public static Class<?> mapValueType(MetaProperty<?> prop, Class<?> targetClass) {
389 if (Map.class.isAssignableFrom(prop.propertyType()) == false) {
390 throw new IllegalArgumentException("Property is not a Map");
391 }
392 return extractType(targetClass, prop, 2, 1);
393 }
394
395 private static Class<?> extractType(Class<?> targetClass, MetaProperty<?> prop, int size, int index) {
396 Type genType = prop.propertyGenericType();
397 if (genType instanceof ParameterizedType) {
398 ParameterizedType pt = (ParameterizedType) genType;
399 Type[] types = pt.getActualTypeArguments();
400 if (types.length == size) {
401 Type type = types[index];
402 if (type instanceof TypeVariable) {
403 type = resolveGenerics(targetClass, (TypeVariable<?>) type);
404 }
405 return eraseToClass(type);
406 }
407 }
408 return null;
409 }
410
411 private static Type resolveGenerics(Class<?> targetClass, TypeVariable<?> typevar) {
412
413 Map<Type, Type> resolved = new HashMap<Type, Type>();
414 Type type = targetClass;
415 while (type != null) {
416 if (type instanceof Class) {
417 type = ((Class<?>) type).getGenericSuperclass();
418 } else if (type instanceof ParameterizedType) {
419
420 ParameterizedType pt = (ParameterizedType) type;
421 Type[] actualTypeArguments = pt.getActualTypeArguments();
422
423 Class<?> rawType = eraseToClass(pt.getRawType());
424 TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
425 for (int i = 0; i < actualTypeArguments.length; i++) {
426 resolved.put(typeParameters[i], actualTypeArguments[i]);
427 }
428 type = rawType.getGenericSuperclass();
429 }
430 }
431
432 Type result = typevar;
433 while (resolved.containsKey(result)) {
434 result = resolved.get(result);
435 }
436 return result;
437 }
438
439 private static Class<?> eraseToClass(Type type) {
440 if (type instanceof Class) {
441 return (Class<?>) type;
442 } else if (type instanceof ParameterizedType) {
443 return eraseToClass(((ParameterizedType) type).getRawType());
444 } else if (type instanceof GenericArrayType) {
445 Type componentType = ((GenericArrayType) type).getGenericComponentType();
446 Class<?> componentClass = eraseToClass(componentType);
447 if (componentClass != null) {
448 return Array.newInstance(componentClass, 0).getClass();
449 }
450 } else if (type instanceof TypeVariable) {
451 Type[] bounds = ((TypeVariable<?>) type).getBounds();
452 if (bounds.length == 0) {
453 return Object.class;
454 } else {
455 return eraseToClass(bounds[0]);
456 }
457 }
458 return null;
459 }
460
461
462
463
464
465
466
467
468
469
470
471 public static Comparator<Bean> comparator(BeanQuery<?> query, boolean ascending) {
472 return (ascending ? comparatorAscending(query) : comparatorDescending(query));
473 }
474
475
476
477
478
479
480
481
482
483 public static Comparator<Bean> comparatorAscending(BeanQuery<?> query) {
484 if (query == null) {
485 throw new NullPointerException("BeanQuery must not be null");
486 }
487 return new Comp(query);
488 }
489
490
491
492
493
494
495
496
497
498 public static Comparator<Bean> comparatorDescending(BeanQuery<?> query) {
499 if (query == null) {
500 throw new NullPointerException("BeanQuery must not be null");
501 }
502 return Collections.reverseOrder(new Comp(query));
503 }
504
505
506
507
508
509 private static final class Comp implements Comparator<Bean> {
510 private final BeanQuery<?> query;
511
512 private Comp(BeanQuery<?> query) {
513 this.query = query;
514 }
515
516 @Override
517 public int compare(Bean bean1, Bean bean2) {
518 @SuppressWarnings("unchecked")
519 Comparable<Object> value1 = (Comparable<Object>) query.get(bean1);
520 Object value2 = query.get(bean2);
521 return value1.compareTo(value2);
522 }
523 }
524
525 }