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: DefaultFieldWriterFactory.java,v $
31   * Revision 1.9  2005/04/09 18:04:17  colinmacleod
32   * Changed copyright text to GPL v2 explicitly.
33   *
34   * Revision 1.8  2005/01/19 12:51:03  colinmacleod
35   * Changed Id --> Name.
36   *
37   * Revision 1.7  2005/01/10 19:05:12  colinmacleod
38   * Removed reflection and simplified override method
39   * for field writer classes.
40   *
41   * Revision 1.6  2005/01/07 08:08:24  colinmacleod
42   * Moved up a version number.
43   * Changed copyright notices to 2005.
44   * Updated the documentation:
45   *   - started working on multiproject:site docu.
46   *   - changed the logo.
47   * Added checkstyle and fixed LOADS of style issues.
48   * Added separate thirdparty subproject.
49   * Added struts (in web), util and webgui (in webtheme) from ivata op.
50   *
51   * Revision 1.5  2004/12/30 20:27:55  colinmacleod
52   * Added reflection to let you override the field writer classes used.
53   *
54   * Revision 1.4  2004/12/29 15:30:46  colinmacleod
55   * Added asserts to check parameters are not null.
56   *
57   * Revision 1.3  2004/11/12 15:10:41  colinmacleod
58   * Moved persistence classes from ivata op as a replacement for
59   * ValueObjectLocator.
60   *
61   * Revision 1.2  2004/11/11 13:44:04  colinmacleod
62   * Added HTMLFormatter.
63   *
64   * Revision 1.1.1.1  2004/05/16 20:40:32  colinmacleod
65   * Ready for 0.1 release
66   * -----------------------------------------------------------------------------
67   */
68  package com.ivata.mask.web.field;
69  import java.beans.PropertyDescriptor;
70  import java.lang.reflect.InvocationTargetException;
71  import java.util.Collection;
72  
73  import org.apache.commons.beanutils.PropertyUtils;
74  
75  import com.ivata.mask.field.Field;
76  import com.ivata.mask.field.FieldValueConvertor;
77  import com.ivata.mask.field.date.DateFieldValueConvertor;
78  import com.ivata.mask.field.number.NumberFieldValueConvertor;
79  import com.ivata.mask.field.valueobject.ValueObjectFieldValueConvertor;
80  import com.ivata.mask.persistence.PersistenceManager;
81  import com.ivata.mask.persistence.PersistenceSession;
82  import com.ivata.mask.util.SystemException;
83  import com.ivata.mask.valueobject.ValueObject;
84  import com.ivata.mask.web.field.hidden.HiddenFieldWriter;
85  import com.ivata.mask.web.field.text.TextAreaFieldWriter;
86  import com.ivata.mask.web.field.text.TextFieldWriter;
87  import com.ivata.mask.web.field.valueobject.ValueObjectFieldWriter;
88  import com.ivata.mask.web.format.HTMLFormatter;
89  import com.ivata.mask.web.format.LineBreakFormat;
90  /***
91   * <p>
92   * Use this utility class to generate an appropriate field writer for a given
93   * mask and field.
94   * </p>
95   *
96   * @since ivata masks 0.1 (2004-05-14)
97   * @author Colin MacLeod
98   * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
99   * @version $Revision: 1.9 $
100  */
101 public class DefaultFieldWriterFactory implements FieldWriterFactory {
102     /***
103      * Refer to {@link #getActionPage}.
104      */
105     private String actionPage;
106     /***
107      * <p>
108      * Formatter used to format all texts.
109      * </p>
110      */
111     private HTMLFormatter formatter = new HTMLFormatter();
112     /***
113      * <p>
114      * This object is used to retrieve lists for collection field writers.
115      * </p>
116      */
117     private PersistenceManager persistenceManager;
118     /***
119      * Construct a writer factory.
120      *
121      * @param persistenceManagerParam
122      *            used to retrieve value objects for a value
123      * @param actionPageParam
124      *            page of the action to which we'll link value objects to.
125      */
126     public DefaultFieldWriterFactory(
127             final PersistenceManager persistenceManagerParam,
128             final String actionPageParam) {
129         this.persistenceManager = persistenceManagerParam;
130         this.actionPage = actionPageParam;
131         // for now, we only have one format in the formatter
132         LineBreakFormat lineBreakFormat = new LineBreakFormat();
133         lineBreakFormat.setConvertLineBreaks(true);
134         formatter.add(lineBreakFormat);
135     }
136     /***
137      * Page of the <strong>Struts </strong> action to which we'll link value
138      * objects to. This must be a full, webapp-relative link, starting with '/'.
139      *
140      * @return Returns the actionPage.
141      */
142     protected final String getActionPage() {
143         return actionPage;
144     }
145     /***
146      * <p>
147      * Get a field writer appropriate to the given field.
148      * </p>
149      *
150      * @param valueObjectParam
151      *            Field for which to return an appropriate field writer.
152      * @param fieldParam
153      *            Field for which to return an appropriate field writer.
154      * @param subFieldParam
155      *            Sub-field within the main field, if the field is a value
156      *            object.
157      * but the value stored in a hidden field.
158      * @param hidden
159      * If <code>true</code>, overrides the field definition, and gets a writer
160      * for a hidden field.
161      * @return valid field writer for the field provided.
162      * @throws SystemException
163      *             thrown if the writer cannot be retrieved for any reason.
164      */
165     public final FieldWriter getFieldWriter(final ValueObject valueObjectParam,
166             final Field fieldParam, final Field subFieldParam,
167             final boolean hidden)
168             throws SystemException {
169         FieldWriter fieldWriter = null;
170         String propertyName;
171         Field writerField;
172         if (subFieldParam == null) {
173             writerField = fieldParam;
174         } else {
175             writerField = subFieldParam;
176         }
177         String type = writerField.getType();
178         propertyName = "valueObject." + writerField.getPath();
179         FieldValueConvertor fieldValueConvertor = null;
180         if (Field.TYPE_RADIO.equals(type)) {
181             throw new UnsupportedOperationException("ERROR: field type '"
182                     + type + "' is not yet supported.");
183         } else if (Field.TYPE_SELECT.equals(type)) {
184             throw new UnsupportedOperationException("ERROR: field type '"
185                     + type + "' is not yet supported.");
186         } else if (Field.TYPE_AMOUNT.equals(type)) {
187             fieldWriter = newTextFieldWriter(writerField,
188                     new NumberFieldValueConvertor("###0.##"), formatter);
189         } else if (Field.TYPE_NUMBER.equals(type)) {
190             fieldWriter = newTextFieldWriter(writerField,
191                     new NumberFieldValueConvertor("###0"), formatter);
192         } else if (Field.TYPE_DATE.equals(type)) {
193             fieldWriter = newTextFieldWriter(writerField,
194                     new DateFieldValueConvertor("yyyy-MM-dd"), formatter);
195         } else if (Field.TYPE_STRING.equals(type)) {
196             fieldWriter = newTextFieldWriter(writerField,
197                     new FieldValueConvertor(), formatter);
198         } else {
199             // if the type was expressly set, then start looking at the class of
200             // the field
201             PropertyDescriptor descriptor;
202             try {
203                 Class valueObjectClass;
204                 if (subFieldParam == null) {
205                     valueObjectClass = valueObjectParam.getClass();
206                 } else {
207                     if (fieldParam.getDOClass() != null) {
208                         valueObjectClass = fieldParam.getDOClass();
209                     } else {
210                         descriptor = PropertyUtils.getPropertyDescriptor(
211                                 valueObjectParam, fieldParam.getPath());
212                         valueObjectClass = descriptor.getPropertyType();
213                         // if it is a collection, we _need_ the class
214                         // specifically defined
215                         if (Collection.class
216                                 .isAssignableFrom(valueObjectClass)) {
217                             throw new NullPointerException(
218                                     "ERROR: you must specify a data object "
219                                             + "class for collection '"
220                                             + fieldParam.getName() + "'.");
221                         }
222                     }
223                 }
224                 descriptor = getPropertyDescriptor(valueObjectClass,
225                         writerField.getPath());
226             } catch (NoSuchMethodException e) {
227                 throw new SystemException(e);
228             } catch (IllegalAccessException e) {
229                 throw new SystemException(e);
230             } catch (InvocationTargetException e) {
231                 throw new SystemException(e);
232             }
233             // if it is a value object, use a value object writer
234             Class propertyType = descriptor.getPropertyType();
235             if (ValueObject.class.isAssignableFrom(propertyType)
236                         || Collection.class.isAssignableFrom(
237                                 propertyType)) {
238                 // if it is a hidden field, use the value object convertor
239                 // to put the id out
240                 if (hidden || writerField.isHidden()) {
241                     fieldValueConvertor = new ValueObjectFieldValueConvertor();
242                 } else {
243                     // for a collection type, you _must_ specify the value
244                     // object class to use!!!
245                     Class dOClass;
246                     boolean multiple = Collection.class
247                             .isAssignableFrom(propertyType);
248                     if (writerField.getDOClass() != null) {
249                         dOClass = writerField.getDOClass();
250                     } else if (multiple) {
251                         throw new SystemException(
252                             "ERROR: for collection field '"
253                             + writerField.getPath()
254                             + "', you must specify attribute 'class' in the "
255                             + "ivata masks configuration.");
256                     } else {
257                         dOClass = propertyType;
258                     }
259                     PersistenceSession persistenceSession = persistenceManager
260                             .openSession();
261                     try {
262                         Collection allEntries = persistenceManager.findAll(
263                                 persistenceSession, dOClass);
264                         int listHeight = 1;
265                         if (multiple) {
266                             listHeight =
267                                 FieldWriterConstants.MULTIPLE_LIST_HEIGHT;
268                         }
269                         fieldWriter = newValueObjectFieldWriter(writerField,
270                                 actionPage, allEntries, formatter, listHeight,
271                                 multiple);
272                     } finally {
273                         persistenceSession.close();
274                     }
275                 }
276             } else {
277                 // default to the string representation of the field
278                 fieldValueConvertor = new FieldValueConvertor();
279             }
280 
281             // if we specified a field value convertor, then either make a
282             // hidden or text/textarea field (otherwise, it must have been the
283             // 'special case' of a value object field writer, which is dealt
284             // with above.
285             if (fieldValueConvertor != null) {
286                 if (hidden || writerField.isHidden()) {
287                     fieldWriter = newHiddenFieldWriter(writerField,
288                             fieldValueConvertor, formatter);
289                 } else if (Field.TYPE_TEXTAREA.equals(type)) {
290                     fieldWriter = newTextAreaFieldWriter(writerField,
291                             fieldValueConvertor, formatter);
292                 } else {
293                     fieldWriter = newTextFieldWriter(writerField,
294                             fieldValueConvertor, formatter);
295                 }
296             }
297         }
298         return fieldWriter;
299     }
300     /***
301      * <p>
302      * Find the descriptor for the class and id provided.
303      * </p>
304      *
305      * @param theClass
306      *            class for which to find a descriptor.
307      * @param name
308      *            name of the property for which to find a descriptor.
309      * @return valid property descriptor describing the property called
310      *         <code>name</code> in class <code>theClass</code>.
311      */
312     private PropertyDescriptor getPropertyDescriptor(final Class theClass,
313             final String name) {
314         PropertyDescriptor[] allDescriptors = PropertyUtils
315                 .getPropertyDescriptors(theClass);
316         PropertyDescriptor descriptor = null;
317         // this property might be nested
318         int dotIndex = name.indexOf('.');
319         String propertyName = null;
320         if (dotIndex == -1) {
321             propertyName = name;
322         } else {
323             propertyName = name.substring(0, dotIndex);
324         }
325         for (int i = 0; i < allDescriptors.length; i++) {
326             if (propertyName.equals(allDescriptors[i].getName())) {
327                 descriptor = allDescriptors[i];
328             }
329         }
330         // if this is a nested property, get the next in the chain
331         if (dotIndex == -1) {
332             return descriptor;
333         } else {
334             return getPropertyDescriptor(theClass, name.substring(++dotIndex));
335         }
336     }
337     /***
338      * Override this method if you need a different field writer for text areas.
339      *
340      * @param field
341      *            Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
342      * @param convertor
343      *            Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
344      * @param formatterParam
345      *            Refer to {@link TextAreaFieldWriter#TextAreaFieldWriter}.
346      * @return valid text area field writer.
347      */
348     protected FieldWriter newTextAreaFieldWriter(final Field field,
349             final FieldValueConvertor convertor,
350             final HTMLFormatter formatterParam) {
351         return new TextAreaFieldWriter(field,
352                 convertor, formatterParam);
353     }
354     /***
355      * Override this method if you need a different field writer for hidden
356      * fields.
357      *
358      * @param fieldParam
359      *            Refer to {@link TextFieldWriter#TextFieldWriter}.
360      * @param convertorParam
361      *            Refer to {@link TextFieldWriter#TextFieldWriter}.
362      * @param formatterParam
363      *            Refer to {@link TextFieldWriter#TextFieldWriter}.
364      * @return valid text field writer.
365      */
366     protected FieldWriter newHiddenFieldWriter(final Field fieldParam,
367             final FieldValueConvertor convertorParam,
368             final HTMLFormatter formatterParam) {
369         return new HiddenFieldWriter(
370                 fieldParam, convertorParam, formatterParam);
371     }
372     /***
373      * Override this method if you need a different field writer for text
374      * fields.
375      *
376      * @param fieldParam
377      *            Refer to {@link TextFieldWriter#TextFieldWriter}.
378      * @param convertorParam
379      *            Refer to {@link TextFieldWriter#TextFieldWriter}.
380      * @param formatterParam
381      *            Refer to {@link TextFieldWriter#TextFieldWriter}.
382      * @return valid text field writer.
383      */
384     protected FieldWriter newTextFieldWriter(final Field fieldParam,
385             final FieldValueConvertor convertorParam,
386             final HTMLFormatter formatterParam) {
387         return new TextFieldWriter(
388                 fieldParam, convertorParam, formatterParam);
389     }
390     /***
391      * Override this method if you need a different field writer for value
392      * objects.
393      *
394      * @param fieldParam
395      * Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
396      * @param actionPageParam
397      * Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
398      * @param allValueObjectsParam
399      * Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
400      * @param formatterParam
401      * Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
402      * @param listHeightParam
403      * Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
404      * @param multipleParam
405      * Refer to {@link ValueObjectFieldWriter#ValueObjectFieldWriter}.
406      * @return valid field writer.
407      */
408     protected FieldWriter newValueObjectFieldWriter(final Field fieldParam,
409             final String actionPageParam,
410             final Collection allValueObjectsParam,
411             final HTMLFormatter formatterParam, final int listHeightParam,
412             final boolean multipleParam) {
413         return new ValueObjectFieldWriter(
414                 fieldParam, actionPageParam, allValueObjectsParam,
415                 formatterParam, listHeightParam,
416                 multipleParam);
417     }
418 }
419