All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.eBusx.time.EInterval Maven / Gradle / Ivy

//
// Copyright 2021 Charles W. Rapp
//
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package net.sf.eBusx.time;

import java.time.Instant;
import java.util.Objects;
import net.sf.eBus.messages.EField;
import net.sf.eBus.util.Validator;

/**
 * Implements a time interval from one {@link Instant} to
 * another. {@code EInterval} guarantees that begin time is ≤
 * end time. Each end point may be separately configured to be
 * inclusive or exclusive. If begin time equals end time then
 * both ends must be marked as inclusive.
 * 

* {@code EInterval} extends {@link EField} so that it can be * used in an eBus message. This means that * {@link EInterval.Builder} must be used to create an * {@code EInterval} instance. *

*

* {@code EInterval} is immutable and thread-safe. *

*

Allen's Interval Algebra

*

* Allen's temporal reasoning calculus is based on 13 base * relations. Since 12 of these relations are simply inverses of * each other (for example the inverse of x precedes y * is y is preceded by x) only 7 relations are * implemented in {@code EInterval} which are: *

*
    *
  1. * precedes: * interval 1 precedes interval 2 if interval 1 end time < * interval 2 begin time. *
  2. *
  3. * meets: interval 1 * meets interval 2 if if interval 1 end time equals interval * 2 begin time. *
  4. *
  5. * overlaps: * interval 1 overlaps interval 2 if interval 1 begin time * < interval 2 begin time and interval 1 end time < * interval 2 end time. *
  6. *
  7. * contains: * interval 1 contains interval 2 if interval 1 begin time * < interval 2 begin time and interval 1 end time > * interval 2 end time. *
  8. *
  9. * starts: interval 1 * starts interval 2 if interval 1 begin time equals interval * 2 begin time and interval 1 end time < interval 2 end * time. *
  10. *
  11. * finishes: * interval 1 finishes interval 2 if interval 1 begin time * > interval 2 begin time and interval 1 end time equals * interval 2 end time. *
  12. *
  13. * equals: interval 1 * begin and end times equal interval 2 begin and end times. *
  14. *
*

* Note: interval algebra methods below are * based solely on the end point times and do not take * clusivity into account. *

* * @author Charles W. Rapp */ public final class EInterval extends EField { //--------------------------------------------------------------- // Member enums. // /** * Specifies whether an end point is inclusive or exclusive. */ public enum Clusivity { /** * The end point time is included in the interval. */ INCLUSIVE, /** * The end point time is excluded from the interval. */ EXCLUSIVE } // end of enum Clusivity //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Constants. // /** * Java serialization identifier. */ private static final long serialVersionUID = 0x00050700l; //----------------------------------------------------------- // Statics. // //----------------------------------------------------------- // Locals. // /** * Interval begin time. */ public final Instant beginTime; /** * Interval begin time clusivity. */ public final Clusivity beginClusivity; /** * Interval end time. */ public final Instant endTime; /** * Interval end time clusivity. */ public final Clusivity endClusivity; //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Creates a new interval instance based on the builder * configuration. {@code Builder} guarantees that the * interval settings are correct and consistent. * @param builder contains interval settings. */ private EInterval(final Builder builder) { super (builder); this.beginTime = builder.mBeginTime; this.beginClusivity = builder.mBeginClusivity; this.endTime = builder.mEndTime; this.endClusivity = builder.mEndClusivity; } // end of EInterval() // // end of Constructors. //----------------------------------------------------------- //----------------------------------------------------------- // Object Method Overrides. // /** * Returns textual representation of end point times and * clusivities. Uses default {@code Instant} time format. * @return interval as text. */ @Override public String toString() { final StringBuilder retval = new StringBuilder(); return ( retval.append(beginClusivity == Clusivity.INCLUSIVE ? '[' : '(') .append(beginTime) .append(", ") .append(endTime) .append(endClusivity == Clusivity.INCLUSIVE ? ']' : ')') .toString()); } // end of toString() /** * equals: * Returns {@code true} if: *
    *
  • * {@code o} is the same {@code EInterval} instance as * {@code this} interval or *
  • *
  • * {@code o} is a non-{@code null EInterval} instance * with the same end points and clusivities as * {@code this EInterval} instance. *
  • *
* Otherwise returns {@code false}. * @param o comparison object. * @return {@code true} if {@code o} is a * non-{@code null EInterval} instance whose end points are * equal and have the same clusivities. */ @Override public boolean equals(final Object o) { boolean retcode = (this == o); if (!retcode && o instanceof EInterval) { final EInterval interval = (EInterval) o; retcode = (beginTime.equals(interval.beginTime) && beginClusivity == interval.beginClusivity && endTime.equals(interval.endTime) && endClusivity == interval.endClusivity); } return (retcode); } // end of equals(Object) /** * Returns hash code based on end point times and * clusivities. * @return hash code based on end point times and * clusivities. */ @Override public int hashCode() { return (Objects.hash(beginTime, beginClusivity, endTime, endClusivity)); } // end of hashCode() // // end of Object Method Overrides. //----------------------------------------------------------- //----------------------------------------------------------- // Get Methods. // /** * precedes: * Returns {@code true} if {@code this EInterval} end time is * < {@code interval} begin time. *

* Note that this comparison is based only on time and does * not take clusivity into account. *

* @param interval comparison time interval. * @return {@code true} if {@code this EInterval} precedes * {@code interval} and {@code false} otherwise. */ public boolean precedes(final EInterval interval) { return (endTime.compareTo(interval.beginTime) < 0); } // end of precedes(EInterval) /** * meets: * Returns {@code true} if {@code this EInterval} end time * equals {@code interval} begin time. *

* Note that this comparison is based only on time and does * not take clusivity into account. *

* @param interval comparison time interval. * @return {@code true} if {@code this EInterval} meets * {@code interval} and {@code false} otherwise. */ public boolean meets(final EInterval interval) { return (endTime.equals(interval.beginTime)); } // end of meets(EInterval) /** * overlaps: * Returns {@code true} if: *
    *
  • * {@code this EInterval} end time is > * {@code interval}'s begin time, *
  • *
  • * {@code this EInterval} end time is < * {@code interval}'s end time, and *
  • *
  • * {@code this EInterval} begin time is < * {@code interval}'s begin time. *
  • *
*

* Note that this comparison is based only on time and does * not take clusivity into account. *

* @param interval comparison time interval. * @return {@code true} if {@code this EInterval} overlaps * {@code interval} and {@code false} otherwise. */ public boolean overlaps(final EInterval interval) { return (endTime.compareTo(interval.beginTime) > 0 && endTime.compareTo(interval.endTime) < 0 && beginTime.compareTo(interval.beginTime) < 0); } // end of overlaps(EInterval) /** * contains: * Returns {@code true} if: *
    *
  • * {@code this EInterval} begin time < * {@code interval} begin time and *
  • *
  • * {@code this EInterval} end time > {@code interval} * end time. *
  • *
* In short, this interval contains the given argument * interval. *

* Note that this comparison is based only on time and does * not take clusivity into account. *

* @param interval comparison time interval. * @return {@code true} if this interval begins before * {@code interval} and ends after. */ public boolean contains(final EInterval interval) { return (beginTime.compareTo(interval.beginTime) < 0 && endTime.compareTo(interval.endTime) > 0); } // end of contains(EInterval) /** * starts: * Returns {@code true} if this interval starts at the same * time as {@code interval} but ends before. That is: *
    *
  • * {@code this EInterval} begin time equals * {@code interval} begin time and *
  • *
  • * {@code this EInterval} end time < {@code interval} * end time. *
  • *
*

* Note that this comparison is based only on time and does * not take clusivity into account. *

* @param interval comparison time interval. * @return {@code true} if this interval starts at the same * time as {@code interval} but ends before. */ public boolean starts(final EInterval interval) { return (beginTime.equals(interval.beginTime) && endTime.compareTo(interval.endTime) < 0); } // end of starts(EInterval) /** * finishes: * Returns {@code true} if this interval starts after * {@code intervaL} buts ends at the same time. That is: *
    *
  • * {@code this EInterval} begin time > * {@code interval} begin time and *
  • *
  • * {@code this EInterval} end time equals * {@code interval} end time. *
  • *
*

* Note that this comparison is based only on time and does * not take clusivity into account. *

* @param interval comparison time interval. * @return {@code true} if this interval starts after * {@code intervaL} buts ends at the same time. */ public boolean finishes(final EInterval interval) { return (beginTime.compareTo(interval.beginTime) > 0 && endTime.equals(interval.endTime)); } // end of finishes(EInterval) // // end of Get Methods. //----------------------------------------------------------- /** * Returns a new {@code EInterval} builder instance which is * used to create an {@code EInterval} object. It is * recommended that a new builder be used when creating an * {@code EInterval} instance and not re-use a {@code Builder} * to create multiple intervals. * @return interval builder instance. */ public static Builder builder() { return (new Builder()); } // end of builder() //--------------------------------------------------------------- // Inner classes. // /** * {@code EInterval} instances may be created only by using * a {@code Builder} instance. A {@code Builder} instance is * obtained by calling {@link #builder()} which returns a * newly instantiated {@code Builder} instance. It is * recommended that a new {@code Builder} instance be used to * create an interval rather than re-using a builder instance * to create multiple intervals. */ public static final class Builder extends EField.Builder { //----------------------------------------------------------- // Member data. // //------------------------------------------------------- // Constants. // private static final String BEGIN_TIME = "beginTime"; //------------------------------------------------------- // Locals. // private Instant mBeginTime; private Clusivity mBeginClusivity; private Instant mEndTime; private Clusivity mEndClusivity; //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Constructor is private because a Builder instance * may only be created by calling {@link #builder()}. */ private Builder() { super (EInterval.class); } // end of Builder() // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // Abstract Builder Overrides. // /** * Checks if: *
    *
  1. * the begin and end times and clusivity are set and *
  2. *
  3. * begin time is either < or ≤ end time * depending on clusivity settings. *
  4. *
  5. * if begin time equals end time then both times must * be inclusive. *
  6. *
* @param problems add each detected problem to this * validator. * @return {@code problems}. */ @Override protected Validator validate(final Validator problems) { return (problems.requireNotNull(mBeginTime, BEGIN_TIME) .requireNotNull(mEndTime, "endTime") .requireTrue(mBeginTime.compareTo(mEndTime) <= 0, BEGIN_TIME, "beginTime > endTime") .requireTrue( (mBeginTime.equals(mEndTime) && (mBeginClusivity == Clusivity.EXCLUSIVE && mEndClusivity == Clusivity.EXCLUSIVE)), BEGIN_TIME, "beginTime == endTime and both ends are not inclusive")); } // end of validate(Validator) /** * Returns a new interval instance based on this * builder's settings. * @return new interval instance. */ @Override protected EInterval buildImpl() { return (new EInterval(this)); } // end of buildImpl() // // end of Abstract Builder Overrides. //------------------------------------------------------- //------------------------------------------------------- // Set Methods. // /** * Sets interval begin time. Returns * {@code this Builder} instance so that builder method * calls can be chained. * @param beginTime interval begin time. * @return {@code this Builder} instance. * @throws NullPointerException * if {@code beginTime} is {@code null}. */ public Builder beginTime(final Instant beginTime) { mBeginTime = Objects.requireNonNull( beginTime, "beginTime is null"); return (this); } // end of beginTime(Instant) /** * Sets interval begin time clusivity. Returns * {@code this Builder} instance so that builder method * calls can be chained. * @param clusivity interval begin time clusivity. * @return {@code this Builder} instance. * @throws NullPointerException * if {@code clusivity} is {@code null}. */ public Builder beginClusivity(final Clusivity clusivity) { mBeginClusivity = Objects.requireNonNull( clusivity, "beginClusivity is null"); return (this); } // end of beginClusivity(Clusivity) /** * Sets the interval end time. Returns * {@code this Builder} instance so that builder method * calls can be chained. * @param endTime interval end time. * @return {@code this Builder} instance. * @throws NullPointerException * if {@code endTime} is {@code null}. */ public Builder endTime(final Instant endTime) { mEndTime = Objects.requireNonNull( endTime, "endTime is null"); return (this); } // end of endTime(Instant) /** * Sets interval end time clusivity. Returns * {@code this Builder} instance so that builder method * calls can be chained. * @param clusivity interval begin time clusivity. * @return {@code this Builder} instance. * @throws NullPointerException * if {@code clusivity} is {@code null}. */ public Builder endClusivity(final Clusivity clusivity) { mEndClusivity = Objects.requireNonNull( clusivity, "endClusivity is null"); return (this); } // end of endClusivity(final Clusivity clusivity) // // end of Set Methods. //------------------------------------------------------- } // end of class Builder } // end of class EInterval




© 2015 - 2024 Weber Informatics LLC | Privacy Policy