Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/main/java/br/com/fluentvalidator/AbstractValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import br.com.fluentvalidator.builder.RuleBuilderProperty;
import br.com.fluentvalidator.context.ProcessorContext;
import br.com.fluentvalidator.context.ValidationContext;
import br.com.fluentvalidator.context.ValidationContext.Context;
import br.com.fluentvalidator.context.ValidationResult;
import br.com.fluentvalidator.rule.Rule;
import br.com.fluentvalidator.rule.RuleBuilderCollectionImpl;
Expand Down Expand Up @@ -121,8 +122,10 @@ public <P> P getPropertyOnContext(final String property, final Class<P> clazz) {
*/
@Override
public ValidationResult validate(final T instance) {
ruleProcessor.process(instance, this);
return ValidationContext.get().getValidationResult();
try (final Context context = ValidationContext.get()) {
ruleProcessor.process(instance, this);
return context.getValidationResult();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public static void remove() {
/**
* Context of processor
*/
public static final class Context {
public static final class Context implements AutoCloseable {

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

Expand All @@ -74,6 +74,10 @@ public Integer get() {
return stackCounter.isEmpty() ? 0 : stackCounter.peek().get();
}

@Override
public void close() {
ProcessorContext.remove();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static void remove() {
/**
* Context of validation
*/
public static final class Context {
public static final class Context implements AutoCloseable {

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

Expand Down Expand Up @@ -99,6 +99,10 @@ public <P> P getProperty(final String property, final Class<P> clazz) {
return clazz.cast(properties.getOrDefault(property, null));
}

@Override
public void close() {
ValidationContext.remove();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.stream.Collectors;

import br.com.fluentvalidator.context.ProcessorContext;
import br.com.fluentvalidator.context.ProcessorContext.Context;

public interface RuleProcessorStrategy {

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

default <E> boolean process(final Object obj, final Collection<E> values, final Rule<E> rule) {
ProcessorContext.get().create();
final boolean allMatch = values.stream().map(value -> {
ProcessorContext.get().inc();
return this.process(obj, value, rule);
}).collect(Collectors.toList()).stream().allMatch(result -> result);
ProcessorContext.get().remove();
return allMatch;
try (final Context context = ProcessorContext.get()) {
return values.stream().map(value -> {
context.inc();
return this.process(obj, value, rule);
}).collect(Collectors.toList()).stream().allMatch(result -> result);
}
}

default <E> boolean process(final Collection<E> values, final Rule<E> rule) {
ProcessorContext.get().create();
final boolean allMatch = values.stream().map(value -> {
ProcessorContext.get().inc();
return this.process(value, rule);
}).collect(Collectors.toList()).stream().allMatch(result -> result);
ProcessorContext.get().remove();
return allMatch;
try (final Context context = ProcessorContext.get()) {
return values.stream().map(value -> {
context.inc();
return this.process(value, rule);
}).collect(Collectors.toList()).stream().allMatch(result -> result);
}
}

default <E> boolean process(final Object obj, final E value, final Collection<Rule<E>> rules) {
Expand Down
90 changes: 65 additions & 25 deletions src/test/java/br/com/fluentvalidator/ValidatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@

package br.com.fluentvalidator;

import br.com.fluentvalidator.context.Error;
import br.com.fluentvalidator.context.ProcessorContext;
import br.com.fluentvalidator.context.ValidationResult;
import br.com.fluentvalidator.model.Bill;
import br.com.fluentvalidator.model.Boy;
import br.com.fluentvalidator.model.Girl;
import br.com.fluentvalidator.model.Parent;
import br.com.fluentvalidator.validator.ValidatorBill;
import br.com.fluentvalidator.validator.ValidatorErrorPredicate;
import br.com.fluentvalidator.validator.ValidatorParent;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static br.com.fluentvalidator.predicate.LogicalPredicate.not;
import static br.com.fluentvalidator.predicate.StringPredicate.stringEmptyOrNull;
import static org.hamcrest.MatcherAssert.assertThat;
Expand All @@ -27,31 +50,11 @@
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.Test;

import br.com.fluentvalidator.context.Error;
import br.com.fluentvalidator.context.ValidationResult;
import br.com.fluentvalidator.model.Bill;
import br.com.fluentvalidator.model.Boy;
import br.com.fluentvalidator.model.Girl;
import br.com.fluentvalidator.model.Parent;
import br.com.fluentvalidator.validator.ValidatorBill;
import br.com.fluentvalidator.validator.ValidatorParent;

class ValidatorTest {

@Test
Expand Down Expand Up @@ -671,7 +674,44 @@ void testSuccessWhenBillDueDateIsExactlyThreeYears() {
assertTrue(validate.isValid());
}

class StringValidator extends AbstractValidator<String> {
@Test
public void testSuccessWhenBrokenPredicate() {
final Validator<Bill> validator = new ValidatorErrorPredicate();

// First validation: bill with null description causes NullPointerException
// This simulates a corrupted or incomplete data scenario
final Bill billWithNullDescription = new Bill(null, 0F, LocalDate.now());

assertThrows(NullPointerException.class, () -> validator.validate(billWithNullDescription));

// Second validation: valid electricity bill with numeric code
// ValidationContext and ProcessorContext should be clean after the exception
final Bill electricityBill = new Bill("12345", 150.75F, LocalDate.now().plusDays(30));

assertTrue(validator.validate(electricityBill).isValid());

}

@Test
public void testSuccessWhenBrokenCollectionPredicate() {
final Validator<Bill> validator = new ValidatorErrorPredicate();

// First validation: bill with non-numeric description causes NumberFormatException
// when the validator tries to parse it as integer in the collection rule
final Bill billWithInvalidCodes = new Bill("WATER-BILL", 85.50F, LocalDate.now());

assertThrows(NumberFormatException.class, () -> validator.validate(billWithInvalidCodes));

// Second validation: valid bill with numeric service codes separated by comma
// ValidationContext and ProcessorContext should be clean after the exception
final Bill billWithValidCodes = new Bill("100,200,300", 250.00F, LocalDate.now().plusDays(15));

assertEquals(0, (int) ProcessorContext.get().get());
assertTrue(validator.validate(billWithValidCodes).isValid());

}

static class StringValidator extends AbstractValidator<String> {

@Override
public void rules() {
Expand All @@ -687,7 +727,7 @@ public void rules() {

}

class String2Validator extends AbstractValidator<String> {
static class String2Validator extends AbstractValidator<String> {

@Override
public void rules() {
Expand All @@ -698,7 +738,7 @@ public void rules() {

}

class String3Validator extends AbstractValidator<Collection<String>> {
static class String3Validator extends AbstractValidator<Collection<String>> {

@Override
public void rules() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ void testMapGetKPredicateOfV() {

@Test
void testMapGetFunctionOfTKPredicateOfV() {
assertThat(mapGet("a", stringSize(6)).test(map), equalTo(true));
assertThat(mapGet("a", isNumber()).test(map), equalTo(true));
assertThat(mapGet("a", not(stringEmptyOrNull())).test(map), equalTo(true));
assertThat(mapGet("b", stringSize(5)).test(map), equalTo(true));
assertThat(mapGet("b", isAlpha()).test(map), equalTo(true));
assertThat(mapGet("b", not(stringEmptyOrNull())).test(map), equalTo(true));
assertThat(mapGet("c", stringEmptyOrNull()).test(map), equalTo(true));
assertThat(mapGet("c", not(stringEmptyOrNull())).test(map), equalTo(false));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", stringSize(6)).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", isNumber()).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", not(stringEmptyOrNull())).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("b", stringSize(5)).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("b", isAlpha()).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("b", not(stringEmptyOrNull())).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("c", stringEmptyOrNull()).test(map), equalTo(true));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("c", not(stringEmptyOrNull())).test(map), equalTo(false));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet("a", null).test(map), equalTo(false));
assertThat(MapPredicate.<String, String, Map<String, String>>mapGet(null, not(stringEmptyOrNull())).test(map), equalTo(false));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package br.com.fluentvalidator.validator;

import br.com.fluentvalidator.AbstractValidator;
import br.com.fluentvalidator.model.Bill;

import java.util.Arrays;

import static br.com.fluentvalidator.predicate.LogicalPredicate.not;
import static br.com.fluentvalidator.predicate.ObjectPredicate.nullValue;

public class ValidatorErrorPredicate extends AbstractValidator<Bill> {

@Override
public void rules() {


ruleFor(bill -> bill)
.must(not(nullValue()))
.withMessage("Object is required")
.withFieldName("root")

.must(bill -> bill.getValue() > 1)
.when(not(nullValue())).withMessage("Value must be greater than 1");


ruleFor(bill -> bill.getDescription())
.must(not(nullValue()))
.when(e -> e.equalsIgnoreCase("1"));

ruleForEach(bill -> Arrays.asList(bill.getDescription().split(",")))
.whenever(not(nullValue()))
.withValidator(new AbstractValidator<String>() {
@Override
public void rules() {
ruleFor(s -> s)
.must(s -> Integer.parseInt(s) > 1);
}
});


}
}
Loading