AnnualStageData.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.phenology;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import fr.inrae.agroclim.indicators.model.data.Data;
import fr.inrae.agroclim.indicators.resources.Messages;
import fr.inrae.agroclim.indicators.util.DateUtils;
import lombok.Getter;
import lombok.Setter;

/**
 * List of stages for a year.
 *
 * Last change $Date$
 *
 * @author $Author$
 * @version $Revision$
 */
public class AnnualStageData implements Cloneable, Data, Serializable {
    /**
     * UUID for Serializable.
     */
    private static final long serialVersionUID = 1367181411523509675L;
    /**
     * Name of required columns, used by GUI.
     */
    public static final List<String> REQUIRED = Arrays.asList("year");
    /**
     * Name of all variables, used by GUI.
     */
    public static final List<String> VAR_NAMES = Arrays.asList("year");
    /**
     * Error messages.
     */
    private final List<String> errors = new ArrayList<>();

    /**
     * All stages are defined for the year.
     *
     * Ex.: false if phenological development does not ends (model was not able
     * to compute all stages).
     */
    @Getter
    @Setter
    private boolean isComplete = true;

    /**
     * Stage list.
     */
    @Getter
    @Setter
    private List<Stage> stages;

    /**
     * Warning messages.
     */
    private final List<String> warnings = new ArrayList<>();

    /**
     * Harvest year for the stage list.
     */
    @Getter
    @Setter
    private Integer year;

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

    /**
     * Constructor.
     *
     * @param y year of list
     */
    public AnnualStageData(final Integer y) {
        this.year = y;
        stages = new ArrayList<>();
    }

    /**
     * @param name stage name
     * @param value doy
     */
    public final void add(final String name, final int value) {
        if (getStages() == null) {
            stages = new ArrayList<>();
        }
        Stage myStage = new Stage(name, value);
        getStages().add(myStage);
    }

    @Override
    public final void check(final int line, final String path) {
        if (year == 0) {
            errors.add(Messages.format("error.year.null", path, line));
        }

        if (stages.size() < 2) {
            errors.add(Messages.format("error.minimal.stages", path, line));
        }

        for (final Stage stage : stages) {
            List<Stage> nextStages = stages.subList(stages.indexOf(stage),
                    stages.size());
            for (final Stage nextStage : nextStages) {
                // TODO ignorer les stades relatifs
                if (stage.getValue() > nextStage.getValue()) {
                    errors.add(Messages.format(
                            "error.endstage.superiorto.startstage",
                            path, line));
                }
            }
        }
    }

    @Override
    public final AnnualStageData clone() throws CloneNotSupportedException {
        AnnualStageData clone = (AnnualStageData) super.clone();
        Collections.copy(clone.errors, errors);
        if (stages != null) {
            clone.stages = new ArrayList<>();
            for (final Stage stage : stages) {
                clone.stages.add(stage.clone());
            }
        }
        Collections.copy(clone.warnings, warnings);
        clone.year = year;
        return clone;
    }

    @Override
    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        AnnualStageData other = (AnnualStageData) obj;
        if (stages == null) {
            if (other.stages != null) {
                return false;
            }
        } else if (!stages.equals(other.stages)) {
            return false;
        }
        if (year == null) {
            if (other.year != null) {
                return false;
            }
        } else if (!year.equals(other.year)) {
            return false;
        }
        return true;
    }

    /**
     * @return if a stage is in next year
     */
    public final boolean existWinterCrop() {
        final int nbOfDays = DateUtils.nbOfDays(year);
        return stages.stream().anyMatch(
                stage -> stage.getValue() > nbOfDays
        );
    }

    @Override
    public final List<String> getErrors() {
        return errors;
    }

    /**
     * @param name stage name
     * @return DOY of stage or null
     */
    public final Integer getStageValue(final String name) {
        Integer value = null;
        for (final Stage stage : stages) {
            if (stage.getName().equals(name)) {
                return stage.getValue();
            }
        }
        // s1p1 => s1+1
        final String relativeName = name.replace("m", "-").replace("p", "+");
        for (final Stage stage : stages) {
            if (stage.getName().equals(relativeName)) {
                return stage.getValue();
            }
        }
        return value;
    }

    @Override
    public final List<String> getWarnings() {
        return warnings;
    }

    @Override
    public final int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + Objects.hashCode(stages);
        result = prime * result + Objects.hashCode(year);
        return result;
    }
    /**
     * @return short string representation
     */
    public final String toShortString() {
        final StringBuilder sb = new StringBuilder("AnnualStageData [year=").append(year).append(", stages=");
        String out = stages.stream() //
                .map(s -> s.getName().concat("=").concat(String.valueOf(s.getValue()))) //
                .collect(Collectors.joining(", ", "[", "]"));
        sb.append(out);
        sb.append("]");
        return sb.toString();
    }

    @Override
    public final String toString() {
        return "AnnualStageData [year=" + year + ", stages=" + stages + "]";
    }
}