BeanUtils.java 12 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4
/**
 * $Revision: 243 $
 * $Date: 2004-11-09 10:37:52 -0800 (Tue, 09 Nov 2004) $
 *
5
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
6
 *
7 8 9 10 11 12 13 14 15 16 17
 * Licensed 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.
Matt Tucker's avatar
Matt Tucker committed
18 19 20 21
 */

package org.jivesoftware.util;

22
import java.awt.Color;
23 24 25 26
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
Matt Tucker's avatar
Matt Tucker committed
27 28 29
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
30 31 32 33 34 35 36 37 38 39
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Matt Tucker's avatar
Matt Tucker committed
40 41 42 43 44 45 46

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

47 48
	private static final Logger Log = LoggerFactory.getLogger(BeanUtils.class);

Matt Tucker's avatar
Matt Tucker committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    /**
     * 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.
     */
69
    public static void setProperties(Object bean, Map<String, String> properties) {
Matt Tucker's avatar
Matt Tucker committed
70 71
        try {
            // Loop through all the property names in the Map
72
            for (String propName : properties.keySet()) {
Matt Tucker's avatar
Matt Tucker committed
73 74 75 76 77 78 79 80 81 82
                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.
83
                    Object value = decode(propertyType, properties.get(propName));
Matt Tucker's avatar
Matt Tucker committed
84
                    // Set the value of the bean.
85
                    descriptor.getWriteMethod().invoke(bean, value);
Matt Tucker's avatar
Matt Tucker committed
86 87 88 89 90 91 92 93 94 95 96 97 98
                }
                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) {
99
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
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
    /**
     * Sets the properties of a Java Bean based on the request's properties. 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 request the HTTP request.
     */
    public static void setProperties(Object bean, HttpServletRequest request) {
        for (Enumeration propNames = request.getParameterNames(); propNames.hasMoreElements();) {
            String propName = (String) propNames.nextElement();
            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, request.getParameter(propName));
                // Set the value of the bean.
                descriptor.getWriteMethod().invoke(bean, 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 (IllegalAccessException e) {
142
                Log.error(e.getMessage(), e);
143 144
            }
            catch (Exception e) {
145
                Log.error(e.getMessage(), e);
146 147 148 149
            }
        }
    }

Matt Tucker's avatar
Matt Tucker committed
150 151 152 153 154 155 156 157 158 159
    /**
     * 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.
     */
160 161
    public static Map<String, String> getProperties(Object bean) {
        Map<String, String> properties = new HashMap<String, String>();
Matt Tucker's avatar
Matt Tucker committed
162 163 164 165 166 167 168 169
        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();
170
                //Class type = descriptors[i].getPropertyType();
Matt Tucker's avatar
Matt Tucker committed
171 172 173 174 175 176 177 178
                // 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) {
179
            Log.error(e.getMessage(), e);
Matt Tucker's avatar
Matt Tucker committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193
        }
        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.
194
     * @throws java.beans.IntrospectionException
Matt Tucker's avatar
Matt Tucker committed
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
     */
    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.
220
     * @return the encoded bean.
Matt Tucker's avatar
Matt Tucker committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
     */
    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.
259
     * @throws Exception
Matt Tucker's avatar
Matt Tucker committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
     */
    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.
    }
}