Skip to content

Commit c6f330c

Browse files
authored
Merge pull request #21 from aboutbits/size-validator
add validation support for annotation Size
2 parents d65a075 + 87302e5 commit c6f330c

5 files changed

Lines changed: 434 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: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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.lang.reflect.Array;
7+
import java.util.ArrayList;
8+
import java.util.Collection;
9+
import java.util.HashMap;
10+
import java.util.HashSet;
11+
import java.util.LinkedList;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Random;
15+
import java.util.Set;
16+
import java.util.TreeSet;
17+
import java.util.function.LongFunction;
18+
import java.util.stream.Stream;
19+
20+
public class SizeGreaterThanValueSource implements ValueSource {
21+
private static final Map<Class<?>, LongFunction<Stream<?>>> TYPE_SOURCES = new HashMap<>();
22+
private static final Random RANDOM = new Random();
23+
24+
static {
25+
TYPE_SOURCES.put(String.class, SizeGreaterThanValueSource::getStringStream);
26+
TYPE_SOURCES.put(Collection.class, SizeGreaterThanValueSource::getArrayListStream);
27+
TYPE_SOURCES.put(List.class, SizeGreaterThanValueSource::getArrayListStream);
28+
TYPE_SOURCES.put(ArrayList.class, SizeGreaterThanValueSource::getArrayListStream);
29+
TYPE_SOURCES.put(LinkedList.class, SizeGreaterThanValueSource::getLinkedListStream);
30+
TYPE_SOURCES.put(Set.class, SizeGreaterThanValueSource::getHashSetStream);
31+
TYPE_SOURCES.put(HashSet.class, SizeGreaterThanValueSource::getHashSetStream);
32+
TYPE_SOURCES.put(TreeSet.class, SizeGreaterThanValueSource::getTreeSetStream);
33+
}
34+
35+
@SuppressWarnings("unused")
36+
public static void registerType(Class<?> type, LongFunction<Stream<?>> source) {
37+
TYPE_SOURCES.put(type, source);
38+
}
39+
40+
@Override
41+
@SuppressWarnings("unchecked")
42+
public <T> Stream<T> values(Class<T> propertyClass, Object... args) {
43+
var value = (Long.valueOf((long) args[0]));
44+
45+
if (propertyClass.isArray()) {
46+
return (Stream<T>) arrayFunction(propertyClass, value);
47+
}
48+
49+
var sourceFunction = TYPE_SOURCES.get(propertyClass);
50+
if (sourceFunction != null) {
51+
return (Stream<T>) sourceFunction.apply(value);
52+
}
53+
54+
throw new IllegalArgumentException("Property class not supported!");
55+
}
56+
57+
private static Stream<?> arrayFunction(Class<?> arrayClass, long value) {
58+
59+
return getTestSizes(value)
60+
.stream()
61+
.map(size -> generateArray(
62+
Math.toIntExact(size),
63+
arrayClass
64+
));
65+
66+
}
67+
68+
@NonNull
69+
private static Stream<String> getStringStream(long value) {
70+
return getTestSizes(value)
71+
.stream()
72+
.map(size -> generateRandomString(Math.toIntExact(size)));
73+
}
74+
75+
@NonNull
76+
private static Stream<Collection<?>> getHashSetStream(long value) {
77+
return getTestSizes(value)
78+
.stream()
79+
.map(size -> generateCollection(
80+
Math.toIntExact(size),
81+
new HashSet<>()
82+
));
83+
}
84+
85+
@NonNull
86+
private static Stream<Collection<?>> getTreeSetStream(long value) {
87+
return getTestSizes(value)
88+
.stream()
89+
.map(size -> generateCollection(
90+
Math.toIntExact(size),
91+
new TreeSet<>()
92+
));
93+
}
94+
95+
@NonNull
96+
private static Stream<Collection<?>> getArrayListStream(long value) {
97+
return getTestSizes(value)
98+
.stream()
99+
.map(size -> generateCollection(
100+
Math.toIntExact(size),
101+
new ArrayList<>()
102+
));
103+
}
104+
105+
@NonNull
106+
private static Stream<Collection<?>> getLinkedListStream(long value) {
107+
return getTestSizes(value)
108+
.stream()
109+
.map(size -> generateCollection(
110+
Math.toIntExact(size),
111+
new LinkedList<>()
112+
));
113+
}
114+
115+
private static List<Long> getTestSizes(long value) {
116+
var sizes = new ArrayList<Long>();
117+
118+
sizes.add(value + 1);
119+
sizes.add(value + 2);
120+
sizes.add(value + 6);
121+
122+
return sizes;
123+
}
124+
125+
private static String generateRandomString(int length) {
126+
// Include printable ASCII characters (32-126) which includes space and common characters
127+
return RANDOM.ints(length, 32, 127)
128+
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
129+
.toString();
130+
}
131+
132+
private static Collection<Object> generateCollection(int size, Collection<Object> collection) {
133+
for (int i = 0; i < size; i++) {
134+
collection.add("dummy_" + i); // Add dummy elements, content doesn't matter
135+
}
136+
return collection;
137+
}
138+
139+
private static Object generateArray(int size, Class<?> arrayClass) {
140+
var componentType = arrayClass.getComponentType();
141+
return Array.newInstance(componentType, size);
142+
}
143+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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.lang.reflect.Array;
7+
import java.util.ArrayList;
8+
import java.util.Collection;
9+
import java.util.HashMap;
10+
import java.util.HashSet;
11+
import java.util.LinkedList;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Random;
15+
import java.util.Set;
16+
import java.util.TreeSet;
17+
import java.util.function.LongFunction;
18+
import java.util.stream.Stream;
19+
20+
public class SizeLessThanValueSource implements ValueSource {
21+
private static final Map<Class<?>, LongFunction<Stream<?>>> TYPE_SOURCES = new HashMap<>();
22+
private static final Random RANDOM = new Random();
23+
24+
static {
25+
TYPE_SOURCES.put(String.class, SizeLessThanValueSource::getStringStream);
26+
TYPE_SOURCES.put(Collection.class, SizeLessThanValueSource::getArrayListStream);
27+
TYPE_SOURCES.put(List.class, SizeLessThanValueSource::getArrayListStream);
28+
TYPE_SOURCES.put(ArrayList.class, SizeLessThanValueSource::getArrayListStream);
29+
TYPE_SOURCES.put(LinkedList.class, SizeLessThanValueSource::getLinkedListStream);
30+
TYPE_SOURCES.put(Set.class, SizeLessThanValueSource::getHashSetStream);
31+
TYPE_SOURCES.put(HashSet.class, SizeLessThanValueSource::getHashSetStream);
32+
TYPE_SOURCES.put(TreeSet.class, SizeLessThanValueSource::getTreeSetStream);
33+
}
34+
35+
@SuppressWarnings("unused")
36+
public static void registerType(Class<?> type, LongFunction<Stream<?>> source) {
37+
TYPE_SOURCES.put(type, source);
38+
}
39+
40+
@Override
41+
@SuppressWarnings("unchecked")
42+
public <T> Stream<T> values(Class<T> propertyClass, Object... args) {
43+
var value = (Long.valueOf((long) args[0]));
44+
45+
if (value == 0) {
46+
return Stream.empty();
47+
}
48+
49+
if (value < 0) {
50+
throw new IllegalArgumentException("Value must be positive or zero. A size cannot be less than empty.");
51+
}
52+
53+
if (propertyClass.isArray()) {
54+
return (Stream<T>) arrayFunction(propertyClass, value);
55+
}
56+
57+
var sourceFunction = TYPE_SOURCES.get(propertyClass);
58+
if (sourceFunction != null) {
59+
return (Stream<T>) sourceFunction.apply(value);
60+
}
61+
62+
throw new IllegalArgumentException("Property class not supported!");
63+
}
64+
65+
private static Stream<?> arrayFunction(Class<?> arrayClass, long value) {
66+
return Stream.concat(
67+
Stream.of(generateArray(0, arrayClass)),
68+
Stream.iterate(1L, i -> i < value, i -> i + 1)
69+
.map(size -> generateArray(
70+
Math.toIntExact(size),
71+
arrayClass
72+
))
73+
);
74+
}
75+
76+
@NonNull
77+
private static Stream<String> getStringStream(long value) {
78+
return Stream.concat(
79+
Stream.of(""),
80+
Stream.iterate(1L, i -> i < value, i -> i + 1)
81+
.map(length -> generateRandomString(Math.toIntExact(length)))
82+
);
83+
}
84+
85+
@NonNull
86+
private static Stream<Collection<?>> getArrayListStream(long value) {
87+
return Stream.concat(
88+
Stream.of(new ArrayList<>()),
89+
Stream.iterate(1L, i -> i < value, i -> i + 1)
90+
.map(size -> generateCollection(
91+
Math.toIntExact(size),
92+
new ArrayList<>()
93+
))
94+
);
95+
}
96+
97+
@NonNull
98+
private static Stream<Collection<?>> getLinkedListStream(long value) {
99+
return Stream.concat(
100+
Stream.of(new LinkedList<>()),
101+
Stream.iterate(1L, i -> i < value, i -> i + 1)
102+
.map(size -> generateCollection(
103+
Math.toIntExact(size),
104+
new LinkedList<>()
105+
))
106+
);
107+
}
108+
109+
@NonNull
110+
private static Stream<Collection<?>> getHashSetStream(long value) {
111+
return Stream.concat(
112+
Stream.of(new HashSet<>()),
113+
Stream.iterate(1L, i -> i < value, i -> i + 1)
114+
.map(size -> generateCollection(
115+
Math.toIntExact(size),
116+
new HashSet<>()
117+
))
118+
);
119+
}
120+
121+
@NonNull
122+
private static Stream<Collection<?>> getTreeSetStream(long value) {
123+
return Stream.concat(
124+
Stream.of(new TreeSet<>()),
125+
Stream.iterate(1L, i -> i < value, i -> i + 1)
126+
.map(size -> generateCollection(
127+
Math.toIntExact(size),
128+
new TreeSet<>()
129+
))
130+
);
131+
}
132+
133+
private static String generateRandomString(int length) {
134+
// Include printable ASCII characters (32-126) which includes space and common characters
135+
return RANDOM.ints(length, 32, 127)
136+
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
137+
.toString();
138+
}
139+
140+
private static Collection<Object> generateCollection(int size, Collection<Object> collection) {
141+
for (int i = 0; i < size; i++) {
142+
collection.add("dummy_" + i); // Add dummy elements, content doesn't matter
143+
}
144+
return collection;
145+
}
146+
147+
private static Object generateArray(int size, Class<?> arrayClass) {
148+
var componentType = arrayClass.getComponentType();
149+
return Array.newInstance(componentType, size);
150+
}
151+
}

0 commit comments

Comments
 (0)