Reservation.java
package io.extact.rms.application.domain;
import static java.time.temporal.ChronoUnit.*;
import static jakarta.persistence.AccessType.*;
import java.time.LocalDateTime;
import java.time.chrono.ChronoLocalDateTime;
import java.util.Objects;
import jakarta.persistence.Access;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import org.apache.commons.lang3.Range;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import lombok.Getter;
import lombok.Setter;
import io.extact.rms.application.domain.constraint.BeforeAfterDateTime;
import io.extact.rms.application.domain.constraint.Note;
import io.extact.rms.application.domain.constraint.ReserveEndDateTime;
import io.extact.rms.application.domain.constraint.ReserveStartDateTime;
import io.extact.rms.application.domain.constraint.ReserveStartDateTimeFuture;
import io.extact.rms.application.domain.constraint.RmsId;
import io.extact.rms.application.domain.constraint.BeforeAfterDateTime.BeforeAfterDateTimeValidatable;
import io.extact.rms.application.domain.constraint.ValidationGroups.Add;
import io.extact.rms.application.domain.constraint.ValidationGroups.Update;
@Access(FIELD)
@Entity
@BeforeAfterDateTime(from = "利用開始日時", to = "利用終了日時")
@Getter
@Setter
public class Reservation implements BeforeAfterDateTimeValidatable, Transformable, IdAccessable {
    /** id */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @RmsId(groups = Update.class)
    private Integer id;
    /** 利用開始日時 */
    @ReserveStartDateTime
    @ReserveStartDateTimeFuture(groups = Add.class)
    @Column(columnDefinition = "TIMESTAMP")
    private LocalDateTime startDateTime;
    /** 利用終了日時 */
    @ReserveEndDateTime
    @Column(columnDefinition = "TIMESTAMP")
    private LocalDateTime endDateTime;
    /** メモ */
    @Note
    private String note;
    /** 予約したレンタル品ID */
    @RmsId
    private int rentalItemId;
    /** 予約したユーザのユーザアカウントID */
    @RmsId
    private int userAccountId;
    /** 予約したユーザ */
    private transient UserAccount userAccount;
    /** 予約したレンタル品 */
    private transient RentalItem rentalItem;
    // ----------------------------------------------------- factory methods
    public static Reservation of(Integer reservationId, LocalDateTime startDateTime, LocalDateTime endDateTime, String note,
            int rentalItemId, int userAccountId) {
        var entity = new Reservation();
        entity.id = reservationId;
        entity.startDateTime = startDateTime;
        entity.endDateTime = endDateTime;
        entity.note = note;
        entity.rentalItemId = rentalItemId;
        entity.userAccountId = userAccountId;
        return entity;
    }
    public static Reservation ofTransient(LocalDateTime startDateTime, LocalDateTime endDateTime, String note, int rentalItemId, int userAccountId) {
        return of(null, startDateTime, endDateTime, note, rentalItemId, userAccountId);
    }
    // ----------------------------------------------------- original setter methods
    public void setStartDateTime(LocalDateTime startDateTime) {
        this.startDateTime = Objects.requireNonNull(startDateTime).truncatedTo(MINUTES);
    }
    public void setEndDateTime(LocalDateTime endDateTime) {
        this.endDateTime = Objects.requireNonNull(endDateTime).truncatedTo(MINUTES);
    }
    // ----------------------------------------------------- service methods
    public DateTimePeriod getReservePeriod() {
        return new DateTimePeriod(startDateTime, endDateTime);
    }
    // ----------------------------------------------------- override methods
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
    // ----------------------------------------------------- inner classes
    public static class DateTimePeriod {
        private LocalDateTime startDateTime;
        private LocalDateTime endDateTime;
        private Range<ChronoLocalDateTime<?>> period;
        public DateTimePeriod(LocalDateTime startDateTime, LocalDateTime endDateTime) {
            this.startDateTime = startDateTime;
            this.endDateTime = endDateTime;
            period = Range.between(startDateTime, endDateTime);        }
        public LocalDateTime getStartDateTime() {
            return this.startDateTime;
        }
        public LocalDateTime getEndDateTime() {
            return this.endDateTime;
        }
        public boolean isOverlappedBy(DateTimePeriod otherPeriod) {
            return this.period.isOverlappedBy(otherPeriod.period);
        }
    }
}