Issue
How do I transform a given Guava Range of one type to a range of another type.
I expect a compose method similar to Predicates.compose. Take Integer and Long for example:
Range<Integer> intRange = Range.compose(Range.atLeast(10l),integerFromLongFunction);
I wrote the compose method:
public static <F extends Comparable<F>, T extends Comparable<T>> Range<T> compose(Range<F> range,
Function<F, T> function) {
Range<T> result;
if (range.hasUpperBound() && range.hasLowerBound()) {
T upperEndpoint = function.apply(range.upperEndpoint());
T lowerEndpoint = function.apply(range.lowerEndpoint());
result = Range.range(lowerEndpoint, range.lowerBoundType(), upperEndpoint, range.upperBoundType());
} else if (range.hasUpperBound()) {
result = Range.upTo(function.apply(range.upperEndpoint()), range.upperBoundType());
} else if (range.hasLowerBound()) {
result = Range.downTo(function.apply(range.lowerEndpoint()), range.lowerBoundType());
} else {
result = Range.all();
}
return result;
}
with the Unit Test:
@Test
public void testLongRangeToInteger() {
Integer inRange = 6;
Integer outOfRange = 3;
Range<Long> longRange = Range.atLeast(5l);
assertTrue(longRange.apply(inRange.longValue()));
assertFalse(longRange.apply(outOfRange.longValue()));
Function<Long, Integer> function = integerFromLongFunction();
Range<Integer> intRange = RangeExtended.compose(longRange, function);
assertTrue(intRange.apply(inRange));
assertFalse(intRange.apply(outOfRange));
}
public static Function<Long, Integer> integerFromLongFunction() {
return new Function<Long, Integer>() {
@Override
public Integer apply(Long input) {
return (input == null) ? null : input.intValue();
}
};
}
My current desire is actually to convert a Joda Duration to it’s corresponding millis, but I wrote the example in Long/Integer for simplicity.
It seems that Guava would have this, but I can’t find anywhere. I’m using v14, but looking at the latest v17 javadoc didn’t expose anything.
Solution
package com.stackoverflow.q24889243;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Duration;
import java.util.List;
import java.util.function.Function;
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import org.joda.time.ReadableDuration;
import org.junit.jupiter.api.Test;
public class Answer {
/**
* Transforms one range to another
*
* @param <T> the from type
* @param <R> the to type
* @param fromRange the from range
* @param transformer the transformer
*
* @return the range
*/
public static final <T extends Comparable<T>, R extends Comparable<R>> Range<R> transform(Range<T> fromRange,
Function<? super T, ? extends R> transformer) {
var hasLowerBound = fromRange.hasLowerBound();
var hasUpperBound = fromRange.hasUpperBound();
if (hasLowerBound && hasUpperBound) {
var fromLowerEndpoint = fromRange.lowerEndpoint();
var toLowerEndpoint = transformer.apply(fromLowerEndpoint);
var fromUpperEndpoint = fromRange.upperEndpoint();
var toUpperEndpoint = transformer.apply(fromUpperEndpoint);
return Range.range(toLowerEndpoint, fromRange.lowerBoundType(), toUpperEndpoint,
fromRange.upperBoundType());
}
if (hasLowerBound) {
var fromLowerEndpoint = fromRange.lowerEndpoint();
var toLowerEndpoint = transformer.apply(fromLowerEndpoint);
return Range.downTo(toLowerEndpoint, fromRange.lowerBoundType());
}
if (hasUpperBound) {
var fromUpperEndpoint = fromRange.upperEndpoint();
var toUpperEndpoint = transformer.apply(fromUpperEndpoint);
return Range.upTo(toUpperEndpoint, fromRange.upperBoundType());
}
return Range.all();
}
@Test
void testDurations() {
assertRangeTransformer(Duration::toMillis, Duration.ofMillis(10), Duration.ofMillis(15));
assertRangeTransformer(ReadableDuration::getMillis, org.joda.time.Duration.millis(10),
org.joda.time.Duration.millis(15));
}
<T extends Comparable<T>, R extends Comparable<R>> void assertRangeTransformer(
Function<? super T, ? extends R> transformer, T fromLowerBound, T fromUpperBound) {
assertRangeTransformer(transformer, fromLowerBound, BoundType.CLOSED, fromUpperBound, BoundType.CLOSED);
assertRangeTransformer(transformer, fromLowerBound, BoundType.CLOSED, fromUpperBound, BoundType.OPEN);
assertRangeTransformer(transformer, fromLowerBound, BoundType.OPEN, fromUpperBound, BoundType.CLOSED);
assertRangeTransformer(transformer, fromLowerBound, BoundType.OPEN, fromUpperBound, BoundType.OPEN);
assertRangeTransformerLowerBoundOnly(transformer, fromLowerBound, BoundType.CLOSED);
assertRangeTransformerLowerBoundOnly(transformer, fromLowerBound, BoundType.OPEN);
assertRangeTransformerUpperBoundOnly(transformer, fromUpperBound, BoundType.CLOSED);
assertRangeTransformerUpperBoundOnly(transformer, fromUpperBound, BoundType.OPEN);
this.<T, R>assertRangeTransformerAll(transformer);
}
<T extends Comparable<T>, R extends Comparable<R>> void assertRangeTransformer(
Function<? super T, ? extends R> transformer, T fromLowerBound, BoundType lowerBoundType, T fromUpperBound,
BoundType upperBoundType) {
var toLowerBound = transformer.apply(fromLowerBound);
var toUpperBound = transformer.apply(fromUpperBound);
var fromRange = Range.range(fromLowerBound, lowerBoundType, fromUpperBound, upperBoundType);
var actualToRange = transform(fromRange, transformer);
assertEquals(toLowerBound, actualToRange.lowerEndpoint());
assertEquals(fromRange.lowerBoundType(), actualToRange.lowerBoundType());
assertEquals(toUpperBound, actualToRange.upperEndpoint());
assertEquals(fromRange.upperBoundType(), actualToRange.upperBoundType());
}
<T extends Comparable<T>, R extends Comparable<R>> void assertRangeTransformerLowerBoundOnly(
Function<? super T, ? extends R> transformer, T fromLowerBound, BoundType lowerBoundType) {
var toLowerBound = transformer.apply(fromLowerBound);
var fromRange = Range.downTo(fromLowerBound, lowerBoundType);
var actualToRange = transform(fromRange, transformer);
assertFalse(actualToRange.hasUpperBound());
assertEquals(toLowerBound, actualToRange.lowerEndpoint());
assertEquals(fromRange.lowerBoundType(), actualToRange.lowerBoundType());
}
<T extends Comparable<T>, R extends Comparable<R>> void assertRangeTransformerUpperBoundOnly(
Function<? super T, ? extends R> transformer, T fromUpperBound, BoundType upperBoundType) {
var toLowerBound = transformer.apply(fromUpperBound);
var fromRange = Range.upTo(fromUpperBound, upperBoundType);
var actualToRange = transform(fromRange, transformer);
assertFalse(actualToRange.hasLowerBound());
assertEquals(toLowerBound, actualToRange.upperEndpoint());
assertEquals(fromRange.upperBoundType(), actualToRange.upperBoundType());
}
<T extends Comparable<T>, R extends Comparable<R>> void assertRangeTransformerAll(
Function<? super T, ? extends R> transformer) {
var fromRange = Range.<T>all();
var actualToRange = transform(fromRange, transformer);
assertFalse(actualToRange.hasLowerBound());
assertFalse(actualToRange.hasUpperBound());
}
}
Answered By – Jeff
Answer Checked By – Mary Flores (AngularFixing Volunteer)