StageUtils.java

/*
 * This file is part of Indicators.
 *
 * Indicators is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Indicators is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indicators. If not, see <https://www.gnu.org/licenses/>.
 */
package fr.inrae.agroclim.indicators.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import fr.inrae.agroclim.indicators.model.Evaluation;
import fr.inrae.agroclim.indicators.model.data.phenology.AnnualStageData;
import fr.inrae.agroclim.indicators.model.data.phenology.Stage;
import fr.inrae.agroclim.indicators.model.data.phenology.StageDelta;
import lombok.extern.log4j.Log4j2;

/**
 * Helper class to handle Stages.
 */
@Log4j2
public final class StageUtils {

    /**
     * @param evaluation evaluation to check
     * @return missing stage ⇒ phases in the evaluation where the stage is used
     */
    public static Map<String, List<String>> check(final Evaluation evaluation) {
        final Map<String, List<String>> errors = new HashMap<>();
        Objects.requireNonNull(evaluation, "evaluation must not be null");
        Objects.requireNonNull(evaluation.getResourceManager(), "resource manager must not be null");
        Objects.requireNonNull(evaluation.getResourceManager().getPhenologicalResource(),
                "pheno resource must not be null");
        final List<String> stages = evaluation.getResourceManager().getPhenologicalResource().getStages();
        Objects.requireNonNull(stages, "stages must not be null");
        evaluation.getPhases().forEach(p -> {
            /* Nom du stade de départ de la phase */
            final String start = p.getFirstIndicator().getName();
            if (!stages.contains(start)) {
                if (!errors.containsKey(start)) {
                    errors.put(start, new ArrayList<>());
                }
                errors.get(start).add(p.getId());
            }
            /* Nom du stage de fin de la phase */
            final String end = p.getName();
            if (!stages.contains(end)) {
                if (!errors.containsKey(end)) {
                    errors.put(end, new ArrayList<>());
                }
                errors.get(end).add(p.getId());
            }
        });
        return errors;
    }

    /**
     * Extract StageDelta from stage name.
     *
     * @param stage stage name
     * @return stage delta
     */
    public static StageDelta getDeltaFromStage(final String stage) {
        if (stage.contains("p")) {
            final int i = stage.indexOf("p");
            final String name = stage.substring(0, i);
            final String days = stage.substring(i + 1);
            return new StageDelta(name, Integer.valueOf(days));
        } else if (stage.contains("m")) {
            final int i = stage.indexOf("m");
            final String name = stage.substring(0, i);
            final String days = stage.substring(i + 1);
            return new StageDelta(name, -1 * Integer.parseInt(days));
        }
        return new StageDelta(stage, 0);
    }
    /**
     * Extract StageDelta from stage name.
     *
     * @param stages list of stage names
     * @return list of stage deltas
     */
    public static List<StageDelta> getDeltasFromStages(final List<String> stages) {
        return stages.stream() //
                .filter(stage -> stage.startsWith("s")) //
                .map(StageUtils::getDeltaFromStage) //
                .filter(Objects::nonNull) //
                .collect(Collectors.toList());
    }

    /**
     * @param stage stage to represent
     * @return representation of stage
     */
    public static String repr(final StageDelta stage) {
        final Integer days = stage.getDays();
        if (days == null || days == 0) {
            return stage.getStage();
        } else if (days > 0) {
            return stage.getStage() + "p" + Math.abs(days);
        } else {
            return stage.getStage() + "m" + Math.abs(days);
        }
    }

    /**
     * SoilCalculator does not need sowing date in stages.
     *
     * @param all stages with sowing date
     * @param sowingDate day of year of sowing date
     * @return stages without sowing date
     */
    public static List<AnnualStageData> sanitizeStagesForSoil(final List<AnnualStageData> all, final int sowingDate) {
        final List<AnnualStageData> newData = new ArrayList<>();
        if (all.isEmpty()) {
            LOGGER.error("Strange no stages with sowing date were given");
        }
        all.stream().map(data -> {
            AnnualStageData adata;
            try {
                adata = data.clone();
                newData.add(adata);
            } catch (final CloneNotSupportedException ex) {
                throw new RuntimeException("This should never occur!", ex);
            }
            return adata;
        }).forEachOrdered(data -> {
            Stage toRemove = null;
            for (final Stage stage : data.getStages()) {
                if (sowingDate == stage.getValue()) {
                    toRemove = stage;
                }
            }
            if (toRemove != null) {
                data.getStages().remove(toRemove);
            }
        });
        // fill first year with second year
        if (newData.get(0).getStages().size() != Stage.FOUR) {
            final int year = newData.get(0).getYear();
            try {
                if (newData.size() == 1) {
                    // use the same year twice if only one year is given
                    newData.add(newData.get(0).clone());
                    newData.get(1).setYear(year + 1);
                } else {
                    newData.set(0, newData.get(1).clone());
                    newData.get(0).setYear(year);
                }
            } catch (final CloneNotSupportedException ex) {
                throw new RuntimeException("This should never occur!", ex);
            }
        }
        // fill last year with previous year
        final int lastIndex = newData.size() - 1;
        if (newData.get(lastIndex).getStages().size() != Stage.FOUR) {
            final int year = newData.get(lastIndex).getYear();
            try {
                newData.set(lastIndex, newData.get(lastIndex - 1).clone());
                newData.get(lastIndex).setYear(year);
            } catch (final CloneNotSupportedException ex) {
                throw new RuntimeException("This should never occur!", ex);
            }
        }
        return newData;
    }

    /**
     * No constructor for helper class.
     */
    private StageUtils() {
    }
}