1
1
package dev .ditsche .validator ;
2
2
3
3
import dev .ditsche .validator .error .ErrorBag ;
4
+ import dev .ditsche .validator .error .FieldNotAccessibleException ;
5
+ import dev .ditsche .validator .error .ValidationError ;
4
6
import dev .ditsche .validator .error .ValidationException ;
5
7
import dev .ditsche .validator .rule .*;
8
+ import dev .ditsche .validator .rule .builder .Builder ;
9
+ import dev .ditsche .validator .validation .Validatable ;
10
+ import dev .ditsche .validator .validation .ValidationResult ;
11
+ import org .apache .commons .lang3 .reflect .FieldUtils ;
6
12
7
13
import java .lang .reflect .Field ;
8
- import java .lang .reflect .InvocationTargetException ;
9
- import java .lang .reflect .Method ;
10
14
import java .util .ArrayList ;
11
15
import java .util .Arrays ;
12
- import java .util .LinkedList ;
13
16
import java .util .List ;
14
17
15
18
/**
16
19
* Validates an object against a defined schema.
17
- *
18
- * @param <T> The type of the validated object.
19
20
*/
20
- public class Validator < T > {
21
+ public class Validator {
21
22
22
23
/**
23
24
* The error bag for the
@@ -28,7 +29,7 @@ public class Validator<T> {
28
29
/**
29
30
* All registered fields.
30
31
*/
31
- private List <? extends Validatable > fields ;
32
+ private List <Validatable > fields ;
32
33
33
34
/**
34
35
* Parses a pattern and creates a Rule instance dynamically if it
@@ -39,121 +40,51 @@ public class Validator<T> {
39
40
/**
40
41
* Create a new validator instance based on a given type.
41
42
*/
42
- public Validator () {
43
+ private Validator () {
43
44
this .errorBag = new ErrorBag ();
44
45
this .ruleParser = new RuleParser ();
45
46
this .fields = new ArrayList <>();
46
47
}
47
48
48
- /**
49
- * Adds a field to the schema.
50
- * You can add any amount of rules for a field.
51
- * <p>
52
- * When a field is already existing the rules will be added to the fields rules.
53
- * Already existing rules will be overwritten.
54
- * <p>
55
- * The given fields need to have a getter method starting with {@code get} or {@code is}.
56
- *
57
- * @param field The name of the field.
58
- * @param rules The assigned rules.
59
- * @return The instance of the validator.
60
- */
61
- public Validator <T > addField (String field , Rule ...rules ) {
62
- ValidationField vf = fields .stream ().filter (f -> f .getField ().equals (field ))
63
- .findFirst ().orElse (null );
64
- if (vf == null ) {
65
- fields .add (new ValidationField (field , rules ));
66
- return this ;
67
- } else {
68
- fields .remove (vf );
69
- for (Rule rule : rules ) {
70
- vf .addRule (rule );
71
- }
49
+ public static Validator fromRules (Builder ...builders ) {
50
+ Validator validator = new Validator ();
51
+ for (Builder builder : builders ) {
52
+ validator .add (builder );
72
53
}
73
- fields .add (vf );
74
- return this ;
54
+ return validator ;
75
55
}
76
56
77
- /**
78
- * Adds a field and rules based on a string representation. Divide rule using a
79
- * {@code |} symbol. Parameters can be passed using a {@code :} syntax.
80
- * E.g: "required|max:50|length:10:50"
81
- * <p>
82
- * The given fields need to have a getter method starting with {@code get} or {@code is}.
83
- *
84
- * @param field The name of the field.
85
- * @param rulesString he assigned rules in string representation.
86
- * @return The instance of the validator.
87
- */
88
- public Validator <T > addField (String field , String rulesString ) {
89
- String [] rules = rulesString .split ("\\ |" );
90
- Rule [] parsed = new Rule [rules .length ];
91
- for (int i = 0 ; i < rules .length ; i ++) {
92
- int finalI = i ;
93
- ruleParser .parse (rules [i ]).ifPresent (rule -> parsed [finalI ] = rule );
57
+ public static Validator fromRules (Validatable ...rules ) {
58
+ Validator validator = new Validator ();
59
+ for (Validatable validatable : rules ) {
60
+ validator .add (validatable );
94
61
}
95
- return addField ( field , parsed ) ;
62
+ return validator ;
96
63
}
97
64
98
- /**
99
- * Adds a field using the ValidationField class.
100
- *
101
- * @param validationField
102
- * @return
103
- */
104
- public Validator <T > addField (ValidationField validationField ) {
105
- return addField (validationField .getField (), (Rule []) validationField .getRules ().toArray ());
65
+ public static Validator empty () {
66
+ return new Validator ();
106
67
}
107
68
108
- /**
109
- * Adds a field using the ValidationObject class.
110
- *
111
- * @param validationField
112
- * @return
113
- */
114
- public Validator <T > addField (ValidationObject validationObject ) {
115
- return null ;
69
+ public Validator add (Builder builder ) {
70
+ return add (builder .build ());
116
71
}
117
72
118
- public T validate (T object ) throws ValidationException , IllegalAccessException {
119
- return validate (object , false );
73
+ public Validator add (Validatable validatable ) {
74
+ this .fields .add (validatable );
75
+ return this ;
120
76
}
121
77
122
- /**
123
- * Validates an object against a schema and returns an error bag.
124
- *
125
- * @param object The object that need to be validated.
126
- * @throws ValidationException Thrown when at least one rule fails.
127
- * @throws IllegalAccessException Thrown when the field is not public.
128
- */
129
- public T validate (T object , boolean abortEarly ) throws ValidationException , IllegalAccessException {
130
- errorBag .clear ();
131
- List <Field > fieldSet = new ArrayList <>();
132
- for (Class <?> c = object .getClass (); c != null ; c = c .getSuperclass ())
133
- {
134
- Field [] fields = c .getDeclaredFields ();
135
- fieldSet .addAll (Arrays .asList (fields ));
136
- }
137
- for (Validatable validatable : this .fields ) {
138
- Field field = fieldSet .stream ().filter (f -> f .getName ().equals (validatable .getField ())).findFirst ().orElse (null );
139
- if (field == null ) continue ;
140
- Object value = getValue (field , object );
141
- errorBag .merge (validatable .validate (value , abortEarly ));
142
- }
143
- if (!errorBag .isEmpty ())
144
- throw new ValidationException (errorBag );
145
-
146
- return object ;
78
+ public <T > T validate (T object ) {
79
+ return validate (object , false );
147
80
}
148
81
149
82
/**
150
83
* Validates an object against a schema and returns an error bag.
151
84
*
152
85
* @param object The object that need to be validated.
153
- * @throws ValidationException Thrown when at least one rule fails.
154
- * @throws IllegalAccessException Thrown when the field is not public.
155
86
*/
156
- public void tryValidate (T object ) throws ValidationException , IllegalAccessException {
87
+ public < T > T validate (T object , boolean abortEarly ) {
157
88
errorBag .clear ();
158
89
List <Field > fieldSet = new ArrayList <>();
159
90
for (Class <?> c = object .getClass (); c != null ; c = c .getSuperclass ())
@@ -162,39 +93,22 @@ public void tryValidate(T object) throws ValidationException, IllegalAccessExcep
162
93
fieldSet .addAll (Arrays .asList (fields ));
163
94
}
164
95
for (Validatable validatable : this .fields ) {
165
- Field field = fieldSet .stream ().filter (f -> f .getName ().equals (validatable .getField ())).findFirst ().orElse (null );
166
- if (field == null ) continue ;
167
- Object value = getValue (field , object );
168
- errorBag .merge (validatable .validate (value , abortEarly ));
96
+ try {
97
+ Field field = fieldSet .stream ().filter (f -> f .getName ().equals (validatable .getField ())).findFirst ().orElse (null );
98
+ if (field == null ) continue ;
99
+ Object value = FieldUtils .readField (field , object , true );
100
+ ValidationResult result = validatable .validate ("" , value , abortEarly );
101
+ if (result .isChanged ())
102
+ FieldUtils .writeField (field , object , result .getValue (), true );
103
+ errorBag .merge (result .getErrorBag ());
104
+ } catch (IllegalAccessException ex ) {
105
+ throw new FieldNotAccessibleException ();
106
+ }
169
107
}
170
108
if (!errorBag .isEmpty ())
171
109
throw new ValidationException (errorBag );
172
- }
173
110
174
- /**
175
- * Uses reflection to invoke a getter of the validation target.
176
- * Falls back to the fields default getter. Will fail if the variable
177
- * is not publicly accessible.
178
- *
179
- * @param field The field name whose value should be received.
180
- * @param object The validation target object.
181
- * @return {@code null} if the field cannot be resolved, or the value.
182
- */
183
- private Object getValue (Field field , Object object ) throws IllegalAccessException {
184
- for (Method method : object .getClass ().getMethods ()) {
185
- if ((method .getName ().startsWith ("get" )) && (method .getName ().length () == (field .getName ().length () + 3 )) ||
186
- (method .getName ().startsWith ("is" )) && (method .getName ().length () == (field .getName ().length () + 2 ))) {
187
- if (method .getName ().toLowerCase ().endsWith (field .getName ().toLowerCase ())) {
188
- try {
189
- return method .invoke (object );
190
- }
191
- catch (IllegalAccessException | InvocationTargetException e ) {
192
- e .printStackTrace ();
193
- }
194
- }
195
- }
196
- }
197
- return field .get (object );
111
+ return object ;
198
112
}
199
113
200
114
/**
0 commit comments