BasicEntity.java

package org.vlaskin.moexiss.entity.base;

import lombok.ToString;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Базовая модель строки таблицы MOEX ISS с типизированным доступом к динамическим полям.
 *
 * @param <E> перечисление полей конкретной таблицы
 */
@ToString
public abstract class BasicEntity<E extends Enum<?> & TypedField>
{
    protected final Map<E, Boolean> booleanFields;
    protected final Map<E, Integer> integerFields;
    protected final Map<E, Long> longFields;
    protected final Map<E, Double> doubleFields;
    protected final Map<E, LocalDate> localDateFields;
    protected final Map<E, LocalTime> localTimeFields;
    protected final Map<E, LocalDateTime> localDateTimeFields;
    protected final Map<E, String> stringFields;

    protected BasicEntity(Map<E, Boolean> booleanFields,
                          Map<E, Integer> integerFields,
                          Map<E, Long> longFields,
                          Map<E, Double> doubleFields,
                          Map<E, LocalDate> localDateFields,
                          Map<E, LocalTime> localTimeFields,
                          Map<E, LocalDateTime> localDateTimeFields,
                          Map<E, String> stringFields)
    {
        this.booleanFields = Map.copyOf(booleanFields);
        this.integerFields = Map.copyOf(integerFields);
        this.longFields = Map.copyOf(longFields);
        this.doubleFields = Map.copyOf(doubleFields);
        this.localDateFields = Map.copyOf(localDateFields);
        this.localTimeFields = Map.copyOf(localTimeFields);
        this.localDateTimeFields = Map.copyOf(localDateTimeFields);
        this.stringFields = Map.copyOf(stringFields);
    }

    protected BasicEntity()
    {
        booleanFields = new HashMap<>();
        integerFields = new HashMap<>();
        longFields = new HashMap<>();
        doubleFields = new HashMap<>();
        localDateFields = new HashMap<>();
        localTimeFields = new HashMap<>();
        localDateTimeFields = new HashMap<>();
        stringFields = new HashMap<>();
    }

    /**
     * Возвращает значение поля и проверяет запрошенный тип, даже если значение отсутствует.
     *
     * @param field поле модели
     * @param type ожидаемый Java-тип поля
     * @param <T> тип возвращаемого значения
     * @return значение или {@code null}, если поле отсутствует в ответе
     * @throws NullPointerException если поле или тип не заданы
     * @throws IllegalArgumentException если ожидаемый тип не совпадает с типом поля
     */
    public final <T> T get(E field, Class<T> type)
    {
        Objects.requireNonNull(field, "Field must not be null");
        Objects.requireNonNull(type, "Field type must not be null");
        if (!field.getType().equals(type))
            throw new IllegalArgumentException(
                    "Field " + field + " has type " + field.getType().getSimpleName()
                            + ", but " + type.getSimpleName() + " was requested");
        return type.cast(getFields(type).get(field));
    }

    /**
     * Проверяет, присутствует ли непустое значение поля в ответе.
     *
     * @param field поле модели
     * @return {@code true}, если ответ содержит непустое значение
     * @throws NullPointerException если поле не задано
     */
    public final boolean has(E field)
    {
        Objects.requireNonNull(field, "Field must not be null");
        return getFields(field.getType()).containsKey(field);
    }

    private Map<E, ?> getFields(Class<?> type)
    {
        if (Boolean.class.equals(type))
            return booleanFields;
        if (Integer.class.equals(type))
            return integerFields;
        if (Long.class.equals(type))
            return longFields;
        if (Double.class.equals(type))
            return doubleFields;
        if (LocalDate.class.equals(type))
            return localDateFields;
        if (LocalTime.class.equals(type))
            return localTimeFields;
        if (LocalDateTime.class.equals(type))
            return localDateTimeFields;
        if (String.class.equals(type))
            return stringFields;
        throw new IllegalArgumentException("Unsupported field type: " + type.getName());
    }
}