package stexfires.record.filter;

import stexfires.record.TextRecord;

import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * A RecordFilter is a filter (boolean-valued function) for a {@link stexfires.record.TextRecord}.
 * <p>
 * It must be {@code thread-safe} and {@code non-interfering}.
 * It should be {@code immutable} and {@code stateless}.
 * <p>
 * This is a {@code functional interface} whose functional method is {@link #isValid(stexfires.record.TextRecord)}.
 *
 * @author Mathias Kalb
 * @see java.util.function.Predicate
 * @see java.util.stream.Stream#filter(Predicate)
 * @since 0.1
 */
@FunctionalInterface
public interface RecordFilter<T extends TextRecord> {

    static <T extends TextRecord> RecordFilter<T> of(Predicate<T> predicate) {
        Objects.requireNonNull(predicate);
        return predicate::test;
    }

    static <T extends TextRecord> RecordFilter<T> concatAnd(RecordFilter<? super T> firstRecordFilter,
                                                            RecordFilter<? super T> secondRecordFilter) {
        Objects.requireNonNull(firstRecordFilter);
        Objects.requireNonNull(secondRecordFilter);
        return record -> firstRecordFilter.isValid(record) && secondRecordFilter.isValid(record);
    }

    static <T extends TextRecord> RecordFilter<T> concatAnd(Stream<RecordFilter<T>> recordFilters) {
        Objects.requireNonNull(recordFilters);
        return recordFilters.reduce(r -> true, RecordFilter::and);
    }

    static <T extends TextRecord> RecordFilter<T> concatOr(RecordFilter<? super T> firstRecordFilter,
                                                           RecordFilter<? super T> secondRecordFilter) {
        Objects.requireNonNull(firstRecordFilter);
        Objects.requireNonNull(secondRecordFilter);
        return record -> firstRecordFilter.isValid(record) || secondRecordFilter.isValid(record);
    }

    static <T extends TextRecord> RecordFilter<T> concatOr(Stream<RecordFilter<T>> recordFilters) {
        Objects.requireNonNull(recordFilters);
        return recordFilters.reduce(r -> false, RecordFilter::or);
    }

    boolean isValid(T record);

    default Predicate<T> asPredicate() {
        return this::isValid;
    }

    default RecordFilter<T> and(RecordFilter<? super T> other) {
        Objects.requireNonNull(other);
        return record -> isValid(record) && other.isValid(record);
    }

    default RecordFilter<T> negate() {
        return record -> !isValid(record);
    }

    default RecordFilter<T> or(RecordFilter<? super T> other) {
        Objects.requireNonNull(other);
        return record -> isValid(record) || other.isValid(record);
    }

}

@@ -62,16 +62,61 @@
 tRecord;
+%0Aimport stexfires.record.mapper.RecordMapper;
 %0A%0Aimport
@@ -919,32 +919,465 @@
 e::test;%0A    %7D%0A%0A
+    static %3CT extends TextRecord, V extends TextRecord%3E RecordFilter%3CT%3E mapAndFilter(RecordMapper%3C? super T, ? extends V%3E recordMapper,%0A                                                                                     RecordFilter%3C? super V%3E recordFilter) %7B%0A        Objects.requireNonNull(recordMapper);%0A        Objects.requireNonNull(recordFilter);%0A        return record -%3E recordFilter.isValid(recordMapper.map(record));%0A    %7D%0A%0A
     static %3CT ex

Add 'mapAndFilter' method