blob: 733968dc84fb86beea2990995e84f33f8511a157 [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.sis.measure;
import java.util.Objects;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.internal.util.Numerics;
import org.apache.sis.util.collection.WeakHashSet;
/**
* A range of numbers capable of widening conversions when performing range operations.
* {@code NumberRange} has no unit of measurement. For a range of physical measurements
* with unit of measure, see {@link MeasurementRange}.
*
* <p>{@code NumberRange} has some capability to convert different number types before to
* perform operations. In order to provide both this flexibility and the safety of generic
* types, most operations in this class are defined in two versions:</p>
* <ul>
* <li>Methods inherited from the {@code Range} parent class
* ({@link #contains(Range) contains}, {@link #intersect(Range) intersect},
* {@link #intersects(Range) intersects}, {@link #union(Range) union} and
* {@link #subtract(Range) subtract}) requires argument or range elements
* of type {@code <E>}. No type conversion is performed.</li>
*
* <li>Methods defined in this class with the {@code Any} suffix
* ({@link #containsAny(NumberRange) containsAny}, {@link #intersectAny(NumberRange) intersectAny},
* {@link #intersectsAny(NumberRange) intersectsAny}, {@link #unionAny(NumberRange) unionAny} and
* {@link #subtractAny(NumberRange) subtractAny}) are more lenient on the argument or range element
* type {@code <E>}. Widening conversions are performed as needed.</li>
* </ul>
*
* The methods from the parent class are preferable when the ranges are known to contain elements
* of the same type, since they avoid the cost of type checks and conversions. The method in this
* class are convenient when the parameterized type is unknown ({@code <?>}).
*
* <p>Other methods defined in this class:</p>
* <ul>
* <li>Convenience {@code create(…)} static methods for every numeric primitive types.</li>
* <li>{@link #castTo(Class)} for casting the range values to an other type.</li>
* </ul>
*
* <h2>Relationship with standards</h2>
* {@code NumberRange} is the SIS class closest to the
* <a href="http://en.wikipedia.org/wiki/Interval_%28mathematics%29">mathematical definition of interval</a>.
* It is closely related, while not identical, to the ISO 19123 (<cite>Coverage geometry and functions</cite>)
* definition of "ranges". At the difference of the parent {@link Range} class, which can be used only with
* discrete coverages, the {@code NumberRange} class can
* also be used with continuous coverages.
*
* <h2>Immutability and thread safety</h2>
* This class and the {@link MeasurementRange} subclasses are immutable, and thus inherently thread-safe.
* Other subclasses may or may not be immutable, at implementation choice. But implementers are encouraged
* to make sure that all subclasses remain immutable for more predictable behavior.
*
* <h2>Shared instances</h2>
* <i><b>Note:</b> following is implementation details provided for information purpose.
* The caching policy may change in any SIS version.</i>
*
* <p>All {@code create} static methods may return a shared instance. Those methods are preferred
* to the constructors when the range is expected to have a long lifetime, typically as instance
* given to {@linkplain org.apache.sis.parameter.DefaultParameterDescriptor parameter descriptor}.
* Other methods do not check for shared instances, since the created object is often temporary.</p>
*
* @author Martin Desruisseaux (IRD)
* @author Jody Garnett (for parameterized type inspiration)
* @version 1.0
*
* @param <E> the type of range elements as a subclass of {@link Number}.
*
* @see RangeFormat
* @see org.apache.sis.util.collection.RangeSet
* @see <a href="http://en.wikipedia.org/wiki/Interval_%28mathematics%29">Wikipedia: Interval</a>
*
* @since 0.3
* @module
*/
public class NumberRange<E extends Number & Comparable<? super E>> extends Range<E> {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -3198281191274903617L;
/**
* The pool of ranges created by the {@code create(…)} methods.
*/
@SuppressWarnings("unchecked")
private static final WeakHashSet<NumberRange<?>> POOL = new WeakHashSet<>((Class) NumberRange.class);
/**
* Returns a unique instance of the given range, except if the range is empty.
*
* <div class="note"><b>Rational:</b>
* we exclude empty ranges because the {@link Range#equals(Object)} consider them as equal.
* Consequently if empty ranges were included in the pool, this method would return in some
* occasions an empty range with different values than the given {@code range} argument.
* </div>
*
* We use this method only for caching range of wrapper of primitive types ({@link Byte},
* {@link Short}, <i>etc.</i>) because those types are known to be immutable.
*/
static <E extends Number & Comparable<? super E>, T extends NumberRange<E>> T unique(T range) {
if (!range.isEmpty()) {
range = POOL.unique(range);
}
return range;
}
/**
* Returns {@code true} if the given value is valid for a range to be cached by {@link #union(Range)}.
* A range can be cached if the {@link Number} values are null or instances of a standard Java class
* known to be immutable, and the wrapped values are not NaN except the canonical {@link Double#NaN}
* or {@link Float#NaN} values. This check is necessary because {@link #equals(Object)} considers all
* {@code NaN} values as equal.
*/
static boolean isCacheable(final Number n) {
if (n == null) {
return true;
} else if (n instanceof Double) {
final double value = (Double) n;
return !Double.isNaN(value) || Double.doubleToRawLongBits(value) == 0x7FF8000000000000L;
} else if (n instanceof Float) {
final float value = (Float) n;
return !Float.isNaN(value) || Float.floatToRawIntBits(value) == 0x7FC00000;
} else {
return Numbers.getEnumConstant(n.getClass()) != Numbers.OTHER;
}
}
/**
* Constructs a range containing a single value of the given type.
* The given value is used as the minimum and maximum values, inclusive.
*
* @param <N> compile-time value of {@code type}.
* @param type the element type, usually one of {@link Byte}, {@link Short},
* {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
* @param value the value, or {@code null} for creating an unbounded range.
* @return a range containing the given value as its inclusive minimum and maximum.
*
* @since 1.0
*/
public static <N extends Number & Comparable<? super N>> NumberRange<N> create(final Class<N> type, final N value) {
NumberRange<N> range = new NumberRange<>(type, value, true, value, true);
if (isCacheable(value)) {
range = unique(range);
}
return range;
}
/**
* Constructs a range of {@code byte} values.
* If the minimum is greater than the maximum, then the range {@linkplain #isEmpty() is empty}.
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values for the given endpoints.
*/
public static NumberRange<Byte> create(final byte minValue, final boolean isMinIncluded,
final byte maxValue, final boolean isMaxIncluded)
{
// No need to check for equality because all bytes values are cached by Byte.valueOf(…).
return unique(new NumberRange<>(Byte.class,
Byte.valueOf(minValue), isMinIncluded,
Byte.valueOf(maxValue), isMaxIncluded));
}
/**
* Constructs a range of {@code short} values.
* If the minimum is greater than the maximum, then the range {@linkplain #isEmpty() is empty}.
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values for the given endpoints.
*/
public static NumberRange<Short> create(final short minValue, final boolean isMinIncluded,
final short maxValue, final boolean isMaxIncluded)
{
final Short min = minValue;
final Short max = (minValue == maxValue) ? min : maxValue;
return unique(new NumberRange<>(Short.class, min, isMinIncluded, max, isMaxIncluded));
}
/**
* Constructs a range of {@code int} values.
* If the minimum is greater than the maximum, then the range {@linkplain #isEmpty() is empty}.
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values for the given endpoints.
*
* @see #createLeftBounded(int, boolean)
*/
public static NumberRange<Integer> create(final int minValue, final boolean isMinIncluded,
final int maxValue, final boolean isMaxIncluded)
{
final Integer min = minValue;
final Integer max = (minValue == maxValue) ? min : maxValue;
return unique(new NumberRange<>(Integer.class, min, isMinIncluded, max, isMaxIncluded));
}
/**
* Constructs a range of {@code long} values.
* If the minimum is greater than the maximum, then the range {@linkplain #isEmpty() is empty}.
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values for the given endpoints.
*/
public static NumberRange<Long> create(final long minValue, final boolean isMinIncluded,
final long maxValue, final boolean isMaxIncluded)
{
final Long min = minValue;
final Long max = (minValue == maxValue) ? min : maxValue;
return unique(new NumberRange<>(Long.class, min, isMinIncluded, max, isMaxIncluded));
}
/**
* Constructs a range of {@code float} values.
* The minimum and maximum values can not be NaN but can be infinite.
* If the minimum is greater than the maximum, then the range {@linkplain #isEmpty() is empty}.
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value, or {@link Float#NEGATIVE_INFINITY} if none.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value, or {@link Float#POSITIVE_INFINITY} if none.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values for the given endpoints.
* @throws IllegalArgumentException if {@link Float#isNaN(float)} is {@code true} for a given value.
*/
public static NumberRange<Float> create(final float minValue, final boolean isMinIncluded,
final float maxValue, final boolean isMaxIncluded)
{
final Float min = valueOf("minValue", minValue, Float.NEGATIVE_INFINITY);
final Float max = valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY);
// No need to test isCacheable(Number) because the type is known and valueOf(…) disallows NaN values.
return unique(new NumberRange<>(Float.class, min, isMinIncluded, Objects.equals(min, max) ? min : max, isMaxIncluded));
}
/**
* Returns the {@code Float} wrapper of the given primitive {@code float},
* or {@code null} if it equals to the infinity value.
*/
static Float valueOf(final String name, final float value, final float infinity) {
if (Float.isNaN(value)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.NotANumber_1, name));
}
return (value != infinity) ? value : null;
}
/**
* Constructs a range of {@code double} values.
* The minimum and maximum values can not be NaN but can be infinite.
* If the minimum is greater than the maximum, then the range {@linkplain #isEmpty() is empty}.
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value, or {@link Double#NEGATIVE_INFINITY} if none.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value, or {@link Double#POSITIVE_INFINITY} if none.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values for the given endpoints.
* @throws IllegalArgumentException if {@link Double#isNaN(double)} is {@code true} for a given value.
*/
public static NumberRange<Double> create(final double minValue, final boolean isMinIncluded,
final double maxValue, final boolean isMaxIncluded)
{
final Double min = valueOf("minValue", minValue, Double.NEGATIVE_INFINITY);
final Double max = valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY);
// No need to test isCacheable(Number) because the type is known and valueOf(…) disallows NaN values.
return unique(new NumberRange<>(Double.class, min, isMinIncluded, Objects.equals(min, max) ? min : max, isMaxIncluded));
}
/**
* Returns the {@code Double} wrapper of the given primitive {@code double},
* or {@code null} if it equals to the infinity value.
*/
static Double valueOf(final String name, final double value, final double infinity) {
if (Double.isNaN(value)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.NotANumber_1, name));
}
return (value != infinity) ? Numerics.valueOf(value) : null;
}
/**
* Constructs a range using the smallest type of {@link Number} that can hold the given values.
* The given numbers do not need to be of the same type since they will
* be {@linkplain Numbers#cast(Number, Class) casted} as needed.
* More specifically this method returns:
*
* <ul>
* <li>{@code NumberRange<Byte>} if the given values are integers between
* {@value java.lang.Byte#MIN_VALUE} and {@value java.lang.Byte#MAX_VALUE} inclusive.</li>
* <li>{@code NumberRange<Short>} if the given values are integers between
* {@value java.lang.Short#MIN_VALUE} and {@value java.lang.Short#MAX_VALUE} inclusive.</li>
* <li>{@code NumberRange<Integer>} if the given values are integers between
* {@value java.lang.Integer#MIN_VALUE} and {@value java.lang.Integer#MAX_VALUE} inclusive.</li>
* <li>{@code NumberRange<Long>} if the given values are integers in the range of {@code long} values.</li>
* <li>{@code NumberRange<Float>} if the given values can be casted to {@code float} values without data lost.</li>
* <li>{@code NumberRange<Double>} if none of the above types is suitable.</li>
* </ul>
*
* This method may return a shared instance, at implementation choice.
*
* @param minValue the minimal value, or {@code null} if none.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value, or {@code null} if none.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
* @return the new range, or {@code null} if both {@code minValue} and {@code maxValue} are {@code null}.
* @throws Illegal­Argument­Exception if the given numbers are not primitive wrappers for numeric types.
*/
@SuppressWarnings({"rawtypes","unchecked"})
public static NumberRange<?> createBestFit(final Number minValue, final boolean isMinIncluded,
final Number maxValue, final boolean isMaxIncluded)
{
final Class<? extends Number> type;
type = Numbers.widestClass(Numbers.narrowestClass(minValue),
Numbers.narrowestClass(maxValue));
if (type == null) {
return null;
}
Number min = Numbers.cast(minValue, type);
Number max = Numbers.cast(maxValue, type);
final boolean isCacheable = isCacheable(min) && isCacheable(max);
if (isCacheable && Objects.equals(min, max)) {
max = min; // Share the same instance.
}
NumberRange range = new NumberRange(type, min, isMinIncluded, max, isMaxIncluded);
if (isCacheable) {
range = unique(range);
}
return range;
}
/**
* Constructs a range of {@code int} values without upper bound.
* This method may return a shared instance, at implementation choice.
*
* <div class="note"><b>Note:</b> for creating left-bounded ranges of floating point values,
* use one of the {@code create(…)} methods with a {@code POSITIVE_INFINITY} constant.
* We do not provide variants for other integer types because this method is typically invoked for
* defining the {@linkplain org.apache.sis.feature.DefaultFeatureType multiplicity of an attribute}.</div>
*
* @param minValue the minimal value.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @return the new range of numeric values from {@code minValue} to positive infinity.
*
* @see #create(int, boolean, int, boolean)
*
* @since 0.5
*/
public static NumberRange<Integer> createLeftBounded(final int minValue, final boolean isMinIncluded) {
// Use POOL.unique(…) directly because we do not need the check for Range.isEmpty() here.
return POOL.unique(new NumberRange<>(Integer.class, Integer.valueOf(minValue), isMinIncluded, null, false));
}
/**
* Returns the specified {@link Range} as a {@code NumberRange} object.
* If the specified range is already an instance of {@code NumberRange}, then it is returned unchanged.
* Otherwise a new number range is created using the {@linkplain #NumberRange(Range) copy constructor}.
*
* @param <N> the type of elements in the given range.
* @param range the range to cast or copy.
* @return the same range than {@code range} as a {@code NumberRange} object.
*/
public static <N extends Number & Comparable<? super N>> NumberRange<N> castOrCopy(final Range<N> range) {
if (range instanceof NumberRange<?>) {
return (NumberRange<N>) range;
}
// The constructor will ensure that the range element type is a subclass of Number.
// Do not invoke unique(NumberRange) because the returned range is often temporary.
return new NumberRange<>(range);
}
/**
* Constructs a range with the same type and the same values than the specified range.
* This is a copy constructor.
*
* @param range the range to copy. The elements must be {@link Number} instances.
*/
public NumberRange(final Range<E> range) {
super(range);
}
/**
* Constructs a range of the given type with values from the given annotation.
* This constructor does not verify if the given type is wide enough for the values of
* the given annotation, because those information are usually static. If nevertheless
* the given type is not wide enough, then the values are truncated in the same way
* than the Java language casts primitive types.
*
* @param type the element type, restricted to one of {@link Byte}, {@link Short},
* {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
* @param range the range of values.
* @throws IllegalArgumentException if the given type is not one of the primitive wrappers for numeric types.
*/
public NumberRange(final Class<E> type, final ValueRange range) throws IllegalArgumentException {
super(type, Numbers.cast(valueOf("minimum", range.minimum(), Double.NEGATIVE_INFINITY), type), range.isMinIncluded(),
Numbers.cast(valueOf("maximum", range.maximum(), Double.POSITIVE_INFINITY), type), range.isMaxIncluded());
}
/**
* Constructs a range of {@link Number} objects.
*
* @param type the element type, usually one of {@link Byte}, {@link Short},
* {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
* @param minValue the minimal value, or {@code null} if none.
* @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
* @param maxValue the maximal value, or {@code null} if none.
* @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
*/
public NumberRange(final Class<E> type,
final E minValue, final boolean isMinIncluded,
final E maxValue, final boolean isMaxIncluded)
{
super(type, minValue, isMinIncluded, maxValue, isMaxIncluded);
}
/**
* Constructs a range with the same values than the specified range, casted to the specified type.
*
* @param type the element type, usually one of {@link Byte}, {@link Short},
* {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
* @param range the range to copy. The elements must be {@link Number} instances.
* @throws IllegalArgumentException if the given type is not one of the primitive wrappers for numeric types.
*/
NumberRange(final Class<E> type, final Range<? extends Number> range)
throws IllegalArgumentException
{
super(type, Numbers.cast(range.minValue, type), range.isMinIncluded,
Numbers.cast(range.maxValue, type), range.isMaxIncluded);
}
/**
* Creates a new range using the same element type than this range. This method will
* be overridden by subclasses in order to create a range of a more specific type.
*/
@Override
Range<E> create(final E minValue, final boolean isMinIncluded,
final E maxValue, final boolean isMaxIncluded)
{
return new NumberRange<>(elementType, minValue, isMinIncluded, maxValue, isMaxIncluded);
}
/**
* Casts the specified range to the specified type. If this class is associated to a unit of measurement,
* then this method converts the {@code range} unit to the same unit than this instance.
* This method is overridden by {@link MeasurementRange} only in the way described above.
*
* @param type the class to cast to. Must be one of {@link Byte}, {@link Short},
* {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
* @return the casted range, or {@code range} if no cast is needed.
* @throws IllegalArgumentException if the given type is not one of the primitive wrappers for numeric types.
*/
@SuppressWarnings("unchecked")
<N extends Number & Comparable<? super N>>
NumberRange<N> convertAndCast(final NumberRange<?> range, final Class<N> type) throws IllegalArgumentException {
if (range.elementType == type) {
return (NumberRange<N>) range;
}
return new NumberRange<>(type, range);
}
/**
* Casts this range to the specified type. If the cast from this range type to the given
* type is a narrowing conversion, then the cast is performed according the rules of the
* Java language: the high-order bytes are silently dropped.
*
* @param <N> the class to cast to.
* @param type the class to cast to. Must be one of {@link Byte}, {@link Short},
* {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
* @return the casted range, or {@code this} if this range already uses the specified type.
* @throws IllegalArgumentException if the given type is not one of the primitive wrappers for numeric types.
*/
@SuppressWarnings("unchecked")
public <N extends Number & Comparable<? super N>> NumberRange<N> castTo(final Class<N> type) throws IllegalArgumentException {
if (elementType == type) {
return (NumberRange<N>) this;
}
return new NumberRange<>(type, this);
}
/**
* Returns an initially empty array of the given length.
*/
@Override
@SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
Range<E>[] newArray(final int length) {
return new NumberRange[length];
}
/**
* Returns the {@linkplain #getMinValue() minimum value} as a {@code double}.
* If this range is unbounded, then {@link Double#NEGATIVE_INFINITY} is returned.
*
* @return the minimum value.
*/
@SuppressWarnings("unchecked")
public double getMinDouble() {
final Number value = getMinValue();
return (value != null) ? value.doubleValue() : Double.NEGATIVE_INFINITY;
}
/**
* Returns the {@linkplain #getMinDouble() minimum value} with the specified inclusive or exclusive state.
* If this range is unbounded, then {@link Double#NEGATIVE_INFINITY} is returned.
*
* @param inclusive {@code true} for the minimum value inclusive, or
* {@code false} for the minimum value exclusive.
* @return the minimum value, inclusive or exclusive as requested.
*/
public double getMinDouble(final boolean inclusive) {
double value = getMinDouble();
if (inclusive != isMinIncluded()) {
value = next(getElementType(), value, inclusive);
}
return value;
}
/**
* Returns the {@linkplain #getMaxValue() maximum value} as a {@code double}.
* If this range is unbounded, then {@link Double#POSITIVE_INFINITY} is returned.
*
* @return the maximum value.
*/
@SuppressWarnings("unchecked")
public double getMaxDouble() {
final Number value = getMaxValue();
return (value != null) ? value.doubleValue() : Double.POSITIVE_INFINITY;
}
/**
* Returns the {@linkplain #getMaxDouble() maximum value} with the specified inclusive or exclusive state.
* If this range is unbounded, then {@link Double#POSITIVE_INFINITY} is returned.
*
* @param inclusive {@code true} for the maximum value inclusive, or
* {@code false} for the maximum value exclusive.
* @return the maximum value, inclusive or exclusive as requested.
*/
public double getMaxDouble(final boolean inclusive) {
double value = getMaxDouble();
if (inclusive != isMaxIncluded()) {
value = next(getElementType(), value, !inclusive);
}
return value;
}
/**
* Returns the next value for the given type.
*
* @param type the element type.
* @param value the value to increment or decrement.
* @param up {@code true} for incrementing, or {@code false} for decrementing.
* @return the adjacent value.
*/
private static double next(final Class<?> type, double value, final boolean up) {
if (Numbers.isInteger(type)) {
if (up) value++; else value--;
} else if (type.equals(Float.class)) {
final float fv = (float) value;
value = up ? Math.nextUp(fv) : Math.nextDown(fv);
} else if (type.equals(Double.class)) {
value = up ? Math.nextUp(value) : Math.nextDown(value);
} else {
// Thrown IllegalStateException instead than IllegalArgumentException because
// the 'type' argument given to this method come from a NumberRange field.
throw new IllegalStateException(Errors.format(Errors.Keys.NotAPrimitiveWrapper_1, type));
}
return value;
}
/**
* Returns {@code true} if this range contains the given value.
* This method converts {@code this} or the given argument to the widest numeric type,
* then performs the same work than {@link #contains(Comparable)}.
*
* @param value the value to check for inclusion in this range.
* @return {@code true} if the given value is included in this range.
* @throws IllegalArgumentException if the given range can not be converted to a valid type
* through widening conversion.
*/
public boolean containsAny(Number value) throws IllegalArgumentException {
if (value == null) {
return false;
}
final Class<? extends Number> type = Numbers.widestClass(elementType, value.getClass());
value = Numbers.cast(value, type);
if (minValue != null) {
@SuppressWarnings("unchecked")
final int c = ((Comparable) Numbers.cast(minValue, type)).compareTo(value);
if (isMinIncluded ? (c > 0) : (c >= 0)) {
return false;
}
}
if (maxValue != null) {
@SuppressWarnings("unchecked")
final int c = ((Comparable) Numbers.cast(maxValue, type)).compareTo(value);
if (isMaxIncluded ? (c < 0) : (c <= 0)) {
return false;
}
}
return true;
}
/**
* Returns {@code true} if the supplied range is fully contained within this range.
* This method converts {@code this} or the given argument to the widest numeric type,
* then delegates to {@link #contains(Range)}.
*
* @param range the range to check for inclusion in this range.
* @return {@code true} if the given range is included in this range.
* @throws IllegalArgumentException if the given range can not be converted to a valid type
* through widening conversion, or if the units of measurement are not convertible.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public boolean containsAny(final NumberRange<?> range) throws IllegalArgumentException {
/*
* The type bounds is actually <? extends Number & Comparable> but I'm unable to express
* it as local variable as of Java 7. So we have to bypass the compiler check, but those
* casts are actually safes.
*/
final Class type = Numbers.widestClass(elementType, range.elementType);
return castTo(type).contains(convertAndCast(range, type));
}
/**
* Returns {@code true} if the supplied range intersects this range.
* This method converts {@code this} or the given argument to the widest numeric type,
* then delegates to {@link #intersects(Range)}.
*
* @param range the range to check for intersection with this range.
* @return {@code true} if the given range intersects this range.
* @throws IllegalArgumentException if the given range can not be converted to a valid type
* through widening conversion, or if the units of measurement are not convertible.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public boolean intersectsAny(final NumberRange<?> range) throws IllegalArgumentException {
final Class type = Numbers.widestClass(elementType, range.elementType);
return castTo(type).intersects(convertAndCast(range, type));
}
/**
* Returns the union of this range with the given range.
* This method converts {@code this} or the given argument to the widest numeric type,
* then delegates to {@link #intersect(Range)}.
*
* @param range the range to add to this range.
* @return the union of this range with the given range.
* @throws IllegalArgumentException if the given range can not be converted to a valid type
* through widening conversion, or if the units of measurement are not convertible.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public NumberRange<?> intersectAny(final NumberRange<?> range) throws IllegalArgumentException {
Class type = Numbers.widestClass(elementType, range.elementType);
final NumberRange<?> intersect = castOrCopy(castTo(type).intersect(convertAndCast(range, type)));
/*
* Use a finer type capable to holds the result (since the intersection
* may have reduced the range), but not finer than the finest type of
* the ranges used in the intersection calculation.
*/
type = Numbers.narrowestClass(elementType, range.elementType);
type = Numbers.widestClass(type, Numbers.narrowestClass((Number) intersect.minValue));
type = Numbers.widestClass(type, Numbers.narrowestClass((Number) intersect.maxValue));
return intersect.castTo(type);
}
/**
* Returns the union of this range with the given range.
* This method converts {@code this} or the given argument to the widest numeric type,
* then delegates to {@link #union(Range)}.
*
* @param range the range to add to this range.
* @return the union of this range with the given range.
* @throws IllegalArgumentException if the given range can not be converted to a valid type
* through widening conversion, or if the units of measurement are not convertible.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public NumberRange<?> unionAny(final NumberRange<?> range) throws IllegalArgumentException {
final Class type = Numbers.widestClass(elementType, range.elementType);
return castOrCopy(castTo(type).union(convertAndCast(range, type)));
}
/**
* Returns the range of values that are in this range but not in the given range.
* This method converts {@code this} or the given argument to the widest numeric type,
* then delegates to {@link #subtract(Range)}.
*
* @param range the range to subtract.
* @return this range without the given range, as an array of length 0, 1 or 2.
* @throws IllegalArgumentException if the given range can not be converted to a valid type
* through widening conversion, or if the units of measurement are not convertible.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public NumberRange<?>[] subtractAny(final NumberRange<?> range) throws IllegalArgumentException {
final Class type = Numbers.widestClass(elementType, range.elementType);
return (NumberRange[]) castTo(type).subtract(convertAndCast(range, type));
}
}