diff --git a/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/range/Range.java b/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/range/Range.java
index 976b8a3e7..7f486d2a7 100644
--- a/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/range/Range.java
+++ b/hibernate-types-55/src/main/java/com/vladmihalcea/hibernate/type/range/Range.java
@@ -12,7 +12,7 @@
/**
* Represents the range/interval with two bounds. Abstraction follows the semantics of the mathematical interval. The
- * range can be unbounded or open from the left or/and unbounded from the right. The range supports half-open or closed
+ * range can be unbounded, empty or open from the left or/and unbounded from the right. The range supports half-open or closed
* bounds on both sides.
*
*
@@ -68,7 +68,7 @@ private Range(T lower, T upper, int mask, Class clazz) {
this.mask = mask;
this.clazz = clazz;
- if (isBounded() && lower.compareTo(upper) > 0) {
+ if (isBounded() && lower != null && upper != null && lower.compareTo(upper) > 0) {
throw new IllegalArgumentException("The lower bound is greater then upper!");
}
}
@@ -250,6 +250,28 @@ public static > Range infinite(Class cls)
return new Range<>(null, null, LOWER_INFINITE | UPPER_INFINITE, cls);
}
+ /**
+ * Creates the empty range. In other words the range that contains no points.
+ *
+ * The mathematical equivalent will be:
+ *
{@code
+ * (a, a) = ∅
+ * }
+ *
+ * @param cls The range class, never null.
+ * @param The type of bounds.
+ *
+ * @return The empty range.
+ */
+ public static > Range emptyRange(Class cls) {
+ return new Range<>(
+ null,
+ null,
+ LOWER_EXCLUSIVE | UPPER_EXCLUSIVE,
+ cls
+ );
+ }
+
public static > Range ofString(String str, Function converter, Class clazz) {
if(str.equals(EMPTY)) {
return emptyRange(clazz);
@@ -561,6 +583,10 @@ public T upper() {
* @return Whether {@code point} in this range or not.
*/
public boolean contains(T point) {
+ if (isEmpty()) {
+ return false;
+ }
+
boolean l = hasLowerBound();
boolean u = hasUpperBound();
@@ -596,7 +622,30 @@ public boolean contains(T point) {
* @return Whether {@code range} in this range or not.
*/
public boolean contains(Range range) {
- return (!range.hasLowerBound() || contains(range.lower)) && (!range.hasUpperBound() || contains(range.upper));
+ return !isEmpty() && (!range.hasLowerBound() || contains(range.lower)) && (!range.hasUpperBound() || contains(range.upper));
+ }
+
+ /**
+ * Determines whether this range is empty or not.
+ *
+ * For example:
+ *
{@code
+ * assertFalse(integerRange("empty").contains(1))
+ * }
+ *
+ * @return Whether {@code range} in this range or not.
+ */
+ public boolean isEmpty() {
+ boolean boundedExclusive = isBounded()
+ && hasMask(LOWER_EXCLUSIVE)
+ && hasMask(UPPER_EXCLUSIVE);
+
+ return boundedExclusive && hasEqualBounds();
+ }
+
+ private boolean hasEqualBounds() {
+ return lower == null && upper == null
+ || lower != null && upper != null && lower.compareTo(upper) == 0;
}
public String asString() {
@@ -624,13 +673,4 @@ private Function boundToString() {
Class getClazz() {
return clazz;
}
-
- public static > Range emptyRange(Class clazz) {
- return new Range<>(
- null,
- null,
- LOWER_INFINITE|UPPER_INFINITE,
- clazz
- );
- }
}
diff --git a/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/range/RangeTest.java b/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/range/RangeTest.java
index a9441b687..513d6d99c 100644
--- a/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/range/RangeTest.java
+++ b/hibernate-types-55/src/test/java/com/vladmihalcea/hibernate/type/range/RangeTest.java
@@ -5,8 +5,15 @@
import static com.vladmihalcea.hibernate.type.range.Range.integerRange;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.time.LocalDate;
/**
* @author Edgar Asatryan
@@ -36,6 +43,9 @@ public void ofStringTest() {
assertThat(integerRange("(-5,5]").isUpperBoundClosed(), is(true));
assertThat(integerRange("(-5,5]").isLowerBoundClosed(), is(false));
+ assertThat(integerRange("(,)").contains(integerRange("empty")), is(true));
+
+ assertThat(integerRange("empty").contains(integerRange("(,)")), is(false));
}
@Test
@@ -74,4 +84,52 @@ public void zonedDateTimeTest() {
assertNotNull(Range.zonedDateTimeRange("[2019-03-27 16:33:10.123456+05:30,)"));
assertNotNull(Range.zonedDateTimeRange("[2019-03-27 16:33:10.123456-06,infinity)"));
}
-}
\ No newline at end of file
+
+
+ @Test
+ public void emptyInfinityEquality() {
+ assertEquals(integerRange("empty"), integerRange("empty"));
+ assertEquals(integerRange("(infinity,infinity)"), integerRange("(infinity,infinity)"));
+ assertEquals(integerRange("(,)"), integerRange("(infinity,infinity)"));
+ assertEquals(integerRange("(infinity,infinity)"), integerRange("(,)"));
+
+ assertNotEquals(integerRange("empty"), integerRange("(infinity,infinity)"));
+ assertNotEquals(integerRange("empty"), integerRange("(,)"));
+ assertNotEquals(integerRange("empty"), integerRange("(5,5)"));
+ }
+
+ @Test
+ public void emptyRangeWithEmptyKeyword() {
+ Range empty = Range.localDateRange("empty");
+
+ assertTrue(empty.isEmpty());
+
+ assertFalse(empty.contains(LocalDate.MIN));
+ assertFalse(empty.contains((LocalDate) null));
+ assertFalse(empty.contains(LocalDate.now()));
+ assertFalse(empty.contains(LocalDate.MAX));
+
+ assertNull(empty.upper());
+ assertNull(empty.lower());
+ }
+
+ @Test
+ public void emptyRangeWithValues() {
+ Range empty = Range.localDateRange("(2019-03-27,2019-03-27)");
+
+ assertTrue(empty.isEmpty());
+ assertFalse(empty.contains(LocalDate.MIN));
+ assertFalse(empty.contains(LocalDate.now()));
+ assertFalse(empty.contains(LocalDate.MAX));
+
+ assertTrue(integerRange("(5,5)").isEmpty());
+ }
+
+ @Test
+ public void notEmptyWithValues() {
+ assertFalse(integerRange("(5,)").isEmpty());
+ assertFalse(integerRange("(5,5]").isEmpty());
+ assertFalse(integerRange("(,5)").isEmpty());
+ assertFalse(integerRange("(,)").isEmpty());
+ }
+}