Skip to content

Commit 75d2c81

Browse files
authored
Merge pull request #104 from avaje/feature/NotBlank-with-max
Add optional max length attribute to @notblank
2 parents c7b9610 + 81ebf13 commit 75d2c81

File tree

7 files changed

+153
-12
lines changed

7 files changed

+153
-12
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package example.avaje.notblank;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import jakarta.validation.Valid;
5+
6+
@Valid
7+
public record ANotBlank(
8+
@NotBlank
9+
String basic,
10+
@NotBlank(max = 4)
11+
String withMax,
12+
13+
@NotBlank(max = 4, message = "NotBlank n max 4")
14+
String withCustom
15+
) {
16+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package example.avaje.notblank;
2+
3+
import io.avaje.validation.ConstraintViolation;
4+
import io.avaje.validation.ConstraintViolationException;
5+
import io.avaje.validation.Validator;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.util.ArrayList;
9+
import java.util.Locale;
10+
11+
import static org.assertj.core.api.Assertions.assertThat;
12+
import static org.assertj.core.api.Assertions.fail;
13+
14+
class ANotBlankTest {
15+
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
17+
18+
@Test
19+
void valid() {
20+
var value = new ANotBlank("ok", "ok", "ok");
21+
validator.validate(value);
22+
}
23+
24+
@Test
25+
void inValidNull() {
26+
var value = new ANotBlank(null, null, null);
27+
try {
28+
validator.validate(value);
29+
fail("not get here");
30+
} catch (ConstraintViolationException e) {
31+
assertThat(e.violations()).hasSize(3);
32+
}
33+
}
34+
35+
@Test
36+
void invalidBlank() {
37+
var violation = one(new ANotBlank("", "ok", "ok"));
38+
assertThat(violation.message()).isEqualTo("must not be blank");
39+
40+
var violation1 = one(new ANotBlank("ok", " ", "ok"));
41+
assertThat(violation1.message()).isEqualTo("must not be blank");
42+
43+
var violation2 = one(new ANotBlank("ok", "ok", "\t"));
44+
assertThat(violation2.message()).isEqualTo("NotBlank n max 4");
45+
}
46+
47+
@Test
48+
void invalidMax() {
49+
var violation = one(new ANotBlank("ok", "NotValid", "ok"));
50+
assertThat(violation.message()).isEqualTo("maximum length 4 exceeded");
51+
52+
var violation1 = one(new ANotBlank("ok", "ok", "NotValid"));
53+
assertThat(violation1.message()).isEqualTo("NotBlank n max 4");
54+
}
55+
56+
@Test
57+
void invalidAsDE() {
58+
var violation = one(new ANotBlank(" ", "ok", "ok"), Locale.GERMAN);
59+
assertThat(violation.message()).isEqualTo("darf nicht leer sein");
60+
}
61+
62+
@Test
63+
void invalidWithMaxDE() {
64+
var violation = one(new ANotBlank("ok", "NotValid", "ok"), Locale.GERMAN);
65+
assertThat(violation.message()).isEqualTo("Länge muss zwischen 1 und 4 sein");
66+
}
67+
68+
@Test
69+
void invalidCustomMessageDE() {
70+
var violation = one(new ANotBlank("ok", "ok", "NotValid"), Locale.GERMAN);
71+
assertThat(violation.message()).isEqualTo("NotBlank n max 4");
72+
}
73+
74+
ConstraintViolation one(Object any) {
75+
return one(any, Locale.ENGLISH);
76+
}
77+
78+
ConstraintViolation one(Object any, Locale locale) {
79+
try {
80+
validator.validate(any, locale);
81+
fail("not expected");
82+
return null;
83+
} catch (ConstraintViolationException e) {
84+
var violations = new ArrayList<>(e.violations());
85+
assertThat(violations).hasSize(1);
86+
return violations.get(0);
87+
}
88+
}
89+
}

validator-constraints/src/main/java/io/avaje/validation/constraints/NotBlank.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
@Repeatable(List.class)
2929
public @interface NotBlank {
3030

31+
/** Set the maximum length. By default this is 0 meaning unlimited. */
32+
int max() default 0;
33+
3134
String message() default "{avaje.NotBlank.message}";
3235

3336
Class<?>[] groups() default {};

validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ interface AdapterCreateRequest {
164164

165165
Message message();
166166

167-
Message message(String key);
167+
Message message(String key, Object... extraKeyValues);
168168

169169
String targetType();
170170

validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,14 @@ public ValidationContext.Message message() {
134134
}
135135

136136
@Override
137-
public ValidationContext.Message message(String messageKey) {
137+
public ValidationContext.Message message(String messageKey, Object... extraKeyValues) {
138138
Map<String, Object> newAttributes = new HashMap<>(attributes);
139139
newAttributes.put("message", messageKey);
140+
if (extraKeyValues != null) {
141+
for (int i = 0; i < extraKeyValues.length; i += 2) {
142+
newAttributes.put(String.valueOf(extraKeyValues[i]), extraKeyValues[i + 1]);
143+
}
144+
}
140145
return ctx.message(newAttributes);
141146
}
142147
}

validator/src/main/java/io/avaje/validation/core/adapters/BasicAdapters.java

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package io.avaje.validation.core.adapters;
22

3-
import java.util.Collection;
4-
import java.util.List;
5-
import java.util.Map;
6-
import java.util.Set;
3+
import java.util.*;
74
import java.util.function.Predicate;
85
import java.util.regex.Pattern;
96

@@ -99,7 +96,7 @@ private static boolean useLength(AdapterCreateRequest request) {
9996

10097
@Override
10198
public boolean validate(Object value, ValidationRequest req, String propertyName) {
102-
if (!checkGroups(groups, req) || value == null) {
99+
if (value == null || !checkGroups(groups, req)) {
103100
return true;
104101
}
105102

@@ -132,15 +129,47 @@ public boolean validate(Object value, ValidationRequest req, String propertyName
132129
}
133130
}
134131

135-
private static final class NotBlankAdapter extends AbstractConstraintAdapter<CharSequence> {
132+
private static final class NotBlankAdapter implements ValidationAdapter<CharSequence> {
133+
134+
private final ValidationContext.Message message;
135+
private final ValidationContext.Message maxLengthMessage;
136+
private final Set<Class<?>> groups;
137+
private final int maxLength;
136138

137139
NotBlankAdapter(AdapterCreateRequest request) {
138-
super(request);
140+
this.groups = request.groups();
141+
this.message = request.message();
142+
this.maxLength = maxLength(request);
143+
if (maxLength > 0 && standardMessage(request)) {
144+
maxLengthMessage = request.message("{avaje.Length.max.message}", "min", 1);
145+
} else {
146+
maxLengthMessage = null;
147+
}
148+
}
149+
150+
private static int maxLength(AdapterCreateRequest request) {
151+
final Integer max = (Integer) request.attribute("max");
152+
return Objects.requireNonNullElse(max, 0);
153+
}
154+
155+
private static boolean standardMessage(AdapterCreateRequest request) {
156+
return "{avaje.NotBlank.message}".equals(request.attribute("message"));
139157
}
140158

141159
@Override
142-
public boolean isValid(CharSequence cs) {
143-
return (cs != null) && !isBlank(cs);
160+
public boolean validate(CharSequence value, ValidationRequest req, String propertyName) {
161+
if (!checkGroups(groups, req)) {
162+
return true;
163+
}
164+
if (value == null || isBlank(value)) {
165+
req.addViolation(message, propertyName);
166+
return false;
167+
}
168+
if (maxLength > 0 && value.length() > maxLength) {
169+
req.addViolation(maxLengthMessage != null ? maxLengthMessage : message, propertyName);
170+
return false;
171+
}
172+
return true;
144173
}
145174

146175
static boolean isBlank(final CharSequence cs) {
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
## Intentionally blank
21
avaje.Length.max.message = maximum length {max} exceeded
32
avaje.Size.max.message = maximum size {max} exceeded

0 commit comments

Comments
 (0)