ChronologyImpl.java

/*
 * Copyright (c) 2019, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.powsybl.glsk.commons.chronology;

import com.powsybl.glsk.commons.GlskException;

import org.threeten.extra.Interval;

import java.time.*;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

/**
 * @param <T> type of the objects stored in the data chronology
 * @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
 */
public final class ChronologyImpl<T> implements Chronology<T> {
    private final Map<Interval, T> storedIntervals = new HashMap<>();

    public static <T> Chronology<T> create() {
        return new ChronologyImpl<>();
    }

    private ChronologyImpl() {
    }

    private void store(T data, Interval intervalToStore) {
        if (storedIntervals.keySet().stream().anyMatch(interval -> interval.overlaps(intervalToStore))) {
            throw new GlskException("A data is already provided for some instant of the interval");
        }
        storedIntervals.put(intervalToStore, data);
    }

    @Override
    public void storeDataAtInstant(T data, Instant instant) {
        store(data, Interval.of(instant, Duration.ofHours(1)));
    }

    @Override
    public void storeDataAtInstant(T data, Instant instant, Duration duration) {
        store(data, Interval.of(instant, duration));
    }

    @Override
    public void storeDataAtInstant(T data, Instant instant, Period period) {
        Instant endInstant = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC).plus(period).toInstant();
        store(data, Interval.of(instant, endInstant));
    }

    @Override
    public void storeDataOnInterval(T data, Interval interval) {
        store(data, interval);
    }

    @Override
    public void storeDataBetweenInstants(T data, Instant from, Instant to) {
        store(data, Interval.of(from, to));
    }

    @Override
    public T selectInstant(Instant instant) {
        return selectInstant(instant, ReplacementStrategy.NO_REPLACEMENT);
    }

    @Override
    public T selectInstant(Instant instant, ReplacementStrategy replacementStrategy) {
        switch (replacementStrategy) {
            case NO_REPLACEMENT:
                return storedIntervals.entrySet().stream()
                        .filter(entry -> entry.getKey().contains(instant))
                        .map(Map.Entry::getValue)
                        .findFirst()
                        .orElse(null);
            case DATA_AT_PREVIOUS_INSTANT:
                return storedIntervals.entrySet().stream()
                        .filter(entry -> entry.getKey().isBefore(instant))
                        .sorted((entry1, entry2) -> entry2.getKey().getStart().compareTo(entry1.getKey().getStart()))
                        .map(Map.Entry::getValue)
                        .findFirst()
                        .orElse(null);
            case DATA_AT_NEXT_INSTANT:
                return storedIntervals.entrySet().stream()
                        .filter(entry -> entry.getKey().isAfter(instant))
                        .sorted(Comparator.comparing(entry -> entry.getKey().getStart()))
                        .map(Map.Entry::getValue)
                        .findFirst()
                        .orElse(null);
            default:
                throw new AssertionError("Invalid replacement strategy");
        }
    }
}