Skip to content

Commit 5c29633

Browse files
committed
Merge branch 'develop' of git@github.com:mvallim/java-fluent-validator.git into feature/update-workflow
2 parents 75bf461 + baf1572 commit 5c29633

File tree

7 files changed

+143
-51
lines changed

7 files changed

+143
-51
lines changed

src/main/java/br/com/fluentvalidator/AbstractValidator.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import br.com.fluentvalidator.builder.RuleBuilderProperty;
2929
import br.com.fluentvalidator.context.ProcessorContext;
3030
import br.com.fluentvalidator.context.ValidationContext;
31+
import br.com.fluentvalidator.context.ValidationContext.Context;
3132
import br.com.fluentvalidator.context.ValidationResult;
3233
import br.com.fluentvalidator.rule.Rule;
3334
import br.com.fluentvalidator.rule.RuleBuilderCollectionImpl;
@@ -239,8 +240,10 @@ public <P> P getPropertyOnContext(final String property, final Class<P> clazz) {
239240
*/
240241
@Override
241242
public ValidationResult validate(final T instance) {
242-
ruleProcessor.process(instance, this);
243-
return ValidationContext.get().getValidationResult();
243+
try (final Context context = ValidationContext.get()) {
244+
ruleProcessor.process(instance, this);
245+
return context.getValidationResult();
246+
}
244247
}
245248

246249
/**

src/main/java/br/com/fluentvalidator/context/ProcessorContext.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public static void remove() {
6767
* <li>{@link #get()} - Retrieves the value of the top counter, or zero if the stack is empty.</li>
6868
* </ul>
6969
*/
70-
public static final class Context {
70+
public static final class Context implements AutoCloseable {
7171

7272
private final Deque<AtomicInteger> stackCounter = new ConcurrentLinkedDeque<>();
7373

@@ -113,6 +113,10 @@ public Integer get() {
113113
return stackCounter.isEmpty() ? 0 : stackCounter.peek().get();
114114
}
115115

116+
@Override
117+
public void close() {
118+
ProcessorContext.remove();
119+
}
116120
}
117121

118122
}

src/main/java/br/com/fluentvalidator/context/ValidationContext.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public static void remove() {
9090
* This class is intended for internal use within the validation framework.
9191
* </p>
9292
*/
93-
public static final class Context {
93+
public static final class Context implements AutoCloseable {
9494

9595
private final Map<String, Object> properties = new ConcurrentHashMap<>();
9696

@@ -146,6 +146,10 @@ public <P> P getProperty(final String property, final Class<P> clazz) {
146146
return clazz.cast(properties.getOrDefault(property, null));
147147
}
148148

149+
@Override
150+
public void close() {
151+
ValidationContext.remove();
152+
}
149153
}
150154

151155
}

src/main/java/br/com/fluentvalidator/rule/RuleProcessorStrategy.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.stream.Collectors;
2121

2222
import br.com.fluentvalidator.context.ProcessorContext;
23+
import br.com.fluentvalidator.context.ProcessorContext.Context;
2324

2425
public interface RuleProcessorStrategy {
2526

@@ -32,23 +33,21 @@ default <E> boolean process(final E value, final Rule<E> rule) {
3233
}
3334

3435
default <E> boolean process(final Object obj, final Collection<E> values, final Rule<E> rule) {
35-
ProcessorContext.get().create();
36-
final boolean allMatch = values.stream().map(value -> {
37-
ProcessorContext.get().inc();
38-
return this.process(obj, value, rule);
39-
}).collect(Collectors.toList()).stream().allMatch(result -> result);
40-
ProcessorContext.get().remove();
41-
return allMatch;
36+
try (final Context context = ProcessorContext.get()) {
37+
return values.stream().map(value -> {
38+
context.inc();
39+
return this.process(obj, value, rule);
40+
}).collect(Collectors.toList()).stream().allMatch(result -> result);
41+
}
4242
}
4343

4444
default <E> boolean process(final Collection<E> values, final Rule<E> rule) {
45-
ProcessorContext.get().create();
46-
final boolean allMatch = values.stream().map(value -> {
47-
ProcessorContext.get().inc();
48-
return this.process(value, rule);
49-
}).collect(Collectors.toList()).stream().allMatch(result -> result);
50-
ProcessorContext.get().remove();
51-
return allMatch;
45+
try (final Context context = ProcessorContext.get()) {
46+
return values.stream().map(value -> {
47+
context.inc();
48+
return this.process(value, rule);
49+
}).collect(Collectors.toList()).stream().allMatch(result -> result);
50+
}
5251
}
5352

5453
default <E> boolean process(final Object obj, final E value, final Collection<Rule<E>> rules) {

src/test/java/br/com/fluentvalidator/ValidatorTest.java

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@
1616

1717
package br.com.fluentvalidator;
1818

19+
import br.com.fluentvalidator.context.Error;
20+
import br.com.fluentvalidator.context.ProcessorContext;
21+
import br.com.fluentvalidator.context.ValidationResult;
22+
import br.com.fluentvalidator.model.Bill;
23+
import br.com.fluentvalidator.model.Boy;
24+
import br.com.fluentvalidator.model.Girl;
25+
import br.com.fluentvalidator.model.Parent;
26+
import br.com.fluentvalidator.validator.ValidatorBill;
27+
import br.com.fluentvalidator.validator.ValidatorErrorPredicate;
28+
import br.com.fluentvalidator.validator.ValidatorParent;
29+
import org.junit.jupiter.api.Test;
30+
31+
import java.time.LocalDate;
32+
import java.util.ArrayList;
33+
import java.util.Arrays;
34+
import java.util.Collection;
35+
import java.util.List;
36+
import java.util.concurrent.ConcurrentLinkedQueue;
37+
import java.util.concurrent.ExecutionException;
38+
import java.util.concurrent.ExecutorService;
39+
import java.util.concurrent.Executors;
40+
import java.util.concurrent.TimeUnit;
41+
1942
import static br.com.fluentvalidator.predicate.LogicalPredicate.not;
2043
import static br.com.fluentvalidator.predicate.StringPredicate.stringEmptyOrNull;
2144
import static org.hamcrest.MatcherAssert.assertThat;
@@ -27,31 +50,11 @@
2750
import static org.hamcrest.Matchers.hasSize;
2851
import static org.hamcrest.Matchers.not;
2952
import static org.hamcrest.Matchers.nullValue;
53+
import static org.junit.jupiter.api.Assertions.assertEquals;
3054
import static org.junit.jupiter.api.Assertions.assertFalse;
55+
import static org.junit.jupiter.api.Assertions.assertThrows;
3156
import static org.junit.jupiter.api.Assertions.assertTrue;
3257

33-
import java.time.LocalDate;
34-
import java.util.ArrayList;
35-
import java.util.Arrays;
36-
import java.util.Collection;
37-
import java.util.List;
38-
import java.util.concurrent.ConcurrentLinkedQueue;
39-
import java.util.concurrent.ExecutionException;
40-
import java.util.concurrent.ExecutorService;
41-
import java.util.concurrent.Executors;
42-
import java.util.concurrent.TimeUnit;
43-
44-
import org.junit.jupiter.api.Test;
45-
46-
import br.com.fluentvalidator.context.Error;
47-
import br.com.fluentvalidator.context.ValidationResult;
48-
import br.com.fluentvalidator.model.Bill;
49-
import br.com.fluentvalidator.model.Boy;
50-
import br.com.fluentvalidator.model.Girl;
51-
import br.com.fluentvalidator.model.Parent;
52-
import br.com.fluentvalidator.validator.ValidatorBill;
53-
import br.com.fluentvalidator.validator.ValidatorParent;
54-
5558
class ValidatorTest {
5659

5760
@Test
@@ -671,7 +674,44 @@ void testSuccessWhenBillDueDateIsExactlyThreeYears() {
671674
assertTrue(validate.isValid());
672675
}
673676

674-
class StringValidator extends AbstractValidator<String> {
677+
@Test
678+
public void testSuccessWhenBrokenPredicate() {
679+
final Validator<Bill> validator = new ValidatorErrorPredicate();
680+
681+
// First validation: bill with null description causes NullPointerException
682+
// This simulates a corrupted or incomplete data scenario
683+
final Bill billWithNullDescription = new Bill(null, 0F, LocalDate.now());
684+
685+
assertThrows(NullPointerException.class, () -> validator.validate(billWithNullDescription));
686+
687+
// Second validation: valid electricity bill with numeric code
688+
// ValidationContext and ProcessorContext should be clean after the exception
689+
final Bill electricityBill = new Bill("12345", 150.75F, LocalDate.now().plusDays(30));
690+
691+
assertTrue(validator.validate(electricityBill).isValid());
692+
693+
}
694+
695+
@Test
696+
public void testSuccessWhenBrokenCollectionPredicate() {
697+
final Validator<Bill> validator = new ValidatorErrorPredicate();
698+
699+
// First validation: bill with non-numeric description causes NumberFormatException
700+
// when the validator tries to parse it as integer in the collection rule
701+
final Bill billWithInvalidCodes = new Bill("WATER-BILL", 85.50F, LocalDate.now());
702+
703+
assertThrows(NumberFormatException.class, () -> validator.validate(billWithInvalidCodes));
704+
705+
// Second validation: valid bill with numeric service codes separated by comma
706+
// ValidationContext and ProcessorContext should be clean after the exception
707+
final Bill billWithValidCodes = new Bill("100,200,300", 250.00F, LocalDate.now().plusDays(15));
708+
709+
assertEquals(0, (int) ProcessorContext.get().get());
710+
assertTrue(validator.validate(billWithValidCodes).isValid());
711+
712+
}
713+
714+
static class StringValidator extends AbstractValidator<String> {
675715

676716
@Override
677717
public void rules() {
@@ -687,7 +727,7 @@ public void rules() {
687727

688728
}
689729

690-
class String2Validator extends AbstractValidator<String> {
730+
static class String2Validator extends AbstractValidator<String> {
691731

692732
@Override
693733
public void rules() {
@@ -698,7 +738,7 @@ public void rules() {
698738

699739
}
700740

701-
class String3Validator extends AbstractValidator<Collection<String>> {
741+
static class String3Validator extends AbstractValidator<Collection<String>> {
702742

703743
@Override
704744
public void rules() {

src/test/java/br/com/fluentvalidator/predicate/MapPredicateTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ void testMapGetKPredicateOfV() {
6060

6161
@Test
6262
void testMapGetFunctionOfTKPredicateOfV() {
63-
assertThat(mapGet("a", stringSize(6)).test(map), equalTo(true));
64-
assertThat(mapGet("a", isNumber()).test(map), equalTo(true));
65-
assertThat(mapGet("a", not(stringEmptyOrNull())).test(map), equalTo(true));
66-
assertThat(mapGet("b", stringSize(5)).test(map), equalTo(true));
67-
assertThat(mapGet("b", isAlpha()).test(map), equalTo(true));
68-
assertThat(mapGet("b", not(stringEmptyOrNull())).test(map), equalTo(true));
69-
assertThat(mapGet("c", stringEmptyOrNull()).test(map), equalTo(true));
70-
assertThat(mapGet("c", not(stringEmptyOrNull())).test(map), equalTo(false));
63+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", stringSize(6)).test(map), equalTo(true));
64+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", isNumber()).test(map), equalTo(true));
65+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", not(stringEmptyOrNull())).test(map), equalTo(true));
66+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("b", stringSize(5)).test(map), equalTo(true));
67+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("b", isAlpha()).test(map), equalTo(true));
68+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("b", not(stringEmptyOrNull())).test(map), equalTo(true));
69+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("c", stringEmptyOrNull()).test(map), equalTo(true));
70+
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("c", not(stringEmptyOrNull())).test(map), equalTo(false));
7171
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", null).test(map), equalTo(false));
7272
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet(null, not(stringEmptyOrNull())).test(map), equalTo(false));
7373
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package br.com.fluentvalidator.validator;
2+
3+
import br.com.fluentvalidator.AbstractValidator;
4+
import br.com.fluentvalidator.model.Bill;
5+
6+
import java.util.Arrays;
7+
8+
import static br.com.fluentvalidator.predicate.LogicalPredicate.not;
9+
import static br.com.fluentvalidator.predicate.ObjectPredicate.nullValue;
10+
11+
public class ValidatorErrorPredicate extends AbstractValidator<Bill> {
12+
13+
@Override
14+
public void rules() {
15+
16+
17+
ruleFor(bill -> bill)
18+
.must(not(nullValue()))
19+
.withMessage("Object is required")
20+
.withFieldName("root")
21+
22+
.must(bill -> bill.getValue() > 1)
23+
.when(not(nullValue())).withMessage("Value must be greater than 1");
24+
25+
26+
ruleFor(bill -> bill.getDescription())
27+
.must(not(nullValue()))
28+
.when(e -> e.equalsIgnoreCase("1"));
29+
30+
ruleForEach(bill -> Arrays.asList(bill.getDescription().split(",")))
31+
.whenever(not(nullValue()))
32+
.withValidator(new AbstractValidator<String>() {
33+
@Override
34+
public void rules() {
35+
ruleFor(s -> s)
36+
.must(s -> Integer.parseInt(s) > 1);
37+
}
38+
});
39+
40+
41+
}
42+
}

0 commit comments

Comments
 (0)