View Javadoc

1   /*
2    * Copyright (c) 2001 - 2005 ivata limited.
3    * All rights reserved.
4    * -----------------------------------------------------------------------------
5    * ivata masks may be redistributed under the GNU General Public
6    * License as published by the Free Software Foundation;
7    * version 2 of the License.
8    *
9    * These programs are free software; you can redistribute them and/or
10   * modify them under the terms of the GNU General Public License
11   * as published by the Free Software Foundation; version 2 of the License.
12   *
13   * These programs are distributed in the hope that they will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16   *
17   * See the GNU General Public License in the file LICENSE.txt for more
18   * details.
19   *
20   * If you would like a copy of the GNU General Public License write to
21   *
22   * Free Software Foundation, Inc.
23   * 59 Temple Place - Suite 330
24   * Boston, MA 02111-1307, USA.
25   *
26   *
27   * To arrange commercial support and licensing, contact ivata at
28   *                  http://www.ivata.com/contact.jsp
29   * -----------------------------------------------------------------------------
30   * $Log: FieldValueConvertor.java,v $
31   * Revision 1.7  2005/04/27 14:28:08  colinmacleod
32   * Fixed so it returns null for a null or empty
33   * string.
34   *
35   * Revision 1.6  2005/04/11 12:27:02  colinmacleod
36   * Added preliminary support for filters.
37   * Added FieldValueConvertor factor interface
38   * to split off value convertors for reuse.
39   *
40   * Revision 1.5  2005/04/09 18:04:15  colinmacleod
41   * Changed copyright text to GPL v2 explicitly.
42   *
43   * Revision 1.4  2005/03/10 10:20:02  colinmacleod
44   * Now implements Serializable.
45   *
46   * Revision 1.3  2005/01/19 12:35:04  colinmacleod
47   * Added hidden fields.
48   *
49   * Revision 1.2  2005/01/06 22:13:21  colinmacleod
50   * Moved up a version number.
51   * Changed copyright notices to 2005.
52   * Updated the documentation:
53   *   - started working on multiproject:site docu.
54   *   - changed the logo.
55   * Added checkstyle and fixed LOADS of style issues.
56   * Added separate thirdparty subproject.
57   * Added struts (in web), util and webgui (in webtheme) from ivata op.
58   *
59   * Revision 1.1  2004/12/29 20:07:07  colinmacleod
60   * Renamed subproject masks to mask.
61   *
62   * Revision 1.2  2004/11/11 13:33:14  colinmacleod
63   * Bug fixes. Added log4j logging.
64   *
65   * Revision 1.1.1.1  2004/05/16 20:40:32  colinmacleod
66   * Ready for 0.1 release
67   * -----------------------------------------------------------------------------
68   */
69  package com.ivata.mask.field;
70  import java.beans.PropertyDescriptor;
71  import java.io.Serializable;
72  import java.lang.reflect.Constructor;
73  import java.lang.reflect.InvocationTargetException;
74  import java.util.Arrays;
75  
76  import org.apache.commons.beanutils.PropertyUtils;
77  import org.apache.log4j.Logger;
78  
79  import com.ivata.mask.util.StringHandling;
80  import com.ivata.mask.util.SystemException;
81  import com.ivata.mask.validation.ValidationError;
82  import com.ivata.mask.validation.ValidationErrors;
83  /***
84   * <p>
85   * Retrieve the value from a value object for a given field.
86   * </p>
87   *
88   * @since ivata masks 0.1 (2004-05-14)
89   * @author Colin MacLeod
90   * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
91   * @version $Revision: 1.7 $
92   */
93  public class FieldValueConvertor implements Serializable {
94      /***
95       * <p>
96       * Wraps any error encountered when trying retrieve a field value via
97       * reflection.
98       * </p>
99       *
100      * @since ivata masks 0.1 (2004-05-14)
101      * @author Colin MacLeod
102  * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
103      * @version $Revision: 1.7 $
104      */
105     public static class FieldValueException extends RuntimeException {
106         /***
107          * <p>
108          * Construct a field value exception from another throwable.
109          * </p>
110          *
111          * @param throwable
112          *            cause of the exception.
113          */
114         public FieldValueException(final Throwable throwable) {
115             super("ERROR (" + throwable.getClass().getName() + "): "
116                     + throwable.getMessage());
117         }
118         /***
119          * <p>
120          * Construct a field value exception from another throwable.
121          * </p>
122          *
123          * @param throwable
124          *            cause of the exception.
125          * @param location
126          *            brief text describing where the error ocurred
127          */
128         public FieldValueException(final Throwable throwable,
129                 final String location) {
130             super("ERROR (" + throwable.getClass().getName() + ") " + location
131                     + ": " + throwable.getMessage());
132         }
133     }
134     /***
135      * <p>
136      * This log provides tracing and debugging information.
137      * </p>
138      */
139     private Logger log = Logger.getLogger(FieldValueConvertor.class);
140     /***
141      * <p>
142      * Convert a value from a string. Override this method to choose how your
143      * class converts string values to your object class values.
144      * </p>
145      *
146      * <p>
147      * This implementation attempts to instantiate an object of the desired
148      * class by locating a constructor which takes a single string as an
149      * argument.
150      * </p>
151      *
152      * @param propertyClassParam
153      *            exact class to be converted to.
154      * @param stringValue
155      *            value to be converted.
156      * @return valid object value converted from a string.
157      */
158     public Object convertFromString(final Class propertyClassParam,
159             final String stringValue) {
160         if (StringHandling.isNullOrEmpty(stringValue)) {
161             return null;
162         }
163         Constructor stringConstructor;
164         Class propertyClass;
165         if (propertyClassParam.isPrimitive()) {
166             try {
167                 propertyClass = DefaultFieldValueConvertorFactory
168                     .convertPrimitiveType(propertyClassParam);
169             } catch (SystemException e) {
170                 throw new RuntimeException(e);
171             }
172         } else {
173             propertyClass = propertyClassParam;
174         }
175         try {
176             stringConstructor = propertyClass
177                     .getConstructor(new Class[] {
178                             String.class
179                     });
180         } catch (SecurityException e) {
181             throw new FieldValueException(e, "constructing '"
182                     + propertyClass.getName() + "' from string value '"
183                     + stringValue + "'");
184         } catch (NoSuchMethodException e) {
185             throw new FieldValueException(e, "constructing '"
186                     + propertyClass.getName() + "' from string value '"
187                     + stringValue + "'");
188         }
189         Object value;
190         try {
191             value = stringConstructor.newInstance(new Object[] {
192                     stringValue
193             });
194         } catch (IllegalArgumentException e) {
195             throw new FieldValueException(e, "constructing '"
196                     + propertyClass.getName() + "' from string value '"
197                     + stringValue + "'");
198         } catch (InstantiationException e) {
199             throw new FieldValueException(e, "constructing '"
200                     + propertyClass.getName() + "' from string value '"
201                     + stringValue + "'");
202         } catch (IllegalAccessException e) {
203             throw new FieldValueException(e, "constructing '"
204                     + propertyClass.getName() + "' from string value '"
205                     + stringValue + "'");
206         } catch (InvocationTargetException e) {
207             throw new FieldValueException(e, "constructing '"
208                     + propertyClass.getName() + "' from string value '"
209                     + stringValue + "'");
210         }
211         return value;
212     }
213     /***
214      * <p>
215      * Get the value of a named property within an object.
216      * </p>
217      *
218      * @param object
219      *            POJO for which to return the field value.
220      * @param propertyName
221      *            name of the property/field to return the value for.
222      * @param defaultValue
223      *            value to use if none is set.
224      * @return the value of the named property.
225      */
226     protected final Object getObjectValue(final Object object,
227             final String propertyName, final Object defaultValue) {
228         Object objectValue;
229         try {
230             objectValue = PropertyUtils.getProperty(object, propertyName);
231         } catch (IllegalAccessException e) {
232             throw new FieldValueException(e);
233         } catch (InvocationTargetException e) {
234             throw new FieldValueException(e);
235         } catch (NoSuchMethodException e) {
236             // if there is no method, just set the value to null
237             objectValue = null;
238         }
239         if (objectValue == null) {
240             objectValue = defaultValue;
241         }
242         return objectValue;
243     }
244     /***
245      * <p>
246      * Get the value of the field provided, in the value object supplied, and
247      * return the string equivalent.
248      * </p>
249      *
250      * @param object
251      *            POJO for which to return the field value.
252      * @param propertyName
253      *            name of the property/field to return the value for.
254      * @param defaultValue
255      *            value to use if none is set.
256      * @return the value of the named property, as a string.
257      */
258     public final String getStringValue(final Object object,
259             final String propertyName, final String defaultValue) {
260         Object objectValue = getObjectValue(object, propertyName, defaultValue);
261         return toString(objectValue);
262     }
263     /***
264      * <p>
265      * Set the value of the field provided, in the value object supplied.
266      * </p>
267      *
268      * <p>
269      * This implementation attempts to instantiate an object of the desired
270      * class by locating a constructor which takes a single string as an
271      * argument.
272      * </p>
273      *
274      * @param object
275      *            POJO for which to set the field value.
276      * @param field
277      *            field to be set.
278      * @param stringValue
279      *            new string equivalent value of this field.
280      * @return errors, if there are any errors with the field values, otherwise
281      *         an empty collection.
282      */
283     public final ValidationErrors setStringValue(final Object object,
284             final Field field, final String stringValue) {
285         ValidationErrors validationErrors = new ValidationErrors();
286         PropertyDescriptor descriptor;
287         try {
288             descriptor = PropertyUtils.getPropertyDescriptor(object, field
289                     .getName());
290         } catch (IllegalAccessException e) {
291             throw new FieldValueException(e);
292         } catch (InvocationTargetException e) {
293             throw new FieldValueException(e);
294         } catch (NoSuchMethodException e) {
295             throw new FieldValueException(e);
296         }
297         // if there is no setter, just get out.
298         if (descriptor == null) {
299             return validationErrors;
300         }
301         Class propertyClass = descriptor.getPropertyType();
302         Object value;
303         try {
304             // work around for primitive types
305             if ("int".equals(propertyClass.getName())) {
306                 propertyClass = Integer.class;
307             } else if ("short".equals(propertyClass.getName())) {
308                 propertyClass = Short.class;
309             } else if ("long".equals(propertyClass.getName())) {
310                 propertyClass = Long.class;
311             } else if ("float".equals(propertyClass.getName())) {
312                 propertyClass = Float.class;
313             } else if ("double".equals(propertyClass.getName())) {
314                 propertyClass = Double.class;
315             }
316             value = convertFromString(propertyClass, stringValue);
317         } catch (FieldValueException e) {
318             validationErrors.add(new ValidationError(
319                     "errors.field.invalidValue",
320                     Arrays.asList(new Object[] {
321                             stringValue, field })));
322             return validationErrors;
323         }
324         if (validationErrors.isEmpty()) {
325             try {
326                 PropertyUtils.setProperty(object, field.getName(), value);
327             } catch (IllegalAccessException e) {
328                 throw new FieldValueException(e);
329             } catch (InvocationTargetException e) {
330                 throw new FieldValueException(e);
331             } catch (NoSuchMethodException e) {
332                 // this means there is no setter - that's probably ok.
333                 if (!("idString".equals(field.getName()) || "class".equals(field
334                         .getName()))) {
335                     log.warn("Warning (" + e.getClass().getName()
336                             + ": setting value '" + value + "' to field '"
337                             + field + " on object " + object + ": "
338                             + e.getMessage());
339                 }
340             }
341         }
342         return validationErrors;
343     }
344     /***
345      * <p>
346      * Convert a field object value into a string. Override this method to
347      * convert for a specific type.
348      * </p>
349      *
350      * @param objectValue
351      *            object to be converted.
352      * @return string equivalent.
353      */
354     protected String toString(final Object objectValue) {
355         // default implementation simply uses toString...
356         if (objectValue == null) {
357             return "";
358         } else {
359             return objectValue.toString();
360         }
361     }
362 }