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ü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 " 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, "\"", """);
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 }