All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.tangly.gleam.model.TsvProperty Maven / Gradle / Ivy

There is a newer version: 0.2.7
Show newest version
/*
 * Copyright 2006-2020 Marcel Baumann
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain
 *  a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations
 *  under the License.
 */

package net.tangly.gleam.model;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import net.tangly.commons.lang.Strings;
import net.tangly.commons.lang.ThrowingIOExceptionConsumer;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.jetbrains.annotations.NotNull;

/**
 * The TSV property defines the mapping between a Java property and one or multiple cells in a TSV file. Two scenarios should be supported. The simple case is
 * the mapping of a Java property to exactly one cell in a TSV file. For example the mapping of a local date property to the textual ISO conform representation
 * in one TSV cell. The more complex case is the mapping of a Java property to multiple cells in a TSV file. For example a Java address object has to be mapped
 * so that each element of the address is stored in a specific cell. Both scenarios are supported through the same abstraction.
 *
 * @param columns   ordered list of columns in the TSV file used to encode the property. Simple properties have one column, complex properties mapped on
 *                  multiple columns have multiple values
 * @param getter    getter function to retrieve the property from a Java entity instance
 * @param setter    optional setter function to set the property of a Java entity instance
 * @param extractor extractor function to read and transform the set of TSV columns into a property value. Factory methods are provided to simplify the
 *                  definition of conversion in the case only one TSV column is used.
 * @param writer    insertor function to transform a property value into a set of TSV columns and write them. Factory methods are provided to simplify the *
 *                  definition of conversion in the case only one TSV column is used.
 * @param        class owning the Java property
 * @param        type of the property
 */
public record TsvProperty(List columns, Function getter, BiConsumer setter, Function extractor,
                                BiConsumer writer) {

    public static final Function CONVERT_BIGDECIMAL_FROM = e -> (e == null) ? BigDecimal.ZERO : new BigDecimal(e);
    public static final Function CONVERT_DATE_FROM = e -> (e != null) ? LocalDate.parse(e) : null;
    public static final Function CONVERT_DATETIME_FROM = e -> (e != null) ? LocalDateTime.parse(e) : null;

    public static  TsvProperty of(@NotNull TsvEntity entity, Function getter, BiConsumer setter) {
        List columns = entity.properties().stream().map(e -> e.columns().get(0)).collect(Collectors.toList());
        return of(columns, getter, setter, entity::imports, entity::exports);
    }

    /**
     * Define a property mapped to one column without any transformation steps between property type and string representation.
     *
     * @param column column in which the property will be stored in the TSV record
     * @param getter getter method to read the property from the entity
     * @param setter setter method to write the property into the entity
     * @param     entity type
     * @return new TSV property
     */
    public static  TsvProperty ofString(@NotNull String column, Function getter, BiConsumer setter) {
        return of(column, getter, setter, v -> v, u -> u);
    }

    public static  TsvProperty ofInt(@NotNull String column, Function getter, BiConsumer setter) {
        return of(column, getter, setter, v -> (v == null) ? 0 : Integer.parseInt(v), u -> u);
    }

    public static  TsvProperty ofLong(@NotNull String column, Function getter, BiConsumer setter) {
        return of(column, getter, setter, v -> (v == null) ? 0 : Long.parseLong(v), u -> u);
    }

    public static  TsvProperty ofBigDecimal(@NotNull String column, Function getter, BiConsumer setter) {
        return of(column, getter, setter, v -> (v == null) ? BigDecimal.ZERO : new BigDecimal(v), BigDecimal::toPlainString);
    }

    /**
     * Define a property mapped to one column with  a transformation step from the string representation to the property type.
     *
     * @param column      column in which the property will be stored in the TSV record
     * @param getter      getter method to read the property from the entity
     * @param setter      setter method to write the property into the entity
     * @param convertFrom transforms a string into the property type representation
     * @param          entity type
     * @param          property type
     * @return new TSV property
     */
    public static  TsvProperty of(@NotNull String column, Function getter, BiConsumer setter, Function convertFrom) {
        return of(column, getter, setter, convertFrom, u -> u);
    }

    /**
     * Define a property mapped to one column with  transformations between string and property type.
     *
     * @param column      column in which the property will be stored in the TSV record
     * @param getter      getter method to read the property from the entity
     * @param setter      setter method to write the property into the entity
     * @param convertFrom transforms a string into the property type representation
     * @param convertTo   transforms a property type representation into a string
     * @param          entity type
     * @param          property type
     * @return new TSV property
     */
    public static  TsvProperty of(@NotNull String column, Function getter, BiConsumer setter, @NotNull Function convertFrom,
                                              @NotNull Function convertTo) {
        Objects.requireNonNull(convertFrom);
        Objects.requireNonNull(convertTo);
        return of(List.of(column), getter, setter, record -> convertFrom.apply(Strings.emptyToNull(record.get(column))),
                (property, out) -> print(out, convertTo.apply(property)));
    }

    public static  TsvProperty of(@NotNull List columns, Function getter, BiConsumer setter,
                                              @NotNull Function extractor, @NotNull BiConsumer writer) {
        return new TsvProperty<>(columns, getter, setter, extractor, writer);
    }

    /**
     * Wrapper function to transformed checked exception to standard unchecked exception for usage in lambda expressions
     *
     * @param out   printer to write values
     * @param value value to be written
     */
    public static void print(@NotNull CSVPrinter out, Object value) {
        ThrowingIOExceptionConsumer.of(out::print).accept(value);
    }

    /**
     * Import the TSV value and set the associated property after an optional conversion.
     *
     * @param entity entity which property will be imported and set
     * @param record record containing the TSV values
     * @see TsvProperty#exports(Object, CSVPrinter)
     */
    public void imports(@NotNull T entity, @NotNull CSVRecord record) {
        U property = extractor.apply(record);
        setter.accept(entity, property);
    }

    /**
     * Export the TSV value from the associated property after an optional conversion.
     *
     * @param entity entity which property will be exported as TSV value
     * @param out    printer to write the TSV value(s)
     * @see TsvProperty#imports(Object, CSVRecord)
     */
    public void exports(@NotNull T entity, @NotNull CSVPrinter out) {
        U property = getter.apply(entity);
        writer.accept(property, out);
    }
}