package org.textfiledatatools.core.mapper;

import org.textfiledatatools.core.Record;

import java.util.Objects;
import java.util.function.Function;

/**
 * A RecordMapper maps a {@link Record} to another record.
 * <p>
 * It must be immutable and thread-safe.
 * It must be <code>non-interfering</code> and <code>stateless</code>.
 * <p>
 * This is a functional interface whose functional method is {@link #map(Record)}.
 *
 * @author Mathias Kalb
 * @see java.util.function.Function
 * @see java.util.stream.Stream#map(Function)
 * @since 0.1
 */
@FunctionalInterface
public interface RecordMapper<T extends Record, R extends Record> {

    static <T extends Record, V extends Record, R extends Record> RecordMapper<T, R> concat(RecordMapper<? super T, ? extends V> firstRecordMapper,
                                                                                            RecordMapper<? super V, ? extends R> secondRecordMapper) {
        Objects.requireNonNull(firstRecordMapper);
        Objects.requireNonNull(secondRecordMapper);
        return (T record) -> secondRecordMapper.map(firstRecordMapper.map(record));
    }

    static <T extends Record, V extends Record, R extends Record> RecordMapper<T, R> concat(RecordMapper<? super T, ? extends V> firstRecordMapper,
                                                                                            RecordMapper<? super V, ? extends V> secondRecordMapper,
                                                                                            RecordMapper<? super V, ? extends R> thirdRecordMapper) {
        Objects.requireNonNull(firstRecordMapper);
        Objects.requireNonNull(secondRecordMapper);
        return (T record) -> thirdRecordMapper.map(secondRecordMapper.map(firstRecordMapper.map(record)));
    }

    R map(T record);

    default <V extends Record> RecordMapper<V, R> compose(RecordMapper<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V record) -> map(before.map(record));
    }

    default <V extends Record> RecordMapper<T, V> andThen(RecordMapper<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T record) -> after.map(map(record));
    }

}

package org.textfiledatatools.core.mapper;

import org.textfiledatatools.core.Record;

import java.util.Objects;
import java.util.function.Function;

/**
 * A RecordMapper maps a {@link Record} to another record.
 * <p>
 * It must be immutable and thread-safe.
 * It must be <code>non-interfering</code> and <code>stateless</code>.
 * <p>
 * This is a functional interface whose functional method is {@link #map(Record)}.
 *
 * @author Mathias Kalb
 * @see java.util.function.Function
 * @see java.util.stream.Stream#map(Function)
 * @since 0.1
 */
@FunctionalInterface
public interface RecordMapper<T extends Record, R extends Record> {

    static <T extends Record, V extends Record, R extends Record> RecordMapper<T, R> concat(RecordMapper<? super T, ? extends V> firstRecordMapper,
                                                                                            RecordMapper<? super V, ? extends R> secondRecordMapper) {
        Objects.requireNonNull(firstRecordMapper);
        Objects.requireNonNull(secondRecordMapper);
        return (T record) -> secondRecordMapper.map(firstRecordMapper.map(record));
    }

    static <T extends Record, V extends Record, R extends Record> RecordMapper<T, R> concat(RecordMapper<? super T, ? extends V> firstRecordMapper,
                                                                                            RecordMapper<? super V, ? extends V> secondRecordMapper,
                                                                                            RecordMapper<? super V, ? extends R> thirdRecordMapper) {
        Objects.requireNonNull(firstRecordMapper);
        Objects.requireNonNull(secondRecordMapper);
        Objects.requireNonNull(thirdRecordMapper);
        return (T record) -> thirdRecordMapper.map(secondRecordMapper.map(firstRecordMapper.map(record)));
    }

    R map(T record);

    default <V extends Record> RecordMapper<V, R> compose(RecordMapper<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V record) -> map(before.map(record));
    }

    default <V extends Record> RecordMapper<T, V> andThen(RecordMapper<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T record) -> after.map(map(record));
    }

}

Fix RecordMapper