blob: b3090d97c786c319b23f3632e9c602fb67736dc9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.util;
import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
import java.util.Iterator;
import java.util.function.BiConsumer;
import java.util.function.Function;
/** Utilities for Guava {@link com.google.common.collect.RangeSet}. */
@SuppressWarnings({"UnstableApiUsage"})
public class RangeSets {
private RangeSets() {}
@SuppressWarnings("rawtypes")
private static final ImmutableRangeSet ALL =
ImmutableRangeSet.of().complement();
/** Subtracts a range from a range set. */
public static <C extends Comparable<C>> RangeSet<C> minus(RangeSet<C> rangeSet,
Range<C> range) {
final TreeRangeSet<C> mutableRangeSet = TreeRangeSet.create(rangeSet);
mutableRangeSet.remove(range);
return mutableRangeSet.equals(rangeSet) ? rangeSet
: ImmutableRangeSet.copyOf(mutableRangeSet);
}
/** Returns the unrestricted range set. */
@SuppressWarnings({"rawtypes", "unchecked"})
public static <C extends Comparable<C>> RangeSet<C> rangeSetAll() {
return (RangeSet) ALL;
}
/** Compares two range sets. */
public static <C extends Comparable<C>> int compare(RangeSet<C> s0,
RangeSet<C> s1) {
final Iterator<Range<C>> i0 = s0.asRanges().iterator();
final Iterator<Range<C>> i1 = s1.asRanges().iterator();
for (;;) {
final boolean h0 = i0.hasNext();
final boolean h1 = i1.hasNext();
if (!h0 || !h1) {
return Boolean.compare(h0, h1);
}
final Range<C> r0 = i0.next();
final Range<C> r1 = i1.next();
int c = compare(r0, r1);
if (c != 0) {
return c;
}
}
}
/** Compares two ranges. */
public static <C extends Comparable<C>> int compare(Range<C> r0,
Range<C> r1) {
int c = Boolean.compare(r0.hasLowerBound(), r1.hasLowerBound());
if (c != 0) {
return c;
}
if (r0.hasLowerBound()) {
c = r0.lowerEndpoint().compareTo(r1.lowerEndpoint());
if (c != 0) {
return c;
}
c = r0.lowerBoundType().compareTo(r1.lowerBoundType());
if (c != 0) {
return c;
}
}
c = Boolean.compare(r0.hasUpperBound(), r1.hasUpperBound());
if (c != 0) {
return -c;
}
if (r0.hasUpperBound()) {
c = r0.upperEndpoint().compareTo(r1.upperEndpoint());
if (c != 0) {
return c;
}
c = r0.upperBoundType().compareTo(r1.upperBoundType());
if (c != 0) {
return c;
}
}
return 0;
}
/** Computes a hash code for a range set.
*
* <p>This method does not compute the same result as
* {@link RangeSet#hashCode}. That is a poor hash code because it is based
* upon {@link java.util.Set#hashCode}).
*
* <p>The algorithm is based on {@link java.util.List#hashCode()},
* which is well-defined because {@link RangeSet#asRanges()} is sorted. */
public static <C extends Comparable<C>> int hashCode(RangeSet<C> rangeSet) {
int h = 1;
for (Range<C> r : rangeSet.asRanges()) {
h = 31 * h + r.hashCode();
}
return h;
}
/** Returns whether a range is a point. */
public static <C extends Comparable<C>> boolean isPoint(Range<C> range) {
return range.hasLowerBound()
&& range.hasUpperBound()
&& range.lowerEndpoint().equals(range.upperEndpoint())
&& !range.isEmpty();
}
/** Returns the number of ranges in a range set that are points.
*
* <p>If every range in a range set is a point then it can be converted to a
* SQL IN list. */
public static <C extends Comparable<C>> int countPoints(RangeSet<C> rangeSet) {
int n = 0;
for (Range<C> range : rangeSet.asRanges()) {
if (isPoint(range)) {
++n;
}
}
return n;
}
/** Calls the appropriate handler method for each range in a range set,
* creating a new range set from the results. */
public static <C extends Comparable<C>, C2 extends Comparable<C2>>
RangeSet<C2> map(RangeSet<C> rangeSet, Handler<C, Range<C2>> handler) {
final ImmutableRangeSet.Builder<C2> builder = ImmutableRangeSet.builder();
rangeSet.asRanges().forEach(range -> builder.add(map(range, handler)));
return builder.build();
}
/** Calls the appropriate handler method for the type of range. */
public static <C extends Comparable<C>, R> R map(Range<C> range,
Handler<C, R> handler) {
if (range.hasLowerBound() && range.hasUpperBound()) {
final C lower = range.lowerEndpoint();
final C upper = range.upperEndpoint();
if (range.lowerBoundType() == BoundType.OPEN) {
if (range.upperBoundType() == BoundType.OPEN) {
return handler.open(lower, upper);
} else {
return handler.openClosed(lower, upper);
}
} else {
if (range.upperBoundType() == BoundType.OPEN) {
return handler.closedOpen(lower, upper);
} else {
if (lower.equals(upper)) {
return handler.singleton(lower);
} else {
return handler.closed(lower, upper);
}
}
}
} else if (range.hasLowerBound()) {
final C lower = range.lowerEndpoint();
if (range.lowerBoundType() == BoundType.OPEN) {
return handler.greaterThan(lower);
} else {
return handler.atLeast(lower);
}
} else if (range.hasUpperBound()) {
final C upper = range.upperEndpoint();
if (range.upperBoundType() == BoundType.OPEN) {
return handler.lessThan(upper);
} else {
return handler.atMost(upper);
}
} else {
return handler.all();
}
}
/** Copies a range set. */
public static <C extends Comparable<C>, C2 extends Comparable<C2>>
RangeSet<C2> copy(RangeSet<C> rangeSet, Function<C, C2> map) {
return map(rangeSet, new CopyingHandler<C, C2>() {
@Override C2 convert(C c) {
return map.apply(c);
}
});
}
/** Copies a range. */
public static <C extends Comparable<C>, C2 extends Comparable<C2>>
Range<C2> copy(Range<C> range, Function<C, C2> map) {
return map(range, new CopyingHandler<C, C2>() {
@Override C2 convert(C c) {
return map.apply(c);
}
});
}
public static <C extends Comparable<C>> void forEach(RangeSet<C> rangeSet,
Consumer<C> consumer) {
rangeSet.asRanges().forEach(range -> forEach(range, consumer));
}
public static <C extends Comparable<C>> void forEach(Range<C> range,
Consumer<C> consumer) {
if (range.hasLowerBound() && range.hasUpperBound()) {
final C lower = range.lowerEndpoint();
final C upper = range.upperEndpoint();
if (range.lowerBoundType() == BoundType.OPEN) {
if (range.upperBoundType() == BoundType.OPEN) {
consumer.open(lower, upper);
} else {
consumer.openClosed(lower, upper);
}
} else {
if (range.upperBoundType() == BoundType.OPEN) {
consumer.closedOpen(lower, upper);
} else {
if (lower.equals(upper)) {
consumer.singleton(lower);
} else {
consumer.closed(lower, upper);
}
}
}
} else if (range.hasLowerBound()) {
final C lower = range.lowerEndpoint();
if (range.lowerBoundType() == BoundType.OPEN) {
consumer.greaterThan(lower);
} else {
consumer.atLeast(lower);
}
} else if (range.hasUpperBound()) {
final C upper = range.upperEndpoint();
if (range.upperBoundType() == BoundType.OPEN) {
consumer.lessThan(upper);
} else {
consumer.atMost(upper);
}
} else {
consumer.all();
}
}
/** Creates a consumer that prints values to a {@link StringBuilder}. */
public static <C extends Comparable<C>> Consumer<C> printer(StringBuilder sb,
BiConsumer<StringBuilder, C> valuePrinter) {
return new Printer<>(sb, valuePrinter);
}
/** Deconstructor for {@link Range} values.
*
* @param <C> Value type
* @param <R> Return type
*
* @see Consumer */
public interface Handler<C extends Comparable<C>, R> {
R all();
R atLeast(C lower);
R atMost(C upper);
R greaterThan(C lower);
R lessThan(C upper);
R singleton(C value);
R closed(C lower, C upper);
R closedOpen(C lower, C upper);
R openClosed(C lower, C upper);
R open(C lower, C upper);
}
/** Consumer of {@link Range} values.
*
* @param <C> Value type
*
* @see Handler */
public interface Consumer<C extends Comparable<C>> {
void all();
void atLeast(C lower);
void atMost(C upper);
void greaterThan(C lower);
void lessThan(C upper);
void singleton(C value);
void closed(C lower, C upper);
void closedOpen(C lower, C upper);
void openClosed(C lower, C upper);
void open(C lower, C upper);
}
/** Handler that converts a Range into another Range of the same type,
* applying a mapping function to the range's bound(s).
*
* @param <C> Value type
* @param <C2> Output value type */
private abstract static
class CopyingHandler<C extends Comparable<C>, C2 extends Comparable<C2>>
implements RangeSets.Handler<C, Range<C2>> {
abstract C2 convert(C c);
@Override public Range<C2> all() {
return Range.all();
}
@Override public Range<C2> atLeast(C lower) {
return Range.atLeast(convert(lower));
}
@Override public Range<C2> atMost(C upper) {
return Range.atMost(convert(upper));
}
@Override public Range<C2> greaterThan(C lower) {
return Range.greaterThan(convert(lower));
}
@Override public Range<C2> lessThan(C upper) {
return Range.lessThan(convert(upper));
}
@Override public Range<C2> singleton(C value) {
return Range.singleton(convert(value));
}
@Override public Range<C2> closed(C lower, C upper) {
return Range.closed(convert(lower), convert(upper));
}
@Override public Range<C2> closedOpen(C lower, C upper) {
return Range.closedOpen(convert(lower), convert(upper));
}
@Override public Range<C2> openClosed(C lower, C upper) {
return Range.openClosed(convert(lower), convert(upper));
}
@Override public Range<C2> open(C lower, C upper) {
return Range.open(convert(lower), convert(upper));
}
}
/** Converts any type of range to a string, using a given value printer.
*
* @param <C> Value type */
static class Printer<C extends Comparable<C>> implements Consumer<C> {
private final StringBuilder sb;
private final BiConsumer<StringBuilder, C> valuePrinter;
Printer(StringBuilder sb, BiConsumer<StringBuilder, C> valuePrinter) {
this.sb = sb;
this.valuePrinter = valuePrinter;
}
@Override public void all() {
sb.append("(-\u221e..+\u221e)");
}
@Override public void atLeast(C lower) {
sb.append('[');
valuePrinter.accept(sb, lower);
sb.append("..+\u221e)");
}
@Override public void atMost(C upper) {
sb.append("(-\u221e..");
valuePrinter.accept(sb, upper);
sb.append("]");
}
@Override public void greaterThan(C lower) {
sb.append('(');
valuePrinter.accept(sb, lower);
sb.append("..+\u221e)");
}
@Override public void lessThan(C upper) {
sb.append("(-\u221e..");
valuePrinter.accept(sb, upper);
sb.append(")");
}
@Override public void singleton(C value) {
valuePrinter.accept(sb, value);
}
@Override public void closed(C lower, C upper) {
sb.append('[');
valuePrinter.accept(sb, lower);
sb.append("..");
valuePrinter.accept(sb, upper);
sb.append(']');
}
@Override public void closedOpen(C lower, C upper) {
sb.append('[');
valuePrinter.accept(sb, lower);
sb.append("..");
valuePrinter.accept(sb, upper);
sb.append(')');
}
@Override public void openClosed(C lower, C upper) {
sb.append('(');
valuePrinter.accept(sb, lower);
sb.append("..");
valuePrinter.accept(sb, upper);
sb.append(']');
}
@Override public void open(C lower, C upper) {
sb.append('(');
valuePrinter.accept(sb, lower);
sb.append("..");
valuePrinter.accept(sb, upper);
sb.append(')');
}
}
}