HourlyData.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;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;

import fr.inrae.agroclim.indicators.util.DateUtils;
import java.time.LocalDate;
import lombok.Getter;

/**
 * Class with date methods.
 *
 * Last changed : $Date$
 *
 * @author $Author$
 * @version $Revision$
 */
public abstract class HourlyData implements Cloneable, Data, Serializable {

    /**
     * Max allowed value for day of month.
     */
    private static final int MAX_DAY = 31;

    /**
     * Max allowed value for hour.
     */
    private static final int MAX_HOUR = 23;

    /**
     * Max allowed value for month.
     */
    private static final int MAX_MONTH = 12;

    /**
     * Max allowed value for year.
     */
    public static final int MAX_YEAR = 2200;

    /**
     * Min allowed value for day.
     */
    private static final int MIN_DAY = 1;

    /**
     * Min allowed value for hour.
     */
    private static final int MIN_HOUR = 0;

    /**
     * Min allowed value for month.
     */
    private static final int MIN_MONTH = 1;

    /**
     * Min allowed value for year.
     */
    public static final int MIN_YEAR = 1800;

    /**
     * UUID for Serializable.
     */
    private static final long serialVersionUID = -4030595237342420098L;

    /**
     * Computed date.
     */
    private Date date;

    /**
     * Day (1-31).
     */
    @Getter
    private Integer day;

    /**
     * Hour in the day [0-23].
     */
    @Getter
    private Integer hour;

    /**
     * Month (1-12).
     */
    @Getter
    private Integer month;

    /**
     * All values used by indicators.
     */
    private final Double[] values = new Double[Variable.values().length];

    /**
     * Year.
     */
    @Getter
    private Integer year;

    /**
     * Constructor.
     */
    protected HourlyData() {
    }

    /**
     * Copy constructor.
     *
     * @param data instance to copy
     */
    protected HourlyData(final HourlyData data) {
        this.hour = data.hour;
        this.day = data.day;
        this.month = data.month;
        this.year = data.year;
        System.arraycopy(data.values, 0, this.values, 0, data.values.length);
    }

    @Override
    @SuppressWarnings("checkstyle:DesignForExtension")
    protected HourlyData clone() throws CloneNotSupportedException {
        final HourlyData clone = (HourlyData) super.clone();
        clone.hour = hour;
        clone.day = day;
        clone.month = month;
        clone.year = year;
        System.arraycopy(values, 0, clone.values, 0, values.length);
        return clone;
    }

    /**
     * Create date object with {@code day}, {@code month}, {@code year} and {@code hour} attribute.<br>
     * If at least one of these attributes is null, then the returned date is null.
     * @return date object
     */
    public Date getDate() {
        if (date != null) {
            return date;
        }
        if (day == null || month == null || year == null || hour == null) {
            return null;
        }
        final Calendar cal = DateUtils.getCalendar();
        cal.set(Calendar.DAY_OF_MONTH, day);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.HOUR_OF_DAY, hour);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        date = cal.getTime();
        return date;
    }

    /**
     * @return day of year
     */
    public final int getDayOfYear() {
        return DateUtils.getDoy(getDate());
    }

    /**
     * Create date object with {@code day}, {@code month}, {@code year} and {@code hour} attribute.<br>
     * If at least one of these attributes is null, then the returned date is null.
     * @return date object
     */
    public LocalDate getLocalDate() {
        return DateUtils.asLocalDate(getDate());
    }

    /**
     * Get value for variable as it was set.
     *
     * @param variable climatic/soil variable
     * @return value
     */
    public final Double getRawValue(final Variable variable) {
        return values[variable.ordinal()];
    }

    /**
     * Get value for variable.
     *
     * @param variable climatic/soil variable
     * @return value
     */
    public abstract Double getValue(Variable variable);

    /**
     * @param value
     *            the day of month to set
     */
    public final void setDay(final Integer value) {
        if (value != null) {
            if (value > MAX_DAY) {
                throw new IllegalArgumentException("Day of month must be inferior to " + MAX_DAY + " : " + value);
            }
            if (value < MIN_DAY) {
                throw new IllegalArgumentException("Day of month must be superior to " + MIN_DAY + " : " + value);
            }
        }
        this.day = value;
        this.date = null;
    }

    /**
     * @param value
     *            the hour to set
     */
    public final void setHour(final Integer value) {
        if (value != null) {
            if (value > MAX_HOUR) {
                throw new IllegalArgumentException("Hour must be inferior to " + MAX_HOUR + " : " + value);
            }
            if (value < MIN_HOUR) {
                throw new IllegalArgumentException("Hour must be superior to " + MIN_HOUR + " : " + value);
            }
        }
        this.hour = value;
        this.date = null;
    }

    /**
     * @param value
     *            the the month-of-year field from 1 to 12.
     */
    public final void setMonth(final Integer value) {
        if (value != null) {
            if (value > MAX_MONTH) {
                throw new IllegalArgumentException("Month must be inferior to " + MAX_MONTH + " : " + value);
            }
            if (value < MIN_MONTH) {
                throw new IllegalArgumentException("Month must be superior to " + MIN_MONTH + " : " + value);
            }
        }
        this.month = value;
        this.date = null;
    }

    /**
     * Set value for variable.
     *
     * @param variable climatic/soil variable
     * @param value value to set
     */
    public final void setValue(final Variable variable, final Double value) {
        values[variable.ordinal()] = value;
    }

    /**
     * @param value
     *            the year to set
     */
    public final void setYear(final Integer value) {
        if (value != null) {
            if (value > MAX_YEAR) {
                throw new IllegalArgumentException("Year must be inferior to " + MAX_YEAR + " : " + value);
            }
            if (value < MIN_YEAR) {
                throw new IllegalArgumentException("Year must be superior to " + MIN_YEAR + " : " + value);
            }
        }
        this.year = value;
        this.date = null;
    }
}