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