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