package com.lambdaworks.redis;
import com.lambdaworks.redis.internal.LettuceAssert;
/**
* {@link Range} defines {@literal lower} and {@literal upper} boundaries to retrieve items from a sorted set.
*
* @author Mark Paluch
* @since 4.3
*/
public class Range<T> {
private Boundary<T> lower;
private Boundary<T> upper;
private Range(Boundary<T> lower, Boundary<T> upper) {
LettuceAssert.notNull(lower, "Lower boundary must not be null");
LettuceAssert.notNull(upper, "Upper boundary must not be null");
this.lower = lower;
this.upper = upper;
}
/**
* Create a new range from {@code lower} and {@code upper} boundary values. Both values are included (greater than or equals
* and less than or equals).
*
* @param lower lower boundary, must not be {@literal null}.
* @param upper upper boundary, must not be {@literal null}.
* @param <T> value type
* @return new {@link Range}
*/
public static <T> Range<T> create(T lower, T upper) {
return new Range<T>(Boundary.including(lower), Boundary.including(upper));
}
/**
* Create a new range from {@code lower} and {@code upper} boundaries.
*
* @param lower lower boundary, must not be {@literal null}.
* @param upper upper boundary, must not be {@literal null}.
* @param <T> value type.
* @return new {@link Range}
*/
public static <T> Range<T> from(Boundary<T> lower, Boundary<T> upper) {
return new Range<T>(lower, upper);
}
/**
* @param <T> value type.
* @return new {@link Range} with {@code lower} and {@code upper} set to {@link Boundary#unbounded()}.
*/
public static <T> Range<T> unbounded() {
return new Range<T>(Boundary.unbounded(), Boundary.unbounded());
}
/**
* Greater than or equals {@code lower}.
*
* @param lower the lower boundary value.
* @return {@code this} {@link Range} with {@code lower} applied.
*/
public Range<T> gte(T lower) {
this.lower = Boundary.including(lower);
return this;
}
/**
* Greater than {@code lower}.
*
* @param lower the lower boundary value.
* @return {@code this} {@link Range} with {@code lower} applied.
*/
public Range<T> gt(T lower) {
this.lower = Boundary.excluding(lower);
return this;
}
/**
* Less than or equals {@code lower}.
*
* @param upper the upper boundary value.
* @return {@code this} {@link Range} with {@code upper} applied.
*/
public Range<T> lte(T upper) {
this.upper = Boundary.including(upper);
return this;
}
/**
* Less than {@code lower}.
*
* @param upper the upper boundary value.
* @return {@code this} {@link Range} with {@code upper} applied.
*/
public Range<T> lt(T upper) {
this.upper = Boundary.excluding(upper);
return this;
}
/**
* @return the lower boundary.
*/
public Boundary<T> getLower() {
return lower;
}
/**
* @return the upper boundary.
*/
public Boundary<T> getUpper() {
return upper;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName()).append(" [");
sb.append(lower).append(" to ").append(upper).append("]");
return sb.toString();
}
/**
* @author Mark Paluch
*/
public static class Boundary<T> {
private final static Boundary<?> UNBOUNDED = new Boundary<>(null, true);
private final T value;
private final boolean including;
private Boundary(T value, boolean including) {
this.value = value;
this.including = including;
}
/**
* Creates an unbounded (infinite) boundary that marks the beginning/end of the range.
*
* @return the unbounded boundary.
* @param <T> inferred type.
*/
@SuppressWarnings("unchecked")
public static <T> Boundary<T> unbounded() {
return (Boundary<T>) UNBOUNDED;
}
/**
* Create a {@link Boundary} based on the {@code value} that includes the value when comparing ranges. Greater or
* equals, less or equals. but not Greater or equal, less or equal to {@code value}.
*
* @param value must not be {@literal null}.
* @param <T> value type.
* @return the {@link Boundary}.
*/
public static <T> Boundary<T> including(T value) {
LettuceAssert.notNull(value, "Value must not be null");
return new Boundary<>(value, true);
}
/**
* Create a {@link Boundary} based on the {@code value} that excludes the value when comparing ranges. Greater or less
* to {@code value} but not greater or equal, less or equal.
*
* @param value must not be {@literal null}.
* @param <T> value type.
* @return the {@link Boundary}.
*/
public static <T> Boundary<T> excluding(T value) {
LettuceAssert.notNull(value, "Value must not be null");
return new Boundary<>(value, false);
}
/**
* @return the value
*/
public T getValue() {
return value;
}
/**
* @return {@literal true} if the boundary includes the value.
*/
public boolean isIncluding() {
return including;
}
@Override
public String toString() {
if (value == null) {
return "[unbounded]";
}
StringBuilder sb = new StringBuilder();
if (including) {
sb.append('[');
} else {
sb.append('(');
}
sb.append(value);
return sb.toString();
}
}
}