1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.validator;
18
19 import java.io.Serializable;
20 import java.lang.reflect.InvocationTargetException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.StringTokenizer;
29
30 import org.apache.commons.beanutils.PropertyUtils;
31 import org.apache.commons.collections.FastHashMap;
32 import org.apache.commons.validator.util.ValidatorUtils;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class Field implements Cloneable, Serializable {
48
49 private static final long serialVersionUID = -8502647722530192185L;
50
51
52
53
54
55 private static final String DEFAULT_ARG =
56 "org.apache.commons.validator.Field.DEFAULT";
57
58
59
60
61 public static final String TOKEN_INDEXED = "[]";
62
63
64
65
66 protected static final String TOKEN_START = "${";
67
68
69
70
71 protected static final String TOKEN_END = "}";
72
73
74
75
76 protected static final String TOKEN_VAR = "var:";
77
78
79
80
81 protected String property;
82
83
84
85
86 protected String indexedProperty;
87
88
89
90
91 protected String indexedListProperty;
92
93
94
95
96 protected String key;
97
98
99
100
101 protected String depends;
102
103
104
105
106 protected volatile int page;
107
108
109
110
111
112
113
114 protected volatile boolean clientValidation = true;
115
116
117
118
119 protected volatile int fieldOrder;
120
121
122
123
124
125
126
127 private final List<String> dependencyList = Collections.synchronizedList(new ArrayList<>());
128
129
130
131
132 @Deprecated
133 protected FastHashMap hVars = new FastHashMap();
134
135
136
137
138 @Deprecated
139 protected FastHashMap hMsgs = new FastHashMap();
140
141
142
143
144
145
146
147
148 @SuppressWarnings("unchecked")
149 protected Map<String, Arg>[] args = new Map[0];
150
151
152
153
154 public Field() {
155
156 }
157
158
159
160
161
162
163
164 public void addArg(final Arg arg) {
165
166 if (arg == null || arg.getKey() == null || arg.getKey().isEmpty()) {
167 return;
168 }
169
170 determineArgPosition(arg);
171 ensureArgsCapacity(arg);
172
173 Map<String, Arg> argMap = args[arg.getPosition()];
174 if (argMap == null) {
175 argMap = new HashMap<>();
176 args[arg.getPosition()] = argMap;
177 }
178
179 final String name = arg.getName();
180 argMap.put(name != null ? name : DEFAULT_ARG, arg);
181 }
182
183
184
185
186
187
188 public void addMsg(final Msg msg) {
189 getMsgMap().put(msg.getName(), msg);
190 }
191
192
193
194
195
196
197
198
199
200 public void addVar(final String name, final String value, final String jsType) {
201 this.addVar(new Var(name, value, jsType));
202 }
203
204
205
206
207
208
209 public void addVar(final Var v) {
210 getVarMap().put(v.getName(), v);
211 }
212
213
214
215
216
217
218 @Override
219 public Object clone() {
220 Field field = null;
221 try {
222 field = (Field) super.clone();
223 } catch (final CloneNotSupportedException e) {
224 throw new UnsupportedOperationException(e.toString(), e);
225 }
226
227 @SuppressWarnings("unchecked")
228 final Map<String, Arg>[] tempMap = new Map[args.length];
229 field.args = tempMap;
230 for (int i = 0; i < args.length; i++) {
231 if (args[i] == null) {
232 continue;
233 }
234
235 final Map<String, Arg> argMap = new HashMap<>(args[i]);
236 argMap.forEach((validatorName, arg) -> argMap.put(validatorName, (Arg) arg.clone()));
237 field.args[i] = argMap;
238 }
239
240 field.hVars = ValidatorUtils.copyFastHashMap(hVars);
241 field.hMsgs = ValidatorUtils.copyFastHashMap(hMsgs);
242
243 return field;
244 }
245
246
247
248
249 private void determineArgPosition(final Arg arg) {
250
251 final int position = arg.getPosition();
252
253
254 if (position >= 0) {
255 return;
256 }
257
258
259 if (args == null || args.length == 0) {
260 arg.setPosition(0);
261 return;
262 }
263
264
265
266 final String keyName = arg.getName() == null ? DEFAULT_ARG : arg.getName();
267 int lastPosition = -1;
268 int lastDefault = -1;
269 for (int i = 0; i < args.length; i++) {
270 if (args[i] != null && args[i].containsKey(keyName)) {
271 lastPosition = i;
272 }
273 if (args[i] != null && args[i].containsKey(DEFAULT_ARG)) {
274 lastDefault = i;
275 }
276 }
277
278 if (lastPosition < 0) {
279 lastPosition = lastDefault;
280 }
281
282
283 arg.setPosition(++lastPosition);
284
285 }
286
287
288
289
290
291
292
293
294 private void ensureArgsCapacity(final Arg arg) {
295 if (arg.getPosition() >= args.length) {
296 @SuppressWarnings("unchecked")
297 final
298 Map<String, Arg>[] newArgs = new Map[arg.getPosition() + 1];
299 System.arraycopy(args, 0, newArgs, 0, args.length);
300 args = newArgs;
301 }
302 }
303
304
305
306
307 public void generateKey() {
308 if (isIndexed()) {
309 key = indexedListProperty + TOKEN_INDEXED + "." + property;
310 } else {
311 key = property;
312 }
313 }
314
315
316
317
318
319
320
321
322 public Arg getArg(final int position) {
323 return this.getArg(DEFAULT_ARG, position);
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337 public Arg getArg(final String key, final int position) {
338 if (position >= args.length || args[position] == null) {
339 return null;
340 }
341
342 final Arg arg = args[position].get(key);
343
344
345
346 if (arg == null && key.equals(DEFAULT_ARG)) {
347 return null;
348 }
349
350 return arg == null ? this.getArg(position) : arg;
351 }
352
353
354
355
356
357
358
359
360
361 public Arg[] getArgs(final String key) {
362 final Arg[] argList = new Arg[args.length];
363
364 for (int i = 0; i < args.length; i++) {
365 argList[i] = this.getArg(key, i);
366 }
367
368 return argList;
369 }
370
371
372
373
374
375
376
377 public List<String> getDependencyList() {
378 return Collections.unmodifiableList(dependencyList);
379 }
380
381
382
383
384
385
386 public String getDepends() {
387 return depends;
388 }
389
390
391
392
393
394
395 public int getFieldOrder() {
396 return fieldOrder;
397 }
398
399
400
401
402
403
404
405
406
407
408 public String getIndexedListProperty() {
409 return indexedListProperty;
410 }
411
412
413
414
415
416
417
418
419 public String getIndexedProperty() {
420 return indexedProperty;
421 }
422
423
424
425
426
427
428
429
430 Object[] getIndexedProperty(final Object bean) throws ValidatorException {
431 Object indexProp = null;
432
433 try {
434 indexProp = PropertyUtils.getProperty(bean, getIndexedListProperty());
435
436 } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
437 throw new ValidatorException(e.getMessage());
438 }
439
440 if (indexProp instanceof Collection) {
441 return ((Collection<?>) indexProp).toArray();
442
443 }
444 if (indexProp.getClass().isArray()) {
445 return (Object[]) indexProp;
446
447 }
448 throw new ValidatorException(getKey() + " is not indexed");
449
450 }
451
452
453
454
455
456
457
458
459 private int getIndexedPropertySize(final Object bean) throws ValidatorException {
460 Object indexProp = null;
461
462 try {
463 indexProp = PropertyUtils.getProperty(bean, getIndexedListProperty());
464
465 } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
466 throw new ValidatorException(e.getMessage());
467 }
468
469 if (indexProp == null) {
470 return 0;
471 }
472 if (indexProp instanceof Collection) {
473 return ((Collection<?>) indexProp).size();
474 }
475 if (indexProp.getClass().isArray()) {
476 return ((Object[]) indexProp).length;
477 }
478 throw new ValidatorException(getKey() + " is not indexed");
479
480 }
481
482
483
484
485
486
487 public String getKey() {
488 if (key == null) {
489 generateKey();
490 }
491
492 return key;
493 }
494
495
496
497
498
499
500
501
502 public Msg getMessage(final String key) {
503 return getMsgMap().get(key);
504 }
505
506
507
508
509
510
511
512
513 public Map<String, Msg> getMessages() {
514 return Collections.unmodifiableMap(getMsgMap());
515 }
516
517
518
519
520
521
522
523 public String getMsg(final String key) {
524 final Msg msg = getMessage(key);
525 return msg == null ? null : msg.getKey();
526 }
527
528
529
530
531
532
533
534 @SuppressWarnings("unchecked")
535 protected Map<String, Msg> getMsgMap() {
536 return hMsgs;
537 }
538
539
540
541
542
543
544
545 public int getPage() {
546 return page;
547 }
548
549
550
551
552
553
554 public String getProperty() {
555 return property;
556 }
557
558
559
560
561
562
563
564 public Var getVar(final String mainKey) {
565 return getVarMap().get(mainKey);
566 }
567
568
569
570
571
572
573
574 @SuppressWarnings("unchecked")
575 protected Map<String, Var> getVarMap() {
576 return hVars;
577 }
578
579
580
581
582
583
584
585 public Map<String, Var> getVars() {
586 return Collections.unmodifiableMap(getVarMap());
587 }
588
589
590
591
592
593
594
595 public String getVarValue(final String mainKey) {
596 String value = null;
597
598 final Var v = getVarMap().get(mainKey);
599 if (v != null) {
600 value = v.getValue();
601 }
602
603 return value;
604 }
605
606
607
608
609
610
611
612
613 private void handleMissingAction(final String name) throws ValidatorException {
614 throw new ValidatorException("No ValidatorAction named " + name
615 + " found for field " + getProperty());
616 }
617
618
619
620
621
622
623
624
625
626 public boolean isClientValidation() {
627 return clientValidation;
628 }
629
630
631
632
633
634
635
636 public boolean isDependency(final String validatorName) {
637 return dependencyList.contains(validatorName);
638 }
639
640
641
642
643
644
645
646
647 public boolean isIndexed() {
648 return indexedListProperty != null && !indexedListProperty.isEmpty();
649 }
650
651
652
653
654
655 void process(final Map<String, String> globalConstants, final Map<String, String> constants) {
656 hMsgs.setFast(false);
657 hVars.setFast(true);
658
659 generateKey();
660
661
662 for (final Entry<String, String> entry : constants.entrySet()) {
663 final String key1 = entry.getKey();
664 final String key2 = TOKEN_START + key1 + TOKEN_END;
665 final String replaceValue = entry.getValue();
666
667 property = ValidatorUtils.replace(property, key2, replaceValue);
668
669 processVars(key2, replaceValue);
670
671 processMessageComponents(key2, replaceValue);
672 }
673
674
675 for (final Entry<String, String> entry : globalConstants.entrySet()) {
676 final String key1 = entry.getKey();
677 final String key2 = TOKEN_START + key1 + TOKEN_END;
678 final String replaceValue = entry.getValue();
679
680 property = ValidatorUtils.replace(property, key2, replaceValue);
681
682 processVars(key2, replaceValue);
683
684 processMessageComponents(key2, replaceValue);
685 }
686
687
688 for (final String key1 : getVarMap().keySet()) {
689 final String key2 = TOKEN_START + TOKEN_VAR + key1 + TOKEN_END;
690 final Var var = getVar(key1);
691 final String replaceValue = var.getValue();
692
693 processMessageComponents(key2, replaceValue);
694 }
695
696 hMsgs.setFast(true);
697 }
698
699
700
701
702
703 private void processArg(final String key, final String replaceValue) {
704 for (final Map<String, Arg> argMap : args) {
705 if (argMap == null) {
706 continue;
707 }
708 for (final Arg arg : argMap.values()) {
709 if (arg != null) {
710 arg.setKey(ValidatorUtils.replace(arg.getKey(), key, replaceValue));
711 }
712 }
713 }
714 }
715
716
717
718
719 private void processMessageComponents(final String key, final String replaceValue) {
720 final String varKey = TOKEN_START + TOKEN_VAR;
721
722 if (key != null && !key.startsWith(varKey)) {
723 for (final Msg msg : getMsgMap().values()) {
724 msg.setKey(ValidatorUtils.replace(msg.getKey(), key, replaceValue));
725 }
726 }
727
728 processArg(key, replaceValue);
729 }
730
731
732
733
734 private void processVars(final String key, final String replaceValue) {
735 for (final String varKey : getVarMap().keySet()) {
736 final Var var = getVar(varKey);
737 var.setValue(ValidatorUtils.replace(var.getValue(), key, replaceValue));
738 }
739
740 }
741
742
743
744
745
746
747
748
749
750
751
752
753 private boolean runDependentValidators(
754 final ValidatorAction va,
755 final ValidatorResults results,
756 final Map<String, ValidatorAction> actions,
757 final Map<String, Object> params,
758 final int pos)
759 throws ValidatorException {
760
761 final List<String> dependentValidators = va.getDependencyList();
762
763 if (dependentValidators.isEmpty()) {
764 return true;
765 }
766
767 for (final String depend : dependentValidators) {
768 final ValidatorAction action = actions.get(depend);
769 if (action == null) {
770 handleMissingAction(depend);
771 }
772
773 if (!validateForRule(action, results, actions, params, pos)) {
774 return false;
775 }
776 }
777
778 return true;
779 }
780
781
782
783
784
785
786
787
788
789 public void setClientValidation(final boolean clientValidation) {
790 this.clientValidation = clientValidation;
791 }
792
793
794
795
796
797
798 public void setDepends(final String depends) {
799 this.depends = depends;
800
801 dependencyList.clear();
802
803 final StringTokenizer st = new StringTokenizer(depends, ",");
804 while (st.hasMoreTokens()) {
805 final String depend = st.nextToken().trim();
806
807 if (depend != null && !depend.isEmpty()) {
808 dependencyList.add(depend);
809 }
810 }
811 }
812
813
814
815
816
817
818 public void setFieldOrder(final int fieldOrder) {
819 this.fieldOrder = fieldOrder;
820 }
821
822
823
824
825
826
827 public void setIndexedListProperty(final String indexedListProperty) {
828 this.indexedListProperty = indexedListProperty;
829 }
830
831
832
833
834
835
836 public void setIndexedProperty(final String indexedProperty) {
837 this.indexedProperty = indexedProperty;
838 }
839
840
841
842
843
844
845
846 public void setKey(final String key) {
847 this.key = key;
848 }
849
850
851
852
853
854
855
856 public void setPage(final int page) {
857 this.page = page;
858 }
859
860
861
862
863
864
865 public void setProperty(final String property) {
866 this.property = property;
867 }
868
869
870
871
872
873
874 @Override
875 public String toString() {
876 final StringBuilder results = new StringBuilder();
877
878 results.append("\t\tkey = " + key + "\n");
879 results.append("\t\tproperty = " + property + "\n");
880 results.append("\t\tindexedProperty = " + indexedProperty + "\n");
881 results.append("\t\tindexedListProperty = " + indexedListProperty + "\n");
882 results.append("\t\tdepends = " + depends + "\n");
883 results.append("\t\tpage = " + page + "\n");
884 results.append("\t\tfieldOrder = " + fieldOrder + "\n");
885
886 if (hVars != null) {
887 results.append("\t\tVars:\n");
888 for (final Object key1 : getVarMap().keySet()) {
889 results.append("\t\t\t");
890 results.append(key1);
891 results.append("=");
892 results.append(getVarMap().get(key1));
893 results.append("\n");
894 }
895 }
896
897 return results.toString();
898 }
899
900
901
902
903
904
905
906
907
908
909
910
911
912 public ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions)
913 throws ValidatorException {
914
915 if (getDepends() == null) {
916 return new ValidatorResults();
917 }
918
919 final ValidatorResults allResults = new ValidatorResults();
920
921 final Object bean = params.get(Validator.BEAN_PARAM);
922 final int numberOfFieldsToValidate = isIndexed() ? getIndexedPropertySize(bean) : 1;
923
924 for (int fieldNumber = 0; fieldNumber < numberOfFieldsToValidate; fieldNumber++) {
925
926 final ValidatorResults results = new ValidatorResults();
927 synchronized (dependencyList) {
928 for (final String depend : dependencyList) {
929
930 final ValidatorAction action = actions.get(depend);
931 if (action == null) {
932 handleMissingAction(depend);
933 }
934
935 final boolean good = validateForRule(action, results, actions, params, fieldNumber);
936
937 if (!good) {
938 allResults.merge(results);
939 return allResults;
940 }
941 }
942 }
943 allResults.merge(results);
944 }
945
946 return allResults;
947 }
948
949
950
951
952
953
954
955 private boolean validateForRule(
956 final ValidatorAction va,
957 final ValidatorResults results,
958 final Map<String, ValidatorAction> actions,
959 final Map<String, Object> params,
960 final int pos)
961 throws ValidatorException {
962
963 final ValidatorResult result = results.getValidatorResult(getKey());
964 if (result != null && result.containsAction(va.getName())) {
965 return result.isValid(va.getName());
966 }
967
968 if (!runDependentValidators(va, results, actions, params, pos)) {
969 return false;
970 }
971
972 return va.executeValidationMethod(this, params, results, pos);
973 }
974 }
975