com.alliander.osgp.dto.valueobjects.smartmetering.CosemDateTimeDto.java Source code

Java tutorial

Introduction

Here is the source code for com.alliander.osgp.dto.valueobjects.smartmetering.CosemDateTimeDto.java

Source

/**
 * Copyright 2016 Smart Society Services B.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */
package com.alliander.osgp.dto.valueobjects.smartmetering;

import java.io.Serializable;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;

public class CosemDateTimeDto implements Serializable, Comparable<CosemDateTimeDto> {

    private static final long serialVersionUID = 4157582293990514746L;

    private static final int MILLISECONDS_PER_MINUTE = 60 * 1000;

    public static final int DEVIATION_NOT_SPECIFIED = 0x8000;

    private final CosemDateDto date;
    private final CosemTimeDto time;

    private final int deviation;

    private final ClockStatusDto clockStatus;

    public CosemDateTimeDto(final CosemDateDto date, final CosemTimeDto time, final int deviation,
            final ClockStatusDto clockStatus) {
        Objects.requireNonNull(date, "date must not be null");
        Objects.requireNonNull(time, "time must not be null");
        Objects.requireNonNull(clockStatus, "clockStatus must not be null");
        this.checkDeviation(deviation);
        this.date = new CosemDateDto(date);
        this.time = new CosemTimeDto(time);
        if (deviation == -DEVIATION_NOT_SPECIFIED) {
            /*
             * Has to do with specifics regarding 4 byte shorts and int values.
             * See comments with isDeviationNotSpecified(int).
             */
            this.deviation = DEVIATION_NOT_SPECIFIED;
        } else {
            this.deviation = deviation;
        }
        this.clockStatus = clockStatus;
    }

    public CosemDateTimeDto(final CosemDateTimeDto cosemDateTime) {
        this(cosemDateTime.getDate(), cosemDateTime.getTime(), cosemDateTime.getDeviation(),
                cosemDateTime.getClockStatus());
    }

    public CosemDateTimeDto(final LocalDate date, final LocalTime time, final int deviation,
            final ClockStatusDto clockStatus) {
        this(new CosemDateDto(date), new CosemTimeDto(time), deviation, clockStatus);
    }

    public CosemDateTimeDto(final LocalDate date, final LocalTime time, final int deviation) {
        this(new CosemDateDto(date), new CosemTimeDto(time), deviation,
                new ClockStatusDto(ClockStatusDto.STATUS_NOT_SPECIFIED));
    }

    public CosemDateTimeDto(final LocalDateTime dateTime, final int deviation, final ClockStatusDto clockStatus) {
        this(dateTime.toLocalDate(), dateTime.toLocalTime(), deviation, clockStatus);
    }

    public CosemDateTimeDto(final LocalDateTime dateTime, final int deviation) {
        this(dateTime.toLocalDate(), dateTime.toLocalTime(), deviation);
    }

    public CosemDateTimeDto(final DateTime dateTime) {
        this(dateTime.toLocalDate(), dateTime.toLocalTime(), determineDeviation(dateTime),
                determineClockStatus(dateTime));
    }

    public CosemDateTimeDto() {
        this(DateTime.now());
    }

    private void checkDeviation(final int deviation) {
        if (!this.isValidDeviation(deviation)) {
            throw new IllegalArgumentException("Deviation not in [-720..720, 0x8000]: " + deviation);
        }
    }

    private boolean isValidDeviation(final int deviation) {
        return this.isSpecificDeviation(deviation) || this.isDeviationNotSpecified(deviation);
    }

    private boolean isSpecificDeviation(final int deviation) {
        return deviation >= -720 && deviation <= 720;
    }

    private boolean isDeviationNotSpecified(final int deviation) {
        /*
         * Deviation comes from a short value (4 bytes), where the int value of
         * DEVIATION_NOT_SPECIFIED equals the negative value as 4 byte short.
         * Take this into account checking the deviation value.
         */
        return DEVIATION_NOT_SPECIFIED == deviation || -DEVIATION_NOT_SPECIFIED == deviation;
    }

    private static int determineDeviation(final DateTime dateTime) {
        return -(dateTime.getZone().getOffset(dateTime.getMillis()) / MILLISECONDS_PER_MINUTE);
    }

    private static ClockStatusDto determineClockStatus(final DateTime dateTime) {
        final Set<ClockStatusBitDto> statusBits = EnumSet.noneOf(ClockStatusBitDto.class);
        if (!dateTime.getZone().isStandardOffset(dateTime.getMillis())) {
            statusBits.add(ClockStatusBitDto.DAYLIGHT_SAVING_ACTIVE);
        }
        return new ClockStatusDto(statusBits);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("CosemDateTime[");
        sb.append(this.date);
        sb.append(' ');
        sb.append(this.time);
        sb.append(", deviation=" + this.deviation);
        sb.append(", " + this.clockStatus);
        return sb.append(']').toString();
    }

    public CosemDateDto getDate() {
        return this.date;
    }

    public CosemTimeDto getTime() {
        return this.time;
    }

    public int getDeviation() {
        return this.deviation;
    }

    public ClockStatusDto getClockStatus() {
        return this.clockStatus;
    }

    public boolean isDeviationSpecified() {
        return DEVIATION_NOT_SPECIFIED != this.deviation;
    }

    /**
     * @return {@code true} if the date, time and deviation are specified;
     *         {@code false} otherwise.
     *
     * @see #isLocalDateTimeSpecified()
     * @see #isDeviationSpecified()
     */
    public boolean isDateTimeSpecified() {
        return this.isLocalDateTimeSpecified() && this.isDeviationSpecified();
    }

    /**
     * Returns this {@link CosemDateTimeDto} as {@link DateTime} if the date, time
     * and deviation are specified.
     *
     * @return this {@link CosemDateTimeDto} as {@link DateTime}, or {@code null}
     *         if not {@link #isDateTimeSpecified()}.
     *
     * @see #isDateTimeSpecified()
     */
    public DateTime asDateTime() {
        if (!this.isDateTimeSpecified()) {
            return null;
        }
        final LocalDateTime localDateTime = this.asLocalDateTime();
        final DateTimeZone zone = DateTimeZone.forOffsetMillis(-this.deviation * MILLISECONDS_PER_MINUTE);
        return localDateTime.toDateTime(zone);
    }

    /**
     * @return {@code true} if the date and time are specified; {@code false}
     *         otherwise.
     *
     * @see #isLocalDateSpecified()
     * @see #isLocalTimeSpecified()
     */
    public boolean isLocalDateTimeSpecified() {
        return this.date.isLocalDateSpecified() && this.time.isLocalTimeSpecified();
    }

    /**
     * Returns this {@link CosemDateTimeDto} as {@link LocalDateTime} if the date
     * and time are specified.
     *
     * @return this {@link CosemDateTimeDto} as {@link LocalDateTime}, or
     *         {@code null} if not {@link #isLocalDateTimeSpecified()}.
     *
     * @see #isLocalDateTimeSpecified()
     */
    public LocalDateTime asLocalDateTime() {
        if (!this.isLocalDateTimeSpecified()) {
            return null;
        }
        if (this.time.isSecondNotSpecified()) {
            return new LocalDateTime(this.date.getYear(), this.date.getMonth(), this.date.getDayOfMonth(),
                    this.time.getHour(), this.time.getMinute());
        }
        if (this.time.isHundredthsNotSpecified()) {
            return new LocalDateTime(this.date.getYear(), this.date.getMonth(), this.date.getDayOfMonth(),
                    this.time.getHour(), this.time.getMinute(), this.time.getSecond());
        }
        return new LocalDateTime(this.date.getYear(), this.date.getMonth(), this.date.getDayOfMonth(),
                this.time.getHour(), this.time.getMinute(), this.time.getSecond(), this.time.getHundredths() * 10);
    }

    /**
     * @return {@code true} if the date is specified; {@code false} otherwise.
     *
     * @see #getDate()
     * @see CosemDateDto#isLocalDateSpecified()
     */
    public boolean isLocalDateSpecified() {
        return this.date.isLocalDateSpecified();
    }

    /**
     * Returns this {@link CosemDateTimeDto} as {@link LocalDate} if the date is
     * specified.
     *
     * @return this {@link CosemDateTimeDto} as {@link LocalDate}, or {@code null}
     *         if not {@link #isLocalDateSpecified()}.
     *
     * @see #isLocalDateSpecified()
     */
    public LocalDate asLocalDate() {
        return this.date.asLocalDate();
    }

    /**
     * @return {@code true} if the time is specified; {@code false} otherwise.
     *
     * @see #getTime()
     * @see CosemTimeDto#isLocalTimeSpecified()
     */
    public boolean isLocalTimeSpecified() {
        return this.time.isLocalTimeSpecified();
    }

    /**
     * Returns this {@link CosemDateTimeDto} as {@link LocalTime} if the time is
     * specified.
     *
     * @return this {@link CosemDateTimeDto} as {@link LocalTime}, or {@code null}
     *         if not {@link #isLocalTimeSpecified()}.
     *
     * @see #isLocalTimeSpecified()
     */
    public LocalTime asLocalTime() {
        return this.time.asLocalTime();
    }

    @Override
    public int compareTo(final CosemDateTimeDto o) {
        // If a valid datetime can be created, use this to compare.
        // This will take deviation in to account.
        final LocalDateTime timeThis = this.asLocalDateTime();
        final LocalDateTime timeOther = o.asLocalDateTime();
        if (timeThis != null && timeOther != null) {
            return timeThis.compareTo(timeOther);
        }

        // Otherwise compare date/time on an byte value basis.
        // Taking deviation into account is complex and bluebook
        // does not describe how that should even work with unspecified values.
        final int compDate = this.date.compareTo(o.date);
        if (compDate != 0) {
            return compDate;
        }

        final int compTime = this.time.compareTo(o.time);
        if (compTime != 0) {
            return compTime;
        }

        return 0;
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.date, this.time, this.clockStatus, this.deviation);
    }

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

        return Objects.equals(this.date, other.date) && Objects.equals(this.time, other.time)
                && Objects.equals(this.clockStatus, other.clockStatus) && this.deviation == other.deviation;
    }
}