1 package org.apache.fulcrum.intake.model;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
43
44
45
46
47
48
49
50
51
52 public abstract class Field<T> implements Serializable, LogEnabled
53 {
54
55 private static final long serialVersionUID = 6897267716698096895L;
56
57
58 private static final String EMPTY = "";
59
60
61 private static final String VALUE_IF_ABSENT_KEY = "_vifa_";
62
63
64 public static final String defaultValidatorPackage = "org.apache.fulcrum.intake.validator.";
65
66
67 public static final String defaultFieldPackage = "org.apache.fulcrum.intake.model.";
68
69
70
71
72 private final String name;
73
74
75 private final String key;
76
77
78 private final String displaySize;
79
80
81 protected String mapToObject;
82
83
84 private String mapToProperty;
85
86
87 private String validatorClassName;
88
89
90 private transient Validator<T> validator;
91
92
93 private Method getter;
94
95
96 private Method setter;
97
98
99 private String ifRequiredMessage;
100
101
102 private final boolean isMultiValued;
103
104
105 private final Group group;
106
107
108 private boolean alwaysRequired;
109
110
111 protected T defaultValue;
112
113
114 protected T emptyValue;
115
116
117 private String displayName;
118
119
120 private String maxSize;
121
122
123
124
125 private boolean setFlag;
126
127
128 private boolean validFlag;
129
130
131 private boolean validated;
132
133
134 private boolean required;
135
136
137 private boolean initialized;
138
139
140 private String message;
141
142
143 private Retrievable retrievable;
144
145
146 private Locale locale;
147
148
149 private String stringValue;
150
151
152 private String[] stringValues;
153
154
155 private T validValue;
156
157
158 private Object testValue;
159
160
161 private final Object[] valArray;
162
163
164 protected ValueParser parser;
165
166
167 private Map<String, Rule> ruleMap;
168
169
170 protected transient Logger log;
171
172
173
174
175
176
177
178
179
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
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
239 mapToObject = field.getMapToObject();
240 mapToProperty = field.getMapToProperty();
241 valArray = new Object[1];
242 }
243
244
245
246
247 @Override
248 public void enableLogging(Logger logger)
249 {
250 this.log = logger.getChildLogger(getClass().getSimpleName());
251 }
252
253
254
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
294
295
296
297
298
299
300
301
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
323 }
324 else if (pp.containsKey(getValueIfAbsent()) &&
325 pp.getString(getValueIfAbsent()) != null)
326 {
327 pp.add(getKey(), pp.getString(getValueIfAbsent()));
328 setFlag = true;
329
330 }
331
332 initialized = true;
333 return this;
334 }
335
336
337
338
339
340
341
342
343
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
358
359
360
361
362 public Group getGroup()
363 {
364 return group;
365 }
366
367
368
369
370
371
372
373 public Locale getLocale()
374 {
375 return locale;
376 }
377
378
379
380
381
382
383 protected String getDefaultValidator()
384 {
385 return DefaultValidator.class.getName();
386 }
387
388
389
390
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
410
411
412
413 public String getMapToObject()
414 {
415 return mapToObject;
416 }
417
418
419
420
421
422
423 public boolean isMultiValued()
424 {
425 return isMultiValued;
426 }
427
428
429
430
431
432
433 public boolean isRequired()
434 {
435 return alwaysRequired || required;
436 }
437
438
439
440
441
442
443
444
445 public void setRequired(boolean v)
446 {
447 setRequired(v, ifRequiredMessage);
448 }
449
450
451
452
453
454
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
468
469
470 public void removeFromRequest()
471 {
472 parser.remove(getKey());
473 parser.remove(getKey()+ VALUE_IF_ABSENT_KEY);
474 }
475
476
477
478
479
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
502
503
504
505 public String getKey()
506 {
507 return (group == null) ? key : group.getObjectKey() + key;
508 }
509
510
511
512
513
514
515
516
517 public String getValueIfAbsent()
518 {
519 return getKey() + VALUE_IF_ABSENT_KEY;
520 }
521
522
523
524
525
526
527
528
529 public boolean isValid()
530 {
531 return validFlag;
532 }
533
534
535
536
537
538
539 public boolean isValidated()
540 {
541 return validated;
542 }
543
544
545
546
547
548
549
550
551
552 public boolean isSet()
553 {
554 return setFlag;
555 }
556
557
558
559
560
561
562
563
564 public String getDisplayName()
565 {
566 return (displayName == null) ? name : displayName;
567 }
568
569
570
571
572
573
574
575
576
577 public void setDisplayName(String newDisplayName)
578 {
579 displayName = newDisplayName;
580 }
581
582
583
584
585
586
587 public String getMessage()
588 {
589 return (message == null) ? EMPTY : message;
590 }
591
592
593
594
595
596
597 public void setMessage(String message)
598 {
599 this.message = message;
600 validFlag = false;
601 }
602
603
604
605
606
607
608 protected void setSet(boolean setFlag)
609 {
610 this.setFlag = setFlag;
611 }
612
613
614
615
616
617
618 protected void setValid(boolean validFlag)
619 {
620 this.validFlag = validFlag;
621 }
622
623
624
625
626
627
628 protected void setValidated(boolean validated)
629 {
630 this.validated = validated;
631 }
632
633
634
635
636
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
662
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
692
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
720
721
722
723
724 public abstract void setDefaultValue(String prop);
725
726
727
728
729
730
731
732
733
734 public abstract void setEmptyValue(String prop);
735
736
737
738
739 protected abstract void doSetValue();
740
741
742
743
744
745
746
747 void setInitialValue(T obj)
748 {
749 validValue = obj;
750 }
751
752
753
754
755
756
757
758
759
760
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
781
782
783
784 void setTestValue(Object obj)
785 {
786 testValue = obj;
787 }
788
789
790
791
792
793
794
795 @SuppressWarnings("unchecked")
796 public <TT> TT getTestValue()
797 {
798 return (TT)testValue;
799 }
800
801
802
803
804
805
806
807
808
809
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
834
835
836
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
856
857
858
859
860
861
862 public String getHTMLString()
863 {
864 String res = toString();
865 return StringUtils.replace(res, "\"", """);
866 }
867
868
869
870
871
872
873
874
875
876 public void getProperty(Object obj)
877 throws IntakeException
878 {
879 try
880 {
881 @SuppressWarnings("unchecked")
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
904
905 public void getDefault()
906 {
907 validValue = getDefaultValue();
908 }
909
910
911
912
913
914
915
916
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
951
952
953
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
988
989
990
991
992
993
994
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
1009
1010
1011
1012 public T getDefaultValue()
1013 {
1014 return defaultValue;
1015 }
1016
1017
1018
1019
1020
1021
1022 public T getEmptyValue()
1023 {
1024 return emptyValue;
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037 protected Object getSafeEmptyValue()
1038 {
1039 return getEmptyValue();
1040 }
1041
1042
1043
1044
1045
1046
1047 public String getName()
1048 {
1049 return name;
1050 }
1051
1052
1053
1054
1055
1056
1057
1058
1059 public String getDisplaySize()
1060 {
1061 return (StringUtils.isEmpty(displaySize) ? "" : displaySize);
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071 public String getMaxSize()
1072 {
1073 return (StringUtils.isEmpty(maxSize) ? "" : maxSize);
1074 }
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 public String getStringValue()
1087 {
1088 return this.toString();
1089 }
1090
1091
1092
1093
1094
1095
1096
1097
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
1135
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 }