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