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