Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty Range now actually empty not infinity. #493

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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.
*
* <p>
Expand Down Expand Up @@ -68,7 +68,7 @@ private Range(T lower, T upper, int mask, Class<T> 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!");
}
}
Expand Down Expand Up @@ -250,6 +250,28 @@ public static <T extends Comparable<? super T>> Range<T> infinite(Class<T> cls)
return new Range<>(null, null, LOWER_INFINITE | UPPER_INFINITE, cls);
}

/**
* Creates the empty range. In other words the range that contains no points.
* <p>
* The mathematical equivalent will be:
* <pre>{@code
* (a, a) = ∅
* }</pre>
*
* @param cls The range class, never null.
* @param <R> The type of bounds.
*
* @return The empty range.
*/
public static <R extends Comparable<? super R>> Range<R> emptyRange(Class<R> cls) {
return new Range<>(
null,
null,
LOWER_EXCLUSIVE | UPPER_EXCLUSIVE,
cls
);
}

public static <T extends Comparable<? super T>> Range<T> ofString(String str, Function<String, T> converter, Class<T> clazz) {
if(str.equals(EMPTY)) {
return emptyRange(clazz);
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -596,7 +622,30 @@ public boolean contains(T point) {
* @return Whether {@code range} in this range or not.
*/
public boolean contains(Range<T> 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.
* <p>
* For example:
* <pre>{@code
* assertFalse(integerRange("empty").contains(1))
* }</pre>
*
* @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() {
Expand Down Expand Up @@ -624,13 +673,4 @@ private Function<T, String> boundToString() {
Class<T> getClazz() {
return clazz;
}

public static <R extends Comparable<? super R>> Range<R> emptyRange(Class<R> clazz) {
return new Range<>(
null,
null,
LOWER_INFINITE|UPPER_INFINITE,
clazz
);
}
}
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)"));
}
}


@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<LocalDate> 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<LocalDate> 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());
}
}