Skip to content

Commit 84d6b1f

Browse files
committed
add validation support for @SiZe
1 parent d65a075 commit 84d6b1f

5 files changed

Lines changed: 373 additions & 0 deletions

File tree

src/main/java/it/aboutbits/springboot/testing/validation/core/BaseRuleBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import it.aboutbits.springboot.testing.validation.rule.PastRule;
1515
import it.aboutbits.springboot.testing.validation.rule.PositiveOrZeroRule;
1616
import it.aboutbits.springboot.testing.validation.rule.PositiveRule;
17+
import it.aboutbits.springboot.testing.validation.rule.SizeRule;
1718
import it.aboutbits.springboot.testing.validation.rule.ValidBeanRule;
1819
import lombok.AccessLevel;
1920
import lombok.Getter;
@@ -42,6 +43,7 @@ public abstract class BaseRuleBuilder<R extends BaseRuleBuilder<R>> implements
4243
PositiveOrZeroRule<R>,
4344
PositiveRule<R>,
4445
NotValidatedRule<R>,
46+
SizeRule<R>,
4547
ValidBeanRule<R> {
4648
@Getter(AccessLevel.PACKAGE)
4749
private final List<Rule> rules = new ArrayList<>();
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package it.aboutbits.springboot.testing.validation.rule;
2+
3+
import it.aboutbits.springboot.testing.validation.core.BaseRuleBuilder;
4+
import it.aboutbits.springboot.testing.validation.core.Rule;
5+
import it.aboutbits.springboot.testing.validation.core.ValidationRulesData;
6+
import it.aboutbits.springboot.testing.validation.source.SizeGreaterThanValueSource;
7+
import it.aboutbits.springboot.testing.validation.source.SizeLessThanValueSource;
8+
import lombok.AccessLevel;
9+
import lombok.NonNull;
10+
import lombok.RequiredArgsConstructor;
11+
12+
@SuppressWarnings("unchecked")
13+
public interface SizeRule<V extends BaseRuleBuilder<?>> extends ValidationRulesData {
14+
default Builder<V> size(@NonNull String property) {
15+
return new Builder<>((V) this, property);
16+
}
17+
18+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
19+
final class Builder<V extends BaseRuleBuilder<?>> {
20+
private final V parent;
21+
private final String property;
22+
23+
public V min(long minSize) {
24+
parent.addRule(
25+
new Rule(property, SizeLessThanValueSource.class, minSize)
26+
);
27+
return parent;
28+
}
29+
30+
public V max(long maxSize) {
31+
parent.addRule(
32+
new Rule(property, SizeGreaterThanValueSource.class, maxSize)
33+
);
34+
return parent;
35+
}
36+
37+
public V minMax(long minSize, long maxSize) {
38+
parent.addRule(
39+
new Rule(property, SizeLessThanValueSource.class, minSize)
40+
);
41+
parent.addRule(
42+
new Rule(property, SizeGreaterThanValueSource.class, maxSize)
43+
);
44+
return parent;
45+
}
46+
}
47+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package it.aboutbits.springboot.testing.validation.source;
2+
3+
import it.aboutbits.springboot.testing.validation.core.ValueSource;
4+
import lombok.NonNull;
5+
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
import java.util.HashMap;
9+
import java.util.HashSet;
10+
import java.util.LinkedList;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Random;
14+
import java.util.Set;
15+
import java.util.TreeSet;
16+
import java.util.function.LongFunction;
17+
import java.util.stream.Stream;
18+
19+
public class SizeGreaterThanValueSource implements ValueSource {
20+
private static final Map<Class<?>, LongFunction<Stream<?>>> TYPE_SOURCES = new HashMap<>();
21+
private static final Random RANDOM = new Random();
22+
23+
static {
24+
TYPE_SOURCES.put(String.class, SizeGreaterThanValueSource::getStringStream);
25+
TYPE_SOURCES.put(Collection.class, SizeGreaterThanValueSource::getArrayListStream);
26+
TYPE_SOURCES.put(List.class, SizeGreaterThanValueSource::getArrayListStream);
27+
TYPE_SOURCES.put(ArrayList.class, SizeGreaterThanValueSource::getArrayListStream);
28+
TYPE_SOURCES.put(LinkedList.class, SizeGreaterThanValueSource::getLinkedListStream);
29+
TYPE_SOURCES.put(Set.class, SizeGreaterThanValueSource::getHashSetStream);
30+
TYPE_SOURCES.put(HashSet.class, SizeGreaterThanValueSource::getHashSetStream);
31+
TYPE_SOURCES.put(TreeSet.class, SizeGreaterThanValueSource::getTreeSetStream);
32+
}
33+
34+
@SuppressWarnings("unused")
35+
public static void registerType(Class<?> type, LongFunction<Stream<?>> source) {
36+
TYPE_SOURCES.put(type, source);
37+
}
38+
39+
@Override
40+
@SuppressWarnings("unchecked")
41+
public <T> Stream<T> values(Class<T> propertyClass, Object... args) {
42+
var value = (Long.valueOf((long) args[0]));
43+
44+
var sourceFunction = TYPE_SOURCES.get(propertyClass);
45+
if (sourceFunction != null) {
46+
return (Stream<T>) sourceFunction.apply(value);
47+
}
48+
49+
throw new IllegalArgumentException("Property class not supported!");
50+
}
51+
52+
@NonNull
53+
private static Stream<String> getStringStream(long value) {
54+
return getTestSizes(value)
55+
.stream()
56+
.map(size -> generateRandomString(Math.toIntExact(size)));
57+
}
58+
59+
@NonNull
60+
private static Stream<Collection<?>> getHashSetStream(long value) {
61+
return getTestSizes(value)
62+
.stream()
63+
.map(size -> generateCollection(
64+
Math.toIntExact(size),
65+
new HashSet<>()
66+
));
67+
}
68+
69+
@NonNull
70+
private static Stream<Collection<?>> getTreeSetStream(long value) {
71+
return getTestSizes(value)
72+
.stream()
73+
.map(size -> generateCollection(
74+
Math.toIntExact(size),
75+
new TreeSet<>()
76+
));
77+
}
78+
79+
@NonNull
80+
private static Stream<Collection<?>> getArrayListStream(long value) {
81+
return getTestSizes(value)
82+
.stream()
83+
.map(size -> generateCollection(
84+
Math.toIntExact(size),
85+
new ArrayList<>()
86+
));
87+
}
88+
89+
@NonNull
90+
private static Stream<Collection<?>> getLinkedListStream(long value) {
91+
return getTestSizes(value)
92+
.stream()
93+
.map(size -> generateCollection(
94+
Math.toIntExact(size),
95+
new LinkedList<>()
96+
));
97+
}
98+
99+
private static List<Long> getTestSizes(long value) {
100+
var sizes = new ArrayList<Long>();
101+
102+
sizes.add(value + 1);
103+
sizes.add(value + 2);
104+
sizes.add(value + 6);
105+
106+
return sizes;
107+
}
108+
109+
private static String generateRandomString(int length) {
110+
// Include printable ASCII characters (32-126) which includes space and common characters
111+
return RANDOM.ints(length, 32, 127)
112+
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
113+
.toString();
114+
}
115+
116+
private static Collection<Object> generateCollection(int size, Collection<Object> collection) {
117+
for (int i = 0; i < size; i++) {
118+
collection.add("dummy_" + i); // Add dummy elements, content doesn't matter
119+
}
120+
return collection;
121+
}
122+
123+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package it.aboutbits.springboot.testing.validation.source;
2+
3+
import it.aboutbits.springboot.testing.validation.core.ValueSource;
4+
import lombok.NonNull;
5+
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
import java.util.HashMap;
9+
import java.util.HashSet;
10+
import java.util.LinkedList;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Random;
14+
import java.util.Set;
15+
import java.util.TreeSet;
16+
import java.util.function.LongFunction;
17+
import java.util.stream.Stream;
18+
19+
public class SizeLessThanValueSource implements ValueSource {
20+
private static final Map<Class<?>, LongFunction<Stream<?>>> TYPE_SOURCES = new HashMap<>();
21+
private static final Random RANDOM = new Random();
22+
23+
static {
24+
TYPE_SOURCES.put(String.class, SizeLessThanValueSource::getStringStream);
25+
TYPE_SOURCES.put(Collection.class, SizeLessThanValueSource::getArrayListStream);
26+
TYPE_SOURCES.put(List.class, SizeLessThanValueSource::getArrayListStream);
27+
TYPE_SOURCES.put(ArrayList.class, SizeLessThanValueSource::getArrayListStream);
28+
TYPE_SOURCES.put(LinkedList.class, SizeLessThanValueSource::getLinkedListStream);
29+
TYPE_SOURCES.put(Set.class, SizeLessThanValueSource::getHashSetStream);
30+
TYPE_SOURCES.put(HashSet.class, SizeLessThanValueSource::getHashSetStream);
31+
TYPE_SOURCES.put(TreeSet.class, SizeLessThanValueSource::getTreeSetStream);
32+
}
33+
34+
@SuppressWarnings("unused")
35+
public static void registerType(Class<?> type, LongFunction<Stream<?>> source) {
36+
TYPE_SOURCES.put(type, source);
37+
}
38+
39+
@Override
40+
@SuppressWarnings("unchecked")
41+
public <T> Stream<T> values(Class<T> propertyClass, Object... args) {
42+
var value = (Long.valueOf((long) args[0]));
43+
44+
if (value == 0) {
45+
return Stream.empty();
46+
}
47+
48+
if (value < 0) {
49+
throw new IllegalArgumentException("Value must be positive or zero. A size cannot be less than empty.");
50+
}
51+
52+
var sourceFunction = TYPE_SOURCES.get(propertyClass);
53+
if (sourceFunction != null) {
54+
return (Stream<T>) sourceFunction.apply(value);
55+
}
56+
57+
throw new IllegalArgumentException("Property class not supported!");
58+
}
59+
60+
@NonNull
61+
private static Stream<String> getStringStream(long value) {
62+
return Stream.concat(
63+
Stream.of(""),
64+
Stream.iterate(1L, i -> i < value, i -> i + 1)
65+
.map(length -> generateRandomString(Math.toIntExact(length)))
66+
);
67+
}
68+
69+
@NonNull
70+
private static Stream<Collection<?>> getArrayListStream(long value) {
71+
return Stream.concat(
72+
Stream.of(new ArrayList<>()),
73+
Stream.iterate(1L, i -> i < value, i -> i + 1)
74+
.map(size -> generateCollection(
75+
Math.toIntExact(size),
76+
new ArrayList<>()
77+
))
78+
);
79+
}
80+
81+
@NonNull
82+
private static Stream<Collection<?>> getLinkedListStream(long value) {
83+
return Stream.concat(
84+
Stream.of(new LinkedList<>()),
85+
Stream.iterate(1L, i -> i < value, i -> i + 1)
86+
.map(size -> generateCollection(
87+
Math.toIntExact(size),
88+
new LinkedList<>()
89+
))
90+
);
91+
}
92+
93+
@NonNull
94+
private static Stream<Collection<?>> getHashSetStream(long value) {
95+
return Stream.concat(
96+
Stream.of(new HashSet<>()),
97+
Stream.iterate(1L, i -> i < value, i -> i + 1)
98+
.map(size -> generateCollection(
99+
Math.toIntExact(size),
100+
new HashSet<>()
101+
))
102+
);
103+
}
104+
105+
@NonNull
106+
private static Stream<Collection<?>> getTreeSetStream(long value) {
107+
return Stream.concat(
108+
Stream.of(new TreeSet<>()),
109+
Stream.iterate(1L, i -> i < value, i -> i + 1)
110+
.map(size -> generateCollection(
111+
Math.toIntExact(size),
112+
new TreeSet<>()
113+
))
114+
);
115+
}
116+
117+
private static String generateRandomString(int length) {
118+
// Include printable ASCII characters (32-126) which includes space and common characters
119+
return RANDOM.ints(length, 32, 127)
120+
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
121+
.toString();
122+
}
123+
124+
private static Collection<Object> generateCollection(int size, Collection<Object> collection) {
125+
for (int i = 0; i < size; i++) {
126+
collection.add("dummy_" + i); // Add dummy elements, content doesn't matter
127+
}
128+
return collection;
129+
}
130+
}

0 commit comments

Comments
 (0)