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 }