BeanUtils.java 8.82 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
/**
 * $Revision: 243 $
 * $Date: 2004-11-09 10:37:52 -0800 (Tue, 09 Nov 2004) $
 *
 * Copyright (C) 2004 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

package org.jivesoftware.util;

import java.beans.*;
import java.awt.Color;
import java.util.*;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * A utility class that provides methods that are useful for dealing with
 * Java Beans.
 */
public class BeanUtils {

    /**
     * The date format recognized for parsing/formattig dates.
     */
    public static final String DATE_FORMAT = "MM/dd/yyyy";

    private static DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);

    /**
     * Sets the properties of a Java Bean based on the String name/value pairs in
     * the specifieed Map. Because this method has to know how to convert a
     * String value into the correct type for the bean, only a few bean property
     * types are supported. They are: String, boolean, int, long, float, double,
     * Color, and Class.<p>
     *
     * If key/value pairs exist in the Map that don't correspond to properties
     * of the bean, they will be ignored.
     *
     * @param bean the JavaBean to set properties on.
     * @param properties String name/value pairs of the properties to set.
     */
    public static void setProperties(Object bean, Map properties) {
        try {
            // Loop through all the property names in the Map
            for (Iterator iter = properties.keySet().iterator(); iter.hasNext();) {
                String propName = (String)iter.next();
                try {
                    // Create a property descriptor for the named property. If
                    // the bean doesn't have the named property, an
                    // Introspection will be thrown.
                    PropertyDescriptor descriptor = new PropertyDescriptor(
                            propName, bean.getClass());
                    // Load the class type of the property.
                    Class propertyType = descriptor.getPropertyType();
                    // Get the value of the property by converting it from a
                    // String to the correct object type.
                    Object value = decode(propertyType, (String)properties.get(propName));
                    // Set the value of the bean.
                    descriptor.getWriteMethod().invoke(bean, new Object[] { value });
                }
                catch (IntrospectionException ie) {
                    // Ignore. This exception means that the key in the map
                    // does not correspond to a property of the bean.
                }
                catch (InvocationTargetException ite) {
                    // Ignore. This exception most often occurs when a
                    // value in the map is null and the target method doesn't
                    // support null properties.
                }
            }
        }
        catch (Exception e) {
            Log.error(e);
        }
    }

    /**
     * Gets the properties from a Java Bean and returns them in a Map of String
     * name/value pairs. Because this method has to know how to convert a
     * bean property into a String value, only a few bean property
     * types are supported. They are: String, boolean, int, long, float, double,
     * Color, and Class.
     *
     * @param bean a Java Bean to get properties from.
     * @return a Map of all properties as String name/value pairs.
     */
    public static Map getProperties(Object bean) {
        Map properties = new HashMap();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
            // Loop through all properties of the bean.
            PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
            String [] names = new String[descriptors.length];
            for (int i=0; i<names.length; i++) {
                // Determine the property name.
                String name = descriptors[i].getName();
                Class type = descriptors[i].getPropertyType();
                // Decode the property value using the property type and
                // encoded String value.
                Object value = descriptors[i].getReadMethod().invoke(bean,(java.lang.Object[]) null);
                // Add to Map, encoding the value as a String.
                properties.put(name, encode(value));
            }
        }
        catch (Exception e) {
            Log.error(e);
        }
        return properties;
    }

    /**
     * Returns the PropertyDescriptor array for the specified Java Bean Class.
     * The method also does a special check to see of the bean has a BeanInfo
     * class that extends the JiveBeanInfo class. If yes, we load the
     * PropertyDescriptor array directly from that BeanInfo class rather than
     * through the Introspector in order to preserve the desired ordering of
     * properties.
     *
     * @param beanClass the Class of the JavaBean.
     * @return the PropertyDescriptor array for the specified Java Bean Class.
     */
    public static PropertyDescriptor[] getPropertyDescriptors(Class beanClass)
            throws IntrospectionException
    {
        // See if the Java Bean has a BeanInfo class that implements
        // JiveBeanInfo. If so, return the PropertyDescriptor from that
        // class. This will bypass properties of parent classes, but this is
        // the normal behavior of classes that implement JiveBeanInfo.
        try {
            JiveBeanInfo beanInfo = (JiveBeanInfo)ClassUtils.forName(
                    beanClass.getName() + "BeanInfo").newInstance();
            return beanInfo.getPropertyDescriptors();
        }
        catch (Exception e) {
            // Ignore.
        }
        // Otherwise, return the PropertyDescriptors from the Introspector.
        return Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
    }

    /**
     * Encodes a bean property value as a String. If the object type is not
     * supported, null will be returned.
     *
     * @param value an Object to encode in a String representation.
     */
    private static String encode(Object value) {
        if (value instanceof String) {
            return (String)value;
        }
        if (value instanceof Boolean ||
            value instanceof Integer ||
            value instanceof Long ||
            value instanceof Float ||
            value instanceof Double)
        {
            return value.toString();
        }
        if (value instanceof Date) {
            try {
                return dateFormatter.format((Date)value);
            }
            catch (Exception ignored) {
                // Ignore.
            }
        }
        if (value instanceof Color) {
            Color color = (Color)value;
            return color.getRed() +","+ color.getGreen() +","+ color.getBlue();
        }
        if (value instanceof Class) {
            return ((Class)value).getName();
        }
        return null;
    }

    /**
     * Decodes a String into an object of the specified type. If the object
     * type is not supported, null will be returned.
     *
     * @param type the type of the property.
     * @param value the encode String value to decode.
     * @return the String value decoded into the specified type.
     */
    private static Object decode(Class type, String value) throws Exception {
        if (type.getName().equals("java.lang.String")) {
            return value;
        }
        if (type.getName().equals("boolean")) {
            return Boolean.valueOf(value);
        }
        if (type.getName().equals("int")) {
            return Integer.valueOf(value);
        }
        if (type.getName().equals("long")) {
            return Long.valueOf(value);
        }
        if (type.getName().equals("float")) {
            return Float.valueOf(value);
        }
        if (type.getName().equals("double")) {
            return Double.valueOf(value);
        }
        if (type.getName().equals("java.util.Date")) {
            try {
                return dateFormatter.parse(value);
            }
            catch (Exception ignored) {
                // Ignore.
            }
        }
        if (type.getName().equals("java.awt.Color")) {
            StringTokenizer tokens = new StringTokenizer(value, ",");
            int red = Integer.parseInt(tokens.nextToken());
            int green = Integer.parseInt(tokens.nextToken());
            int blue = Integer.parseInt(tokens.nextToken());
            return new Color(red, green, blue);
        }
        if (type.getName().equals("java.lang.Class")) {
            return ClassUtils.forName(value);
        }
        return null;
    }

    // This class is not instantiable.
    private BeanUtils() {
        // do nothing.
    }
}