130 lines
5.1 KiB
Java
130 lines
5.1 KiB
Java
package tc.oc.commons.core.util;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.function.IntConsumer;
|
|
|
|
import com.google.common.collect.BoundType;
|
|
import com.google.common.collect.Range;
|
|
import com.google.common.reflect.TypeParameter;
|
|
import com.google.common.reflect.TypeToken;
|
|
import com.google.inject.TypeLiteral;
|
|
import tc.oc.commons.core.reflect.Types;
|
|
|
|
public class Ranges {
|
|
private Ranges() {}
|
|
|
|
public static void assertLowerBound(Range<?> range) {
|
|
if(!range.hasLowerBound()) {
|
|
throw new IllegalArgumentException("Range has no lower bound");
|
|
}
|
|
}
|
|
|
|
public static void assertUpperBound(Range<?> range) {
|
|
if(!range.hasLowerBound()) {
|
|
throw new IllegalArgumentException("Range has no upper bound");
|
|
}
|
|
}
|
|
|
|
public static void assertBounded(Range<?> range) {
|
|
assertLowerBound(range);
|
|
assertUpperBound(range);
|
|
}
|
|
|
|
public static Optional<Integer> minimum(Range<Integer> range) {
|
|
return range.hasLowerBound() ? Optional.of(range.lowerBoundType() == BoundType.CLOSED ? range.lowerEndpoint()
|
|
: Integer.valueOf(range.lowerEndpoint() + 1))
|
|
: Optional.empty();
|
|
}
|
|
|
|
public static Optional<Integer> maximum(Range<Integer> range) {
|
|
return range.hasUpperBound() ? Optional.of(range.upperBoundType() == BoundType.CLOSED ? range.upperEndpoint()
|
|
: Integer.valueOf(range.upperEndpoint() - 1))
|
|
: Optional.empty();
|
|
}
|
|
|
|
public static int needMinimum(Range<Integer> range) {
|
|
assertLowerBound(range);
|
|
return range.lowerBoundType() == BoundType.CLOSED ? range.lowerEndpoint()
|
|
: range.lowerEndpoint() + 1;
|
|
}
|
|
|
|
public static int needOpenMinimum(Range<Integer> range) {
|
|
assertLowerBound(range);
|
|
return range.lowerBoundType() == BoundType.CLOSED ? range.lowerEndpoint() - 1
|
|
: range.lowerEndpoint();
|
|
}
|
|
|
|
public static int needMaximum(Range<Integer> range) {
|
|
assertUpperBound(range);
|
|
return range.upperBoundType() == BoundType.CLOSED ? range.upperEndpoint()
|
|
: range.upperEndpoint() - 1;
|
|
}
|
|
|
|
public static int needOpenMaximum(Range<Integer> range) {
|
|
assertUpperBound(range);
|
|
return range.upperBoundType() == BoundType.CLOSED ? range.upperEndpoint() + 1
|
|
: range.upperEndpoint();
|
|
}
|
|
|
|
public static Range<Integer> toClosed(Range<Integer> range) {
|
|
assertBounded(range);
|
|
return range.lowerBoundType() == BoundType.CLOSED && range.upperBoundType() == BoundType.CLOSED
|
|
? range
|
|
: Range.closed(needMinimum(range), needMaximum(range));
|
|
}
|
|
|
|
public static void forEach(Range<Integer> range, IntConsumer consumer) {
|
|
final int max = needOpenMaximum(range);
|
|
for(int i = needMinimum(range); i < max; i++) {
|
|
consumer.accept(i);
|
|
}
|
|
}
|
|
|
|
public static <T extends Comparable<T>> TypeToken<Range<T>> typeOf(TypeToken<T> type) {
|
|
return new TypeToken<Range<T>>(){}.where(new TypeParameter<T>(){}, type);
|
|
}
|
|
|
|
public static <T extends Comparable<T>> TypeLiteral<Range<T>> typeOf(TypeLiteral<T> type) {
|
|
return Types.toLiteral(typeOf(Types.toToken(type)));
|
|
}
|
|
|
|
/**
|
|
* Return an english phrase describing the given {@link Range} e.g.
|
|
*
|
|
* Range.all() -> "unbounded"
|
|
* Range.singleton(3) -> "3"
|
|
* Range.atLeast(3) -> "at least 3"
|
|
* Range.closedOpen(3, 7) -> "at least 3 and less than 7"
|
|
* Range.closed(3, 7) -> "between 3 and 7"
|
|
*/
|
|
public static String describe(Range<?> range) {
|
|
if(range.hasLowerBound() && range.hasUpperBound() && range.lowerBoundType() == BoundType.CLOSED && range.upperBoundType() == BoundType.CLOSED) {
|
|
if(range.lowerEndpoint().equals(range.upperEndpoint())) {
|
|
// singleton
|
|
return range.lowerEndpoint().toString();
|
|
} else {
|
|
// closed-closed
|
|
return "between " + range.lowerEndpoint() + " and " + range.upperEndpoint();
|
|
}
|
|
}
|
|
|
|
final List<String> parts = new ArrayList<>(2);
|
|
|
|
if(range.hasLowerBound()) {
|
|
parts.add((range.lowerBoundType() == BoundType.CLOSED ? "at least " : "more than ") + range.lowerEndpoint());
|
|
}
|
|
|
|
if(range.hasUpperBound()) {
|
|
parts.add((range.upperBoundType() == BoundType.CLOSED ? "at most " : "less than ") + range.upperEndpoint());
|
|
}
|
|
|
|
switch(parts.size()) {
|
|
case 0: return "unbounded";
|
|
case 1: return parts.get(0);
|
|
default: return parts.get(0) + " and " + parts.get(1);
|
|
}
|
|
}
|
|
}
|