Skip to content

Commit 65cdfc6

Browse files
committed
Implemented builder for validation fields
1 parent 3c64de4 commit 65cdfc6

File tree

14 files changed

+324
-23
lines changed

14 files changed

+324
-23
lines changed

src/main/java/dev/ditsche/validator/Validator.java

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
import dev.ditsche.validator.error.ErrorBag;
44
import dev.ditsche.validator.error.ValidationException;
5-
import dev.ditsche.validator.rule.Rule;
6-
import dev.ditsche.validator.rule.RuleInfo;
7-
import dev.ditsche.validator.rule.RuleParser;
8-
import dev.ditsche.validator.rule.ValidationField;
5+
import dev.ditsche.validator.rule.*;
96

107
import java.lang.reflect.Field;
118
import java.lang.reflect.InvocationTargetException;
@@ -31,7 +28,7 @@ public class Validator<T> {
3128
/**
3229
* All registered fields.
3330
*/
34-
private List<ValidationField> fields;
31+
private List<? extends Validatable> fields;
3532

3633
/**
3734
* Parses a pattern and creates a Rule instance dynamically if it
@@ -98,32 +95,55 @@ public Validator<T> addField(String field, String rulesString) {
9895
return addField(field, parsed);
9996
}
10097

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());
106+
}
107+
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;
116+
}
117+
118+
public T validate(T object) throws ValidationException, IllegalAccessException {
119+
return validate(object, false);
120+
}
121+
101122
/**
102123
* Validates an object against a schema and returns an error bag.
103-
* @deprecated since 1.0.4
124+
*
104125
* @param object The object that need to be validated.
105126
* @throws ValidationException Thrown when at least one rule fails.
106127
* @throws IllegalAccessException Thrown when the field is not public.
107128
*/
108-
@Deprecated
109-
public void validate(T object) throws ValidationException, IllegalAccessException {
129+
public T validate(T object, boolean abortEarly) throws ValidationException, IllegalAccessException {
110130
errorBag.clear();
111131
List<Field> fieldSet = new ArrayList<>();
112132
for (Class<?> c = object.getClass(); c != null; c = c.getSuperclass())
113133
{
114134
Field[] fields = c.getDeclaredFields();
115135
fieldSet.addAll(Arrays.asList(fields));
116136
}
117-
for(ValidationField vf : this.fields) {
118-
Field field = fieldSet.stream().filter(f -> f.getName().equals(vf.getField())).findFirst().orElse(null);
137+
for(Validatable validatable : this.fields) {
138+
Field field = fieldSet.stream().filter(f -> f.getName().equals(validatable.getField())).findFirst().orElse(null);
119139
if(field == null) continue;
120140
Object value = getValue(field, object);
121-
for(Rule rule : vf.getRules()) {
122-
if(!rule.passes(value)) errorBag.add(vf.getField(), rule.message(vf.getField()));
123-
}
141+
errorBag.merge(validatable.validate(value, abortEarly));
124142
}
125143
if(!errorBag.isEmpty())
126144
throw new ValidationException(errorBag);
145+
146+
return object;
127147
}
128148

129149
/**
@@ -141,13 +161,11 @@ public void tryValidate(T object) throws ValidationException, IllegalAccessExcep
141161
Field[] fields = c.getDeclaredFields();
142162
fieldSet.addAll(Arrays.asList(fields));
143163
}
144-
for(ValidationField vf : this.fields) {
145-
Field field = fieldSet.stream().filter(f -> f.getName().equals(vf.getField())).findFirst().orElse(null);
164+
for(Validatable validatable : this.fields) {
165+
Field field = fieldSet.stream().filter(f -> f.getName().equals(validatable.getField())).findFirst().orElse(null);
146166
if(field == null) continue;
147167
Object value = getValue(field, object);
148-
for(Rule rule : vf.getRules()) {
149-
if(!rule.passes(value)) errorBag.add(vf.getField(), rule.message(vf.getField()));
150-
}
168+
errorBag.merge(validatable.validate(value, abortEarly));
151169
}
152170
if(!errorBag.isEmpty())
153171
throw new ValidationException(errorBag);

src/main/java/dev/ditsche/validator/error/ErrorBag.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.HashMap;
66
import java.util.LinkedList;
77
import java.util.List;
8+
import java.util.Map;
89

910
/**
1011
* The error bag holds the fields for which at least one rule has not passed.
@@ -29,9 +30,13 @@ public ErrorBag() {
2930
* @param message The error message.
3031
*/
3132
public void add(String field, String message) {
32-
List<String> errorList = errors.getOrDefault(field, new ValidationError(field)).getErrors();
33-
errorList.add(message);
34-
errors.put(field, new ValidationError(field, errorList));
33+
add(new ValidationError(field, List.of(message)));
34+
}
35+
36+
public void add(ValidationError validationError) {
37+
List<String> errorList = errors.getOrDefault(validationError.getField(), new ValidationError(validationError.getField())).getErrors();
38+
errorList.addAll(validationError.getErrors());
39+
errors.put(validationError.getField(), new ValidationError(validationError.getField(), errorList));
3540
}
3641

3742
/**
@@ -52,4 +57,10 @@ public boolean isEmpty() {
5257
return errors.isEmpty();
5358
}
5459

60+
public void merge(ErrorBag errorBag) {
61+
for(ValidationError error: errorBag.getErrors().values()) {
62+
add(error);
63+
}
64+
}
65+
5566
}

src/main/java/dev/ditsche/validator/error/ValidationException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Thrown when the validation of an object fails.
1010
* Gets the error bag of the Validator and provides access to the errors.
1111
*/
12-
public class ValidationException extends Exception {
12+
public class ValidationException extends RuntimeException {
1313

1414
/**
1515
* The error bag holding the errors.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package dev.ditsche.validator.error;
2+
3+
/**
4+
* @author Tobias Dittmann
5+
*/
6+
public class ValueNotAccessibleException extends RuntimeException {
7+
}

src/main/java/dev/ditsche/validator/rule/RuleMap.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.ditsche.validator.rule;
22

3+
import dev.ditsche.validator.rule.builder.StringRuleBuilder;
34
import dev.ditsche.validator.ruleset.*;
45

56
import java.util.HashMap;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.ditsche.validator.rule;
2+
3+
import dev.ditsche.validator.error.ErrorBag;
4+
5+
public interface Validatable {
6+
7+
String getField();
8+
9+
ErrorBag validate(Object object, boolean abortEarly);
10+
11+
}

src/main/java/dev/ditsche/validator/rule/ValidationField.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dev.ditsche.validator.rule;
22

3+
import dev.ditsche.validator.error.ErrorBag;
4+
import dev.ditsche.validator.error.ValidationException;
35
import lombok.Getter;
46

57
import java.util.List;
@@ -10,7 +12,7 @@
1012
* Describes a validatable field.
1113
* Holds information about the fields name and the assigned rules.
1214
*/
13-
public class ValidationField {
15+
public class ValidationField implements Validatable {
1416

1517
/**
1618
* The fields name.
@@ -60,4 +62,17 @@ public void addRule(Rule rule) {
6062
this.rules.add(rule);
6163
}
6264

65+
@Override
66+
public ErrorBag validate(Object object, boolean abortEarly) {
67+
ErrorBag errorBag = new ErrorBag();
68+
for(Rule rule : rules) {
69+
boolean passed = rule.passes(object);
70+
if(!passed) {
71+
errorBag.add(field, rule.message(field));
72+
if(abortEarly)
73+
throw new ValidationException(errorBag);
74+
}
75+
}
76+
return errorBag;
77+
}
6378
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package dev.ditsche.validator.rule;
2+
3+
import dev.ditsche.validator.error.ErrorBag;
4+
import dev.ditsche.validator.error.ValueNotAccessibleException;
5+
import lombok.Getter;
6+
7+
import java.lang.reflect.Field;
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Method;
10+
import java.util.ArrayList;
11+
import java.util.Arrays;
12+
import java.util.List;
13+
14+
/**
15+
* @author Tobias Dittmann
16+
*/
17+
public class ValidationObject implements Validatable {
18+
19+
@Getter
20+
private String field;
21+
22+
@Getter
23+
private List<Validatable> fields;
24+
25+
@Override
26+
public ErrorBag validate(Object object, boolean abortEarly) {
27+
ErrorBag errorBag = new ErrorBag();
28+
List<Field> fieldSet = new ArrayList<>();
29+
for (Class<?> c = object.getClass(); c != null; c = c.getSuperclass()) {
30+
Field[] fields = c.getDeclaredFields();
31+
fieldSet.addAll(Arrays.asList(fields));
32+
}
33+
for(Validatable validatable : fields) {
34+
Field field = fieldSet.stream().filter(f -> f.getName().equals(validatable.getField())).findFirst().orElse(null);
35+
if(field == null) continue;
36+
try {
37+
Object value = getValue(field, object);
38+
errorBag.merge(validatable.validate(value, abortEarly));
39+
} catch (IllegalAccessException e) {
40+
throw new ValueNotAccessibleException();
41+
}
42+
}
43+
return errorBag;
44+
}
45+
46+
/**
47+
* Uses reflection to invoke a getter of the validation target.
48+
* Falls back to the fields default getter. Will fail if the variable
49+
* is not publicly accessible.
50+
*
51+
* @param field The field name whose value should be received.
52+
* @param object The validation target object.
53+
* @return {@code null} if the field cannot be resolved, or the value.
54+
*/
55+
private Object getValue(Field field, Object object) throws IllegalAccessException {
56+
for (Method method : object.getClass().getMethods()) {
57+
if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)) ||
58+
(method.getName().startsWith("is")) && (method.getName().length() == (field.getName().length() + 2))) {
59+
if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase())) {
60+
try {
61+
return method.invoke(object);
62+
}
63+
catch (IllegalAccessException | InvocationTargetException e) {
64+
e.printStackTrace();
65+
}
66+
}
67+
}
68+
}
69+
return field.get(object);
70+
}
71+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dev.ditsche.validator.rule.builder;
2+
3+
import dev.ditsche.validator.rule.ValidationField;
4+
5+
public interface Builder {
6+
7+
ValidationField build();
8+
9+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package dev.ditsche.validator.rule.builder;
2+
3+
import dev.ditsche.validator.rule.Rule;
4+
import dev.ditsche.validator.ruleset.StringRule;
5+
6+
import java.util.LinkedList;
7+
import java.util.List;
8+
9+
/**
10+
* @author Tobias Dittmann
11+
*/
12+
public final class RuleBuilder {
13+
14+
private String field;
15+
16+
private List<Rule> rules;
17+
18+
private RuleBuilder(String field, Rule rule) {
19+
this.field = field;
20+
this.rules = new LinkedList<>();
21+
this.rules.add(rule);
22+
}
23+
24+
public static StringRuleBuilder string(String field) {
25+
return new StringRuleBuilder(field);
26+
}
27+
28+
public static NumberRuleBuilder number(String field) {
29+
return new NumberRuleBuilder(field);
30+
}
31+
32+
public static ObjectRuleBuilder object(String field) {
33+
return new ObjectRuleBuilder(field);
34+
}
35+
36+
public static ArrayRuleBuilder object(String field) {
37+
return new ArrayRuleBuilder(field);
38+
}
39+
40+
}

0 commit comments

Comments
 (0)