sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1444586 - /sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java
Date Sun, 10 Feb 2013 19:13:04 GMT
Author: desruisseaux
Date: Sun Feb 10 19:13:03 2013
New Revision: 1444586

URL: http://svn.apache.org/r1444586
Log:
Relax the type of the contains methods, for allowing widening conversions.
Provide an implementation of contains(Range<?>) which take in account the inclusion
states of the bounds.

Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java

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=1444586&r1=1444585&r2=1444586&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 Sun Feb
10 19:13:03 2013
@@ -34,8 +34,15 @@ import static org.apache.sis.util.Argume
  * within the range. Null values are always considered <em>exclusive</em>,
  * since iterations over the values will never reach the infinite bound.
  *
- * <p>To be a member of a {@code Range}, the class type defining the range must implement
- * the {@link Comparable} interface.</p>
+ * {@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.}
  *
  * @param <T> The type of range elements, typically a {@link Number} subclass or {@link
java.util.Date}.
  *
@@ -217,8 +224,34 @@ public class Range<T extends Comparable<
         return (c != 0) || !isMinIncluded || !isMaxIncluded;
     }
 
-    public boolean contains(final T value) throws IllegalArgumentException
-    {
+    /**
+     * Returns {@code true} if this range contains the given value. A range never contains
the
+     * {@code null} value. This is consistent with the <a href="#skip-navbar_top">class
javadoc</a>
+     * stating that null {@linkplain #getMinValue() minimum} or {@linkplain #getMaxValue()
maximum}
+     * values are exclusive.
+     *
+     * @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
{
+        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
@@ -232,9 +265,6 @@ public class Range<T extends Comparable<
          * 'compareTo' method. Intead than using those user implementations, we always
          * use the implementations provided by min/maxValue.
          */
-        if (value == null) {
-            return false;
-        }
         if (minValue != null) {
             final int c = minValue.compareTo(value);
             if (isMinIncluded ? (c > 0) : (c >= 0)) {
@@ -250,11 +280,48 @@ public class Range<T extends Comparable<
         return true;
     }
 
+    /**
+     * Returns {@code true} if the supplied range is fully contained within this 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 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.
+     */
+    public boolean contains(final Range<?> range) throws IllegalArgumentException {
+        return containsNC(ensureCompatible(range));
+    }
 
-    public boolean contains(final Range<T> value) throws IllegalArgumentException
-    {
-        ensureCompatible(value);
-        return this.contains(value.getMinValue()) && this.contains(value.getMaxValue());
+    /**
+     * 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) {
+        /*
+         * We could implement this method as below:
+         *
+         *     return contains(range.minValue) && contains(range.maxValue);
+         *
+         * However the above code performs more comparisons than necessary,
+         * since it implicitly performs the following redundant checks:
+         *
+         *     (range.minValue < maxValue) redundant with (range.maxValue < maxValue)
+         *     (range.maxValue > minValue) redundant with (range.minValue > minValue)
+         *
+         * We can implement this method with less comparisons as below:
+         *
+         *     return minValue.compareTo(range.minValue) <= 0 &&
+         *            maxValue.compareTo(range.maxValue) >= 0;
+         *
+         * However we still have a little bit of additional checks to perform for the
+         * inclusion status of both ranges.  Since the same checks will be needed for
+         * intersection methods,  we factor out the comparisons in 'compareMinTo' and
+         * 'compareMaxTo' methods.
+         */
+        return (compareMinTo(range.minValue, range.isMinIncluded ? 0 : -1) <= 0) &&
+               (compareMaxTo(range.maxValue, range.isMaxIncluded ? 0 : +1) >= 0);
     }
 
 
@@ -348,6 +415,85 @@ public class Range<T extends Comparable<
         return ranges;
     }
 
+    /**
+     * Compares the {@linkplain #getMinValue() minimum value} of this range with the given
bound of
+     * another range. Since the given value is either the minimal or maximal value of another
range,
+     * it may be inclusive or exclusive. The later is specified by {@code position} as below:
+     *
+     * <ul>
+     *   <li> 0 if {@code value} is inclusive.</li>
+     *   <li>-1 if {@code value} is exclusive and lower than the inclusive values of
the other range.</li>
+     *   <li>+1 if {@code value} is exclusive and higher than the inclusive values
of the other range.</li>
+     * </ul>
+     *
+     * Note that the non-zero position shall be exactly -1 or +1, not arbitrary negative
or positive.
+     *
+     * @param  value    A bound value of the other range to be compared to the minimal value
of this range.
+     * @param  position The position of {@code value} relative to the inclusive values of
the other range.
+     * @return Position (-, + or 0) of the inclusive values of this range compared to the
other range.
+     *
+     * @see #containsNC(Range)
+     */
+    final int compareMinTo(final T 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'
+         * can not be 0 in such case, since infinities are always exclusive in this class.
+         */
+        if (minValue == null) {
+            return (value == null) ? 0 : -1;
+        }
+        if (value == null) {
+            return -position;
+        }
+        /*
+         * Compare the two finite values. If they are not equal, we are done regardless the
+         * inclusion states, because the difference between included and excluded values
is
+         * considered smaller than any quantity we can represent.
+         */
+        final int c = minValue.compareTo(value);
+        if (c != 0) {
+            return c;
+        }
+        /*
+         * The two values are equal. If the 'minValue' of this range is inclusive, then the
given
+         * 'value' is directly at the "right" place (the beginning of the interior of this
range),
+         * so the 'position' argument gives directly the position of the "true minValue"
relative
+         * to the interior of the other range.
+         *
+         * But if 'minValue' is exclusive, then the "true minValue" of this range is one
position
+         * to the right  (where "position" is a counter for an infinitely small quantity,
similar
+         * to 'dx' in calculus). The effect is to return 0 if the given 'value' is also exclusive
+         * and lower than the interior of the other range (position == -1),  and a positive
value
+         * in all other cases.
+         */
+        if (!isMinIncluded) {
+            position++;
+        }
+        return position;
+    }
+
+    /**
+     * 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.
+     */
+    final int compareMaxTo(final T value, int position) {
+        if (maxValue == null) {
+            return (value == null) ? 0 : +1;
+        }
+        if (value == null) {
+            return -position;
+        }
+        final int c = maxValue.compareTo(value);
+        if (c != 0) {
+            return c;
+        }
+        if (!isMaxIncluded) {
+            position--;
+        }
+        return position;
+    }
+
     @Override
     public boolean equals(Object object)
     {



Mime
View raw message