1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.jxpath.util;
19
20 import java.beans.IndexedPropertyDescriptor;
21 import java.beans.PropertyDescriptor;
22 import java.lang.reflect.Array;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.apache.commons.jxpath.Container;
35 import org.apache.commons.jxpath.DynamicPropertyHandler;
36 import org.apache.commons.jxpath.JXPathException;
37
38
39
40
41 public class ValueUtils {
42
43 private static Map<Class, DynamicPropertyHandler> dynamicPropertyHandlerMap = new HashMap<>();
44 private static final int UNKNOWN_LENGTH_MAX_COUNT = 16000;
45
46
47
48
49
50
51
52
53 private static Object convert(final Object value, final Class type) {
54 try {
55 return TypeUtils.convert(value, type);
56 } catch (final Exception ex) {
57 throw new JXPathException("Cannot convert value of class " + (value == null ? "null" : value.getClass().getName()) + " to type " + type, ex);
58 }
59 }
60
61
62
63
64
65
66
67
68 public static Object expandCollection(final Object collection, final int size) {
69 if (collection == null) {
70 return null;
71 }
72 if (size < getLength(collection)) {
73 throw new JXPathException("adjustment of " + collection + " to size " + size + " is not an expansion");
74 }
75 if (collection.getClass().isArray()) {
76 final Object bigger = Array.newInstance(collection.getClass().getComponentType(), size);
77 System.arraycopy(collection, 0, bigger, 0, Array.getLength(collection));
78 return bigger;
79 }
80 if (collection instanceof Collection) {
81 while (((Collection) collection).size() < size) {
82 ((Collection) collection).add(null);
83 }
84 return collection;
85 }
86 throw new JXPathException("Cannot turn " + collection.getClass().getName() + " into a collection of size " + size);
87 }
88
89
90
91
92
93
94
95
96 public static Method getAccessibleMethod(final Method method) {
97
98 if (method == null) {
99 return null;
100 }
101
102 if (!Modifier.isPublic(method.getModifiers())) {
103 return null;
104 }
105
106 Class clazz = method.getDeclaringClass();
107 if (Modifier.isPublic(clazz.getModifiers())) {
108 return method;
109 }
110 final String name = method.getName();
111 final Class[] parameterTypes = method.getParameterTypes();
112 while (clazz != null) {
113
114 final Method aMethod = getAccessibleMethodFromInterfaceNest(clazz, name, parameterTypes);
115 if (aMethod != null) {
116 return aMethod;
117 }
118 clazz = clazz.getSuperclass();
119 if (clazz != null && Modifier.isPublic(clazz.getModifiers())) {
120 try {
121 return clazz.getDeclaredMethod(name, parameterTypes);
122 } catch (final NoSuchMethodException ignore) {
123
124 }
125 }
126 }
127 return null;
128 }
129
130
131
132
133
134
135
136
137
138
139 private static Method getAccessibleMethodFromInterfaceNest(final Class clazz, final String methodName, final Class[] parameterTypes) {
140 Method method = null;
141
142 final Class[] interfaces = clazz.getInterfaces();
143 for (final Class element : interfaces) {
144
145 if (!Modifier.isPublic(element.getModifiers())) {
146 continue;
147 }
148
149 try {
150 method = element.getDeclaredMethod(methodName, parameterTypes);
151 } catch (final NoSuchMethodException ignore) {
152
153 }
154 if (method != null) {
155 break;
156 }
157
158 method = getAccessibleMethodFromInterfaceNest(element, methodName, parameterTypes);
159 if (method != null) {
160 break;
161 }
162 }
163
164 return method;
165 }
166
167
168
169
170
171
172
173 public static int getCollectionHint(final Class clazz) {
174 if (clazz.isArray()) {
175 return 1;
176 }
177 if (Collection.class.isAssignableFrom(clazz)) {
178 return 1;
179 }
180 if (clazz.isPrimitive()) {
181 return -1;
182 }
183 if (clazz.isInterface()) {
184 return 0;
185 }
186 if (Modifier.isFinal(clazz.getModifiers())) {
187 return -1;
188 }
189 return 0;
190 }
191
192
193
194
195
196
197
198 public static DynamicPropertyHandler getDynamicPropertyHandler(final Class clazz) {
199 return dynamicPropertyHandlerMap.computeIfAbsent(clazz, k -> {
200 try {
201 return (DynamicPropertyHandler) clazz.getConstructor().newInstance();
202 } catch (final Exception ex) {
203 throw new JXPathException("Cannot allocate dynamic property handler of class " + clazz.getName(), ex);
204 }
205 });
206 }
207
208
209
210
211
212
213
214
215
216
217 public static int getIndexedPropertyLength(final Object object, final IndexedPropertyDescriptor pd) {
218 if (pd.getReadMethod() != null) {
219 return getLength(getValue(object, pd));
220 }
221 final Method readMethod = pd.getIndexedReadMethod();
222 if (readMethod == null) {
223 throw new JXPathException("No indexed read method for property " + pd.getName());
224 }
225 for (int i = 0; i < UNKNOWN_LENGTH_MAX_COUNT; i++) {
226 try {
227 readMethod.invoke(object, Integer.valueOf(i));
228 } catch (final Throwable t) {
229 return i;
230 }
231 }
232 throw new JXPathException("Cannot determine the length of the indexed property " + pd.getName());
233 }
234
235
236
237
238
239
240
241 public static int getLength(Object collection) {
242 if (collection == null) {
243 return 0;
244 }
245 collection = getValue(collection);
246 if (collection.getClass().isArray()) {
247 return Array.getLength(collection);
248 }
249 if (collection instanceof Collection) {
250 return ((Collection) collection).size();
251 }
252 return 1;
253 }
254
255
256
257
258
259
260
261 public static Object getValue(Object object) {
262 while (object instanceof Container) {
263 object = ((Container) object).getValue();
264 }
265 return object;
266 }
267
268
269
270
271
272
273
274
275 public static Object getValue(Object collection, final int index) {
276 collection = getValue(collection);
277 Object value = collection;
278 if (collection != null) {
279 if (collection.getClass().isArray()) {
280 if (index < 0 || index >= Array.getLength(collection)) {
281 return null;
282 }
283 value = Array.get(collection, index);
284 } else if (collection instanceof List) {
285 if (index < 0 || index >= ((List) collection).size()) {
286 return null;
287 }
288 value = ((List) collection).get(index);
289 } else if (collection instanceof Collection) {
290 if (index < 0 || index >= ((Collection) collection).size()) {
291 return null;
292 }
293 int i = 0;
294 final Iterator it = ((Collection) collection).iterator();
295 for (; i < index; i++) {
296 it.next();
297 }
298 if (it.hasNext()) {
299 value = it.next();
300 } else {
301 value = null;
302 }
303 }
304 }
305 return value;
306 }
307
308
309
310
311
312
313
314
315 public static Object getValue(final Object bean, final PropertyDescriptor propertyDescriptor) {
316 Object value;
317 try {
318 final Method method = getAccessibleMethod(propertyDescriptor.getReadMethod());
319 if (method == null) {
320 throw new JXPathException("No read method");
321 }
322 value = method.invoke(bean);
323 } catch (final Exception ex) {
324 throw new JXPathException("Cannot access property: " + (bean == null ? "null" : bean.getClass().getName()) + "." + propertyDescriptor.getName(),
325 ex);
326 }
327 return value;
328 }
329
330
331
332
333
334
335
336
337
338 public static Object getValue(final Object bean, final PropertyDescriptor propertyDescriptor, final int index) {
339 if (propertyDescriptor instanceof IndexedPropertyDescriptor) {
340 try {
341 final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) propertyDescriptor;
342 final Method method = ipd.getIndexedReadMethod();
343 if (method != null) {
344 return method.invoke(bean, Integer.valueOf(index));
345 }
346 } catch (final InvocationTargetException ex) {
347 final Throwable t = ex.getTargetException();
348 if (t instanceof IndexOutOfBoundsException) {
349 return null;
350 }
351 throw new JXPathException("Cannot access property: " + propertyDescriptor.getName(), t);
352 } catch (final Throwable ex) {
353 throw new JXPathException("Cannot access property: " + propertyDescriptor.getName(), ex);
354 }
355 }
356
357 return getValue(getValue(bean, propertyDescriptor), index);
358 }
359
360
361
362
363
364
365
366 public static boolean isCollection(Object value) {
367 value = getValue(value);
368 if (value == null) {
369 return false;
370 }
371 if (value.getClass().isArray()) {
372 return true;
373 }
374 return value instanceof Collection;
375 }
376
377
378
379
380
381
382
383
384 public static Iterator iterate(final Object collection) {
385 if (collection == null) {
386 return Collections.EMPTY_LIST.iterator();
387 }
388 if (collection.getClass().isArray()) {
389 final int length = Array.getLength(collection);
390 if (length == 0) {
391 return Collections.EMPTY_LIST.iterator();
392 }
393 final ArrayList list = new ArrayList();
394 for (int i = 0; i < length; i++) {
395 list.add(Array.get(collection, i));
396 }
397 return list.iterator();
398 }
399 if (collection instanceof Collection) {
400 return ((Collection) collection).iterator();
401 }
402 return Collections.singletonList(collection).iterator();
403 }
404
405
406
407
408
409
410
411
412 public static Object remove(Object collection, final int index) {
413 collection = getValue(collection);
414 if (collection == null) {
415 return null;
416 }
417 if (index >= getLength(collection)) {
418 throw new JXPathException("No such element at index " + index);
419 }
420 if (collection.getClass().isArray()) {
421 final int length = Array.getLength(collection);
422 final Object smaller = Array.newInstance(collection.getClass().getComponentType(), length - 1);
423 if (index > 0) {
424 System.arraycopy(collection, 0, smaller, 0, index);
425 }
426 if (index < length - 1) {
427 System.arraycopy(collection, index + 1, smaller, index, length - index - 1);
428 }
429 return smaller;
430 }
431 if (collection instanceof List) {
432 final int size = ((List) collection).size();
433 if (index < size) {
434 ((List) collection).remove(index);
435 }
436 return collection;
437 }
438 if (collection instanceof Collection) {
439 final Iterator it = ((Collection) collection).iterator();
440 for (int i = 0; i < index; i++) {
441 if (!it.hasNext()) {
442 break;
443 }
444 it.next();
445 }
446 if (it.hasNext()) {
447 it.next();
448 it.remove();
449 }
450 return collection;
451 }
452 throw new JXPathException("Cannot remove " + collection.getClass().getName() + "[" + index + "]");
453 }
454
455
456
457
458
459
460
461
462 public static void setValue(Object collection, final int index, final Object value) {
463 collection = getValue(collection);
464 if (collection != null) {
465 if (collection.getClass().isArray()) {
466 Array.set(collection, index, convert(value, collection.getClass().getComponentType()));
467 } else if (collection instanceof List) {
468 ((List) collection).set(index, value);
469 } else if (collection instanceof Collection) {
470 throw new UnsupportedOperationException("Cannot set value of an element of a " + collection.getClass().getName());
471 }
472 }
473 }
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489 public static void setValue(final Object bean, final PropertyDescriptor propertyDescriptor, final int index, final Object value) {
490 if (propertyDescriptor instanceof IndexedPropertyDescriptor) {
491 try {
492 final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) propertyDescriptor;
493 final Method method = ipd.getIndexedWriteMethod();
494 if (method != null) {
495 method.invoke(bean, Integer.valueOf(index), convert(value, ipd.getIndexedPropertyType()));
496 return;
497 }
498 } catch (final Exception ex) {
499 throw new IllegalArgumentException("Cannot access property: " + propertyDescriptor.getName() + ", " + ex.getMessage());
500 }
501 }
502
503 final Object collection = getValue(bean, propertyDescriptor);
504 if (isCollection(collection)) {
505 setValue(collection, index, value);
506 } else if (index == 0) {
507 setValue(bean, propertyDescriptor, value);
508 } else {
509 throw new IllegalArgumentException("Not a collection: " + propertyDescriptor.getName());
510 }
511 }
512
513
514
515
516
517
518
519
520 public static void setValue(final Object bean, final PropertyDescriptor propertyDescriptor, Object value) {
521 try {
522 final Method method = getAccessibleMethod(propertyDescriptor.getWriteMethod());
523 if (method == null) {
524 throw new JXPathException("No write method");
525 }
526 value = convert(value, propertyDescriptor.getPropertyType());
527 method.invoke(bean, value);
528 } catch (final Exception ex) {
529 throw new JXPathException("Cannot modify property: " + (bean == null ? "null" : bean.getClass().getName()) + "." + propertyDescriptor.getName(),
530 ex);
531 }
532 }
533
534
535
536
537
538
539 @Deprecated
540 public ValueUtils() {
541
542 }
543 }