1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jxpath.util;
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.Modifier;
21 import java.math.BigDecimal;
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.SortedSet;
31
32 import org.apache.commons.beanutils.ConvertUtils;
33 import org.apache.commons.beanutils.Converter;
34 import org.apache.commons.jxpath.JXPathInvalidAccessException;
35 import org.apache.commons.jxpath.JXPathTypeConversionException;
36 import org.apache.commons.jxpath.NodeSet;
37 import org.apache.commons.jxpath.Pointer;
38
39
40
41
42
43
44
45 public class BasicTypeConverter implements TypeConverter {
46
47
48
49
50
51
52
53
54 public boolean canConvert(Object object, final Class toType) {
55 if (object == null) {
56 return true;
57 }
58 final Class useType = TypeUtils.wrapPrimitive(toType);
59 Class fromType = object.getClass();
60
61 if (useType.isAssignableFrom(fromType)) {
62 return true;
63 }
64
65 if (useType == String.class) {
66 return true;
67 }
68
69 if (object instanceof Boolean && (Number.class.isAssignableFrom(useType)
70 || "java.util.concurrent.atomic.AtomicBoolean"
71 .equals(useType.getName()))) {
72 return true;
73 }
74 if (object instanceof Number
75 && (Number.class.isAssignableFrom(useType) || useType == Boolean.class)) {
76 return true;
77 }
78 if (object instanceof String
79 && (useType == Boolean.class
80 || useType == Character.class
81 || useType == Byte.class
82 || useType == Short.class
83 || useType == Integer.class
84 || useType == Long.class
85 || useType == Float.class
86 || useType == Double.class)) {
87 return true;
88 }
89 if (fromType.isArray()) {
90
91 if (useType.isArray()) {
92 Class cType = useType.getComponentType();
93 int length = Array.getLength(object);
94 for (int i = 0; i < length; i++) {
95 Object value = Array.get(object, i);
96 if (!canConvert(value, cType)) {
97 return false;
98 }
99 }
100 return true;
101 }
102 if (Collection.class.isAssignableFrom(useType)) {
103 return canCreateCollection(useType);
104 }
105 if (Array.getLength(object) > 0) {
106 Object value = Array.get(object, 0);
107 return canConvert(value, useType);
108 }
109 return canConvert("", useType);
110 }
111 if (object instanceof Collection) {
112
113 if (useType.isArray()) {
114 Class cType = useType.getComponentType();
115 Iterator it = ((Collection) object).iterator();
116 while (it.hasNext()) {
117 Object value = it.next();
118 if (!canConvert(value, cType)) {
119 return false;
120 }
121 }
122 return true;
123 }
124 if (Collection.class.isAssignableFrom(useType)) {
125 return canCreateCollection(useType);
126 }
127 if (((Collection) object).size() > 0) {
128 Object value;
129 if (object instanceof List) {
130 value = ((List) object).get(0);
131 }
132 else {
133 Iterator it = ((Collection) object).iterator();
134 value = it.next();
135 }
136 return canConvert(value, useType);
137 }
138 return canConvert("", useType);
139 }
140 if (object instanceof NodeSet) {
141 return canConvert(((NodeSet) object).getValues(), useType);
142 }
143 if (object instanceof Pointer) {
144 return canConvert(((Pointer) object).getValue(), useType);
145 }
146 return ConvertUtils.lookup(useType) != null;
147 }
148
149
150
151
152
153
154
155
156
157 public Object convert(Object object, final Class toType) {
158 if (object == null) {
159 return toType.isPrimitive() ? convertNullToPrimitive(toType) : null;
160 }
161
162 if (toType == Object.class) {
163 if (object instanceof NodeSet) {
164 return convert(((NodeSet) object).getValues(), toType);
165 }
166 if (object instanceof Pointer) {
167 return convert(((Pointer) object).getValue(), toType);
168 }
169 return object;
170 }
171 final Class useType = TypeUtils.wrapPrimitive(toType);
172 Class fromType = object.getClass();
173
174 if (useType.isAssignableFrom(fromType)) {
175 return object;
176 }
177
178 if (fromType.isArray()) {
179 int length = Array.getLength(object);
180 if (useType.isArray()) {
181 Class cType = useType.getComponentType();
182
183 Object array = Array.newInstance(cType, length);
184 for (int i = 0; i < length; i++) {
185 Object value = Array.get(object, i);
186 Array.set(array, i, convert(value, cType));
187 }
188 return array;
189 }
190 if (Collection.class.isAssignableFrom(useType)) {
191 Collection collection = allocateCollection(useType);
192 for (int i = 0; i < length; i++) {
193 collection.add(Array.get(object, i));
194 }
195 return unmodifiableCollection(collection);
196 }
197 if (length > 0) {
198 Object value = Array.get(object, 0);
199 return convert(value, useType);
200 }
201 return convert("", useType);
202 }
203 if (object instanceof Collection) {
204 int length = ((Collection) object).size();
205 if (useType.isArray()) {
206 Class cType = useType.getComponentType();
207 Object array = Array.newInstance(cType, length);
208 Iterator it = ((Collection) object).iterator();
209 for (int i = 0; i < length; i++) {
210 Object value = it.next();
211 Array.set(array, i, convert(value, cType));
212 }
213 return array;
214 }
215 if (Collection.class.isAssignableFrom(useType)) {
216 Collection collection = allocateCollection(useType);
217 collection.addAll((Collection) object);
218 return unmodifiableCollection(collection);
219 }
220 if (length > 0) {
221 Object value;
222 if (object instanceof List) {
223 value = ((List) object).get(0);
224 }
225 else {
226 Iterator it = ((Collection) object).iterator();
227 value = it.next();
228 }
229 return convert(value, useType);
230 }
231 return convert("", useType);
232 }
233 if (object instanceof NodeSet) {
234 return convert(((NodeSet) object).getValues(), useType);
235 }
236 if (object instanceof Pointer) {
237 return convert(((Pointer) object).getValue(), useType);
238 }
239 if (useType == String.class) {
240 return object.toString();
241 }
242 if (object instanceof Boolean) {
243 if (Number.class.isAssignableFrom(useType)) {
244 return allocateNumber(useType, ((Boolean) object).booleanValue() ? 1 : 0);
245 }
246 if ("java.util.concurrent.atomic.AtomicBoolean".equals(useType.getName())) {
247 try {
248 return useType.getConstructor(new Class[] { boolean.class })
249 .newInstance(new Object[] { object });
250 }
251 catch (Exception e) {
252 throw new JXPathTypeConversionException(useType.getName(), e);
253 }
254 }
255 }
256 if (object instanceof Number) {
257 double value = ((Number) object).doubleValue();
258 if (useType == Boolean.class) {
259 return value == 0.0 ? Boolean.FALSE : Boolean.TRUE;
260 }
261 if (Number.class.isAssignableFrom(useType)) {
262 return allocateNumber(useType, value);
263 }
264 }
265 if (object instanceof String) {
266 Object value = convertStringToPrimitive(object, useType);
267 if (value != null) {
268 return value;
269 }
270 }
271
272 Converter converter = ConvertUtils.lookup(useType);
273 if (converter != null) {
274 return converter.convert(useType, object);
275 }
276
277 throw new JXPathTypeConversionException("Cannot convert "
278 + object.getClass() + " to " + useType);
279 }
280
281
282
283
284
285
286 protected Object convertNullToPrimitive(Class toType) {
287 if (toType == boolean.class) {
288 return Boolean.FALSE;
289 }
290 if (toType == char.class) {
291 return new Character('\0');
292 }
293 if (toType == byte.class) {
294 return new Byte((byte) 0);
295 }
296 if (toType == short.class) {
297 return new Short((short) 0);
298 }
299 if (toType == int.class) {
300 return new Integer(0);
301 }
302 if (toType == long.class) {
303 return new Long(0L);
304 }
305 if (toType == float.class) {
306 return new Float(0.0f);
307 }
308 if (toType == double.class) {
309 return new Double(0.0);
310 }
311 return null;
312 }
313
314
315
316
317
318
319
320 protected Object convertStringToPrimitive(Object object, Class toType) {
321 toType = TypeUtils.wrapPrimitive(toType);
322 if (toType == Boolean.class) {
323 return Boolean.valueOf((String) object);
324 }
325 if (toType == Character.class) {
326 return new Character(((String) object).charAt(0));
327 }
328 if (toType == Byte.class) {
329 return new Byte((String) object);
330 }
331 if (toType == Short.class) {
332 return new Short((String) object);
333 }
334 if (toType == Integer.class) {
335 return new Integer((String) object);
336 }
337 if (toType == Long.class) {
338 return new Long((String) object);
339 }
340 if (toType == Float.class) {
341 return new Float((String) object);
342 }
343 if (toType == Double.class) {
344 return new Double((String) object);
345 }
346 return null;
347 }
348
349
350
351
352
353
354
355 protected Number allocateNumber(Class type, double value) {
356 type = TypeUtils.wrapPrimitive(type);
357 if (type == Byte.class) {
358 return new Byte((byte) value);
359 }
360 if (type == Short.class) {
361 return new Short((short) value);
362 }
363 if (type == Integer.class) {
364 return new Integer((int) value);
365 }
366 if (type == Long.class) {
367 return new Long((long) value);
368 }
369 if (type == Float.class) {
370 return new Float((float) value);
371 }
372 if (type == Double.class) {
373 return new Double(value);
374 }
375 if (type == BigInteger.class) {
376 return BigInteger.valueOf((long) value);
377 }
378 if (type == BigDecimal.class) {
379 return new BigDecimal(value);
380 }
381 String classname = type.getName();
382 Class initialValueType = null;
383 if ("java.util.concurrent.atomic.AtomicInteger".equals(classname)) {
384 initialValueType = int.class;
385 }
386 if ("java.util.concurrent.atomic.AtomicLong".equals(classname)) {
387 initialValueType = long.class;
388 }
389 if (initialValueType != null) {
390 try {
391 return (Number) type.getConstructor(
392 new Class[] { initialValueType })
393 .newInstance(
394 new Object[] { allocateNumber(initialValueType,
395 value) });
396 }
397 catch (Exception e) {
398 throw new JXPathTypeConversionException(classname, e);
399 }
400 }
401 return null;
402 }
403
404
405
406
407
408
409 protected boolean canCreateCollection(Class type) {
410 if (!type.isInterface()
411 && ((type.getModifiers() & Modifier.ABSTRACT) == 0)) {
412 try {
413 type.getConstructor(new Class[0]);
414 return true;
415 }
416 catch (Exception e) {
417 return false;
418 }
419 }
420 return type == List.class || type == Collection.class || type == Set.class;
421 }
422
423
424
425
426
427
428 protected Collection allocateCollection(Class type) {
429 if (!type.isInterface()
430 && ((type.getModifiers() & Modifier.ABSTRACT) == 0)) {
431 try {
432 return (Collection) type.newInstance();
433 }
434 catch (Exception ex) {
435 throw new JXPathInvalidAccessException(
436 "Cannot create collection of type: " + type, ex);
437 }
438 }
439
440 if (type == List.class || type == Collection.class) {
441 return new ArrayList();
442 }
443 if (type == Set.class) {
444 return new HashSet();
445 }
446 throw new JXPathInvalidAccessException(
447 "Cannot create collection of type: " + type);
448 }
449
450
451
452
453
454
455 protected Collection unmodifiableCollection(Collection collection) {
456 if (collection instanceof List) {
457 return Collections.unmodifiableList((List) collection);
458 }
459 if (collection instanceof SortedSet) {
460 return Collections.unmodifiableSortedSet((SortedSet) collection);
461 }
462 if (collection instanceof Set) {
463 return Collections.unmodifiableSet((Set) collection);
464 }
465 return Collections.unmodifiableCollection(collection);
466 }
467
468
469
470
471 static final class ValueNodeSet implements NodeSet {
472 private List values;
473 private List pointers;
474
475
476
477
478
479 public ValueNodeSet(List values) {
480 this.values = values;
481 }
482
483 public List getValues() {
484 return Collections.unmodifiableList(values);
485 }
486
487 public List getNodes() {
488 return Collections.unmodifiableList(values);
489 }
490
491 public List getPointers() {
492 if (pointers == null) {
493 pointers = new ArrayList();
494 for (int i = 0; i < values.size(); i++) {
495 pointers.add(new ValuePointer(values.get(i)));
496 }
497 pointers = Collections.unmodifiableList(pointers);
498 }
499 return pointers;
500 }
501 }
502
503
504
505
506 static final class ValuePointer implements Pointer {
507 private static final long serialVersionUID = -4817239482392206188L;
508
509 private Object bean;
510
511
512
513
514
515 public ValuePointer(Object object) {
516 this.bean = object;
517 }
518
519 public Object getValue() {
520 return bean;
521 }
522
523 public Object getNode() {
524 return bean;
525 }
526
527 public Object getRootNode() {
528 return bean;
529 }
530
531 public void setValue(Object value) {
532 throw new UnsupportedOperationException();
533 }
534
535 public Object clone() {
536 return this;
537 }
538
539 public int compareTo(Object object) {
540 return 0;
541 }
542
543 public String asPath() {
544 if (bean == null) {
545 return "null()";
546 }
547 if (bean instanceof Number) {
548 String string = bean.toString();
549 if (string.endsWith(".0")) {
550 string = string.substring(0, string.length() - 2);
551 }
552 return string;
553 }
554 if (bean instanceof Boolean) {
555 return ((Boolean) bean).booleanValue() ? "true()" : "false()";
556 }
557 if (bean instanceof String) {
558 return "'" + bean + "'";
559 }
560 return "{object of type " + bean.getClass().getName() + "}";
561 }
562 }
563 }