| /* |
| * 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 IllegalArgumentException 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)); |
| } |
| } |