View Javadoc

1   package org.apache.turbine.services.intake.model;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  
22  import java.util.Locale;
23  
24  import org.apache.commons.lang.StringUtils;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import org.apache.turbine.om.Retrievable;
30  import org.apache.turbine.services.TurbineServices;
31  import org.apache.turbine.services.intake.IntakeException;
32  import org.apache.turbine.services.intake.TurbineIntake;
33  import org.apache.turbine.services.intake.validator.DefaultValidator;
34  import org.apache.turbine.services.intake.validator.InitableByConstraintMap;
35  import org.apache.turbine.services.intake.validator.ValidationException;
36  import org.apache.turbine.services.intake.validator.Validator;
37  import org.apache.turbine.services.intake.xmlmodel.Rule;
38  import org.apache.turbine.services.intake.xmlmodel.XmlField;
39  import org.apache.turbine.services.localization.Localization;
40  import org.apache.turbine.services.localization.LocalizationService;
41  import org.apache.turbine.util.SystemError;
42  import org.apache.turbine.util.parser.ParameterParser;
43  import org.apache.turbine.util.parser.ValueParser;
44  
45  /***
46   * Base class for Intake generated input processing classes.
47   *
48   * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
49   * @author <a href="mailto:dlr@finemaltcoding.com>Daniel Rall</a>
50   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
51   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
52   * @version $Id: Field.java 264148 2005-08-29 14:21:04Z henning $
53   */
54  public abstract class Field
55  {
56      /*** Empty Value */
57      private static final String EMPTY = "";
58  
59      /*** CGI Key for "value if absent" */
60      private static final String VALUE_IF_ABSENT_KEY = "_vifa_";
61  
62      /*** Default Package */
63      public static final String defaultFieldPackage = "org.apache.turbine.services.intake.validator.";
64  
65      // the following are set from the xml file and are permanent (final)
66  
67      /*** Name of the field. */
68      protected final String name;
69  
70      /*** Key used to identify the field in the parser */
71      protected final String key;
72  
73      /*** Display name of the field to be used on data entry forms... */
74      protected String displayName;
75  
76      /*** Class name of the object to which the field is mapped */
77      protected final String mapToObject;
78  
79      /*** Used to validate the contents of the field */
80      protected Validator validator;
81  
82      /*** Getter method in the mapped object used to populate the field */
83      protected final Method getter;
84  
85      /*** Setter method in the mapped object used to store the value of field */
86      protected final Method setter;
87  
88      /*** Error message set on the field if required and not set by parser */
89      protected String ifRequiredMessage;
90  
91      /*** Does this field accept multiple values? */
92      protected final boolean isMultiValued;
93  
94      /*** Group to which the field belongs */
95      protected final Group group;
96  
97      /*** Is this field always required?  This is only set through the XML file */
98      protected boolean alwaysRequired;
99  
100     /***
101      * Value of the field if an error occurs while getting
102      * the value from the mapped object
103      */
104     protected Object onError;
105 
106     /*** Default value of the field */
107     protected Object defaultValue;
108 
109     /*** Value of the field to use if the mapped parameter is empty or non-existant */
110     protected Object emptyValue;
111 
112     /*** Display size of the field */
113     private String displaySize;
114 
115     /*** Max size of the field */
116     private String maxSize;
117 
118     // these are reset when the Field is returned to the pool
119 
120     /*** Has the field has been set from the parser? */
121     protected boolean setFlag;
122 
123     /*** Has the field passed the validation test? */
124     protected boolean validFlag;
125 
126     /*** Does the field require a value? */
127     protected boolean required;
128 
129     /*** Has the field has been set from the parser? */
130     protected boolean initialized;
131 
132     /*** Error message, is any, resulting from validation */
133     protected String message;
134 
135     /*** Mapped object used to set the initial field value */
136     protected Retrievable retrievable;
137 
138     private Locale locale;
139     /*** String value of the field */
140     private String stringValue;
141     /*** String valuess of the field if isMultiValued=true */
142     private String[] stringValues;
143     /*** Stores the value of the field from the Retrievable object */
144     private Object validValue;
145     /*** Stores the value of the field from the parser */
146     private Object testValue;
147     /*** Used to pass testValue to the setter mathod through reflection */
148     private Object[] valArray;
149     /*** The object containing the field data. */
150     protected ValueParser parser;
151 
152     /*** Logging */
153     protected Log log = LogFactory.getLog(this.getClass());
154     protected boolean isDebugEnabled = false;
155 
156     /***
157      * Constructs a field based on data in the xml specification
158      * and assigns it to a Group.
159      *
160      * @param field a <code>XmlField</code> value
161      * @param group a <code>Group</code> value
162      * @throws IntakeException indicates the validator was not valid or
163      * could not be loaded.
164      * @throws SystemError only occurs is the Validation object does not
165      * extend InitableByConstraintMap
166      */
167     public Field(XmlField field, Group group) throws IntakeException
168     {
169         isDebugEnabled = log.isDebugEnabled();
170 
171         this.group = group;
172         key = field.getKey();
173         name = field.getName();
174         displayName = field.getDisplayName();
175         displaySize = field.getDisplaySize();
176         isMultiValued = field.isMultiValued();
177 
178         try
179         {
180             setDefaultValue(field.getDefaultValue());
181         }
182         catch (RuntimeException e)
183         {
184             log.error("Could not set default value of " +
185                     this.getDisplayName() + " to "
186                     + field.getDefaultValue(), e);
187         }
188 
189         try
190         {
191             setEmptyValue(field.getEmptyValue());
192         }
193         catch (RuntimeException e)
194         {
195             log.error("Could not set empty value of " +
196                     this.getDisplayName() + " to "
197                     + field.getEmptyValue(), e);
198         }
199 
200         String validatorClassName = field.getValidator();
201         if (validatorClassName == null && field.getRules().size() > 0)
202         {
203             validatorClassName = getDefaultValidator();
204         }
205         else if (validatorClassName != null
206                 && validatorClassName.indexOf('.') == -1)
207         {
208             validatorClassName = defaultFieldPackage + validatorClassName;
209         }
210 
211         if (validatorClassName != null)
212         {
213             try
214             {
215                 validator = (Validator)
216                         Class.forName(validatorClassName).newInstance();
217             }
218             catch (InstantiationException e)
219             {
220                 throw new IntakeException(
221                         "Could not create new instance of Validator("
222                         + validatorClassName + ")", e);
223             }
224             catch (IllegalAccessException e)
225             {
226                 throw new IntakeException(
227                         "Could not create new instance of Validator("
228                         + validatorClassName + ")", e);
229             }
230             catch (ClassNotFoundException e)
231             {
232                 throw new IntakeException(
233                         "Could not load Validator class("
234                         + validatorClassName + ")", e);
235             }
236             // this should always be true for now
237             // (until bean property initialization is implemented)
238             if (validator instanceof InitableByConstraintMap)
239             {
240                 ((InitableByConstraintMap) validator).init(field.getRuleMap());
241             }
242             else
243             {
244                 throw new SystemError(
245                         "All Validation objects must be subclasses of "
246                         + "InitableByConstraintMap");
247             }
248         }
249 
250         // field may have been declared as always required in the xml spec
251         Rule reqRule = (Rule) field.getRuleMap().get("required");
252         if (reqRule != null)
253         {
254             alwaysRequired = Boolean.valueOf(reqRule.getValue()).booleanValue();
255             ifRequiredMessage = reqRule.getMessage();
256         }
257 
258         Rule maxLengthRule = (Rule) field.getRuleMap().get("maxLength");
259         if (maxLengthRule != null)
260         {
261             maxSize = maxLengthRule.getValue();
262         }
263 
264         // map the getter and setter methods
265         mapToObject = field.getMapToObject();
266         String propName = field.getMapToProperty();
267         Method tmpGetter = null;
268         Method tmpSetter = null;
269         if (StringUtils.isNotEmpty(mapToObject)
270                 && StringUtils.isNotEmpty(propName))
271         {
272             try
273             {
274                 tmpGetter = TurbineIntake.getFieldGetter(mapToObject, propName);
275             }
276             catch (Exception e)
277             {
278                 log.error("IntakeService could not map the getter for field "
279                         + this.getDisplayName() + " in group "
280                         + this.group.getIntakeGroupName()
281                         + " to the property " + propName + " in object "
282                         + mapToObject, e);
283             }
284             try
285             {
286                 tmpSetter = TurbineIntake.getFieldSetter(mapToObject, propName);
287             }
288             catch (Exception e)
289             {
290                 log.error("IntakeService could not map the setter for field "
291                         + this.getDisplayName() + " in group "
292                         + this.group.getIntakeGroupName()
293                         + " to the property " + propName + " in object "
294                         + mapToObject, e);
295             }
296         }
297         getter = tmpGetter;
298         setter = tmpSetter;
299 
300         valArray = new Object[1];
301     }
302 
303     /***
304      * Method called when this field (the group it belongs to) is
305      * pulled from the pool.  The request data is searched to determine
306      * if a value has been supplied for this field.  If so, the value
307      * is validated.
308      *
309      * @param pp a <code>ValueParser</code> value
310      * @return a <code>Field</code> value
311      * @throws IntakeException this exception is only thrown by subclasses
312      * overriding this implementation.
313      */
314     public Field init(ValueParser pp)
315             throws IntakeException
316     {
317         this.parser = pp;
318         validFlag = true;
319 
320         // If the parser is for a HTTP request, use the request it's
321         // associated with to grok the locale.
322         if (TurbineServices.getInstance()
323                 .isRegistered(LocalizationService.SERVICE_NAME))
324         {
325             if (pp instanceof ParameterParser)
326             {
327                 this.locale = Localization.getLocale
328                         (((ParameterParser) pp).getRequest());
329             }
330             else
331             {
332                 this.locale = Localization.getLocale((String) null);
333             }
334         }
335 
336         if (pp.containsKey(getKey()))
337         {
338             if (isDebugEnabled)
339             {
340                 log.debug(name + ": Found our Key in the request, setting Value");
341             }
342             if (StringUtils.isNotEmpty(pp.getString(getKey())))
343             {
344                 setFlag = true;
345             }
346             validate();
347         }
348         else if (pp.containsKey(getValueIfAbsent()) &&
349                 pp.getString(getValueIfAbsent()) != null)
350         {
351             pp.add(getKey(), pp.getString(getValueIfAbsent()));
352             setFlag = true;
353             validate();
354         }
355 
356         initialized = true;
357         return this;
358     }
359 
360     /***
361      * Method called when this field or the group it belongs to is
362      * pulled from the pool.  The retrievable object can provide
363      * a default value for the field, or using setProperty the field's
364      * value can be transferred to the retrievable.
365      *
366      * @param obj a <code>Retrievable</code> value
367      * @return a <code>Field</code> value
368      */
369     public Field init(Retrievable obj)
370     {
371         if (!initialized)
372         {
373             validFlag = true;
374         }
375         retrievable = obj;
376         return this;
377     }
378 
379     /***
380      * Returns the <code>Locale</code> used when localizing data for
381      * this field, or <code>null</code> if unknown.
382      *
383      * @return Where to localize for.
384      */
385     protected Locale getLocale()
386     {
387         return locale;
388     }
389 
390     /***
391      * Produces the fully qualified class name of the default validator.
392      *
393      * @return class name of the default validator
394      */
395     protected String getDefaultValidator()
396     {
397         return DefaultValidator.class.getName();
398     }
399 
400     /***
401      * Gets the Validator object for this field.
402      * @return a <code>Validator</code> object
403      */
404     public Validator getValidator()
405     {
406         return validator;
407     }
408 
409     /***
410      * Flag to determine whether the field has been declared as required.
411      *
412      * @return value of required.
413      */
414     public boolean isRequired()
415     {
416         return alwaysRequired || required;
417     }
418 
419     /***
420      * Set whether this field is required to have a value.  If the field
421      * is already required due to a setting in the XML file, this method
422      * can not set it to false.
423      *
424      * @param v  Value to assign to required.
425      */
426     public void setRequired(boolean v)
427     {
428         setRequired(v, ifRequiredMessage);
429     }
430 
431     /***
432      * Set the value of required.
433      *
434      * @param v a <code>boolean</code> value
435      * @param message override the value from intake.xml
436      */
437     public void setRequired(boolean v, String message)
438     {
439         this.required = v;
440         if (v && (!setFlag || null == getTestValue()))
441         {
442             validFlag = false;
443             this.message = message;
444         }
445     }
446 
447     /***
448      * Removes references to this group and its fields from the
449      * query parameters
450      */
451     public void removeFromRequest()
452     {
453         parser.remove(getKey());
454         parser.remove(getKey()+ VALUE_IF_ABSENT_KEY);
455     }
456 
457     /***
458      * Disposes the object after use. The method is called
459      * when the Group is returned to its pool.
460      * if overridden, super.dispose() should be called.
461      */
462     public void dispose()
463     {
464         parser = null;
465         initialized = false;
466         setFlag = false;
467         validFlag = false;
468         required = false;
469         message = null;
470         retrievable = null;
471 
472         locale = null;
473         stringValue = null;
474         stringValues = null;
475         validValue = null;
476         testValue = null;
477         valArray[0] = null;
478     }
479 
480     /***
481      * Get the key used to identify the field.
482      *
483      * @return the query data key.
484      */
485     public String getKey()
486     {
487         return (group == null) ? key : group.getObjectKey() + key;
488     }
489 
490     /***
491      * Use in a hidden field assign a default value in the event the
492      * field is absent from the query parameters.  Used to track checkboxes,
493      * since they only show up if checked.
494      */
495     public String getValueIfAbsent()
496     {
497         return getKey() + VALUE_IF_ABSENT_KEY;
498     }
499 
500     /***
501      * Flag set to true, if the test value met the constraints.
502      * Is also true, in the case the test value was not set,
503      * unless this field has been marked as required.
504      *
505      * @return a <code>boolean</code> value
506      */
507     public boolean isValid()
508     {
509         return validFlag;
510     }
511 
512     /***
513      * Flag set to true, if the test value has been set to
514      * anything other than an empty value.
515      *
516      * @return a <code>boolean</code> value
517      */
518     public boolean isSet()
519     {
520         return setFlag;
521     }
522 
523     /***
524      * Get the display name of the field. Useful for building
525      * data entry forms. Returns name of field if no display
526      * name has been assigned to the field by xml input file.
527      *
528      * @return a <code>String</code> value
529      */
530     public String getDisplayName()
531     {
532         return (displayName == null) ? name : displayName;
533     }
534 
535     /***
536      * Set the display name of the field. Display names are
537      * used in building data entry forms and serve as a
538      * user friendly description of the data contained in
539      * the field.
540      */
541     public void setDisplayName(String newDisplayName)
542     {
543         displayName = newDisplayName;
544     }
545 
546     /***
547      * Get any error message resulting from invalid input.
548      *
549      * @return a <code>String</code> value
550      */
551     public String getMessage()
552     {
553         return (message == null) ? EMPTY : message;
554     }
555 
556     /***
557      * Sets an error message.  The field is also marked as invalid.
558      */
559     public void setMessage(String message)
560     {
561         this.message = message;
562         validFlag = false;
563     }
564 
565     /***
566      * @deprecated Call validate() instead (with no parameters).
567      */
568     protected boolean validate(ValueParser pp)
569     {
570         return validate();
571     }
572 
573     /***
574      * Compares request data with constraints and sets the valid flag.
575      */
576     protected boolean validate()
577     {
578         log.debug(name + ": validate()");
579 
580         if (isMultiValued)
581         {
582             stringValues = parser.getStrings(getKey());
583 
584             if (isDebugEnabled)
585             {
586                 log.debug(name + ": Multi-Valued");
587                 for (int i = 0; i < stringValues.length; i++)
588                 {
589                     log.debug(name + ": " + i + ". Wert: " + stringValues[i]);
590                 }
591             }
592 
593 
594             if (validator != null)
595             {
596                 // set the test value as a String[] which might be replaced by
597                 // the correct type if the input is valid.
598                 setTestValue(parser.getStrings(getKey()));
599                 for (int i = 0; i < stringValues.length; i++)
600                 {
601                     try
602                     {
603                         validator.assertValidity(stringValues[i]);
604                     }
605                     catch (ValidationException ve)
606                     {
607                         setMessage(ve.getMessage());
608                     }
609                 }
610             }
611 
612             if (validFlag)
613             {
614                 doSetValue();
615             }
616         }
617         else
618         {
619             stringValue = parser.getString(getKey());
620 
621             if (isDebugEnabled)
622             {
623                 log.debug(name + ": Single Valued, Value is " + stringValue);
624             }
625 
626             if (validator != null)
627             {
628                 // set the test value as a String which might be replaced by
629                 // the correct type if the input is valid.
630                 setTestValue(parser.getString(getKey()));
631 
632                 try
633                 {
634                     validator.assertValidity(stringValue);
635                     log.debug(name + ": Value is ok");
636                     doSetValue();
637                 }
638                 catch (ValidationException ve)
639                 {
640                     log.debug(name + ": Value failed validation!");
641                     setMessage(ve.getMessage());
642                 }
643             }
644             else
645             {
646                 doSetValue();
647             }
648         }
649 
650         return validFlag;
651     }
652 
653     /***
654      * Set the default Value. This value is used if
655      * Intake should map this field to a new object.
656      *
657      * @param prop The value to use if the field is mapped to a new object.
658      */
659     public abstract void setDefaultValue(String prop);
660 
661     /***
662      * Set the empty Value. This value is used if Intake
663      * maps a field to a parameter returned by the user and
664      * the corresponding field is either empty (empty string)
665      * or non-existant.
666      *
667      * @param prop The value to use if the field is empty.
668      */
669     public abstract void setEmptyValue(String prop);
670 
671     /***
672      * @deprecated Use doSetValue() instead (with no parameters).
673      */
674     protected void doSetValue(ValueParser pp)
675     {
676         doSetValue();
677     }
678 
679     /***
680      * Sets the value of the field from data in the parser.
681      */
682     protected abstract void doSetValue();
683 
684     /***
685      * Set the value used as a default, in the event the field
686      * has not been set yet.
687      *
688      * @param obj an <code>Object</code> value
689      */
690     void setInitialValue(Object obj)
691     {
692         validValue = obj;
693     }
694 
695     /***
696      * Get the value used as a default.  If the initial value has
697      * not been set and a <code>Retrievable</code> object has
698      * been associated with this field, the objects property will
699      * be used as the initial value.
700      *
701      * @return an <code>Object</code> value
702      * @exception IntakeException indicates the value could not be
703      * returned from the mapped object
704      */
705     public Object getInitialValue() throws IntakeException
706     {
707         if (validValue == null)
708         {
709             if (retrievable != null)
710             {
711                 getProperty(retrievable);
712             }
713             else
714             {
715                 getDefault();
716             }
717         }
718         return validValue;
719     }
720 
721     /***
722      * Set the value input by a user that will be validated.
723      *
724      * @param obj an <code>Object</code> value
725      */
726     void setTestValue(Object obj)
727     {
728         testValue = obj;
729     }
730 
731     /***
732      * Get the value input by a user that will be validated.
733      *
734      * @return an <code>Object</code> value
735      */
736     public Object getTestValue()
737     {
738         return testValue;
739     }
740 
741     /***
742      * Get the value of the field.  if a test value has been set, it
743      * will be returned as is, unless it is so badly formed that the
744      * validation could not parse it.  In most cases the test value
745      * is returned even though invalid, so that it can be returned to
746      * the user to make modifications.  If the test value is not set
747      * the initial value is returned.
748      *
749      * @return an <code>Object</code> value
750      */
751     public Object getValue()
752     {
753         Object val = null;
754         try
755         {
756             val = getInitialValue();
757         }
758         catch (IntakeException e)
759         {
760             log.error("Could not get intial value of " + this.getDisplayName() +
761                     " in group " + this.group.getIntakeGroupName(), e);
762         }
763 
764         if (getTestValue() != null)
765         {
766             val = getTestValue();
767         }
768 
769         if (val == null)
770         {
771             val = onError;
772         }
773         return val;
774     }
775 
776     /***
777      * Calls toString() on the object returned by getValue(),
778      * unless null; and then it returns "", the empty String.
779      *
780      * @return a <code>String</code> value
781      */
782     public String toString()
783     {
784         String res = EMPTY;
785 
786         if (stringValue != null)
787         {
788             res = stringValue;
789         }
790         else if (getValue() != null)
791         {
792             res = getValue().toString();
793         }
794         return res;
795     }
796 
797     /***
798      * Calls toString() on the object returned by getValue(),
799      * unless null; and then it returns "", the empty String.
800      * Escapes &quot; characters to be able to display these
801      * in HTML form fields.
802      *
803      * @return a <code>String</code> value
804      */
805     public String getHTMLString()
806     {
807         String res = toString();
808         return StringUtils.replace(res, "\"", "&quot;");
809     }
810 
811     /***
812      * Loads the valid value from a bean
813      *
814      * @throws IntakeException indicates a problem during the execution of the
815      * object's getter method
816      */
817     public void getProperty(Object obj)
818             throws IntakeException
819     {
820         try
821         {
822             validValue = getter.invoke(obj, null);
823         }
824         catch (IllegalAccessException e)
825         {
826             throwSetGetException("getter", obj, this.getDisplayName(),
827                     this.group.getIntakeGroupName(), e);
828         }
829         catch (IllegalArgumentException e)
830         {
831             throwSetGetException("getter", obj, this.getDisplayName(),
832                     this.group.getIntakeGroupName(), e);
833         }
834         catch (InvocationTargetException e)
835         {
836             throwSetGetException("getter", obj, this.getDisplayName(),
837                     this.group.getIntakeGroupName(), e);
838         }
839     }
840 
841     /***
842      * Loads the default value from the object
843      */
844 
845     public void getDefault()
846     {
847         validValue = getDefaultValue();
848     }
849 
850     /***
851      * Calls a setter method on obj, if this field has been set.
852      *
853      * @throws IntakeException indicates a problem during the execution of the
854      * object's setter method
855      */
856     public void setProperty(Object obj) throws IntakeException
857     {
858         if (isDebugEnabled)
859         {
860             log.debug(name + ".setProperty(" + obj.getClass().getName() + ")");
861         }
862 
863         if (!isValid())
864         {
865             throw new IntakeException(
866                     "Attempted to assign an invalid input.");
867         }
868         if (isSet())
869         {
870             valArray[0] = getTestValue();
871             if (isDebugEnabled)
872             {
873                 log.debug(name + ": Property is set, value is " + valArray[0]);
874             }
875         }
876         else
877         {
878             valArray[0] = getSafeEmptyValue();
879             if (isDebugEnabled)
880             {
881                 log.debug(name + ": Property is not set, using emptyValue " + valArray[0]);
882             }
883         }
884 
885         try
886         {
887             setter.invoke(obj, valArray);
888         }
889         catch (IllegalAccessException e)
890         {
891             throwSetGetException("setter", obj, this.getDisplayName(),
892                     this.group.getIntakeGroupName(), e);
893         }
894         catch (IllegalArgumentException e)
895         {
896             throwSetGetException("setter", obj, this.getDisplayName(),
897                     this.group.getIntakeGroupName(), e);
898         }
899         catch (InvocationTargetException e)
900         {
901             throwSetGetException("setter", obj, this.getDisplayName(),
902                     this.group.getIntakeGroupName(), e);
903         }
904     }
905 
906     /***
907      * Used to throw an IntakeException when an error occurs execuing the
908      * get/set method of the mapped persistent object.
909      *
910      * @param type Type of method. (setter/getter)
911      * @param fieldName Name of the field
912      * @param groupName Name of the group
913      * @param e Exception that was thrown
914      * @throws IntakeException New exception with formatted message
915      */
916     private void throwSetGetException(String type, Object obj,
917                                       String fieldName, String groupName,
918                                       Exception e)
919             throws IntakeException
920     {
921         throw new IntakeException("Could not execute " + type
922                 + " method for " + fieldName + " in group " + groupName
923                 + " on " + obj.getClass().getName(), e);
924 
925     }
926 
927     /***
928      * Get the default Value
929      *
930      * @return the default value
931      */
932     public Object getDefaultValue()
933     {
934         return defaultValue;
935     }
936 
937     /***
938      * Get the Value to use if the field is empty
939      *
940      * @return the value to use if the field is empty.
941      */
942     public Object getEmptyValue()
943     {
944         return emptyValue;
945     }
946 
947     /***
948      * Provides access to emptyValue such that the value returned will be
949      * acceptable as an argument parameter to Method.invoke.  Subclasses
950      * that deal with primitive types should ensure that they return an
951      * appropriate value wrapped in the object wrapper class for the
952      * primitive type.
953      *
954      * @return the value to use when the field is empty or an Object that
955      * wraps the empty value for primitive types.
956      */
957     protected Object getSafeEmptyValue()
958     {
959         return getEmptyValue();
960     }
961 
962     /***
963      * Gets the name of the field.
964      *
965      * @return name of the field as specified in the XML file.
966      */
967     public String getName()
968     {
969         return name;
970     }
971 
972     /***
973      * Gets the diplay size of the field.  This is useful when
974      * building the HTML input tag.  If no displaySize was set,
975      * an empty string is returned.
976      */
977     public String getDisplaySize()
978     {
979         return (StringUtils.isEmpty(displaySize) ? "" : displaySize);
980     }
981 
982     /***
983      * Gets the maximum size of the field.  This is useful when
984      * building the HTML input tag.  The maxSize is set with the maxLength
985      * rule.  If this rul was not set, an enmpty string is returned.
986      */
987     public String getMaxSize()
988     {
989         return (StringUtils.isEmpty(maxSize) ? "" : maxSize);
990     }
991 
992     /***
993      * Gets the String representation of the Value. This is basically a wrapper
994      * method for the toString method which doesn't seem to show anything on
995      * screen if accessed from Template. Name is also more in line with getValue
996      * method which returns the actual Object.
997      * This is useful for displaying correctly formatted data such as dates,
998      * such as 18/11/1968 instead of the toString dump of a Date Object.
999      *
1000      * @return the String Value
1001      */
1002     public String getStringValue()
1003     {
1004         return this.toString();
1005     }
1006 
1007 }