From ff643fdff683d06b6905ebc22ea42f5a805b9e43 Mon Sep 17 00:00:00 2001 From: Mustapha Zorgati <15628173+mustaphazorgati@users.noreply.github.com> Date: Thu, 16 Feb 2023 17:05:46 +0100 Subject: [PATCH] TSK-1994: introduce new Generic Interval Co-authored-by: Alex --- .../pro/taskana/common/api/IntInterval.java | 48 +----------- .../pro/taskana/common/api/TimeInterval.java | 58 +------------- .../pro/taskana/common/internal/Interval.java | 69 +++++++++++++++++ .../taskana/common/api/IntIntervalTest.java | 60 +++++++++++++++ .../taskana/common/api/TimeIntervalTest.java | 77 +++++++++++++++++++ .../java/acceptance/ArchitectureTest.java | 3 + 6 files changed, 216 insertions(+), 99 deletions(-) create mode 100644 common/taskana-common/src/main/java/pro/taskana/common/internal/Interval.java create mode 100644 common/taskana-common/src/test/java/pro/taskana/common/api/IntIntervalTest.java create mode 100644 common/taskana-common/src/test/java/pro/taskana/common/api/TimeIntervalTest.java diff --git a/common/taskana-common/src/main/java/pro/taskana/common/api/IntInterval.java b/common/taskana-common/src/main/java/pro/taskana/common/api/IntInterval.java index b8cdabf75..adabce095 100644 --- a/common/taskana-common/src/main/java/pro/taskana/common/api/IntInterval.java +++ b/common/taskana-common/src/main/java/pro/taskana/common/api/IntInterval.java @@ -1,56 +1,14 @@ package pro.taskana.common.api; -import java.util.Objects; +import pro.taskana.common.internal.Interval; /** * IntInterval captures an Integer interval. A fixed interval has defined begin and end. An open * ended interval has either begin == null or end ==null. */ -public class IntInterval { - - private final Integer begin; - private final Integer end; +public class IntInterval extends Interval { public IntInterval(Integer begin, Integer end) { - this.begin = begin; - this.end = end; - } - - public boolean isValid() { - boolean isValid = begin != null || end != null; - if (begin != null && end != null && begin > end) { - isValid = false; - } - return isValid; - } - - public Integer getBegin() { - return begin; - } - - public Integer getEnd() { - return end; - } - - @Override - public int hashCode() { - return Objects.hash(begin, end); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof IntInterval)) { - return false; - } - IntInterval other = (IntInterval) obj; - return Objects.equals(begin, other.begin) && Objects.equals(end, other.end); - } - - @Override - public String toString() { - return "IntInterval [" + "begin=" + this.begin + ", end=" + this.end + "]"; + super(begin, end); } } diff --git a/common/taskana-common/src/main/java/pro/taskana/common/api/TimeInterval.java b/common/taskana-common/src/main/java/pro/taskana/common/api/TimeInterval.java index d1d852c8f..9a7f8f8bf 100644 --- a/common/taskana-common/src/main/java/pro/taskana/common/api/TimeInterval.java +++ b/common/taskana-common/src/main/java/pro/taskana/common/api/TimeInterval.java @@ -1,66 +1,16 @@ package pro.taskana.common.api; import java.time.Instant; -import java.util.Objects; + +import pro.taskana.common.internal.Interval; /** * Capture a time interval. A fixed interval has defined begin and end Instant. An open ended * interval has either begin == null or end ==null. */ -public class TimeInterval { - - private final Instant begin; - private final Instant end; +public class TimeInterval extends Interval { public TimeInterval(Instant begin, Instant end) { - this.begin = begin; - this.end = end; - } - - public boolean contains(Instant i) { - if (i == null) { - return false; - } - boolean isAfterBegin = begin == null || !i.isBefore(begin); - boolean isBeforeEnd = end == null || !i.isAfter(end); - return (isAfterBegin && isBeforeEnd); - } - - public boolean isValid() { - boolean isValid = begin != null || end != null; - if (begin != null && end != null && begin.isAfter(end)) { - isValid = false; - } - return isValid; - } - - public Instant getBegin() { - return begin; - } - - public Instant getEnd() { - return end; - } - - @Override - public int hashCode() { - return Objects.hash(begin, end); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof TimeInterval)) { - return false; - } - TimeInterval other = (TimeInterval) obj; - return Objects.equals(begin, other.begin) && Objects.equals(end, other.end); - } - - @Override - public String toString() { - return "TimeInterval [" + "begin=" + this.begin + ", end=" + this.end + "]"; + super(begin, end); } } diff --git a/common/taskana-common/src/main/java/pro/taskana/common/internal/Interval.java b/common/taskana-common/src/main/java/pro/taskana/common/internal/Interval.java new file mode 100644 index 000000000..7f5c5763f --- /dev/null +++ b/common/taskana-common/src/main/java/pro/taskana/common/internal/Interval.java @@ -0,0 +1,69 @@ +package pro.taskana.common.internal; + +import java.util.Objects; + +/** + * An Interval captures an interval of Type T extends Comparable. A fixed interval has + * defined begin and end. An open ended interval has either begin == null or end ==null. + * + *

Example: Interval<Integer> interval = new Interval<>(1, 2); or + * Interval<Instant> timeInterval = new Interval<>(instant_1, instant_2) + */ +public class Interval> { + + private final T begin; + + private final T end; + + public Interval(T begin, T end) { + this.begin = begin; + this.end = end; + } + + public T getBegin() { + return begin; + } + + public T getEnd() { + return end; + } + + public boolean contains(T i) { + if (i == null) { + return false; + } + boolean isAfterBegin = begin == null || i.compareTo(begin) >= 0; + boolean isBeforeEnd = end == null || i.compareTo(end) <= 0; + return (isAfterBegin && isBeforeEnd); + } + + public boolean isValid() { + boolean isValid = begin != null || end != null; + if (begin != null && end != null && begin.compareTo(end) > 0) { + isValid = false; + } + return isValid; + } + + @Override + public int hashCode() { + return Objects.hash(begin, end); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Interval)) { + return false; + } + Interval other = (Interval) obj; + return Objects.equals(begin, other.begin) && Objects.equals(end, other.end); + } + + @Override + public String toString() { + return "Interval [" + "begin=" + this.begin + ", end=" + this.end + "]"; + } +} diff --git a/common/taskana-common/src/test/java/pro/taskana/common/api/IntIntervalTest.java b/common/taskana-common/src/test/java/pro/taskana/common/api/IntIntervalTest.java new file mode 100644 index 000000000..ea86390f6 --- /dev/null +++ b/common/taskana-common/src/test/java/pro/taskana/common/api/IntIntervalTest.java @@ -0,0 +1,60 @@ +package pro.taskana.common.api; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class IntIntervalTest { + + @Test + void should_BeAValidIntervall_when_BeginIsBeforEnd() { + IntInterval interval = new IntInterval(1, 2); + assertThat(interval.isValid()).isTrue(); + } + + @Test + void should_BeAValidIntervall_when_BeginAndEndAreEqual() { + IntInterval interval = new IntInterval(1, 1); + assertThat(interval.isValid()).isTrue(); + } + + @Test + void should_NotBeAValidIntervall_when_BeginIsAfterEnd() { + IntInterval interval = new IntInterval(2, 1); + assertThat(interval.isValid()).isFalse(); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + void should_ContainIntegerInIntervall_when_BeginIsOneAndEndIsThree(int number) { + IntInterval interval = new IntInterval(1, 3); + assertThat(interval.contains(number)).isTrue(); + } + + @ParameterizedTest + @ValueSource(ints = {0, 4}) + void should_NotContainIntegerInIntervall_when_BeginIsOneAndEndIsThree(int number) { + IntInterval interval = new IntInterval(1, 3); + assertThat(interval.contains(number)).isFalse(); + } + + @Test + void should_TwoIntervallsAreEqual_when_BothHaveSameBeginAndEnd() { + IntInterval interval1 = new IntInterval(1, 2); + IntInterval interval2 = new IntInterval(1, 2); + + assertThat(interval1).isEqualTo(interval2); + assertThat(interval2).isEqualTo(interval1); + } + + @Test + void should_TwoIntervallsAreNotEqual_when_BeginAndEndAreDifferent() { + IntInterval interval1 = new IntInterval(1, 2); + IntInterval interval2 = new IntInterval(1, 3); + + assertThat(interval1).isNotEqualTo(interval2); + assertThat(interval2).isNotEqualTo(interval1); + } +} diff --git a/common/taskana-common/src/test/java/pro/taskana/common/api/TimeIntervalTest.java b/common/taskana-common/src/test/java/pro/taskana/common/api/TimeIntervalTest.java new file mode 100644 index 000000000..715884f50 --- /dev/null +++ b/common/taskana-common/src/test/java/pro/taskana/common/api/TimeIntervalTest.java @@ -0,0 +1,77 @@ +package pro.taskana.common.api; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TimeIntervalTest { + + private static final ZoneId UTC = ZoneId.of("UTC"); + private final Instant date1 = LocalDate.of(2023, 2, 10).atStartOfDay(UTC).toInstant(); + private final Instant date2 = LocalDate.of(2023, 2, 13).atStartOfDay(UTC).toInstant(); + + @Test + void should_BeAValidIntervall_when_BeginIsBeforEnd() { + TimeInterval timeInterval = new TimeInterval(date1, date2); + + assertThat(timeInterval.isValid()).isTrue(); + } + + @Test + void should_BeAValidIntervall_when_BeginAndEndAreEqual() { + TimeInterval timeInterval = new TimeInterval(date1, date1); + + assertThat(timeInterval.isValid()).isTrue(); + } + + @Test + void should_NotBeAValidIntervall_when_BeginIsAfterEnd() { + TimeInterval timeInterval = new TimeInterval(date2, date1); + + assertThat(timeInterval.isValid()).isFalse(); + } + + @ParameterizedTest + @ValueSource(ints = {10, 11, 12, 13}) + void should_ContainDateInIntervall(int day) { + TimeInterval timeInterval = new TimeInterval(date1, date2); + + Instant actualInstant = LocalDate.of(2023, 2, day).atStartOfDay(UTC).toInstant(); + + assertThat(timeInterval.contains(actualInstant)).isTrue(); + } + + @ParameterizedTest + @ValueSource(ints = {8, 9, 14, 15}) + void should_NotContainDateInIntervall_when_InstantIsBeforeOrAfter(int day) { + TimeInterval timeInterval = new TimeInterval(date1, date2); + + Instant actualInstant = LocalDate.of(2023, 2, day).atStartOfDay(UTC).toInstant(); + + assertThat(timeInterval.contains(actualInstant)).isFalse(); + } + + @Test + void should_TwoIntervallsAreEqual_when_BothHaveSameBeginAndEnd() { + TimeInterval timeInterval1 = new TimeInterval(date1, date2); + TimeInterval timeInterval2 = new TimeInterval(date1, date2); + + assertThat(timeInterval1).isEqualTo(timeInterval2); + assertThat(timeInterval2).isEqualTo(timeInterval1); + } + + @Test + void should_TwoIntervallsAreNotEqual_when_BeginAndEndAreDifferent() { + TimeInterval timeInterval1 = new TimeInterval(date1, date2); + TimeInterval timeInterval2 = + new TimeInterval(date1, LocalDate.of(2023, 2, 14).atStartOfDay(UTC).toInstant()); + + assertThat(timeInterval1).isNotEqualTo(timeInterval2); + assertThat(timeInterval2).isNotEqualTo(timeInterval1); + } +} diff --git a/lib/taskana-core-test/src/test/java/acceptance/ArchitectureTest.java b/lib/taskana-core-test/src/test/java/acceptance/ArchitectureTest.java index d78397c69..6b6fbf141 100644 --- a/lib/taskana-core-test/src/test/java/acceptance/ArchitectureTest.java +++ b/lib/taskana-core-test/src/test/java/acceptance/ArchitectureTest.java @@ -66,6 +66,7 @@ import pro.taskana.common.api.exceptions.ErrorCode; import pro.taskana.common.api.exceptions.TaskanaException; import pro.taskana.common.api.exceptions.TaskanaRuntimeException; import pro.taskana.common.internal.InternalTaskanaEngine; +import pro.taskana.common.internal.Interval; import pro.taskana.common.internal.logging.LoggingAspect; import pro.taskana.common.internal.util.MapCreator; import pro.taskana.testapi.TaskanaIntegrationTest; @@ -214,6 +215,8 @@ class ArchitectureTest { .resideInAPackage("..api..") .and() .areNotAssignableFrom(TaskanaEngine.class) + .and() + .areNotAssignableTo(Interval.class) .should() .onlyDependOnClassesThat( resideOutsideOfPackage("..pro.taskana..internal..")