    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.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             // Collection -> array
    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             // Collection -> array
    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         // Cannot wrap it into a proper unmodifiable collection,
    428         // so we just return the original collection itself
    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 }