SoilLoaderProxy.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.model.data.soil;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import fr.inrae.agroclim.indicators.model.TimeScale;
import fr.inrae.agroclim.indicators.model.data.DataLoadingListenerHandler;
import fr.inrae.agroclim.indicators.model.data.Resource;
import fr.inrae.agroclim.indicators.model.data.Variable;
import fr.inrae.agroclim.indicators.model.data.climate.ClimaticDailyData;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlTransient;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

/**
 * Proxy to load soil data.
 *
 * Loader is chosen according to properties (XML tag).
 *
 * The proxy is initialized from evaluation XML file.
 *
 * Last changed : $Date$
 *
 * @author $Author$
 * @version $Revision$
 */
@Log4j2
@XmlAccessorType(XmlAccessType.FIELD)
public final class SoilLoaderProxy extends DataLoadingListenerHandler implements HasSoilCalculatorParams, SoilLoader {
    /**
     * UID for serialization.
     */
    private static final long serialVersionUID = 4796900688021151186L;

    /**
     * Climatic daily data used to compute soil water balance.
     */
    @XmlTransient
    private List<ClimaticDailyData> climaticDailyData;

    /**
     * CSV file.
     */
    @XmlElement(name = "file")
    @Getter
    @Setter
    private File file;

    /**
     * Headers of CSV file.
     */
    @XmlElement(name = "header")
    @Getter
    @Setter
    private String[] headers;

    /**
     * Coefficient cultural en situation non stressée durant la période initiale
     * de la saison [0-1] (semis-levee selon notre hypothèse).
     */
    private Double kcIni;

    /**
     * Coefficient cultural en situation non stressée durant la late season
     * [0-1] (à partir de floraison selon nous).
     */
    private Double kcLate;

    /**
     * Coefficient cultural en situation non stressée durant la middle season
     * [0-1] (epi 1 cm - floraison selon notre hypothèse).
     */
    private Double kcMid;

    /**
     * Loader/Calculator for soil data.
     */
    @XmlTransient
    private SoilLoader loader;

    /**
     * Proportion of easily available water in the soil.
     *
     * Average fraction of TAW(pour obtenir RAW).
     */
    private Double p;

    /**
     * Réserve utile du sol (mm).
     *
     * Ou utilisation de swcFc, swcWp, soilDepth.
     */
    private Double ru;

    /**
     * CSV separator.
     */
    @XmlElement(name = "separator")
    @Getter
    @Setter
    private String separator = Resource.DEFAULT_SEP;

    /**
     * Soil depth (cm).
     */
    private Double soilDepth;

    /**
     * 4 stages per year to compute soil water balance (SWC and R).
     */
    @XmlTransient
    @Getter
    private List<Date> stages;

    /**
     * Teneur en eau du sol à la capacité au champ (% volumique).
     */
    private Double swcFc;

    /**
     * Teneur en eau du sol ... (% volumique).
     */
    private double swcMax;

    /**
     * Teneur en eau du sol au point de flétrissement permanent (% volumique).
     */
    private Double swcWp;

    /**
     * Constructor.
     */
    public SoilLoaderProxy() {
        super();
    }

    @Override
    public SoilLoaderProxy clone() throws CloneNotSupportedException {
        final SoilLoaderProxy clone = (SoilLoaderProxy) super.clone();
        if (climaticDailyData != null) {
            clone.climaticDailyData = new ArrayList<>();
            Collections.copy(clone.climaticDailyData, climaticDailyData);
        }
        clone.file = file;
        clone.headers = headers;
        clone.kcIni = kcIni;
        clone.kcLate = kcLate;
        clone.kcMid = kcMid;
        clone.loader = loader.clone();
        clone.p = p;
        clone.ru = ru;
        clone.separator = separator;
        clone.soilDepth = soilDepth;
        if (stages != null) {
            clone.stages = new ArrayList<>();
            Collections.copy(clone.stages, stages);
        }
        clone.swcFc = swcFc;
        clone.swcMax = swcMax;
        clone.swcWp = swcWp;
        return clone;
    }

    @Override
    public Map<String, String> getConfigurationErrors() {
        return getLoader().getConfigurationErrors();
    }

    /**
     * @return loader/calculator for soil data according to configuration.
     */
    public SoilLoader getLoader() {
        if (loader == null) {
            if (file != null) {
                final SoilFileLoader soilFileLoader = new SoilFileLoader(getFile(),
                        getHeaders(), getSeparator());
                soilFileLoader
                .addDataLoadingListeners(getDataLoadingListeners());
                loader = soilFileLoader;
            } else {
                final SoilCalculator calc = new SoilCalculator();
                calc.setClimaticDailyData(climaticDailyData);
                calc.setStages(stages);
                calc.setKcIni(kcIni);
                calc.setKcLate(kcLate);
                calc.setKcMid(kcMid);
                calc.setP(p);
                calc.setRu(ru);
                calc.setSoilDepth(soilDepth);
                calc.setSwcFc(swcFc);
                calc.setSwcMax(swcMax);
                calc.setSwcWp(swcWp);
                calc.addDataLoadingListeners(getDataLoadingListeners());
                loader = calc;
            }
        }
        return loader;
    }

    @Override
    public Collection<String> getMissingVariables() {
        throw new RuntimeException("Not implemented for soil!");
    }

    @Override
    public Set<Variable> getProvidedVariables() {
        return getLoader().getProvidedVariables();
    }

    @Override
    public Set<Variable> getVariables() {
        return getLoader().getVariables();
    }

    @Override
    public List<SoilDailyData> load() {
        return getLoader().load();
    }

    /**
     * @param calc model for soil water balance
     */
    public void setCalculator(final SoilCalculator calc) {
        loader = calc;
    }

    /**
     * @param values Climatic daily data used to compute soil water balance.
     */
    public void setClimaticDailyData(final List<ClimaticDailyData> values) {
        this.climaticDailyData = values;
        if (loader instanceof final SoilCalculator calc) {
            calc.setClimaticDailyData(values);
        } else {
            LOGGER.warn("No soil loader!");
        }
    }

    @Override
    public void setKcIni(final Double value) {
        this.kcIni = value;
    }

    @Override
    public void setKcLate(final Double value) {
        this.kcLate = value;
    }

    @Override
    public void setKcMid(final Double value) {
        this.kcMid = value;
    }

    @Override
    public void setP(final Double value) {
        p = value;
    }

    @Override
    public void setSoilDepth(final Double value) {
        this.soilDepth = value;
    }

    /**
     * @param values 4 stages per year to compute soil water balance (SWC and
     * R).
     */
    public void setStages(final List<Date> values) {
        this.stages = values;
        if (loader instanceof final SoilCalculator calc) {
            calc.setStages(stages);
        }
    }

    @Override
    public void setSwcFc(final Double value) {
        this.swcFc = value;
    }

    @Override
    public void setSwcMax(final Double value) {
        this.swcMax = value;
    }

    @Override
    public void setSwcWp(final Double value) {
        this.swcWp = value;
    }

    @Override
    public void setTimeScale(final TimeScale timeScale) {
        // do nothing
    }
}