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: InputMaskForm.java,v $
31   * Revision 1.7.2.1  2005/10/08 10:53:58  colinmacleod
32   * Added validation of mandatory fields.
33   *
34   * Revision 1.7  2005/04/27 17:23:28  colinmacleod
35   * Fixed bugs resulting from ivata masks changes
36   * for ivata groupware v0.11.
37   *
38   * Revision 1.6  2005/04/09 18:04:18  colinmacleod
39   * Changed copyright text to GPL v2 explicitly.
40   *
41   * Revision 1.5  2005/03/10 10:40:21  colinmacleod
42   * Added *button hidden* attributes.
43   *
44   * Revision 1.4  2005/01/19 13:14:02  colinmacleod
45   * Renamed CausedByException to SystemException.
46   *
47   * Revision 1.3  2005/01/07 08:08:24  colinmacleod
48   * Moved up a version number.
49   * Changed copyright notices to 2005.
50   * Updated the documentation:
51   *   - started working on multiproject:site docu.
52   *   - changed the logo.
53   * Added checkstyle and fixed LOADS of style issues.
54   * Added separate thirdparty subproject.
55   * Added struts (in web), util and webgui (in webtheme) from ivata op.
56   *
57   * Revision 1.2  2004/12/23 21:28:32  colinmacleod
58   * Modifications to add ivata op compatibility.
59   *
60   * Revision 1.1.1.1  2004/05/16 20:40:32  colinmacleod
61   * Ready for 0.1 release
62   * -----------------------------------------------------------------------------
63   */
64  package com.ivata.mask.web.struts;
65  
66  import java.beans.PropertyDescriptor;
67  import java.util.Collection;
68  import java.util.Iterator;
69  import java.util.List;
70  import java.util.Vector;
71  
72  import javax.naming.OperationNotSupportedException;
73  import javax.servlet.http.HttpServletRequest;
74  import javax.servlet.http.HttpSession;
75  
76  import org.apache.commons.beanutils.PropertyUtils;
77  import org.apache.log4j.Logger;
78  import org.apache.struts.action.ActionMapping;
79  
80  import com.ivata.mask.Mask;
81  import com.ivata.mask.field.Field;
82  import com.ivata.mask.validation.ValidationError;
83  import com.ivata.mask.validation.ValidationErrors;
84  import com.ivata.mask.valueobject.ValueObject;
85  /***
86   * This form references a single value object to be displayed.
87   *
88   * @author Colin MacLeod
89   * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
90   * @since ivata masks 0.1 (2004-05-09)
91   * @version $Revision: 1.7.2.1 $
92   */
93  public class InputMaskForm extends MaskForm {
94      /***
95       * <strong>Log4J </strong> logger to provide tracing information.
96       */
97      private static Logger log = Logger.getLogger(InputMaskForm.class);
98      /***
99       * <p>
100      * Request attribute under which name this form is stored.
101      * </p>
102      */
103     public static final String REQUEST_ATTRIBUTE = "InputMaskForm";
104     /***
105      * Refer to {@link #isApplyButtonHidden}.
106      */
107     private boolean applyButtonHidden = false;
108     /***
109      * Refer to {@link #isClearButtonHidden}.
110      */
111     private boolean clearButtonHidden = false;
112     /***
113      * Refer to {@link #isDeleteButtonHidden}.
114      */
115     private boolean deleteButtonHidden = false;
116     /***
117      * Refer to {@link #isDeleteWithoutWarn}.
118      */
119     private boolean deleteWithoutWarn = false;
120     /***
121      * Refer to {@link #isDisplayOnly}.
122      */
123     private boolean displayOnly = false;
124     /***
125      * Refer to {@link #isRefreshOpener}.
126      */
127     private boolean refreshOpener = false;
128     /***
129      * Refer to {@link #getResourceFieldPath}.
130      */
131     private String resourceFieldPath;
132     /***
133      * These are the names of properties which are not cleared, in the
134      * <code>clear</code> method.
135      */
136     private List savedProperties = new Vector();
137     /***
138      * Refer to {@link #getValueObject}.
139      */
140     private ValueObject valueObject;
141     /***
142      * <p>
143      * Create a new mask form for the given value object.
144      * </p>
145      *
146      * @param valueObjectParam
147      *            value object to be displayed.
148      * @param maskParam
149      *            mask containing all the fields definitions to be displayed.
150      * @param baseClassParam
151      *            base class of all value objects to show in the list associated
152      *            with this mask.
153      */
154     public InputMaskForm(final ValueObject valueObjectParam,
155             final Mask maskParam, final Class baseClassParam) {
156         super(maskParam, baseClassParam);
157         this.valueObject = valueObjectParam;
158         // total hack and temporary workaround for ivata groupware v0.11.x
159         // will be removed in later releases
160         this.saveProperty("modified");
161         this.saveProperty("modifiedBy");
162         this.saveProperty("created");
163         this.saveProperty("createdBy");
164     }
165 
166     /***
167      * <p>
168      * Clear all bean properties to their default state.The difference between
169      * this and <code>reset</code> is that all properties are changed,
170      * regardless of current request state.
171      * </p>
172      *
173      * @see com.ivata.mask.web.struts.MaskForm#clear()
174      * @throws OperationNotSupported if the value object does not have a default
175      * constructor.
176      */
177     protected void clear()
178             throws OperationNotSupportedException {
179         Class dOClass = valueObject.getClass();
180         try {
181             valueObject = (ValueObject) dOClass.newInstance();
182         } catch (InstantiationException e) {
183             throw new OperationNotSupportedException(
184                     "You must override InputMask.clear to create a new "
185                     + "value object of class '"
186                     + dOClass.getName()
187                     + "' ("
188                     + e.getClass().getName()
189                     + ": "
190                     + e.getMessage()
191                     + ")");
192         } catch (IllegalAccessException e) {
193             throw new OperationNotSupportedException(
194                     "You must override InputMask.clear to create a new "
195                     + "value object of class '"
196                     + dOClass.getName()
197                     + "' ("
198                     + e.getClass().getName()
199                     + ": "
200                     + e.getMessage()
201                     + ")");
202         }
203     }
204     /***
205      * <p>
206      * The resource field path is the &quot;stem&quot; - the first string before
207      * the dot separator (.) - in the application resources for the fields in
208      * this form.
209      * </p>
210      * <p>
211      * For example, in a form for editing group information this might be simply
212      * <b>&quot;group&quot;</b>, and the message resource key for the group
213      * parent field would then be specified in the
214      * <code>ApplicationResources.properties</code> file as
215      * <b>&quot;group.field.parent&quot;</b> (without the quotes).
216      * </p>
217      * @return Returns the path to application resources for this form.
218      */
219     public String getResourceFieldPath() {
220         return resourceFieldPath;
221     }
222     /***
223      * Value object whose values are to be displayed in the mask.
224      *
225      * @return Value object to be displayed.
226      */
227     public ValueObject getValueObject() {
228         return valueObject;
229     }
230     /***
231      * Should the 'apply' button be shown or hidden?
232      * @return <code>true</code> if the apply button should <u>NOT</u> be shown.
233      */
234     public boolean isApplyButtonHidden() {
235         return applyButtonHidden;
236     }
237     /***
238      * Should the 'new' button be shown or hidden?
239      * @return <code>true</code> if the new button should <u>NOT</u> be shown.
240      */
241     public boolean isClearButtonHidden() {
242         return clearButtonHidden;
243     }
244     /***
245      * Should the 'delete' button be shown or hidden?
246      * @return <code>true</code> if the delete button should <u>NOT</u> be
247      * shown.
248      */
249     public boolean isDeleteButtonHidden() {
250         return deleteButtonHidden;
251     }
252     /***
253      * Sets whether or not the dialog should warn before deleting a value
254      * object.
255      * @return <code>true</code> if a warning is displayed before deleting.
256      */
257     public boolean isDeleteWithoutWarn() {
258         return deleteWithoutWarn;
259     }
260     /***
261      * If the mask is marked display only, only the text values of the fields
262      * are show (i.e. display mode), and there are no fields for user input.
263      *
264      * @return <code>true</code> if only text field values should be shown.
265      */
266     public boolean isDisplayOnly() {
267         return displayOnly || getMask().isDisplayOnly();
268     }
269     /***
270      * For pop-up windows, it is often useful to refresh the page which opened
271      * the pop-up, after the pop-up has changed something.
272      *
273      * @return Returns <code>true</code> if the page which opened this one
274      * should be refreshed on delete or confirm.
275      */
276     public boolean isRefreshOpener() {
277         return refreshOpener;
278     }
279     /***
280      * Refer to {@link DialogForm#reset}.
281      *
282      * @param mapping
283      *            Refer to {@link DialogForm#reset}.
284      * @param request
285      *            Refer to {@link DialogForm#reset}.
286      * @see DialogForm#reset
287      */
288     public void reset(final ActionMapping mapping,
289             final HttpServletRequest request) {
290         PropertyDescriptor[] descriptors = PropertyUtils
291             .getPropertyDescriptors(valueObject.getClass());
292         for (int i = 0; i < descriptors.length; i++) {
293             PropertyDescriptor descriptor = descriptors[i];
294             try {
295                 String propertyName = descriptor.getName();
296                 if (!savedProperties.contains(propertyName)) {
297                     // clear any collection
298                     if (Collection.class.isAssignableFrom(
299                             descriptor.getPropertyType())) {
300                         Collection collection = (Collection)
301                             PropertyUtils.getProperty(
302                                 valueObject, descriptor.getName());
303                         collection.clear();
304                     } else {
305                         PropertyUtils.setProperty(valueObject, descriptor.getName(),
306                                 null);
307                     }
308                 }
309             } catch (Exception e) {
310                 // ignore all exceptions here...
311                 if (log.isDebugEnabled()) {
312                     log.debug("Exception in InputMaskForm.reset", e);
313                 }
314             }
315         }
316         super.reset(mapping, request);
317     }
318 
319     /***
320      * Mark a property to be saved. By default, the <code>clear</code> method
321      * (and hence the <code>reset</code> method)
322      * will set all property values to <code>null</code>. By adding your
323      * property to the list of those saved here, it will be ignored in the
324      * <code>clear</code> method.
325      *
326      * @param propertyNameParam name of the property to save.
327      */
328     protected final void saveProperty(final String propertyNameParam) {
329         savedProperties.add(propertyNameParam);
330     }
331     /***
332      * Refer to {@link #isApplyButtonHidden}.
333      * @param applyButtonHiddenParam Refer to {@link #isApplyButtonHidden}.
334      */
335     public final void setApplyButtonHidden(boolean applyButtonHiddenParam) {
336         if (log.isDebugEnabled()) {
337             log.debug("setApplyButtonHidden before: '" + applyButtonHidden
338                     + "', after: '" + applyButtonHiddenParam + "'");
339         }
340 
341         applyButtonHidden = applyButtonHiddenParam;
342     }
343     /***
344      * Refer to {@link #isClearButtonHidden}.
345      * @param clearButtonHiddenParam Refer to {@link #isClearButtonHidden}.
346      */
347     public final void setClearButtonHidden(boolean clearButtonHiddenParam) {
348         if (log.isDebugEnabled()) {
349             log.debug("setClearButtonHidden before: '" + clearButtonHidden
350                     + "', after: '" + clearButtonHiddenParam + "'");
351         }
352 
353         clearButtonHidden = clearButtonHiddenParam;
354     }
355     /***
356      * Refer to {@link #isDeleteButtonHidden}.
357      * @param deleteButtonHiddenParam Refer to {@link #isDeleteButtonHidden}.
358      */
359     public final void setDeleteButtonHidden(boolean deleteButtonHiddenParam) {
360         if (log.isDebugEnabled()) {
361             log.debug("setDeleteButtonHidden before: '" + deleteButtonHidden
362                     + "', after: '" + deleteButtonHiddenParam + "'");
363         }
364 
365         deleteButtonHidden = deleteButtonHiddenParam;
366     }
367     /***
368      * Refer to {@link #isDeleteWithoutWarn}.
369      * @param deleteWithoutWarnParam Refer to {@link #isDeleteWithoutWarn}.
370      */
371     public final void setDeleteWithoutWarn(boolean deleteWithoutWarnParam) {
372         if (log.isDebugEnabled()) {
373             log.debug("setDeleteWithoutWarn before: '" + deleteWithoutWarn
374                     + "', after: '" + deleteWithoutWarnParam + "'");
375         }
376 
377         deleteWithoutWarn = deleteWithoutWarnParam;
378     }
379     /***
380      * Refer to {@link #isDisplayOnly}.
381      *
382      * @param b
383      *            Refer to {@link #isDisplayOnly}.
384      */
385     public final void setDisplayOnly(final boolean b) {
386         displayOnly = b;
387     }
388     /***
389      * Refer to {@link #isRefreshOpener}.
390      * @param refreshOpenerParam Refer to {@link #isRefreshOpener}.
391      */
392     public final void setRefreshOpener(boolean refreshOpenerParam) {
393         if (log.isDebugEnabled()) {
394             log.debug("setRefreshOpener before: '" + refreshOpener
395                     + "', after: '" + refreshOpenerParam + "'");
396         }
397 
398         refreshOpener = refreshOpenerParam;
399     }
400     /***
401      * Refer to {@link #getResourceFieldPath}.
402      * @param resourceFieldPathParam Refer to {@link #getResourceFieldPath}.
403      */
404     public void setResourceFieldPath(String resourceFieldPathParam) {
405         if (log.isDebugEnabled()) {
406             log.debug("setResourceFieldPath before: '" + resourceFieldPath
407                     + "', after: '" + resourceFieldPathParam + "'");
408         }
409 
410         resourceFieldPath = resourceFieldPathParam;
411     }
412     /***
413      * Overridden to check mandatory fields.
414      *
415      * @param requestParam {@inheritDoc}
416      * @param sessionParam {@inheritDoc} <copyDoc>Refer to {@link DialogForm#validate}.</copyDoc>
417      * @return errors from the super class, with any mandatory field errors
418      * added.
419      */
420     public ValidationErrors validate(final HttpServletRequest requestParam,
421             final HttpSession sessionParam) {
422         if (log.isDebugEnabled()) {
423             log.debug("validate(HttpServletRequest requestParam = "
424                     + requestParam + ", HttpSession sessionParam = "
425                     + sessionParam + ") - start");
426         }
427 
428         ValidationErrors errors
429             =  super.validate(requestParam, sessionParam);
430         Mask mask = getMask();
431         List fields = mask.getFields();
432         Iterator fieldIterator = fields.iterator();
433         while (fieldIterator.hasNext()) {
434             Field field = (Field) fieldIterator.next();
435             if (!field.isMandatory()) {
436                 continue;
437             }
438             try {
439                 Object value = PropertyUtils.getProperty(valueObject,
440                     field.getName());
441                 if ((value == null)
442                         || "".equals(value)
443                         || "0".equals(value.toString())
444                         || ((value instanceof Collection)
445                             && ((Collection) value).isEmpty())) {
446                   errors.add(new ValidationError(
447                         field.getName(),
448                         field,
449                         "errors.required"));
450                 }
451             } catch (Exception e) {
452                 throw new RuntimeException(e);
453             }
454         }
455 
456         if (log.isDebugEnabled()) {
457             log.debug("validate - end - return value = "
458                             + errors);
459         }
460         return errors;
461     }
462 }
463