sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1446212 [1/2] - in /sis/trunk: ./ sis-utility/src/main/java/org/apache/sis/measure/ sis-utility/src/main/java/org/apache/sis/util/collection/ sis-utility/src/test/java/org/apache/sis/measure/
Date Thu, 14 Feb 2013 14:45:24 GMT
Author: desruisseaux
Date: Thu Feb 14 14:45:23 2013
New Revision: 1446212

URL: http://svn.apache.org/r1446212
Log:
Merge from the JDK6 branch.

Modified:
    sis/trunk/   (props changed)
    sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/DateRange.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/Range.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/measure/MeasurementRangeTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/measure/RangeTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK7:r1445224-1446208
  Merged /sis/branches/JDK6:r1445253-1446211

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/DateRange.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/DateRange.java?rev=1446212&r1=1446211&r2=1446212&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/DateRange.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/DateRange.java Thu Feb 14 14:45:23 2013
@@ -73,10 +73,10 @@ public class DateRange extends Range<Dat
      * @param  range The range to convert.
      * @param  origin The date to use as the origin.
      * @throws ConversionException if the given range doesn't have a
-     *         {@linkplain MeasurementRange#getUnits unit} compatible with milliseconds.
+     *         {@linkplain MeasurementRange#unit unit} compatible with milliseconds.
      */
     public DateRange(final MeasurementRange<?> range, final Date origin) throws ConversionException {
-        this(range, getConverter(range.getUnits()), origin.getTime());
+        this(range, getConverter(range.unit()), origin.getTime());
     }
 
     /**
@@ -87,8 +87,8 @@ public class DateRange extends Range<Dat
             throws ConversionException
     {
         super(Date.class,
-              new Date(origin + Math.round(converter.convert(range.getMinimum()))), range.isMinIncluded(),
-              new Date(origin + Math.round(converter.convert(range.getMaximum()))), range.isMaxIncluded());
+              new Date(origin + Math.round(converter.convert(range.getMinDouble()))), range.isMinIncluded(),
+              new Date(origin + Math.round(converter.convert(range.getMaxDouble()))), range.isMaxIncluded());
     }
 
     /**
@@ -111,21 +111,8 @@ public class DateRange extends Range<Dat
     }
 
     /**
-     * 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 {
-        // No need to call super.checkElementClass() because Date implements Comparable.
-        if (!Date.class.isAssignableFrom(elementType)) {
-            throw new IllegalArgumentException(Errors.format(
-                    Errors.Keys.IllegalClass_2, Date.class, elementType));
-        }
-    }
-
-    /**
      * Casts the given {@code Range} object to a {@code DateRange}. This method shall be invoked
-     * only in context where we have verified that the range element class is compatible.
+     * only in context where we have verified that the range element type is compatible.
      * This verification is performed by {@link Range#ensureCompatible(Range)} method.
      */
     private static DateRange cast(final Range<?> range) {
@@ -174,7 +161,7 @@ public class DateRange extends Range<Dat
      * {@inheritDoc}
      */
     @Override
-    public DateRange union(final Range<?> range) throws IllegalArgumentException {
+    public DateRange union(final Range<Date> range) {
         return cast(super.union(range));
     }
 
@@ -182,7 +169,7 @@ public class DateRange extends Range<Dat
      * {@inheritDoc}
      */
     @Override
-    public DateRange intersect(final Range<?> range) throws IllegalArgumentException {
+    public DateRange intersect(final Range<Date> range) {
         return cast(super.intersect(range));
     }
 
@@ -190,7 +177,7 @@ public class DateRange extends Range<Dat
      * {@inheritDoc}
      */
     @Override
-    public DateRange[] subtract(final Range<?> range) throws IllegalArgumentException {
+    public DateRange[] subtract(final Range<Date> range) {
         return (DateRange[]) super.subtract(range);
         // Should never throw ClassCastException because super.subtract(Range) invokes newArray(int)
         // and create(...), which are overridden in this class with DateRange return type.

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java?rev=1446212&r1=1446211&r2=1446212&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java Thu Feb 14 14:45:23 2013
@@ -28,10 +28,22 @@ import org.apache.sis.internal.util.Obje
 
 
 /**
- * A range of numbers associated with a unit of measurement. Unit conversions are applied as
- * needed by {@linkplain #union union} and {@linkplain #intersect intersection} operations.
+ * A range of numbers associated with a unit of measurement. All operations performed by this
+ * class ({@linkplain #union union}, {@linkplain #intersect intersection}, <i>etc.</i>) are
+ * performed in the unit of measurement of {@code this} range object - values of the range
+ * object given in argument are converted if needed before an operation is applied.
  *
- * @param <T> The type of range elements as a subclass of {@link Number}.
+ * <p>Other methods defined in this class:</p>
+ * <ul>
+ *   <li>Convenience {@code create(…)} static methods for every floating point primitive types.
+ *       Usage of {@code MeasurementRange} with integer types is possible, but no convenience
+ *       method is provided for integers because they are usually not representative of the
+ *       nature of physical measurements.</li>
+ *   <li>{@link #convertTo(Unit)} for converting the unit of measurement.</li>
+ *   <li>{@link #castTo(Class)} for casting the range values to an other type.</li>
+ * </ul>
+ *
+ * @param <E> The type of range elements as a subclass of {@link Number}.
  *
  * @author  Martin Desruisseaux (IRD)
  * @since   0.3 (derived from geotk-2.4)
@@ -39,29 +51,31 @@ import org.apache.sis.internal.util.Obje
  * @module
  */
 @Immutable
-public class MeasurementRange<T extends Number & Comparable<? super T>> extends NumberRange<T> {
+public class MeasurementRange<E extends Number & Comparable<? super E>> extends NumberRange<E> {
     /**
      * Serial number for inter-operability with different versions.
      */
     private static final long serialVersionUID = 3980319420337513745L;
 
     /**
-     * The units of measurement, or {@code null} if unknown.
+     * The unit of measurement, or {@code null} if unknown.
+     *
+     * @see #unit()
      */
-    private final Unit<?> units;
+    private final Unit<?> unit;
 
     /**
      * Constructs an inclusive range of {@code float} values.
      *
      * @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.
-     * @param  units    The units of measurement, or {@code null} if unknown.
+     * @param  unit     The unit of measurement, or {@code null} if unknown.
      * @return The new range of numeric values for the given bounds and unit of measurement.
      */
-    public static MeasurementRange<Float> create(float minValue, float maxValue, Unit<?> units) {
+    public static MeasurementRange<Float> create(float minValue, float maxValue, Unit<?> unit) {
         return new MeasurementRange<Float>(Float.class,
                 valueOf("minValue", minValue, Float.NEGATIVE_INFINITY),
-                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), units);
+                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), unit);
     }
 
     /**
@@ -71,15 +85,15 @@ public class MeasurementRange<T extends 
      * @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.
-     * @param  units          The units of measurement, or {@code null} if unknown.
+     * @param  unit           The unit of measurement, or {@code null} if unknown.
      * @return The new range of numeric values for the given bounds and unit of measurement.
      */
     public static MeasurementRange<Float> create(float minValue, boolean isMinIncluded,
-                                                 float maxValue, boolean isMaxIncluded, Unit<?> units)
+                                                 float maxValue, boolean isMaxIncluded, Unit<?> unit)
     {
         return new MeasurementRange<Float>(Float.class,
                 valueOf("minValue", minValue, Float.NEGATIVE_INFINITY), isMinIncluded,
-                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), isMaxIncluded, units);
+                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), isMaxIncluded, unit);
     }
 
     /**
@@ -87,13 +101,13 @@ public class MeasurementRange<T extends 
      *
      * @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.
-     * @param  units    The units of measurement, or {@code null} if unknown.
+     * @param  unit     The unit of measurement, or {@code null} if unknown.
      * @return The new range of numeric values for the given bounds and unit of measurement.
      */
-    public static MeasurementRange<Double> create(double minValue, double maxValue, Unit<?> units) {
+    public static MeasurementRange<Double> create(double minValue, double maxValue, Unit<?> unit) {
         return new MeasurementRange<Double>(Double.class,
                 valueOf("minValue", minValue, Double.NEGATIVE_INFINITY),
-                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), units);
+                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), unit);
     }
 
     /**
@@ -103,148 +117,169 @@ public class MeasurementRange<T extends 
      * @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.
-     * @param  units          The units of measurement, or {@code null} if unknown.
+     * @param  unit           The unit of measurement, or {@code null} if unknown.
      * @return The new range of numeric values for the given bounds and unit of measurement.
      */
     public static MeasurementRange<Double> create(double minValue, boolean isMinIncluded,
-                                                  double maxValue, boolean isMaxIncluded, Unit<?> units)
+                                                  double maxValue, boolean isMaxIncluded, Unit<?> unit)
     {
         return new MeasurementRange<Double>(Double.class,
                 valueOf("minValue", minValue, Double.NEGATIVE_INFINITY), isMinIncluded,
-                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), isMaxIncluded, units);
+                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), isMaxIncluded, unit);
     }
 
     /**
      * Constructs a range using the smallest type of {@link Number} that can hold the given values.
      * This method performs the same work than {@link NumberRange#createBestFit
-     * NumberRange.createBestFit(…)} with an additional {@code units} argument.
+     * NumberRange.createBestFit(…)} with an additional {@code unit} argument.
      *
-     * @param  minimum        The minimum value, or {@code null} for negative infinity.
-     * @param  isMinIncluded  Defines whether the minimum value is included in the range.
-     * @param  maximum        The maximum value, or {@code null} for positive infinity.
-     * @param  isMaxIncluded  Defines whether the maximum value is included in the range.
-     * @param  units          The units of measurement, or {@code null} if unknown.
-     * @return The new range, or {@code null} if both {@code minimum} and {@code maximum}
-     *         are {@code null}.
+     * @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.
+     * @param  unit           The unit of measurement, or {@code null} if unknown.
+     * @return The new range, or {@code null} if both {@code minValue} and {@code maxValue} are {@code null}.
      *
      * @see NumberRange#createBestFit(Number, boolean, Number, boolean)
      */
     @SuppressWarnings({"rawtypes","unchecked"})
-    public static MeasurementRange<?> createBestFit(final Number minimum, final boolean isMinIncluded,
-            final Number maximum, final boolean isMaxIncluded, final Unit<?> units)
+    public static MeasurementRange<?> createBestFit(final Number minValue, final boolean isMinIncluded,
+            final Number maxValue, final boolean isMaxIncluded, final Unit<?> unit)
     {
         final Class<? extends Number> type = Numbers.widestClass(
-                Numbers.narrowestClass(minimum), Numbers.narrowestClass(maximum));
+                Numbers.narrowestClass(minValue), Numbers.narrowestClass(maxValue));
         return (type == null) ? null :
-            new MeasurementRange(type, Numbers.cast(minimum, type), isMinIncluded,
-                                       Numbers.cast(maximum, type), isMaxIncluded, units);
+            new MeasurementRange(type, Numbers.cast(minValue, type), isMinIncluded,
+                                       Numbers.cast(maxValue, type), isMaxIncluded, unit);
     }
 
     /**
-     * Constructs a range with the same values than the specified range and the given units.
+     * Constructs a range with the same values than the specified range and the given unit.
      * This is a copy constructor, with the addition of a unit of measurement.
      *
      * @param range The range to copy. The elements must be {@link Number} instances.
-     * @param units The units of measurement, or {@code null} if unknown.
+     * @param unit  The unit of measurement, or {@code null} if unknown.
      */
-    public MeasurementRange(final Range<T> range, final Unit<?> units) {
+    public MeasurementRange(final Range<E> range, final Unit<?> unit) {
         super(range);
-        this.units = units;
+        this.unit = unit;
     }
 
     /**
      * 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 minimum       The minimum value.
-     * @param maximum       The maximum value.
-     * @param units         The units of measurement, or {@code null} if unknown.
-     */
-    public MeasurementRange(final Class<T> type, final T minimum, final T maximum, final Unit<?> units) {
-        super(type, minimum, maximum);
-        this.units = units;
+     * @param type     The element type, usually one of {@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.
+     * @param unit     The unit of measurement, or {@code null} if unknown.
+     */
+    public MeasurementRange(final Class<E> type, final E minValue, final E maxValue, final Unit<?> unit) {
+        super(type, minValue, maxValue);
+        this.unit = unit;
     }
 
     /**
      * 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 minimum       The minimum value.
-     * @param isMinIncluded Defines whether the minimum value is included in the Range.
-     * @param maximum       The maximum value.
-     * @param isMaxIncluded Defines whether the maximum value is included in the Range.
-     * @param units         The units of measurement, or {@code null} if unknown.
-     */
-    public MeasurementRange(final Class<T> type,
-                            final T minimum, final boolean isMinIncluded,
-                            final T maximum, final boolean isMaxIncluded,
-                            final Unit<?> units)
+     * @param type          The element type, usually one of {@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.
+     * @param unit          The unit of measurement, or {@code null} if unknown.
+     */
+    public MeasurementRange(final Class<E> type,
+                            final E minValue, final boolean isMinIncluded,
+                            final E maxValue, final boolean isMaxIncluded,
+                            final Unit<?> unit)
     {
-        super(type, minimum, isMinIncluded, maximum, isMaxIncluded);
-        this.units = units;
+        super(type, minValue, isMinIncluded, maxValue, isMaxIncluded);
+        this.unit = unit;
     }
 
     /**
      * 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 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.
-     * @param units   The units of measurement, or {@code null} if unknown.
+     * @param unit  The unit of measurement, or {@code null} if unknown.
      */
-    private MeasurementRange(Class<T> type, Range<? extends Number> range, final Unit<?> units) {
+    private MeasurementRange(final Class<E> type, final Range<? extends Number> range, final Unit<?> unit) {
         super(type, range);
-        this.units = units;
+        this.unit = unit;
     }
 
     /**
-     * Creates a new range using the same element class than this range.
+     * Creates a new range using the same element type and the same unit than this range.
      */
     @Override
-    MeasurementRange<T> create(final T minValue, final boolean isMinIncluded,
-                               final T maxValue, final boolean isMaxIncluded)
+    Range<E> create(final E minValue, final boolean isMinIncluded,
+                    final E maxValue, final boolean isMaxIncluded)
     {
-        return new MeasurementRange<T>(elementType, minValue, isMinIncluded, maxValue, isMaxIncluded, units);
+        return new MeasurementRange<E>(elementType, minValue, isMinIncluded, maxValue, isMaxIncluded, unit);
     }
 
     /**
-     * Returns the units of measurement, or {@code null} if unknown.
+     * Returns the unit of measurement, or {@code null} if unknown.
      *
-     * @return The units of measurement, or {@code null}.
+     * @return The unit of measurement, or {@code null}.
      */
     @Override
-    public Unit<?> getUnits() {
-        return units;
+    public Unit<?> unit() {
+        return unit;
     }
 
     /**
-     * Converts this range to the specified units. If this measurement range has null units,
-     * then the specified target units are simply assigned to the returned range with no
+     * Converts this range to the specified unit. If this measurement range has null unit,
+     * then the specified target unit are simply assigned to the returned range with no
      * other changes.
      *
-     * @param  targetUnits the target units, or {@code null} for keeping the units unchanged.
+     * @param  targetUnit the target unit, or {@code null} for keeping the unit unchanged.
      * @return The converted range, or {@code this} if no conversion is needed.
-     * @throws ConversionException if the target units are not compatible with
-     *         this {@linkplain #getUnits range units}.
+     * @throws ConversionException if the target unit are not compatible with
+     *         this {@linkplain #unit() range unit}.
      */
-    public MeasurementRange<T> convertTo(final Unit<?> targetUnits) throws ConversionException {
-        return convertAndCast(elementType, targetUnits);
+    public MeasurementRange<E> convertTo(final Unit<?> targetUnit) throws ConversionException {
+        return convertAndCast(elementType, targetUnit);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public <N extends Number & Comparable<? super N>> MeasurementRange<N> castTo(Class<N> type) {
-        return convertAndCast(this, type);
+    @SuppressWarnings("unchecked")
+    public <N extends Number & Comparable<? super N>> MeasurementRange<N> castTo(final Class<N> type) {
+        if (elementType == type) {
+            return (MeasurementRange<N>) this;
+        } else {
+            return new MeasurementRange<N>(type, this, unit);
+        }
+    }
+
+    /**
+     * If the given range is an instance of {@code MeasurementRange}, converts that
+     * range to the unit of this range. Otherwise returns the given range unchanged.
+     *
+     * @param  range The range to convert.
+     * @return The converted range.
+     * @throws IllegalArgumentException if the given target unit is not compatible with
+     *         the unit of this range.
+     */
+    private <N extends E> Range<N> convert(final Range<N> range) throws IllegalArgumentException {
+        if (range instanceof MeasurementRange<?>) try {
+            return ((MeasurementRange<N>) range).convertAndCast(range.elementType, unit);
+        } catch (ConversionException e) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2,
+                    ((MeasurementRange<?>) range).unit, unit), e);
+        }
+        return range;
     }
 
     /**
      * Casts the specified range to the specified type. If this class is associated to a unit of
-     * measurement, then this method convert the {@code range} units to the same units than this
+     * measurement, then this method convert the {@code range} unit to the same unit than this
      * instance.
      *
      * @param type The class to cast to. Must be one of {@link Byte}, {@link Short},
@@ -253,64 +288,60 @@ public class MeasurementRange<T extends 
      */
     @Override
     <N extends Number & Comparable<? super N>>
-    MeasurementRange<N> convertAndCast(final Range<? extends Number> range, final Class<N> type)
+    NumberRange<N> convertAndCast(final NumberRange<?> range, final Class<N> type)
             throws IllegalArgumentException
     {
-        if (range instanceof MeasurementRange<?>) {
-            final MeasurementRange<?> casted = (MeasurementRange<?>) range;
-            try {
-                return casted.convertAndCast(type, units);
-            } catch (ConversionException e) {
-                throw new IllegalArgumentException(Errors.format(
-                        Errors.Keys.IncompatibleUnits_2, casted.units, units), e);
-            }
+        if (range instanceof MeasurementRange<?>) try {
+            return ((MeasurementRange<?>) range).convertAndCast(type, unit);
+        } catch (ConversionException e) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2,
+                    ((MeasurementRange<?>) range).unit, unit), e);
         }
-        return new MeasurementRange<N>(type, range, units);
+        return new MeasurementRange<N>(type, range, unit);
     }
 
     /**
-     * Casts this range to the specified type and converts to the specified units.
+     * Casts this range to the specified type and converts to the specified unit.
+     * This method is invoked on the {@code other} instance in expressions like
+     * {@code this.operation(other)}.
      *
      * @param  type The class to cast to. Must be one of {@link Byte}, {@link Short},
      *             {@link Integer}, {@link Long}, {@link Float} or {@link Double}.
-     * @param  targetUnit the target units, or {@code null} for no change.
+     * @param  targetUnit the target unit, or {@code null} for no change.
      * @return The casted range, or {@code this}.
-     * @throws ConversionException if the target units are not compatible with
-     *         this {@linkplain #getUnits range units}.
+     * @throws ConversionException if the given target unit is not compatible with
+     *         the unit of this range.
      */
     @SuppressWarnings("unchecked")
     private <N extends Number & Comparable<? super N>> MeasurementRange<N>
-            convertAndCast(final Class<N> type, final Unit<?> targetUnits) throws ConversionException
+            convertAndCast(final Class<N> type, Unit<?> targetUnit) throws ConversionException
     {
-        if (targetUnits == null || targetUnits.equals(units)) {
-            if (type.equals(elementType)) {
+        if (targetUnit == null || targetUnit.equals(unit)) {
+            if (elementType == type) {
                 return (MeasurementRange<N>) this;
-            } else {
-                return new MeasurementRange<N>(type, this, units);
+            }
+            targetUnit = unit;
+        } else if (unit != null) {
+            final UnitConverter converter = unit.getConverterToAny(targetUnit);
+            if (!converter.equals(UnitConverter.IDENTITY)) {
+                boolean minInc = isMinIncluded;
+                boolean maxInc = isMaxIncluded;
+                double minimum = converter.convert(getMinDouble());
+                double maximum = converter.convert(getMaxDouble());
+                if (minimum > maximum) {
+                    final double  td = minimum; minimum = maximum; maximum = td;
+                    final boolean tb = minInc;  minInc  = maxInc;  maxInc  = tb;
+                }
+                if (Numbers.isInteger(type)) {
+                    minInc &= (minimum == (minimum = Math.floor(minimum)));
+                    maxInc &= (maximum == (maximum = Math.ceil (maximum)));
+                }
+                return new MeasurementRange<N>(type,
+                        Numbers.cast(minimum, type), minInc,
+                        Numbers.cast(maximum, type), maxInc, targetUnit);
             }
         }
-        if (units == null) {
-            return new MeasurementRange<N>(type, this, targetUnits);
-        }
-        final UnitConverter converter = units.getConverterToAny(targetUnits);
-        if (converter.equals(UnitConverter.IDENTITY)) {
-            return new MeasurementRange<N>(type, this, targetUnits);
-        }
-        boolean isMinIncluded = isMinIncluded();
-        boolean isMaxIncluded = isMaxIncluded();
-        Double minimum = converter.convert(getMinimum());
-        Double maximum = converter.convert(getMaximum());
-        if (minimum.compareTo(maximum) > 0) {
-            final Double td = minimum;
-            minimum = maximum;
-            maximum = td;
-            final boolean tb = isMinIncluded;
-            isMinIncluded = isMaxIncluded;
-            isMaxIncluded = tb;
-        }
-        return new MeasurementRange<N>(type,
-                Numbers.cast(minimum, type), isMinIncluded,
-                Numbers.cast(maximum, type), isMaxIncluded, targetUnits);
+        return new MeasurementRange<N>(type, this, targetUnit);
     }
 
     /**
@@ -318,39 +349,78 @@ public class MeasurementRange<T extends 
      */
     @Override
     @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
-    MeasurementRange<T>[] newArray(final int length) {
+    final Range<E>[] newArray(final int length) {
         return new MeasurementRange[length];
     }
 
     /**
      * {@inheritDoc}
+     * If the given range is an instance of {@code MeasurementRange}, then this method converts
+     * the value of the other range to the unit of measurement of this range before to perform
+     * the operation.
+     *
+     * @throws IllegalArgumentException is the given range is an instance of
+     *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
-    public MeasurementRange<?> union(final Range<?> range) throws IllegalArgumentException {
-        return (MeasurementRange<?>) super.union(range);
-        // Should never throw ClassCastException because super.union(Range) invokes create(...),
-        // which is overridden in this class with MeasurementRange return type.
+    public boolean contains(final Range<? extends E> range) throws IllegalArgumentException {
+        return super.contains(convert(range));
     }
 
     /**
      * {@inheritDoc}
+     * If the given range is an instance of {@code MeasurementRange}, then this method converts
+     * the value of the other range to the unit of measurement of this range before to perform
+     * the operation.
+     *
+     * @throws IllegalArgumentException is the given range is an instance of
+     *         {@code MeasurementRange} using incommensurable unit of measurement.
+     */
+    @Override
+    public boolean intersects(final Range<? extends E> range) throws IllegalArgumentException {
+        return super.intersects(convert(range));
+    }
+
+    /**
+     * {@inheritDoc}
+     * If the given range is an instance of {@code MeasurementRange}, then this method converts
+     * the value of the other range to the unit of measurement of this range before to perform
+     * the operation.
+     *
+     * @throws IllegalArgumentException is the given range is an instance of
+     *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
-    public MeasurementRange<?> intersect(final Range<?> range) throws IllegalArgumentException {
-        return (MeasurementRange<?>) super.intersect(range);
-        // Should never throw ClassCastException because super.intersect(Range) invokes
-        // convertAndCast(...),  which is overridden in this class with MeasurementRange
-        // return type.
+    public Range<E> intersect(final Range<E> range) throws IllegalArgumentException {
+        return super.intersect(convert(range));
     }
 
     /**
      * {@inheritDoc}
+     * If the given range is an instance of {@code MeasurementRange}, then this method converts
+     * the value of the other range to the unit of measurement of this range before to perform
+     * the operation.
+     *
+     * @throws IllegalArgumentException is the given range is an instance of
+     *         {@code MeasurementRange} using incommensurable unit of measurement.
+     */
+    @Override
+    public Range<E> union(final Range<E> range) throws IllegalArgumentException {
+        return super.union(convert(range));
+    }
+
+    /**
+     * {@inheritDoc}
+     * If the given range is an instance of {@code MeasurementRange}, then this method converts
+     * the value of the other range to the unit of measurement of this range before to perform
+     * the operation.
+     *
+     * @throws IllegalArgumentException is the given range is an instance of
+     *         {@code MeasurementRange} using incommensurable unit of measurement.
      */
     @Override
-    public MeasurementRange<?>[] subtract(final Range<?> range) throws IllegalArgumentException {
-        return (MeasurementRange<?>[]) super.subtract(range);
-        // Should never throw ClassCastException because super.subtract(Range) invokes newArray(int)
-        // and create(...), which are overridden in this class with MeasurementRange return type.
+    public Range<E>[] subtract(final Range<E> range) throws IllegalArgumentException {
+        return super.subtract(convert(range));
     }
 
     /**
@@ -360,8 +430,7 @@ public class MeasurementRange<T extends 
     public boolean equals(final Object object) {
         if (super.equals(object)) {
             if (object instanceof MeasurementRange<?>) {
-                final MeasurementRange<?> that = (MeasurementRange<?>) object;
-                return Objects.equals(this.units, that.units);
+                return Objects.equals(unit, ((MeasurementRange<?>) object).unit);
             }
             return true;
         }

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java?rev=1446212&r1=1446211&r2=1446212&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java Thu Feb 14 14:45:23 2013
@@ -18,18 +18,40 @@ 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.
+ * 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 no units. For a range of physical measurements with units of
- * measure, see {@link MeasurementRange}.</p>
+ * <p>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>
  *
- * @param <T> The type of range elements as a subclass of {@link Number}.
+ *   <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>
+ *
+ * @param <E> The type of range elements as a subclass of {@link Number}.
  *
  * @author  Martin Desruisseaux (IRD)
  * @author  Jody Garnett (for parameterized type inspiration)
@@ -40,13 +62,7 @@ import org.apache.sis.util.resources.Err
  * @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.
-    //
-
+public class NumberRange<E extends Number & Comparable<? super E>> extends Range<E> {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -253,18 +269,19 @@ public class NumberRange<T extends Numbe
     /**
      * 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:
+     * be {@linkplain Numbers#cast(Number, Class) casted} as needed. More specifically
+     * this method returns:
      *
      * <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>
+     *   <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>
      *
      * @param  minValue       The minimal value, or {@code null} if none.
@@ -285,20 +302,20 @@ public class NumberRange<T extends Numbe
     }
 
     /**
-     * Wraps the specified {@link Range} in a {@code NumberRange} object. If the specified
+     * 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 wrap.
+     * @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> wrap(final Range<N> range) {
+    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 class is a subclass of Number.
+        // The constructor will ensure that the range element type is a subclass of Number.
         return new NumberRange<N>(range);
     }
 
@@ -308,35 +325,35 @@ public class NumberRange<T extends Numbe
      *
      * @param range The range to copy. The elements must be {@link Number} instances.
      */
-    public NumberRange(final Range<T> range) {
+    public NumberRange(final Range<E> range) {
         super(range);
     }
 
     /**
      * Constructs an inclusive range of {@link Number} objects.
      *
-     * @param  type     The element class, usually one of {@link Byte}, {@link Short},
+     * @param  type     The element type, 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) {
+    public NumberRange(final Class<E> type, final E minValue, final E maxValue) {
         super(type, minValue, maxValue);
     }
 
     /**
      * Constructs a range of {@link Number} objects.
      *
-     * @param type           The element class, usually one of {@link Byte}, {@link Short},
+     * @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<T> type,
-                       final T minValue, final boolean isMinIncluded,
-                       final T maxValue, final boolean isMaxIncluded)
+    public NumberRange(final Class<E> type,
+                       final E minValue, final boolean isMinIncluded,
+                       final E maxValue, final boolean isMaxIncluded)
     {
         super(type, minValue, isMinIncluded, maxValue, isMaxIncluded);
     }
@@ -345,13 +362,13 @@ public class NumberRange<T extends Numbe
      * 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},
+     * @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<T> type, final Range<? extends Number> range)
+    NumberRange(final Class<E> type, final Range<? extends Number> range)
             throws IllegalArgumentException
     {
         super(type, Numbers.cast(range.minValue, type), range.isMinIncluded,
@@ -359,56 +376,20 @@ public class NumberRange<T extends Numbe
     }
 
     /**
-     * Creates a new range using the same element class than this range. This method will
+     * 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
-    NumberRange<T> create(final T minValue, final boolean isMinIncluded,
-                          final T maxValue, final boolean isMaxIncluded)
+    Range<E> create(final E minValue, final boolean isMinIncluded,
+                    final E maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<T>(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;
+        return new NumberRange<E>(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} units to the same units than this
-     * instance.  This method is overridden by {@link MeasurementRange} only in the way described
+     * 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},
@@ -417,14 +398,13 @@ public class NumberRange<T extends Numbe
      * @throws IllegalArgumentException If the given type is not one of the primitive
      *         wrappers for numeric types.
      */
-    @SuppressWarnings({"unchecked","rawtypes"})
+    @SuppressWarnings("unchecked")
     <N extends Number & Comparable<? super N>>
-    NumberRange<N> convertAndCast(final Range<? extends Number> range, final Class<N> type)
+    NumberRange<N> convertAndCast(final NumberRange<?> 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);
+        if (range.elementType == type) {
+            return (NumberRange<N>) range;
         }
         return new NumberRange<N>(type, range);
     }
@@ -441,10 +421,14 @@ public class NumberRange<T extends Numbe
      * @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
     {
-        return convertAndCast(this, type);
+        if (elementType == type) {
+            return (NumberRange<N>) this;
+        }
+        return new NumberRange<N>(type, this);
     }
 
     /**
@@ -452,142 +436,24 @@ public class NumberRange<T extends Numbe
      */
     @Override
     @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
-    NumberRange<T>[] newArray(final int length) {
+    Range<E>[] 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() {
+    public double getMinDouble() {
         final Number value = (Number) getMinValue();
         return (value != null) ? value.doubleValue() : Double.NEGATIVE_INFINITY;
     }
 
     /**
-     * Returns the {@linkplain #getMinimum() minimum value} with the specified inclusive or
+     * 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.
      *
@@ -595,8 +461,8 @@ public class NumberRange<T extends Numbe
      *         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();
+    public double getMinDouble(final boolean inclusive) {
+        double value = getMinDouble();
         if (inclusive != isMinIncluded()) {
             value = next(getElementType(), value, inclusive);
         }
@@ -610,13 +476,13 @@ public class NumberRange<T extends Numbe
      * @return The maximum value.
      */
     @SuppressWarnings("unchecked")
-    public double getMaximum() {
+    public double getMaxDouble() {
         final Number value = (Number) getMaxValue();
         return (value != null) ? value.doubleValue() : Double.POSITIVE_INFINITY;
     }
 
     /**
-     * Returns the {@linkplain #getMaximum() maximum value} with the specified inclusive or
+     * 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.
      *
@@ -624,8 +490,8 @@ public class NumberRange<T extends Numbe
      *         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();
+    public double getMaxDouble(final boolean inclusive) {
+        double value = getMaxDouble();
         if (inclusive != isMaxIncluded()) {
             value = next(getElementType(), value, !inclusive);
         }
@@ -660,4 +526,131 @@ public class NumberRange<T extends Numbe
         }
         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 is fully contained within 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 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 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));
+    }
 }

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/Range.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/Range.java?rev=1446212&r1=1446211&r2=1446212&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/Range.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/Range.java Thu Feb 14 14:45:23 2013
@@ -20,11 +20,9 @@ import java.io.Serializable;
 import javax.measure.unit.Unit;
 import net.jcip.annotations.Immutable;
 import org.apache.sis.util.collection.CheckedContainer;
-import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Numbers;
 
-import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
-
 // Related to JDK7
 import org.apache.sis.internal.util.Objects;
 
@@ -39,22 +37,21 @@ import org.apache.sis.internal.util.Obje
  * within the range. Null values are always considered <em>exclusive</em>,
  * since iterations over the values will never reach the infinite bound.
  *
- * {@section Type of range elements}
- * To be a member of a {@code Range}, the {@code <T>} type defining the range must implement the
- * {@link Comparable} interface. Some methods like {@link #contains(Comparable)}, which would
- * normally expect an argument of type {@code T}, accept the base type {@code Comparable<?>} in
- * order to allow widening conversions by the {@link NumberRange} subclass. Passing an argument of
- * non-convertible type to any method will cause an {@link IllegalArgumentException} to be thrown.
- *
- * {@note This class should never throw <code>ClassCastException</code>, unless there is a bug
- *        in the <code>Range</code> class or subclasses implementation.}
+ * {@section Type and value of range elements}
+ * To be a member of a {@code Range}, the {@code <E>} type defining the range must implement the
+ * {@link Comparable} interface. All argument values given to the methods of this class shall be
+ * or contain instances of the same {@code <E>} type. The type is enforced by parameterized type,
+ * but some subclasses may put additional constraints. For example {@link MeasurementRange} will
+ * additionally checks the units of measurement. Every methods defined in this class may throw
+ * an {@link IllegalArgumentException} if a given argument does not meet a constraint other than
+ * the type.
  *
  * {@section String representation}
  * The {@linkplain #toString() string representation} of a {@code Range} is defined
- * in a locale-insensitive way. In order to format a range using the current locale,
+ * in a locale-insensitive way. In order to format a range using a different locale,
  * or for parsing a range, use {@link RangeFormat}.
  *
- * @param <T> The type of range elements, typically a {@link Number} subclass or {@link java.util.Date}.
+ * @param <E> The type of range elements, typically a {@link Number} subclass or {@link java.util.Date}.
  *
  * @author  Joe White
  * @author  Martin Desruisseaux (Geomatys)
@@ -66,7 +63,7 @@ import org.apache.sis.internal.util.Obje
  * @see RangeFormat
  */
 @Immutable
-public class Range<T extends Comparable<? super T>> implements CheckedContainer<T>, Serializable {
+public class Range<E extends Comparable<? super E>> implements CheckedContainer<E>, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -77,12 +74,12 @@ public class Range<T extends Comparable<
      *
      * @see #getElementType()
      */
-    final Class<T> elementType;
+    final Class<E> elementType;
 
     /**
      * The minimal and maximal values.
      */
-    final T minValue, maxValue;
+    final E minValue, maxValue;
 
     /**
      * Whether the minimal or maximum value is included.
@@ -95,13 +92,13 @@ public class Range<T extends Comparable<
      *
      * @param range The range to copy.
      */
-    public Range(final Range<T> range) {
+    public Range(final Range<E> range) {
         elementType   = range.elementType;
         minValue      = range.minValue;
         isMinIncluded = range.isMinIncluded;
         maxValue      = range.maxValue;
         isMaxIncluded = range.isMaxIncluded;
-        ensureValidType();
+        assert validate();
     }
 
     /**
@@ -111,8 +108,14 @@ public class Range<T extends Comparable<
      * @param minValue     The minimal value (inclusive), or {@code null} if none.
      * @param maxValue     The maximal value (inclusive), or {@code null} if none.
      */
-    public Range(final Class<T> elementType, final T minValue, final T maxValue) {
-        this(elementType, minValue, true, maxValue, true);
+    public Range(final Class<E> elementType, final E minValue, final E maxValue) {
+        ArgumentChecks.ensureNonNull("elementType", elementType);
+        this.elementType   = elementType;
+        this.minValue      = minValue;
+        this.isMinIncluded = (minValue != null);
+        this.maxValue      = maxValue;
+        this.isMaxIncluded = (maxValue != null);
+        assert validate();
     }
 
     /**
@@ -124,11 +127,11 @@ public class Range<T extends Comparable<
      * @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 Range(final Class<T> elementType,
-            final T minValue, final boolean isMinIncluded,
-            final T maxValue, final boolean isMaxIncluded)
+    public Range(final Class<E> elementType,
+            final E minValue, final boolean isMinIncluded,
+            final E maxValue, final boolean isMaxIncluded)
     {
-        ensureNonNull("elementType", elementType);
+        ArgumentChecks.ensureNonNull("elementType", elementType);
         /*
          * The 'isMin/Maxincluded' flags must be forced to 'false' if 'minValue' or 'maxValue'
          * are null. This is required for proper working of algorithms implemented in this class.
@@ -138,70 +141,54 @@ public class Range<T extends Comparable<
         this.isMinIncluded = isMinIncluded && (minValue != null);
         this.maxValue      = maxValue;
         this.isMaxIncluded = isMaxIncluded && (maxValue != null);
-        ensureValidType();
-        if (minValue != null) ensureCompatibleType(minValue.getClass());
-        if (maxValue != null) ensureCompatibleType(maxValue.getClass());
+        assert validate();
     }
 
     /**
-     * Creates a new range using the same element class than this range. This method will
+     * 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.
+     *
+     * {@note This method is invoked by all operations (union, intersection, <i>etc.</i>) that may
+     * create a new range. But despite this fact, the return type of those methods are nailed down
+     * to <code>Range</code> (i.e. subclasses shall not override the above-cited operations with
+     * covariant return type) because those operations may return the given argument directly,
+     * and we have no guarantees on the type of those arguments.}
      */
-    Range<T> create(final T minValue, final boolean isMinIncluded,
-                    final T maxValue, final boolean isMaxIncluded)
+    Range<E> create(final E minValue, final boolean isMinIncluded,
+                    final E maxValue, final boolean isMaxIncluded)
     {
-        return new Range<T>(elementType, minValue, isMinIncluded, maxValue, isMaxIncluded);
+        return new Range<E>(elementType, minValue, isMinIncluded, maxValue, isMaxIncluded);
     }
 
     /**
-     * Returns an initially empty array of the given length. To be overridden
-     * by subclasses in order to create arrays of more specific type.
+     * Returns an initially empty array of {@code getClass()} type and of the given length.
+     * This method is overridden by subclasses in order to create arrays of more specific type.
+     * This method is invoked by the {@link #subtract(Range)} method. It is okay to use the new
+     * array only if the ranges to store in that array are only {@code this} or new ranges created
+     * by the {@link #create(Comparable, boolean, Comparable, boolean)} method - otherwise we may
+     * get an {@link ArrayStoreException}.
      */
     @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
-    Range<T>[] newArray(final int length) {
+    Range<E>[] newArray(final int length) {
         return new Range[length];
     }
 
     /**
      * To be overridden by {@link MeasurementRange} only.
      */
-    Unit<?> getUnits() {
+    Unit<?> unit() {
         return null;
     }
 
     /**
-     * Ensures that the given range uses the same element class than this range,
-     * then return the casted argument value.
-     *
-     * @param range The range to test for compatibility.
-     */
-    @SuppressWarnings("unchecked")
-    private Range<? extends T> ensureCompatible(final Range<?> range) throws IllegalArgumentException {
-        ensureNonNull("range", range);
-        ensureCompatibleType(range.elementType);
-        return (Range<? extends T>) range;
-    }
-
-    /**
-     * Ensures that the given type is compatible with the type expected by this range.
-     */
-    private void ensureCompatibleType(final Class<?> type) throws IllegalArgumentException {
-        if (!elementType.isAssignableFrom(type)) {
-            throw new IllegalArgumentException(Errors.format(
-                    Errors.Keys.IllegalClass_2, elementType, type));
-        }
-    }
-
-    /**
-     * Ensures that {@link #elementType} is compatible with the type expected by this range class.
-     * This method is invoked at construction time for validating the type argument. This method
-     * is overridden by {@link NumberRange} and {@link DateRange} for more specific check.
-     */
-    void ensureValidType() throws IllegalArgumentException {
-        if (!Comparable.class.isAssignableFrom(elementType)) {
-            throw new IllegalArgumentException(Errors.format(
-                    Errors.Keys.IllegalClass_2, Comparable.class, elementType));
-        }
+     * Invoked by the constructors in order to ensure that the argument are of valid types.
+     * This check is performed only when assertions are enabled. This test is not needed in
+     * normal execution if the users do not bypass the checks performed by generic types.
+     */
+    private boolean validate() {
+        ArgumentChecks.ensureCanCast("minValue", elementType, minValue);
+        ArgumentChecks.ensureCanCast("maxValue", elementType, maxValue);
+        return Comparable.class.isAssignableFrom(elementType);
     }
 
     /**
@@ -209,7 +196,7 @@ public class Range<T extends Comparable<
      * This is the type specified at construction time.
      */
     @Override
-    public Class<T> getElementType() {
+    public Class<E> getElementType() {
         return elementType;
     }
 
@@ -220,7 +207,7 @@ public class Range<T extends Comparable<
      *
      * @return The minimal value, or {@code null} if this range is unbounded on the lower side.
      */
-    public T getMinValue() {
+    public E getMinValue() {
         return minValue;
     }
 
@@ -242,7 +229,7 @@ public class Range<T extends Comparable<
      *
      * @return The maximal value, or {@code null} if this range is unbounded on the upper side.
      */
-    public T getMaxValue() {
+    public E getMaxValue() {
         return maxValue;
     }
 
@@ -263,9 +250,12 @@ public class Range<T extends Comparable<
      * {@linkplain #getMaxValue() maximum value}, or if they are equal while
      * at least one of them is exclusive.
      *
+     * {@note This method is final because often used by the internal implementation.
+     *        Making the method final ensures that the other methods behave consistently.}
+     *
      * @return {@code true} if this range is empty.
      */
-    public boolean isEmpty() {
+    public final boolean isEmpty() {
         if (minValue == null || maxValue == null) {
             return false; // Unbounded: can't be empty.
         }
@@ -285,26 +275,11 @@ public class Range<T extends 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 is the given value can not be converted to a valid type
-     *         through widening conversion.
      */
-    @SuppressWarnings("unchecked")
-    public boolean contains(final Comparable<?> value) throws IllegalArgumentException {
+    public boolean contains(final E value) {
         if (value == null) {
             return false;
         }
-        ensureCompatibleType(value.getClass());
-        return containsNC((T) value);
-    }
-
-    /**
-     * Implementation of {@link #contains(Comparable)} to be invoked directly by subclasses.
-     * "NC" stands for "No Conversion" - this method does not try to convert the value to a
-     * compatible type.
-     *
-     * @param value The value to test for inclusion. Can not be null.
-     */
-    final boolean containsNC(final T value) {
         /*
          * Implementation note: when testing for inclusion or intersection in a range
          * (or in a rectangle, cube, etc.), it is often easier to test when we do not
@@ -338,20 +313,10 @@ public class Range<T extends Comparable<
      *
      * @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 is the bounds of the given range can not be converted to
-     *         a valid type through widening conversion, or if the units of measurement are not
-     *         convertible.
+     * @throws IllegalArgumentException is the given range is incompatible,
+     *         for example because of incommensurable units of measurement.
      */
-    public boolean contains(final Range<?> range) throws IllegalArgumentException {
-        return containsNC(ensureCompatible(range));
-    }
-
-    /**
-     * Implementation of {@link #contains(Range)} to be invoked directly by subclasses.
-     * "NC" stands for "No Conversion" - this method does not try to convert the bounds
-     * to a compatible type.
-     */
-    final boolean containsNC(final Range<? extends T> range) {
+    public boolean contains(final Range<? extends E> range) {
         /*
          * We could implement this method as below:
          *
@@ -382,19 +347,10 @@ public class Range<T extends Comparable<
      *
      * @param  range The range to check for intersection with this range.
      * @return {@code true} if the given range intersects this range.
-     * @throws IllegalArgumentException is the given range can not be converted to a valid type
-     *         through widening conversion, or if the units of measurement are not convertible.
-     */
-    public boolean intersects(final Range<?> range) throws IllegalArgumentException {
-        return intersectsNC(ensureCompatible(range));
-    }
-
-    /**
-     * Implementation of {@link #intersects(Range)} to be invoked directly by subclasses.
-     * "NC" stands for "No Conversion" - this method does not try to convert the bounds
-     * to a compatible type.
+     * @throws IllegalArgumentException is the given range is incompatible,
+     *         for example because of incommensurable units of measurement.
      */
-    final boolean intersectsNC(final Range<? extends T> range) {
+    public boolean intersects(final Range<? extends E> range) {
         return (compareMinTo(range.maxValue, range.isMaxIncluded ? 0 : +1) <= 0) &&
                (compareMaxTo(range.minValue, range.isMinIncluded ? 0 : -1) >= 0);
     }
@@ -404,21 +360,10 @@ public class Range<T extends Comparable<
      *
      * @param  range The range to intersect.
      * @return The intersection of this range with the given range.
-     * @throws IllegalArgumentException is the given range can not be converted to a valid type
-     *         through widening conversion, or if the units of measurement are not convertible.
-     */
-    public Range<?> intersect(final Range<?> range) throws IllegalArgumentException {
-        return intersectNC(ensureCompatible(range));
-    }
-
-    /**
-     * Implementation of {@link #intersect(Range)} to be invoked directly by subclasses.
-     * "NC" stands for "No Conversion" - this method does not try to convert the bounds
-     * to a compatible type.
+     * @throws IllegalArgumentException is the given range is incompatible,
+     *         for example because of incommensurable units of measurement.
      */
-    final Range<? extends T> intersectNC(final Range<? extends T> range)
-            throws IllegalArgumentException
-    {
+    public Range<E> intersect(final Range<E> range) {
         /*
          * For two ranges [L₁ … H₁] and [L₂ … H₂], the intersection is given by
          * ([max(L₁, L₂) … min(H₁, H₂)]). Only two comparisons is needed.
@@ -430,7 +375,7 @@ public class Range<T extends Comparable<
          * be either 'this' or 'range), return that range. Otherwise we need to create a
          * new one.
          */
-        final Range<? extends T> intersect, min, max;
+        final Range<E> intersect, min, max;
         min = compareMinTo(range.minValue, range.isMinIncluded ? 0 : -1) < 0 ? range : this;
         max = compareMaxTo(range.maxValue, range.isMaxIncluded ? 0 : +1) > 0 ? range : this;
         if (min == max) {
@@ -447,19 +392,11 @@ public class Range<T extends Comparable<
      *
      * @param  range The range to add to this range.
      * @return The union of this range with the given range.
-     * @throws IllegalArgumentException is the given range can not be converted to a valid type
-     *         through widening conversion, or if the units of measurement are not convertible.
-     */
-    public Range<?> union(final Range<?> range) throws IllegalArgumentException {
-        return unionNC(ensureCompatible(range));
-    }
-
-    /**
-     * Implementation of {@link #union(Range)} to be invoked directly by subclasses.
-     * "NC" stands for "No Cast" - this method do not try to cast the value to a compatible type.
+     * @throws IllegalArgumentException is the given range is incompatible,
+     *         for example because of incommensurable units of measurement.
      */
-    final Range<?> unionNC(final Range<? extends T> range) throws IllegalArgumentException {
-        final Range<? extends T> union, min, max;
+    public Range<E> union(final Range<E> range) {
+        final Range<E> union, min, max;
         min = compareMinTo(range.minValue, range.isMinIncluded ? 0 : -1) > 0 ? range : this;
         max = compareMaxTo(range.maxValue, range.isMaxIncluded ? 0 : +1) < 0 ? range : this;
         if (min == max) {
@@ -484,20 +421,16 @@ public class Range<T extends Comparable<
      * </ul>
      *
      * @param  range The range to subtract.
-     * @return This range without the given range.
-     * @throws IllegalArgumentException is the given range can not be converted to a valid type
-     *         through widening conversion, or if the units of measurement are not convertible.
+     * @return This range without the given range, as an array of length 0, 1 or 2.
+     * @throws IllegalArgumentException is the given range is incompatible,
+     *         for example because of incommensurable units of measurement.
      */
-    public Range<?>[] subtract(final Range<?> range) throws IllegalArgumentException {
-        return subtractNC(ensureCompatible(range));
-    }
-
-    /**
-     * Implementation of {@link #subtract(Range)} to be invoked directly by subclasses.
-     * "NC" stands for "No Cast" - this method do not try to cast the value to a compatible type.
-     */
-    final Range<T>[] subtractNC(final Range<? extends T> range) throws IllegalArgumentException {
-        final Range<T> subtract;
+    public Range<E>[] subtract(final Range<E> range) {
+        /*
+         * Implementation note: never store the 'range' argument value in the array
+         * returned by 'newArray(int)', otherwise we may get an ArrayStoreException.
+         */
+        final Range<E> subtract;
         if (!intersects(range)) {
             subtract = this;
         } else {
@@ -512,7 +445,7 @@ public class Range<T extends Comparable<
                 subtract = create(range.maxValue, !range.isMaxIncluded, maxValue, isMaxIncluded);
             } else {
                 if (!clipMax) {
-                    final Range<T>[] array = newArray(2);
+                    final Range<E>[] array = newArray(2);
                     array[0] = create(minValue, isMinIncluded, range.minValue, !range.isMinIncluded);
                     array[1] = create(range.maxValue, !range.isMaxIncluded, maxValue, isMaxIncluded);
                     return array;
@@ -522,7 +455,7 @@ public class Range<T extends Comparable<
         }
         assert contains(subtract) : subtract;
         assert !subtract.intersects(range) : subtract;
-        final Range<T>[] array = newArray(1);
+        final Range<E>[] array = newArray(1);
         array[0] = subtract;
         return array;
     }
@@ -546,7 +479,7 @@ public class Range<T extends Comparable<
      *
      * @see #containsNC(Range)
      */
-    private int compareMinTo(final T value, int position) {
+    private int compareMinTo(final E value, int position) {
         /*
          * Check for infinite values.  If the given value is infinite, it can be either positive or
          * negative infinity, which we can infer from the 'position' argument. Note that 'position'
@@ -589,7 +522,7 @@ public class Range<T extends Comparable<
      * Compares the {@linkplain #getMaxValue() maximum value} of this range with the given bound of
      * another range. See the comment in {@link #compareMinTo(Comparable, int)} for more details.
      */
-    private int compareMaxTo(final T value, int position) {
+    private int compareMaxTo(final E value, int position) {
         if (maxValue == null) {
             return (value == null) ? 0 : +1;
         }
@@ -688,9 +621,9 @@ public class Range<T extends Comparable<
         }
         if (minValue != null && minValue.equals(maxValue)) {
             String value = minValue.toString();
-            final Unit<?> units = getUnits();
-            if (units != null) {
-                value = value + ' ' + units;
+            final Unit<?> unit = unit();
+            if (unit != null) {
+                value = value + ' ' + unit;
             }
             return value;
         }
@@ -713,9 +646,9 @@ public class Range<T extends Comparable<
             buffer.append(maxValue);
         }
         buffer.append(isMaxIncluded ? ']' : ')');
-        final Unit<?> units = getUnits();
-        if (units != null) {
-            buffer.append(' ').append(units);
+        final Unit<?> unit = unit();
+        if (unit != null) {
+            buffer.append(' ').append(unit);
         }
         return buffer.toString();
     }



Mime
View raw message