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