sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1444834 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/measure/ main/java/org/apache/sis/util/resources/ test/java/org/apache/sis/measure/ test/java/org/apache/sis/test/suite/
Date Mon, 11 Feb 2013 15:43:20 GMT
Author: desruisseaux
Date: Mon Feb 11 15:43:20 2013
New Revision: 1444834

URL: http://svn.apache.org/r1444834
Log:
Ported NumberRange.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java  
(with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
  (with props)
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeTest.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java?rev=1444834&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java Mon
Feb 11 15:43:20 2013
@@ -0,0 +1,663 @@
+/*
+ *    Geotoolkit.org - An Open Source Java GIS Toolkit
+ *    http://www.geotoolkit.org
+ *
+ *    (C) 2003-2012, Open Source Geospatial Foundation (OSGeo)
+ *    (C) 2009-2012, Geomatys
+ *
+ *    This library is free software; you can redistribute it and/or
+ *    modify it under the terms of the GNU Lesser General Public
+ *    License as published by the Free Software Foundation;
+ *    version 2.1 of the License.
+ *
+ *    This library is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *    Lesser General Public License for more details.
+ */
+package org.apache.sis.measure;
+
+import net.jcip.annotations.Immutable;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * A range of numbers. {@linkplain #union Union} and {@linkplain #intersect intersection}
+ * are computed as usual, except that widening conversions will be applied as needed.
+ *
+ * <p>{@code NumberRange} has no units. For a range of physical measurements with units
of
+ * measure, see {@link MeasurementRange}.</p>
+ *
+ * @param <T> The type of range elements as a subclass of {@link Number}.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ *
+ * @see RangeFormat
+ */
+@Immutable
+public class NumberRange<T extends Number & Comparable<? super T>> extends
Range<T> {
+    //
+    // IMPLEMENTATION NOTE: This class is full of @SuppressWarnings("unchecked") annotations.
+    // Nevertheless we should never get ClassCastException - if we get some, this would be
a
+    // bug in this implementation. Users may get IllegalArgumentException however.
+    //
+
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = -818167965963008231L;
+
+    /**
+     * Constructs an inclusive range of {@code byte} values.
+     *
+     * @param  minValue The minimal value, inclusive.
+     * @param  maxValue The maximal value, <strong>inclusive</strong>.
+     * @return The new range of numeric values for the given bounds.
+     */
+    public static NumberRange<Byte> create(final byte minValue, final byte maxValue)
{
+        return new NumberRange<>(Byte.class, Byte.valueOf(minValue), Byte.valueOf(maxValue));
+    }
+
+    /**
+     * Constructs a range of {@code byte} values.
+     *
+     * @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 bounds.
+     */
+    public static NumberRange<Byte> create(final byte minValue, final boolean isMinIncluded,
+                                           final byte maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(Byte.class,
+                Byte.valueOf(minValue), isMinIncluded,
+                Byte.valueOf(maxValue), isMaxIncluded);
+    }
+
+    /**
+     * Constructs an inclusive range of {@code short} values.
+     *
+     * @param  minValue The minimal value, inclusive.
+     * @param  maxValue The maximal value, <strong>inclusive</strong>.
+     * @return The new range of numeric values for the given bounds.
+     */
+    public static NumberRange<Short> create(final short minValue, final short maxValue)
{
+        return new NumberRange<>(Short.class, Short.valueOf(minValue), Short.valueOf(maxValue));
+    }
+
+    /**
+     * Constructs a range of {@code short} values.
+     *
+     * @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 bounds.
+     */
+    public static NumberRange<Short> create(final short minValue, final boolean isMinIncluded,
+                                            final short maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(Short.class,
+                Short.valueOf(minValue), isMinIncluded,
+                Short.valueOf(maxValue), isMaxIncluded);
+    }
+
+    /**
+     * Constructs an inclusive range of {@code int} values.
+     *
+     * @param  minValue The minimal value, inclusive.
+     * @param  maxValue The maximal value, <strong>inclusive</strong>.
+     * @return The new range of numeric values for the given bounds.
+     */
+    public static NumberRange<Integer> create(final int minValue, final int maxValue)
{
+        return new NumberRange<>(Integer.class, Integer.valueOf(minValue), Integer.valueOf(maxValue));
+    }
+
+    /**
+     * Constructs a range of {@code int} values.
+     *
+     * @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 bounds.
+     */
+    public static NumberRange<Integer> create(final int minValue, final boolean isMinIncluded,
+                                              final int maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(Integer.class,
+                Integer.valueOf(minValue), isMinIncluded,
+                Integer.valueOf(maxValue), isMaxIncluded);
+    }
+
+    /**
+     * Constructs an inclusive range of {@code long} values.
+     *
+     * @param  minValue The minimal value, inclusive.
+     * @param  maxValue The maximal value, <strong>inclusive</strong>.
+     * @return The new range of numeric values for the given bounds.
+     */
+    public static NumberRange<Long> create(final long minValue, final long maxValue)
{
+        return new NumberRange<>(Long.class, Long.valueOf(minValue), Long.valueOf(maxValue));
+    }
+
+    /**
+     * Constructs a range of {@code long} values.
+     *
+     * @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 bounds.
+     */
+    public static NumberRange<Long> create(final long minValue, final boolean isMinIncluded,
+                                           final long maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(Long.class,
+                Long.valueOf(minValue), isMinIncluded,
+                Long.valueOf(maxValue), isMaxIncluded);
+    }
+
+    /**
+     * Constructs an inclusive range of {@code float} values.
+     * The values can not be {@link Float#NaN}.
+     *
+     * @param  minValue The minimal value, inclusive, or {@link Float#NEGATIVE_INFINITY}
if none..
+     * @param  maxValue The maximal value, <strong>inclusive</strong>, or {@link
Float#POSITIVE_INFINITY} if none.
+     * @return The new range of numeric values for the given bounds.
+     */
+    public static NumberRange<Float> create(final float minValue, final float maxValue)
{
+        return new NumberRange<>(Float.class,
+                valueOf("minValue", minValue, Float.NEGATIVE_INFINITY),
+                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY));
+    }
+
+    /**
+     * Constructs a range of {@code float} values.
+     * The values can not be {@link Float#NaN}.
+     *
+     * @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 bounds.
+     */
+    public static NumberRange<Float> create(final float minValue, final boolean isMinIncluded,
+                                            final float maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(Float.class,
+                valueOf("minValue", minValue, Float.NEGATIVE_INFINITY), isMinIncluded,
+                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), 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) ? Float.valueOf(value) : null;
+    }
+
+    /**
+     * Constructs an inclusive range of {@code double} values.
+     * The values can not be {@link Double#NaN}.
+     *
+     * @param  minValue The minimal value, inclusive, or {@link Double#NEGATIVE_INFINITY}
if none..
+     * @param  maxValue The maximal value, <strong>inclusive</strong>, or {@link
Double#POSITIVE_INFINITY} if none.
+     * @return The new range of numeric values for the given bounds.
+     */
+    public static NumberRange<Double> create(final double minValue, final double maxValue)
{
+        return new NumberRange<>(Double.class,
+                valueOf("minValue", minValue, Double.NEGATIVE_INFINITY),
+                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY));
+    }
+
+    /**
+     * Constructs a range of {@code double} values.
+     * The values can not be {@link Double#NaN}.
+     *
+     * @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 bounds.
+     */
+    public static NumberRange<Double> create(final double minValue, final boolean isMinIncluded,
+                                             final double maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(Double.class,
+                valueOf("minValue", minValue, Double.NEGATIVE_INFINITY), isMinIncluded,
+                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), 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) ? Double.valueOf(value) : null;
+    }
+
+    /**
+     * Constructs a range using the smallest type of {@link Number} that can hold the
+     * given values. The given numbers don't need to be of the same type since they will
+     * be {@linkplain Numbers#cast(Number, Class) casted} as needed. More specifically:
+     *
+     * <ul>
+     *   <li>If the values are between {@value java.lang.Byte#MIN_VALUE} and
+     *       {@value java.lang.Byte#MAX_VALUE} inclusive, then the given values are converted
+     *       to {@link Byte} objects and a {@code NumberRange} is created from them.</li>
+     *   <li>Otherwise if the values are between {@value java.lang.Short#MIN_VALUE}
and
+     *       {@value java.lang.Short#MAX_VALUE} inclusive, then the given values are converted
+     *       to {@link Short} objects and a {@code NumberRange} is created from them.</li>
+     *   <li>Otherwise the {@link Integer} type is tested in the same way, then the
+     *       {@link Long} type, and finally the {@link Float} type.</li>
+     *   <li>If none of the above types is suitable, then the {@link Double} type is
used.</li>
+     * </ul>
+     *
+     * @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}.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"})
+    public static NumberRange<?> createBestFit(final Number minValue, final boolean
isMinIncluded,
+                                               final Number maxValue, final boolean isMaxIncluded)
+    {
+        final Class<? extends Number> type = Numbers.widestClass(
+                Numbers.narrowestClass(minValue), Numbers.narrowestClass(maxValue));
+        return (type == null) ? null :
+            new NumberRange(type, Numbers.cast(minValue, type), isMinIncluded,
+                                  Numbers.cast(maxValue, type), isMaxIncluded);
+    }
+
+    /**
+     * Wraps the specified {@link Range} in 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 wrap.
+     * @return The same range than {@code range} as a {@code NumberRange} object.
+     */
+    public static <N extends Number & Comparable<? super N>> NumberRange<N>
wrap(final Range<N> range) {
+        if (range instanceof NumberRange<?>) {
+            return (NumberRange<N>) range;
+        }
+        // The constructor will ensure that the range element class is a subclass of Number.
+        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<T> range) {
+        super(range);
+    }
+
+    /**
+     * Constructs an inclusive range of {@link Number} objects.
+     *
+     * @param  type     The element class, usually one of {@link Byte}, {@link Short},
+     *                  {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
+     * @param  minValue The minimum value, inclusive, or {@code null} if none.
+     * @param  maxValue The maximum value, <strong>inclusive</strong>, or {@code
null} if none.
+     */
+    public NumberRange(final Class<T> type, final T minValue, final T maxValue) {
+        super(type, minValue, maxValue);
+    }
+
+    /**
+     * Constructs a range of {@link Number} objects.
+     *
+     * @param type           The element class, 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<T> type,
+                       final T minValue, final boolean isMinIncluded,
+                       final T 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 class, 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<T> 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 class than this range. This method will
+     * be overridden by subclasses in order to create a range of a more specific type.
+     */
+    @Override
+    NumberRange<T> create(final T minValue, final boolean isMinIncluded,
+                          final T maxValue, final boolean isMaxIncluded)
+    {
+        return new NumberRange<>(elementType, minValue, isMinIncluded, maxValue, isMaxIncluded);
+    }
+
+    /**
+     * Ensures that {@link #elementType} is compatible with the type expected by this range
class.
+     * Invoked for argument checking by the super-class constructor.
+     */
+    @Override
+    final void ensureValidType() throws IllegalArgumentException {
+        ensureNumberClass(elementType);
+        super.ensureValidType(); // Check that the type implements also Comparable.
+    }
+
+    /**
+     * Ensures that the given class is {@link Number} or a subclass.
+     */
+    private static void ensureNumberClass(final Class<?> type) throws IllegalArgumentException
{
+        if (!Number.class.isAssignableFrom(type)) {
+            throw new IllegalArgumentException(Errors.format(
+                    Errors.Keys.IllegalClass_2, Number.class, type));
+        }
+    }
+
+    /**
+     * Returns the type of minimum and maximum values.
+     */
+    @SuppressWarnings("unchecked")
+    private static Class<? extends Number> getElementType(final Range<?> range)
{
+        ArgumentChecks.ensureNonNull("range", range);
+        final Class<?> type = range.elementType;
+        ensureNumberClass(type);
+        /*
+         * Safe because we checked in the above line. We could have used Class.asSubclass(Class)
+         * instead but we want an IllegalArgumentException in case of failure rather than
a
+         * ClassCastException.
+         */
+        return (Class<? extends Number>) type;
+    }
+
+    /**
+     * 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} units to the same units 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","rawtypes"})
+    <N extends Number & Comparable<? super N>>
+    NumberRange<N> convertAndCast(final Range<? extends Number> range, final
Class<N> type)
+            throws IllegalArgumentException
+    {
+        if (type.equals(range.getElementType())) {
+            // Safe because we checked in the line just above.
+            return (NumberRange<N>) wrap((Range) 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.
+     */
+    public <N extends Number & Comparable<? super N>> NumberRange<N>
castTo(final Class<N> type)
+            throws IllegalArgumentException
+    {
+        return convertAndCast(this, type);
+    }
+
+    /**
+     * Returns an initially empty array of the given length.
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
+    NumberRange<T>[] newArray(final int length) {
+        return new NumberRange[length];
+    }
+
+    /**
+     * Returns {@code true} if the specified value is within this range.
+     * This method delegates to {@link #contains(Comparable)}.
+     *
+     * @param  value The value to check for inclusion.
+     * @return {@code true} if the given value is within this range.
+     * @throws IllegalArgumentException if the given value is not comparable.
+     */
+    public final boolean contains(final Number value) throws IllegalArgumentException {
+        if (value != null && !(value instanceof Comparable<?>)) {
+            throw new IllegalArgumentException(Errors.format(
+                    Errors.Keys.NotComparableClass_1, value.getClass()));
+        }
+        return contains((Comparable<?>) value);
+    }
+
+    /**
+     * Returns {@code true} if the specified value is within this range.
+     * The given value must be a subclass of {@link Number}.
+     *
+     * @throws IllegalArgumentException If the given type is not one of the primitive
+     *         wrappers for numeric types.
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public boolean contains(Comparable<?> value) throws IllegalArgumentException {
+        if (value == null) {
+            return false;
+        }
+        ArgumentChecks.ensureCanCast("value", Number.class, value);
+        /*
+         * Suppress warning because we checked the class in the line just above, so we are
safe.
+         * We could have used Class.cast(Object) but we want an IllegalArgumentException
with a
+         * localized message.
+         */
+        Number number = (Number) value;
+        final Class<? extends Number> type = Numbers.widestClass(elementType, number.getClass());
+        number = Numbers.cast(number, type);
+        /*
+         * The 'type' bounds should actually be <? extends Number & Comparable>
since the method
+         * signature expect a Comparable and we have additionally casted to a Number.  However
I
+         * have not found a way to express that safely in a local variable with Java 6.
+         */
+        return castTo((Class) type).containsNC((Comparable<?>) number);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public boolean contains(Range<?> range) throws IllegalArgumentException {
+        final Class<? extends Number> type = Numbers.widestClass(elementType, getElementType(range));
+        /*
+         * The type bounds is actually <? extends Number & Comparable> but I'm
unable to express
+         * it as local variable as of Java 6. So we have to bypass the compiler check, but
those
+         * casts are actually safes - including the (Range) cast - because getElementType(range)
+         * would have throw an exception otherwise.
+         */
+        range = convertAndCast((Range) range, (Class) type);
+        return castTo((Class) type).containsNC(range);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public boolean intersects(Range<?> range) throws IllegalArgumentException {
+        final Class<? extends Number> type = Numbers.widestClass(elementType, getElementType(range));
+        range = convertAndCast((Range) range, (Class) type); // Same comment than contains(Range).
+        return castTo((Class) type).intersectsNC(range);
+    }
+
+    /**
+     * {@inheritDoc}
+     * Widening conversions will be applied as needed.
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public NumberRange<?> union(Range<?> range) throws IllegalArgumentException
{
+        final Class<? extends Number> type = Numbers.widestClass(elementType, getElementType(range));
+        range = convertAndCast((Range) range, (Class) type); // Same comment than contains(Range).
+        return (NumberRange) castTo((Class) type).unionNC(range);
+    }
+
+    /**
+     * {@inheritDoc}
+     * Widening conversions will be applied as needed.
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public NumberRange<?> intersect(Range<?> range) throws IllegalArgumentException
{
+        final Class<? extends Number> rangeType = getElementType(range);
+        Class<? extends Number> type = Numbers.widestClass(elementType, rangeType);
+        range = castTo((Class) type).intersectNC(convertAndCast((Range) range, (Class) 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, rangeType);
+        type = Numbers.widestClass(type, Numbers.narrowestClass((Number) range.minValue));
+        type = Numbers.widestClass(type, Numbers.narrowestClass((Number) range.maxValue));
+        return convertAndCast((Range) range, (Class) type);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @SuppressWarnings({"unchecked","rawtypes"})
+    public NumberRange<?>[] subtract(Range<?> range) throws IllegalArgumentException
{
+        Class<? extends Number> type = Numbers.widestClass(elementType, getElementType(range));
+        return (NumberRange[]) castTo((Class) type)
+                .subtractNC(convertAndCast((Range) range, (Class) type));
+    }
+
+    /**
+     * 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 getMinimum() {
+        final Number value = (Number) getMinValue();
+        return (value != null) ? value.doubleValue() : Double.NEGATIVE_INFINITY;
+    }
+
+    /**
+     * Returns the {@linkplain #getMinimum() 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 getMinimum(final boolean inclusive) {
+        double value = getMinimum();
+        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 getMaximum() {
+        final Number value = (Number) getMaxValue();
+        return (value != null) ? value.doubleValue() : Double.POSITIVE_INFINITY;
+    }
+
+    /**
+     * Returns the {@linkplain #getMaximum() 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 getMaximum(final boolean inclusive) {
+        double value = getMaximum();
+        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 (!up) {
+            value = -value;
+        }
+        if (Numbers.isInteger(type)) {
+            value++;
+        } else if (type.equals(Float.class)) {
+            value = Math.nextUp((float) value);
+        } else if (type.equals(Double.class)) {
+            value = Math.nextUp(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));
+        }
+        if (!up) {
+            value = -value;
+        }
+        return value;
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java?rev=1444834&r1=1444833&r2=1444834&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java Mon Feb
11 15:43:20 2013
@@ -86,7 +86,22 @@ public class Range<T extends Comparable<
     /**
      * Whether the minimal or maximum value is included.
      */
-    private final boolean isMinIncluded, isMaxIncluded;
+    final boolean isMinIncluded, isMaxIncluded;
+
+    /**
+     * 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.
+     */
+    public Range(final Range<T> range) {
+        elementType   = range.elementType;
+        minValue      = range.minValue;
+        isMinIncluded = range.isMinIncluded;
+        maxValue      = range.maxValue;
+        isMaxIncluded = range.isMaxIncluded;
+        ensureValidType();
+    }
 
     /**
      * Creates a new range bounded by the given inclusive values.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1444834&r1=1444833&r2=1444834&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
(original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
Mon Feb 11 15:43:20 2013
@@ -290,6 +290,11 @@ public final class Errors extends Indexe
         public static final int NotAPrimitiveWrapper_1 = 10;
 
         /**
+         * Class ‘{0}’ is not a comparable.
+         */
+        public static final int NotComparableClass_1 = 66;
+
+        /**
          * Argument ‘{0}’ shall not be null.
          */
         public static final int NullArgument_1 = 0;

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1444834&r1=1444833&r2=1444834&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
(original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
Mon Feb 11 15:43:20 2013
@@ -70,6 +70,7 @@ NonScaleUnit_1                  = \u201c
 NonTemporalUnit_1               = \u201c{0}\u201d is not a time unit.
 NotANumber_1                    = Argument \u2018{0}\u2019 shall not be NaN (Not-a-Number).
 NotAPrimitiveWrapper_1          = Class \u2018{0}\u2019 is not a primitive type wrapper.
+NotComparableClass_1            = Class \u2018{0}\u2019 is not a comparable.
 NullArgument_1                  = Argument \u2018{0}\u2019 shall not be null.
 # Use the OGC/ISO "Dictionary" word instead of "Map" for avoiding confusion with geographic
map.
 NullMapKey                      = Null key is not allowed in this dictionary.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1444834&r1=1444833&r2=1444834&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
(original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
Mon Feb 11 15:43:20 2013
@@ -59,6 +59,7 @@ NonScaleUnit_1                  = \u201c
 NonTemporalUnit_1               = \u201c{0}\u201d n\u2019est pas une unit\u00e9 de temps.
 NotANumber_1                    = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre NaN
(Not-a-Number).
 NotAPrimitiveWrapper_1          = La classe \u2018{0}\u2019 n\u2019est pas un adaptateur
d\u2019un type primitif.
+NotComparableClass_1            = La classe \u2018{0}\u2019 n\u2019est pas comparable.
 NullArgument_1                  = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre nul.
 NullMapKey                      = La cl\u00e9 nulle n\u2019est pas autoris\u00e9e dans ce
dictionnaire.
 NullMapValue                    = Les valeurs nulles ne sont pas autoris\u00e9es dans ce
dictionnaire.

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java?rev=1444834&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
(added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
Mon Feb 11 15:43:20 2013
@@ -0,0 +1,118 @@
+/*
+ * 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 org.junit.Test;
+import org.apache.sis.test.DependsOn;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link NumberRange}.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+@DependsOn(RangeTest.class)
+public final strictfp class NumberRangeTest {
+    /**
+     * Tests the bounds values of a range of integers.
+     */
+    @Test
+    public void testIntegerBounds() {
+        final NumberRange<Integer> range = NumberRange.create(10, 20);
+        assertEquals(10, range.getMinimum(     ), 0);
+        assertEquals(10, range.getMinimum(true ), 0);
+        assertEquals( 9, range.getMinimum(false), 0);
+        assertEquals(20, range.getMaximum(     ), 0);
+        assertEquals(20, range.getMaximum(true ), 0);
+        assertEquals(21, range.getMaximum(false), 0);
+    }
+
+    /**
+     * Tests union and intersection without units and type change.
+     */
+    @Test
+    public void testIntegerIntersect() {
+        NumberRange<Integer> r1 = NumberRange.create(10, 20);
+        NumberRange<Integer> r2 = NumberRange.create(15, 30);
+        assertTrue (r1.equals(r1));
+        assertTrue (r2.equals(r2));
+        assertFalse(r1.equals(r2));
+        assertEquals(Integer.class, r1.getElementType());
+        assertEquals(Integer.class, r2.getElementType());
+        assertEquals(NumberRange.create(10, 30), r1.union(r2));
+        assertEquals(NumberRange.create(15, 20), r1.intersect(r2));
+    }
+
+    /**
+     * Tests union and intersection with type change.
+     */
+    @Test
+    public void testDoubleIntersect() {
+        NumberRange<Double> r1 = NumberRange.create(10.0, 20.0);
+        NumberRange<Double> r2 = NumberRange.create(15.0, 30.0);
+        assertEquals(Double.class, r1.getElementType());
+        assertEquals(Double.class, r2.getElementType());
+        assertEquals(NumberRange.create(10.0, 30.0), r1.union(r2));
+        assertEquals(NumberRange.create(15.0, 20.0), r1.intersect(r2));
+    }
+
+    /**
+     * Tests union and intersection with type change.
+     */
+    @Test
+    public void testIntegerDoubleIntersect() {
+        NumberRange<Integer> r1 = NumberRange.create(10, 20);
+        NumberRange<Double>  r2 = NumberRange.create(15.0, 30.0);
+        assertEquals(Integer.class, r1.getElementType());
+        assertEquals(Double .class, r2.getElementType());
+        assertEquals(NumberRange.create(10.0, 30.0), r1.union(r2));
+        assertEquals(NumberRange.create(15, 20), r1.intersect(r2));
+
+        r2 = NumberRange.create(15.5, 30.0);
+        assertEquals(NumberRange.create(15.5f, 20.0f), r1.intersect(r2));
+    }
+
+    /**
+     * Tests union and intersection with type change.
+     */
+    @Test
+    public void testDoubleIntegerIntersect() {
+        NumberRange<Double>  r1 = NumberRange.create(10.0, 20.0);
+        NumberRange<Integer> r2 = NumberRange.create(15, 30);
+        assertEquals(Double .class, r1.getElementType());
+        assertEquals(Integer.class, r2.getElementType());
+        assertEquals(NumberRange.create(10.0, 30.0), r1.union(r2));
+        assertEquals(NumberRange.create(15, 20), r1.intersect(r2));
+
+        r1 = NumberRange.create(10.0, 20.5);
+        assertEquals(NumberRange.create(15.0f, 20.5f), r1.intersect(r2));
+    }
+
+    /**
+     * Tests the {@link NumberRange#createBestFit} method.
+     */
+    @Test
+    public void testCreateBestFit() {
+        assertEquals(NumberRange.create((short) 2, (short) 200),
+                NumberRange.createBestFit(2, true, 200.0, true));
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeTest.java?rev=1444834&r1=1444833&r2=1444834&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeTest.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeTest.java Mon
Feb 11 15:43:20 2013
@@ -25,7 +25,10 @@ import static org.apache.sis.test.Assert
 /**
  * Tests the {@link Range} class.
  *
- * @author Joe White
+ * @author  Joe White
+ * @since   0.3
+ * @version 0.3
+ * @module
  */
 public final strictfp class RangeTest extends TestCase {
     /**

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1444834&r1=1444833&r2=1444834&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
(original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
Mon Feb 11 15:43:20 2013
@@ -72,6 +72,7 @@ import org.junit.runners.Suite;
     // Measurements and formatting.
     org.apache.sis.measure.UnitsTest.class,
     org.apache.sis.measure.RangeTest.class,
+    org.apache.sis.measure.NumberRangeTest.class,
     org.apache.sis.measure.FormattedCharacterIteratorTest.class,
     org.apache.sis.measure.AngleFormatTest.class,
     org.apache.sis.measure.AngleTest.class,



Mime
View raw message