| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you under the Apache License, |
| * Version 2.0 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.pivot.beans; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.Type; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.Locale; |
| import java.util.NoSuchElementException; |
| |
| import org.apache.pivot.annotations.UnsupportedOperation; |
| import org.apache.pivot.collections.Map; |
| import org.apache.pivot.collections.MapListener; |
| import org.apache.pivot.util.ListenerList; |
| import org.apache.pivot.util.Utils; |
| |
| /** |
| * Exposes Java bean properties of an object via the {@link Map} interface. A |
| * call to {@link Map#get(Object)} invokes the getter for the corresponding |
| * property, and a call to {@link Map#put(Object, Object)} invokes the |
| * property's setter. <p> Properties may provide multiple setters; the |
| * appropriate setter to invoke is determined by the type of the value being |
| * set. If the value is <tt>null</tt>, the return type of the getter method is |
| * used. <p> Getter methods must be named "getProperty" where "property" is the |
| * property name. If there is no "get" method, then an "isProperty" method can |
| * also be used. Setter methods (if present) must be named "setProperty". <p> |
| * Getter and setter methods are checked before straight fields named "property" |
| * in order to support proper data encapsulation. And only <code>public</code> |
| * and non-<code>static</code> methods and fields can be accessed. |
| */ |
| public class BeanAdapter implements Map<String, Object> { |
| /** |
| * Property iterator. Returns a value for each getter method and public, |
| * non-final field defined by the bean. |
| */ |
| private class PropertyIterator implements Iterator<String> { |
| private Method[] methods = null; |
| private Field[] fields = null; |
| |
| private int i = 0; |
| private int j = 0; |
| private String nextProperty = null; |
| |
| public PropertyIterator() { |
| Class<?> type = bean.getClass(); |
| methods = type.getMethods(); |
| fields = type.getFields(); |
| nextProperty(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return (nextProperty != null); |
| } |
| |
| @Override |
| public String next() { |
| if (!hasNext()) { |
| throw new NoSuchElementException(); |
| } |
| |
| String nextPropertyLocal = this.nextProperty; |
| nextProperty(); |
| |
| return nextPropertyLocal; |
| } |
| |
| private void nextProperty() { |
| nextProperty = null; |
| |
| while (i < methods.length && nextProperty == null) { |
| Method method = methods[i++]; |
| |
| if (method.getParameterTypes().length == 0 |
| && (method.getModifiers() & Modifier.STATIC) == 0) { |
| String methodName = method.getName(); |
| |
| String prefix = null; |
| if (methodName.startsWith(GET_PREFIX)) { |
| prefix = GET_PREFIX; |
| } else { |
| if (methodName.startsWith(IS_PREFIX)) { |
| prefix = IS_PREFIX; |
| } |
| } |
| |
| if (prefix != null) { |
| int propertyOffset = prefix.length(); |
| nextProperty = Character.toLowerCase(methodName.charAt(propertyOffset)) |
| + methodName.substring(propertyOffset + 1); |
| |
| if (nextProperty.equals("class")) { |
| nextProperty = null; |
| } |
| } |
| |
| if (nextProperty != null && ignoreReadOnlyProperties |
| && isReadOnly(nextProperty)) { |
| nextProperty = null; |
| } |
| } |
| } |
| |
| if (nextProperty == null) { |
| while (j < fields.length && nextProperty == null) { |
| Field field = fields[j++]; |
| |
| int modifiers = field.getModifiers(); |
| if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & Modifier.STATIC) == 0) { |
| nextProperty = field.getName(); |
| } |
| |
| if (nextProperty != null && ignoreReadOnlyProperties |
| && (modifiers & Modifier.FINAL) != 0) { |
| nextProperty = null; |
| } |
| } |
| } |
| } |
| |
| @Override |
| @UnsupportedOperation |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| private Object bean; |
| private boolean ignoreReadOnlyProperties; |
| |
| private MapListener.Listeners<String, Object> mapListeners = new MapListener.Listeners<>(); |
| |
| public static final String GET_PREFIX = "get"; |
| public static final String IS_PREFIX = "is"; |
| public static final String SET_PREFIX = "set"; |
| |
| private static final String ENUM_VALUE_OF_METHOD_NAME = "valueOf"; |
| |
| private static final String ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT = |
| "Unable to access property \"%s\" for type %s."; |
| private static final String ENUM_COERCION_EXCEPTION_MESSAGE = |
| "Unable to coerce %s (\"%s\") to %s.\nValid enum constants - %s"; |
| |
| /** |
| * Creates a new bean dictionary. |
| * |
| * @param bean The bean object to wrap. |
| */ |
| public BeanAdapter(final Object bean) { |
| this(bean, false); |
| } |
| |
| /** |
| * Creates a new bean dictionary which can ignore readonly fields (that is, |
| * straight fields marked as <code>final</code> or bean properties where |
| * there is a "get" method but no corresponding "set" method). |
| * |
| * @param bean The bean object to wrap. |
| * @param ignoreReadOnlyProperties <tt>true</tt> if {@code final} or non-settable |
| * fields should be excluded from the dictionary, <tt>false</tt> to include all fields. |
| */ |
| public BeanAdapter(final Object bean, final boolean ignoreReadOnlyProperties) { |
| Utils.checkNull(bean, "bean object"); |
| |
| this.bean = bean; |
| this.ignoreReadOnlyProperties = ignoreReadOnlyProperties; |
| } |
| |
| /** |
| * Returns the bean object this dictionary wraps. |
| * |
| * @return The bean object, or <tt>null</tt> if no bean has been set. |
| */ |
| public Object getBean() { |
| return bean; |
| } |
| |
| /** |
| * Invokes the getter method for the given property. |
| * |
| * @param key The property name. |
| * @return The value returned by the method, or <tt>null</tt> if no such |
| * method exists. |
| */ |
| @Override |
| public Object get(final String key) { |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| Object value = null; |
| |
| Method getterMethod = getGetterMethod(key); |
| |
| if (getterMethod == null) { |
| Field field = getField(key); |
| |
| if (field != null) { |
| try { |
| value = field.get(bean); |
| } catch (IllegalAccessException exception) { |
| throw new RuntimeException(String.format( |
| ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, key, bean.getClass().getName()), |
| exception); |
| } |
| } |
| } else { |
| try { |
| value = getterMethod.invoke(bean, new Object[] {}); |
| } catch (IllegalAccessException exception) { |
| throw new RuntimeException(String.format(ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, |
| key, bean.getClass().getName()), exception); |
| } catch (InvocationTargetException exception) { |
| throw new RuntimeException(String.format( |
| "Error getting property \"%s\" for type %s.", key, bean.getClass().getName()), |
| exception.getCause()); |
| } |
| } |
| |
| return value; |
| } |
| |
| /** |
| * Invokes the setter method for the given property. The method signature is |
| * determined by the type of the value. If the value is <tt>null</tt>, the |
| * return type of the getter method is used. |
| * |
| * @param key The property name. |
| * @param value The new property value. |
| * @return Returns <tt>null</tt>, since returning the previous value would |
| * require a call to the getter method, which may not be an efficient |
| * operation. |
| * @throws PropertyNotFoundException If the given property does not exist or |
| * is read-only. |
| */ |
| @Override |
| public Object put(final String key, final Object value) { |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| Method setterMethod = null; |
| Object valueUpdated = value; |
| |
| if (valueUpdated != null) { |
| // Get the setter method for the value type |
| setterMethod = getSetterMethod(key, valueUpdated.getClass()); |
| } |
| |
| if (setterMethod == null) { |
| // Get the property type and attempt to coerce the value to it |
| Class<?> propertyType = getType(key); |
| |
| if (propertyType != null) { |
| setterMethod = getSetterMethod(key, propertyType); |
| valueUpdated = coerce(valueUpdated, propertyType, key); |
| } |
| } |
| |
| if (setterMethod == null) { |
| Field field = getField(key); |
| |
| if (field == null) { |
| throw new PropertyNotFoundException("Property \"" + key + "\"" |
| + " does not exist or is read-only."); |
| } |
| |
| Class<?> fieldType = field.getType(); |
| if (valueUpdated != null) { |
| Class<?> valueType = valueUpdated.getClass(); |
| if (!fieldType.isAssignableFrom(valueType)) { |
| valueUpdated = coerce(valueUpdated, fieldType, key); |
| } |
| } |
| |
| try { |
| field.set(bean, valueUpdated); |
| } catch (IllegalAccessException exception) { |
| throw new RuntimeException(String.format(ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, |
| key, bean.getClass().getName()), exception); |
| } |
| } else { |
| try { |
| setterMethod.invoke(bean, new Object[] {valueUpdated}); |
| } catch (IllegalAccessException exception) { |
| throw new RuntimeException(String.format(ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, |
| key, bean.getClass().getName()), exception); |
| } catch (InvocationTargetException exception) { |
| throw new RuntimeException(String.format( |
| "Error setting property \"%s\" for type %s to value \"%s\"", key, |
| bean.getClass().getName(), "" + valueUpdated), exception.getCause()); |
| } |
| |
| } |
| |
| Object previousValue = null; |
| mapListeners.valueUpdated(this, key, previousValue); |
| |
| return previousValue; |
| } |
| |
| /** |
| * Invokes the setter methods for all the given properties that are present |
| * in the map. The method signatures are determined by the type of the |
| * values. If any value is <tt>null</tt>, the return type of the getter |
| * method is used. |
| * |
| * @param valueMap The map of keys and values to be set. |
| * @throws PropertyNotFoundException If any of the given properties do not |
| * exist or are read-only. |
| */ |
| @Override |
| public void putAll(final Map<String, Object> valueMap) { |
| for (String key : valueMap) { |
| put(key, valueMap.get(key)); |
| } |
| } |
| |
| /** |
| * Invokes the setter methods for all the given properties that are present |
| * in the map. The method signatures are determined by the type of the |
| * values. If any value is <tt>null</tt>, the return type of the getter |
| * method is used. There is an option to ignore (that is, not throw) |
| * exceptions during the process, but to return status if any exceptions |
| * were caught and ignored. |
| * |
| * @param valueMap The map of keys and values to be set. |
| * @param ignoreErrors If <code>true</code> then any |
| * {@link PropertyNotFoundException} thrown by the {@link #put put()} method |
| * will be caught and ignored. |
| * @return <code>true</code> if any exceptions were caught, |
| * <code>false</code> if not. |
| */ |
| public boolean putAll(final Map<String, ?> valueMap, final boolean ignoreErrors) { |
| boolean anyErrors = false; |
| for (String key : valueMap) { |
| try { |
| put(key, valueMap.get(key)); |
| } catch (PropertyNotFoundException ex) { |
| if (!ignoreErrors) { |
| throw ex; |
| } |
| anyErrors = true; |
| } |
| } |
| return anyErrors; |
| } |
| |
| /** |
| * @throws UnsupportedOperationException This operation is not supported. |
| */ |
| @Override |
| @UnsupportedOperation |
| public Object remove(final String key) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * @throws UnsupportedOperationException This operation is not supported. |
| */ |
| @Override |
| @UnsupportedOperation |
| public synchronized void clear() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Verifies the existence of a property. The property must have a getter |
| * method; write-only properties are not supported. |
| * |
| * @param key The property name. |
| * @return <tt>true</tt> if the property exists; <tt>false</tt>, otherwise. |
| */ |
| @Override |
| public boolean containsKey(final String key) { |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| boolean containsKey = (getGetterMethod(key) != null); |
| |
| if (!containsKey) { |
| containsKey = (getField(key) != null); |
| } |
| |
| return containsKey; |
| } |
| |
| /** |
| * @throws UnsupportedOperationException This operation is not supported. |
| */ |
| @Override |
| @UnsupportedOperation |
| public boolean isEmpty() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * @throws UnsupportedOperationException This operation is not supported. |
| */ |
| @Override |
| @UnsupportedOperation |
| public int getCount() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Comparator<String> getComparator() { |
| return null; |
| } |
| |
| /** |
| * @throws UnsupportedOperationException This operation is not supported. |
| */ |
| @Override |
| @UnsupportedOperation |
| public void setComparator(final Comparator<String> comparator) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Tests the read-only state of a property. |
| * |
| * @param key The property name. |
| * @return <tt>true</tt> if the property is read-only; <tt>false</tt>, |
| * otherwise. |
| */ |
| public boolean isReadOnly(final String key) { |
| return isReadOnly(bean.getClass(), key); |
| } |
| |
| /** |
| * Returns the type of a property. |
| * |
| * @param key The property name. |
| * @return The real class type of this property. |
| * @see #getType(Class, String) |
| */ |
| public Class<?> getType(final String key) { |
| return getType(bean.getClass(), key); |
| } |
| |
| /** |
| * Returns the generic type of a property. |
| * |
| * @param key The property name. |
| * @return The generic type of this property. |
| * @see #getGenericType(Class, String) |
| */ |
| public Type getGenericType(final String key) { |
| return getGenericType(bean.getClass(), key); |
| } |
| |
| /** |
| * Returns an iterator over the bean's properties. |
| * |
| * @return A property iterator for this bean. |
| */ |
| @Override |
| public Iterator<String> iterator() { |
| return new PropertyIterator(); |
| } |
| |
| @Override |
| public ListenerList<MapListener<String, Object>> getMapListeners() { |
| return mapListeners; |
| } |
| |
| /** |
| * Returns the getter method for a property. |
| * |
| * @param key The property name. |
| * @return The getter method, or <tt>null</tt> if the method does not exist. |
| */ |
| private Method getGetterMethod(final String key) { |
| return getGetterMethod(bean.getClass(), key); |
| } |
| |
| /** |
| * Returns the setter method for a property. |
| * |
| * @param key The property name. |
| * @param valueType The value type of the property in question. |
| * @return The getter method, or <tt>null</tt> if the method does not exist. |
| */ |
| private Method getSetterMethod(final String key, final Class<?> valueType) { |
| return getSetterMethod(bean.getClass(), key, valueType); |
| } |
| |
| /** |
| * Returns the public, non-static field for a property. Note that fields |
| * will only be consulted for bean properties after bean methods. |
| * |
| * @param key The property name |
| * @return The field, or <tt>null</tt> if the field does not exist, or is |
| * non-public or static |
| */ |
| private Field getField(final String key) { |
| return getField(bean.getClass(), key); |
| } |
| |
| /** |
| * Tests the read-only state of a property. Note that if no such property |
| * exists, this method will return <tt>true</tt> (it will <u>not</u> throw |
| * an exception). |
| * |
| * @param beanClass The bean class. |
| * @param key The property name. |
| * @return <tt>true</tt> if the property is read-only; <tt>false</tt>, |
| * otherwise. |
| */ |
| public static boolean isReadOnly(final Class<?> beanClass, final String key) { |
| Utils.checkNull(beanClass, "beanClass"); |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| boolean isReadOnly = true; |
| |
| Method getterMethod = getGetterMethod(beanClass, key); |
| if (getterMethod == null) { |
| Field field = getField(beanClass, key); |
| if (field != null) { |
| isReadOnly = ((field.getModifiers() & Modifier.FINAL) != 0); |
| } |
| } else { |
| Method setterMethod = getSetterMethod(beanClass, key, getType(beanClass, key)); |
| isReadOnly = (setterMethod == null); |
| } |
| |
| return isReadOnly; |
| } |
| |
| /** |
| * Returns the type of a property. |
| * |
| * @param beanClass The bean class. |
| * @param key The property name. |
| * @return The type of the property, or <tt>null</tt> if no such bean |
| * property exists. |
| */ |
| public static Class<?> getType(final Class<?> beanClass, final String key) { |
| Utils.checkNull(beanClass, "beanClass"); |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| Class<?> type = null; |
| |
| Method getterMethod = getGetterMethod(beanClass, key); |
| |
| if (getterMethod == null) { |
| Field field = getField(beanClass, key); |
| |
| if (field != null) { |
| type = field.getType(); |
| } |
| } else { |
| type = getterMethod.getReturnType(); |
| } |
| |
| return type; |
| } |
| |
| /** |
| * Returns the generic type of a property. |
| * |
| * @param beanClass The bean class. |
| * @param key The property name. |
| * @return The generic type of the property, or <tt>null</tt> if no such bean |
| * property exists. If the type is a generic, an instance of |
| * {@link java.lang.reflect.ParameterizedType} will be returned. Otherwise, |
| * an instance of {@link java.lang.Class} will be returned. |
| */ |
| public static Type getGenericType(final Class<?> beanClass, final String key) { |
| Utils.checkNull(beanClass, "beanClass"); |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| Type genericType = null; |
| |
| Method getterMethod = getGetterMethod(beanClass, key); |
| |
| if (getterMethod == null) { |
| Field field = getField(beanClass, key); |
| |
| if (field != null) { |
| genericType = field.getGenericType(); |
| } |
| } else { |
| genericType = getterMethod.getGenericReturnType(); |
| } |
| |
| return genericType; |
| } |
| |
| /** |
| * Returns the public, non-static fields for a property. Note that fields |
| * will only be consulted for bean properties after bean methods. |
| * |
| * @param beanClass The bean class. |
| * @param key The property name. |
| * @return The field, or <tt>null</tt> if the field does not exist, or is |
| * non-public or static. |
| */ |
| public static Field getField(final Class<?> beanClass, final String key) { |
| Utils.checkNull(beanClass, "beanClass"); |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| Field field = null; |
| |
| try { |
| field = beanClass.getField(key); |
| |
| int modifiers = field.getModifiers(); |
| |
| // Exclude non-public and static fields |
| if ((modifiers & Modifier.PUBLIC) == 0 || (modifiers & Modifier.STATIC) > 0) { |
| field = null; |
| } |
| } catch (NoSuchFieldException exception) { |
| // No-op |
| } |
| |
| return field; |
| } |
| |
| /** |
| * Returns the getter method for a property. |
| * |
| * @param beanClass The bean class. |
| * @param key The property name. |
| * @return The getter method, or <tt>null</tt> if the method does not exist. |
| */ |
| public static Method getGetterMethod(final Class<?> beanClass, final String key) { |
| Utils.checkNull(beanClass, "beanClass"); |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| // Upper-case the first letter |
| String keyUpdated = Character.toUpperCase(key.charAt(0)) + key.substring(1); |
| Method getterMethod = null; |
| |
| try { |
| getterMethod = beanClass.getMethod(GET_PREFIX + keyUpdated); |
| } catch (NoSuchMethodException exception) { |
| // No-op |
| } |
| |
| if (getterMethod == null) { |
| try { |
| getterMethod = beanClass.getMethod(IS_PREFIX + keyUpdated); |
| } catch (NoSuchMethodException exception) { |
| // No-op |
| } |
| } |
| |
| return getterMethod; |
| } |
| |
| /** |
| * Returns the setter method for a property. |
| * |
| * @param beanClass The bean class. |
| * @param key The property name. |
| * @param valueType The type of the property. |
| * @return The getter method, or <tt>null</tt> if the method does not exist. |
| */ |
| public static Method getSetterMethod(final Class<?> beanClass, final String key, |
| final Class<?> valueType) { |
| Utils.checkNull(beanClass, "beanClass"); |
| Utils.checkNullOrEmpty(key, "key"); |
| |
| Method setterMethod = null; |
| |
| if (valueType != null) { |
| // Upper-case the first letter and prepend the "set" prefix to |
| // determine the method name |
| String keyUpdated = Character.toUpperCase(key.charAt(0)) + key.substring(1); |
| final String methodName = SET_PREFIX + keyUpdated; |
| |
| try { |
| setterMethod = beanClass.getMethod(methodName, valueType); |
| } catch (NoSuchMethodException exception) { |
| // No-op |
| } |
| |
| if (setterMethod == null) { |
| // Look for a match on the value's super type |
| Class<?> superType = valueType.getSuperclass(); |
| setterMethod = getSetterMethod(beanClass, key, superType); |
| } |
| |
| if (setterMethod == null) { |
| // If value type is a primitive wrapper, look for a method |
| // signature with the corresponding primitive type |
| try { |
| Field primitiveTypeField = valueType.getField("TYPE"); |
| Class<?> primitiveValueType = (Class<?>) primitiveTypeField.get(null); |
| |
| try { |
| setterMethod = beanClass.getMethod(methodName, primitiveValueType); |
| } catch (NoSuchMethodException exception) { |
| // No-op |
| } |
| } catch (NoSuchFieldException exception) { |
| // No-op |
| } catch (IllegalAccessException exception) { |
| throw new RuntimeException(String.format( |
| ILLEGAL_ACCESS_EXCEPTION_MESSAGE_FORMAT, keyUpdated, beanClass.getName()), |
| exception); |
| } |
| } |
| |
| if (setterMethod == null) { |
| // Walk the interface graph to find a matching method |
| Class<?>[] interfaces = valueType.getInterfaces(); |
| |
| int i = 0, n = interfaces.length; |
| while (setterMethod == null && i < n) { |
| Class<?> interfaceType = interfaces[i++]; |
| setterMethod = getSetterMethod(beanClass, key, interfaceType); |
| } |
| } |
| } |
| |
| return setterMethod; |
| } |
| |
| /** |
| * Coerces a value to a given type. |
| * |
| * @param <T> The parametric type to coerce to. |
| * @param value The object to be coerced. |
| * @param type The type to coerce it to. |
| * @param key The property name in question. |
| * @return The coerced value. |
| * @throws IllegalArgumentException for all the possible other exceptions. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <T> T coerce(final Object value, final Class<? extends T> type, final String key) { |
| Utils.checkNull(type, "type"); |
| |
| Object coercedValue; |
| |
| if (value == null) { |
| // Null values can only be coerced to null |
| coercedValue = null; |
| } else { |
| if (type.isAssignableFrom(value.getClass())) { |
| // Value doesn't need coercion |
| coercedValue = value; |
| } else if (type.isEnum()) { |
| // Find and invoke the valueOf(String) method using an upper |
| // case conversion of the supplied Object's toString() value |
| try { |
| String valueString = value.toString().toUpperCase(Locale.ENGLISH); |
| Method valueOfMethod = type.getMethod(ENUM_VALUE_OF_METHOD_NAME, String.class); |
| coercedValue = valueOfMethod.invoke(null, valueString); |
| } catch (IllegalAccessException | InvocationTargetException |
| | SecurityException | NoSuchMethodException e) { |
| // Nothing to be gained by handling the getMethod() & invoke() exceptions separately |
| throw new IllegalArgumentException(String.format( |
| ENUM_COERCION_EXCEPTION_MESSAGE, value.getClass().getName(), value, type, |
| Arrays.toString(type.getEnumConstants())), e); |
| } |
| } else { |
| // Coerce the value to the requested type |
| if (type == String.class) { |
| coercedValue = value.toString(); |
| } else if (type == Boolean.class || type == Boolean.TYPE) { |
| coercedValue = Boolean.parseBoolean(value.toString()); |
| } else if (type == Character.class || type == Character.TYPE) { |
| coercedValue = value.toString().charAt(0); |
| } else if (type == Byte.class || type == Byte.TYPE) { |
| if (value instanceof Number) { |
| coercedValue = ((Number) value).byteValue(); |
| } else { |
| coercedValue = Byte.parseByte(value.toString()); |
| } |
| } else if (type == Short.class || type == Short.TYPE) { |
| if (value instanceof Number) { |
| coercedValue = ((Number) value).shortValue(); |
| } else { |
| coercedValue = Short.parseShort(value.toString()); |
| } |
| } else if (type == Integer.class || type == Integer.TYPE) { |
| if (value instanceof Number) { |
| coercedValue = ((Number) value).intValue(); |
| } else { |
| coercedValue = Integer.parseInt(value.toString()); |
| } |
| } else if (type == Long.class || type == Long.TYPE) { |
| if (value instanceof Number) { |
| coercedValue = ((Number) value).longValue(); |
| } else { |
| coercedValue = Long.parseLong(value.toString()); |
| } |
| } else if (type == Float.class || type == Float.TYPE) { |
| if (value instanceof Number) { |
| coercedValue = ((Number) value).floatValue(); |
| } else { |
| coercedValue = Float.parseFloat(value.toString()); |
| } |
| } else if (type == Double.class || type == Double.TYPE) { |
| if (value instanceof Number) { |
| coercedValue = ((Number) value).doubleValue(); |
| } else { |
| coercedValue = Double.parseDouble(value.toString()); |
| } |
| } else if (type == BigInteger.class) { |
| if (value instanceof Number) { |
| coercedValue = new BigInteger(((Number) value).toString()); |
| } else { |
| coercedValue = new BigInteger(value.toString()); |
| } |
| } else if (type == BigDecimal.class) { |
| if (value instanceof Number) { |
| coercedValue = new BigDecimal(((Number) value).toString()); |
| } else { |
| coercedValue = new BigDecimal(value.toString()); |
| } |
| } else { |
| throw new IllegalArgumentException("Unable to coerce " |
| + value.getClass().getName() + " to " + type + " for \"" + key + "\" property."); |
| } |
| } |
| } |
| |
| return (T) coercedValue; |
| } |
| } |