View Javadoc

1   /*
2    * Copyright 1999-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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             // Collection -> array
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             // Collection -> array
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         // Cannot wrap it into a proper unmodifiable collection, 
430         // so we just return the original collection itself
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 }