001: /**
002: * $Revision: 243 $
003: * $Date: 2004-11-09 10:37:52 -0800 (Tue, 09 Nov 2004) $
004: *
005: * Copyright (C) 2004 Jive Software. All rights reserved.
006: *
007: * This software is published under the terms of the GNU Public License (GPL),
008: * a copy of which is included in this distribution.
009: */package org.jivesoftware.util;
010:
011: import javax.servlet.http.HttpServletRequest;
012: import java.awt.*;
013: import java.beans.BeanInfo;
014: import java.beans.IntrospectionException;
015: import java.beans.Introspector;
016: import java.beans.PropertyDescriptor;
017: import java.lang.reflect.InvocationTargetException;
018: import java.text.DateFormat;
019: import java.text.SimpleDateFormat;
020: import java.util.*;
021:
022: /**
023: * A utility class that provides methods that are useful for dealing with
024: * Java Beans.
025: */
026: public class BeanUtils {
027:
028: /**
029: * The date format recognized for parsing/formattig dates.
030: */
031: public static final String DATE_FORMAT = "MM/dd/yyyy";
032:
033: private static DateFormat dateFormatter = new SimpleDateFormat(
034: DATE_FORMAT);
035:
036: /**
037: * Sets the properties of a Java Bean based on the String name/value pairs in
038: * the specifieed Map. Because this method has to know how to convert a
039: * String value into the correct type for the bean, only a few bean property
040: * types are supported. They are: String, boolean, int, long, float, double,
041: * Color, and Class.<p>
042: *
043: * If key/value pairs exist in the Map that don't correspond to properties
044: * of the bean, they will be ignored.
045: *
046: * @param bean the JavaBean to set properties on.
047: * @param properties String name/value pairs of the properties to set.
048: */
049: public static void setProperties(Object bean,
050: Map<String, String> properties) {
051: try {
052: // Loop through all the property names in the Map
053: for (String propName : properties.keySet()) {
054: try {
055: // Create a property descriptor for the named property. If
056: // the bean doesn't have the named property, an
057: // Introspection will be thrown.
058: PropertyDescriptor descriptor = new PropertyDescriptor(
059: propName, bean.getClass());
060: // Load the class type of the property.
061: Class propertyType = descriptor.getPropertyType();
062: // Get the value of the property by converting it from a
063: // String to the correct object type.
064: Object value = decode(propertyType, properties
065: .get(propName));
066: // Set the value of the bean.
067: descriptor.getWriteMethod().invoke(bean, value);
068: } catch (IntrospectionException ie) {
069: // Ignore. This exception means that the key in the map
070: // does not correspond to a property of the bean.
071: } catch (InvocationTargetException ite) {
072: // Ignore. This exception most often occurs when a
073: // value in the map is null and the target method doesn't
074: // support null properties.
075: }
076: }
077: } catch (Exception e) {
078: Log.error(e);
079: }
080: }
081:
082: /**
083: * Sets the properties of a Java Bean based on the request's properties. Because
084: * this method has to know how to convert a String value into the correct type
085: * for the bean, only a few bean property types are supported. They are: String,
086: * boolean, int, long, float, double, Color, and Class.<p>
087: *
088: * If key/value pairs exist in the Map that don't correspond to properties
089: * of the bean, they will be ignored.
090: *
091: * @param bean the JavaBean to set properties on.
092: * @param request the HTTP request.
093: */
094: public static void setProperties(Object bean,
095: HttpServletRequest request) {
096: for (Enumeration propNames = request.getParameterNames(); propNames
097: .hasMoreElements();) {
098: String propName = (String) propNames.nextElement();
099: try {
100: // Create a property descriptor for the named property. If
101: // the bean doesn't have the named property, an
102: // Introspection will be thrown.
103: PropertyDescriptor descriptor = new PropertyDescriptor(
104: propName, bean.getClass());
105: // Load the class type of the property.
106: Class propertyType = descriptor.getPropertyType();
107: // Get the value of the property by converting it from a
108: // String to the correct object type.
109: Object value = decode(propertyType, request
110: .getParameter(propName));
111: // Set the value of the bean.
112: descriptor.getWriteMethod().invoke(bean, value);
113: } catch (IntrospectionException ie) {
114: // Ignore. This exception means that the key in the map
115: // does not correspond to a property of the bean.
116: } catch (InvocationTargetException ite) {
117: // Ignore. This exception most often occurs when a
118: // value in the map is null and the target method doesn't
119: // support null properties.
120: } catch (IllegalAccessException e) {
121: Log.error(e);
122: } catch (Exception e) {
123: Log.error(e);
124: }
125: }
126: }
127:
128: /**
129: * Gets the properties from a Java Bean and returns them in a Map of String
130: * name/value pairs. Because this method has to know how to convert a
131: * bean property into a String value, only a few bean property
132: * types are supported. They are: String, boolean, int, long, float, double,
133: * Color, and Class.
134: *
135: * @param bean a Java Bean to get properties from.
136: * @return a Map of all properties as String name/value pairs.
137: */
138: public static Map<String, String> getProperties(Object bean) {
139: Map<String, String> properties = new HashMap<String, String>();
140: try {
141: BeanInfo beanInfo = Introspector.getBeanInfo(bean
142: .getClass());
143: // Loop through all properties of the bean.
144: PropertyDescriptor[] descriptors = beanInfo
145: .getPropertyDescriptors();
146: String[] names = new String[descriptors.length];
147: for (int i = 0; i < names.length; i++) {
148: // Determine the property name.
149: String name = descriptors[i].getName();
150: //Class type = descriptors[i].getPropertyType();
151: // Decode the property value using the property type and
152: // encoded String value.
153: Object value = descriptors[i].getReadMethod().invoke(
154: bean, (java.lang.Object[]) null);
155: // Add to Map, encoding the value as a String.
156: properties.put(name, encode(value));
157: }
158: } catch (Exception e) {
159: Log.error(e);
160: }
161: return properties;
162: }
163:
164: /**
165: * Returns the PropertyDescriptor array for the specified Java Bean Class.
166: * The method also does a special check to see of the bean has a BeanInfo
167: * class that extends the JiveBeanInfo class. If yes, we load the
168: * PropertyDescriptor array directly from that BeanInfo class rather than
169: * through the Introspector in order to preserve the desired ordering of
170: * properties.
171: *
172: * @param beanClass the Class of the JavaBean.
173: * @return the PropertyDescriptor array for the specified Java Bean Class.
174: * @throws java.beans.IntrospectionException
175: */
176: public static PropertyDescriptor[] getPropertyDescriptors(
177: Class beanClass) throws IntrospectionException {
178: // See if the Java Bean has a BeanInfo class that implements
179: // JiveBeanInfo. If so, return the PropertyDescriptor from that
180: // class. This will bypass properties of parent classes, but this is
181: // the normal behavior of classes that implement JiveBeanInfo.
182: try {
183: JiveBeanInfo beanInfo = (JiveBeanInfo) ClassUtils.forName(
184: beanClass.getName() + "BeanInfo").newInstance();
185: return beanInfo.getPropertyDescriptors();
186: } catch (Exception e) {
187: // Ignore.
188: }
189: // Otherwise, return the PropertyDescriptors from the Introspector.
190: return Introspector.getBeanInfo(beanClass)
191: .getPropertyDescriptors();
192: }
193:
194: /**
195: * Encodes a bean property value as a String. If the object type is not
196: * supported, null will be returned.
197: *
198: * @param value an Object to encode in a String representation.
199: * @return the encoded bean.
200: */
201: private static String encode(Object value) {
202: if (value instanceof String) {
203: return (String) value;
204: }
205: if (value instanceof Boolean || value instanceof Integer
206: || value instanceof Long || value instanceof Float
207: || value instanceof Double) {
208: return value.toString();
209: }
210: if (value instanceof Date) {
211: try {
212: return dateFormatter.format((Date) value);
213: } catch (Exception ignored) {
214: // Ignore.
215: }
216: }
217: if (value instanceof Color) {
218: Color color = (Color) value;
219: return color.getRed() + "," + color.getGreen() + ","
220: + color.getBlue();
221: }
222: if (value instanceof Class) {
223: return ((Class) value).getName();
224: }
225: return null;
226: }
227:
228: /**
229: * Decodes a String into an object of the specified type. If the object
230: * type is not supported, null will be returned.
231: *
232: * @param type the type of the property.
233: * @param value the encode String value to decode.
234: * @return the String value decoded into the specified type.
235: * @throws Exception
236: */
237: private static Object decode(Class type, String value)
238: throws Exception {
239: if (type.getName().equals("java.lang.String")) {
240: return value;
241: }
242: if (type.getName().equals("boolean")) {
243: return Boolean.valueOf(value);
244: }
245: if (type.getName().equals("int")) {
246: return Integer.valueOf(value);
247: }
248: if (type.getName().equals("long")) {
249: return Long.valueOf(value);
250: }
251: if (type.getName().equals("float")) {
252: return Float.valueOf(value);
253: }
254: if (type.getName().equals("double")) {
255: return Double.valueOf(value);
256: }
257: if (type.getName().equals("java.util.Date")) {
258: try {
259: return dateFormatter.parse(value);
260: } catch (Exception ignored) {
261: // Ignore.
262: }
263: }
264: if (type.getName().equals("java.awt.Color")) {
265: StringTokenizer tokens = new StringTokenizer(value, ",");
266: int red = Integer.parseInt(tokens.nextToken());
267: int green = Integer.parseInt(tokens.nextToken());
268: int blue = Integer.parseInt(tokens.nextToken());
269: return new Color(red, green, blue);
270: }
271: if (type.getName().equals("java.lang.Class")) {
272: return ClassUtils.forName(value);
273: }
274: return null;
275: }
276:
277: // This class is not instantiable.
278: private BeanUtils() {
279: // do nothing.
280: }
281: }
|