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