sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1573740 - in /sis/branches/JDK7/core: sis-referencing/src/main/java/org/apache/sis/parameter/ sis-referencing/src/test/java/org/apache/sis/parameter/ sis-utility/src/main/java/org/apache/sis/measure/ sis-utility/src/main/java/org/apache/si...
Date Mon, 03 Mar 2014 20:42:35 GMT
Author: desruisseaux
Date: Mon Mar  3 20:42:34 2014
New Revision: 1573740

URL: http://svn.apache.org/r1573740
Log:
Bug fix in validation of ParameterValue of type double[].

Modified:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Verifier.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueTest.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Range.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -19,15 +19,13 @@ package org.apache.sis.parameter;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.Map;
-import java.util.Collection;
 import javax.measure.unit.Unit;
-
 import org.opengis.util.CodeList;
 import org.opengis.parameter.ParameterValue;
 import org.opengis.parameter.ParameterDescriptor;
-
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.Classes;
+import org.apache.sis.util.Numbers;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.measure.Range;
@@ -81,15 +79,6 @@ public class DefaultParameterDescriptor<
     private static final long serialVersionUID = 7433401733923393656L;
 
     /**
-     * Key for the <code>{@value}</code> property to be given to the constructor.
-     * This is used for setting the value to be returned by {@link #getValidValues()}.
-     *
-     * <p>This property is mostly for restricting values to a {@linkplain CodeList code list} or enumeration subset.
-     * It is not necessary to provide this property when all values from the code list or enumeration are valid.</p>
-     */
-    public static final String VALID_VALUES_KEY = "validValues";
-
-    /**
      * {@code true} if this parameter is mandatory, or {@code false} if it is optional.
      *
      * @see #getMinimumOccurs()
@@ -113,11 +102,18 @@ public class DefaultParameterDescriptor<
 
     /**
      * The minimum and maximum parameter value with their unit of measurement, or {@code null} if none.
-     * If non-null, then the range element type shall be the same than {@link #valueClass}.
+     * If this field is non-null, then <code>valueDomain.{@linkplain Range#getElementType() getElementType()}</code>
+     * shall be one of the following:
+     *
+     * <ul>
+     *   <li>If {@link #valueClass} is not an array, then the range element type shall be the same class.</li>
+     *   <li>If {@code valueClass} is an array, then the range element type shall be the wrapper of
+     *       <code>valueClass.{@linkplain Class#getComponentType() getComponentType()}</code>.</li>
+     * </ul>
      *
      * @see #getValueDomain()
      */
-    private final Range<? extends T> valueDomain;
+    private final Range<?> valueDomain;
 
     /**
      * The default value for the parameter, or {@code null}.
@@ -127,9 +123,9 @@ public class DefaultParameterDescriptor<
     private final T defaultValue;
 
     /**
-     * Constructs a descriptor from a set of properties. The properties given in argument follow the same rules
-     * than for the {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
-     * Additionally, the following properties are understood by this constructor:
+     * Constructs a descriptor from a set of properties. The properties map is given unchanged to the
+     * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
+     * The following table is a reminder of main (not all) properties:
      *
      * <table class="sis">
      *   <tr>
@@ -138,14 +134,6 @@ public class DefaultParameterDescriptor<
      *     <th>Returned by</th>
      *   </tr>
      *   <tr>
-     *     <td>{@value #VALID_VALUES_KEY}</td>
-     *     <td>{@code Collection<T>} or {@code T[]}</td>
-     *     <td>{@link #getValidValues()}</td>
-     *   </tr>
-     *   <tr>
-     *     <th colspan="3" class="hsep">Defined in parent class (reminder)</th>
-     *   </tr>
-     *   <tr>
      *     <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
      *     <td>{@link org.opengis.referencing.ReferenceIdentifier} or {@link String}</td>
      *     <td>{@link #getName()}</td>
@@ -167,31 +155,57 @@ public class DefaultParameterDescriptor<
      *   </tr>
      * </table>
      *
-     * The {@code valueDomain} argument groups the {@linkplain #getMinimumValue() minimum value},
+     * The {@code valueDomain} argument combines the {@linkplain #getMinimumValue() minimum value},
      * {@linkplain #getMaximumValue() maximum value}, {@linkplain #getUnit() unit of measurement}
-     * (if any) and information about whether the bounds are inclusive or exclusive. This argument
-     * can be provided only if the {@code valueClass} is assignable to {@link Comparable}, and the
-     * value returned by {@link Range#getElementType()} shall be the same than {@code valueClass}.
+     * (if any) and information about whether the bounds are inclusive or exclusive.
+     * If this argument is non-null, then it shall comply to the following conditions:
+     *
+     * <ul>
+     *   <li>The range shall be non-{@linkplain Range#isEmpty() empty}.</li>
+     *   <li><code>valueDomain.{@linkplain Range#getElementType() getElementType()}</code> shall be equals
+     *       to one of the following:
+     *     <ul>
+     *       <li>to {@code valueClass} if the later is not an array,</li>
+     *       <li>or to <code>{@linkplain Numbers#primitiveToWrapper(Class)
+     *           primitiveToWrapper}(valueClass.{@linkplain Class#getComponentType() getComponentType()})</code>
+     *           if {@code valueClass} is an array.</li>
+     *     </ul>
+     *   </li>
+     * </ul>
+     *
+     * If both {@code valueDomain} and {@code validValues} are non-null, then all valid values shall be contained
+     * in the value domain.
      *
      * @param properties   The properties to be given to the identified object.
      * @param valueClass   The class that describes the type of the parameter value.
      * @param valueDomain  The minimum value, maximum value and unit of measurement, or {@code null} if none.
+     * @param validValues  The list of valid values, or {@code null} if there is no restriction.
+     *                     This property is mostly for restricting values to a {@linkplain CodeList code list}
+     *                     or enumeration subset. It is not necessary to provide this property when all values
+     *                     from the code list or enumeration are valid.
      * @param defaultValue The default value for the parameter, or {@code null} if none.
      * @param required     {@code true} if this parameter is mandatory, or {@code false} if it is optional.
      */
     @SuppressWarnings("unchecked")
-    public DefaultParameterDescriptor(final Map<String,?>      properties,
-                                      final Class<T>           valueClass,
-                                      final Range<? extends T> valueDomain,
-                                      final T                  defaultValue,
-                                      final boolean            required)
+    public DefaultParameterDescriptor(final Map<String,?> properties,
+                                      final Class<T>      valueClass,
+                                      final Range<?>      valueDomain,
+                                      final T[]           validValues,
+                                      final T             defaultValue,
+                                      final boolean       required)
     {
         super(properties);
         ensureNonNull("valueClass",   valueClass);
         ensureCanCast("defaultValue", valueClass, defaultValue);
         if (valueDomain != null) {
+            Class<?> componentType = valueClass.getComponentType();
+            if (componentType != null) {
+                componentType = Numbers.primitiveToWrapper(componentType);
+            } else {
+                componentType = valueClass;
+            }
             final Class<?> elementType = valueDomain.getElementType();
-            if (elementType != valueClass) {
+            if (elementType != componentType) {
                 throw new IllegalArgumentException(Errors.getResources(properties).getString(
                         Errors.Keys.IllegalArgumentClass_2, "valueDomain",
                         "Range<" + Classes.getShortName(elementType) + '>'));
@@ -209,37 +223,27 @@ public class DefaultParameterDescriptor<
          * If the caller specified a set of valid values, then copy the values in
          * a new set and verify their type and inclusion in the [min … max] range.
          */
-        final Object values = properties.get(VALID_VALUES_KEY);
-        if (values != null) {
-            final Object[] array;
-            if (values instanceof Object[]) {
-                array = (Object[]) values;
-            } else if (values instanceof Collection<?>) {
-                array = ((Collection<?>) values).toArray();
-            } else {
-                throw new IllegalArgumentException(Errors.getResources(properties)
-                        .getString(Errors.Keys.IllegalPropertyClass_2, VALID_VALUES_KEY, values.getClass()));
-            }
-            final Set<T> valids = CollectionsExt.createSetForType(valueClass, array.length);
-            for (Object value : array) {
+        if (validValues != null) {
+            final Set<T> valids = CollectionsExt.createSetForType(valueClass, validValues.length);
+            for (T value : validValues) {
                 if (value != null) {
                     value = Numerics.cached(value);
                     final Verifier error = Verifier.ensureValidValue(valueClass, null, valueDomain, value);
                     if (error != null) {
                         throw new IllegalArgumentException(error.message(properties, super.getName().getCode(), value));
                     }
+                    valids.add(value);
                 }
-                valids.add((T) value);
             }
-            validValues = CollectionsExt.unmodifiableOrCopy(valids);
+            this.validValues = CollectionsExt.unmodifiableOrCopy(valids);
         } else {
-            validValues = null;
+            this.validValues = null;
         }
         /*
          * Finally, verify the default value if any.
          */
         if (defaultValue != null) {
-            final Verifier error = Verifier.ensureValidValue(valueClass, validValues, valueDomain, defaultValue);
+            final Verifier error = Verifier.ensureValidValue(valueClass, this.validValues, valueDomain, defaultValue);
             if (error != null) {
                 throw new IllegalArgumentException(error.message(properties, super.getName().getCode(), defaultValue));
             }
@@ -348,19 +352,24 @@ public class DefaultParameterDescriptor<
     /**
      * Returns the domain of values with their unit of measurement (if any), or {@code null} if none.
      * The {@code Range} object combines the {@linkplain #getValueClass() value class},
-     * {@linkplain #getMinimumValue() minimum value} and {@link #getMaximumValue() maximum value}
+     * {@linkplain #getMinimumValue() minimum value}, {@linkplain #getMaximumValue() maximum value}
      * and whether these values are inclusive or inclusive. If the range is an instance of
      * {@link MeasurementRange}, then it contains also the {@linkplain #getUnit() unit of measurement}.
      *
-     * <div class="note"><b>API note:</b> If this method returns a non-null value, then its type is exactly
-     * {@code Range<T>}. The {@code <? extends T>} in this method signature is because range types need to
-     * extend {@link Comparable}, while {@code ParameterDescriptor<T>} does not have this requirement.</div>
+     * <div class="note"><b>API note:</b> If this method returns a non-null value, then its type is either exactly
+     * {@code Range<T>}, or {@code Range<E>} where {@code <E>} is the {@linkplain Class#getComponentType() component
+     * type} of {@code <T>} (using wrapper classes for primitive types).</div>
      *
      * @return The domain of values, or {@code null}.
      *
      * @see Parameters#getValueDomain(ParameterDescriptor)
      */
-    public Range<? extends T> getValueDomain() {
+    /* Implementation note: this method is final because the constructor performs various checks on range validity,
+     * and we can not express those rules in the method signature. The 'Verifier.ensureValidValue(…)' method needs
+     * some guarantees about range validity, so we can not let users override this method with a range that may
+     * break them.
+     */
+    public final Range<?> getValueDomain() {
         return valueDomain;
     }
 
@@ -378,7 +387,8 @@ public class DefaultParameterDescriptor<
     @Override
     @SuppressWarnings("unchecked")
     public Comparable<T> getMinimumValue() {
-        return (valueDomain != null) ? (Comparable<T>) valueDomain.getMinValue() : null;
+        return (valueDomain != null && valueDomain.getElementType() == valueClass)
+               ? (Comparable<T>) valueDomain.getMinValue() : null;
     }
 
     /**
@@ -395,7 +405,8 @@ public class DefaultParameterDescriptor<
     @Override
     @SuppressWarnings("unchecked")
     public Comparable<T> getMaximumValue() {
-        return (valueDomain != null) ? (Comparable<T>) valueDomain.getMaxValue() : null;
+        return (valueDomain != null && valueDomain.getElementType() == valueClass)
+               ? (Comparable<T>) valueDomain.getMaxValue() : null;
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -613,7 +613,7 @@ public class DefaultParameterValue<T> ex
      */
     @Override
     public void setValue(final double[] values, final Unit<?> unit) throws InvalidParameterValueException {
-        setValue(value, unit);
+        setValue((Object) values, unit);
     }
 
     /**
@@ -634,9 +634,38 @@ public class DefaultParameterValue<T> ex
      * @throws InvalidParameterValueException if the type of {@code value} is inappropriate for this parameter,
      *         or if the value is illegal for some other reason (for example the value is numeric and out of range).
      */
+    @SuppressWarnings("unchecked")
     protected void setValue(final Object value, final Unit<?> unit) throws InvalidParameterValueException {
-        this.value = Verifier.ensureValidValue(descriptor, value, unit);
-        this.unit  = unit; // Assign only on success.
+        final T convertedValue = Verifier.ensureValidValue(descriptor, value, unit);
+        if (value != null) {
+            validate(convertedValue);
+            this.value = (T) value; // Type has been verified by Verifier.ensureValidValue(…).
+        } else {
+            this.value = descriptor.getDefaultValue();
+        }
+        this.unit = unit; // Assign only on success.
+    }
+
+    /**
+     * Invoked by {@link #setValue(Object, Unit)} after the basic verifications have been done and before
+     * the value is stored. Subclasses can override this method for performing additional verifications.
+     *
+     * {@section Unit of measurement}
+     * If the user specified a unit of measurement, then the value given to this method has been converted
+     * to the unit specified by the {@linkplain #getDescriptor() descriptor}, for easier comparisons against
+     * standardized values. This converted value may be different than the value to be stored in this
+     * {@code ParameterValue}, since the later value will be stored in the unit specified by the user.
+     *
+     * {@section Standard validations}
+     * The checks for {@linkplain DefaultParameterDescriptor#getValueClass() value class},
+     * for {@linkplain DefaultParameterDescriptor#getValueDomain() value domain} and for
+     * {@linkplain DefaultParameterDescriptor#getValidValues() valid values} are performed
+     * before this method is invoked. The default implementation of this method does nothing.
+     *
+     * @param  value The value converted to the unit of measurement specified by the descriptor.
+     * @throws InvalidParameterValueException If the given value is invalid for implementation-specific reasons.
+     */
+    protected void validate(final T value) throws InvalidParameterValueException {
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Verifier.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Verifier.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Verifier.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Verifier.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -18,6 +18,7 @@ package org.apache.sis.parameter;
 
 import java.util.Map;
 import java.util.Set;
+import java.lang.reflect.Array;
 import javax.measure.unit.Unit;
 import javax.measure.converter.UnitConverter;
 import javax.measure.converter.ConversionException;
@@ -33,7 +34,7 @@ import org.apache.sis.util.resources.Err
 /**
  * Verifies the validity of a given value.
  * An instance of {@code Verifier} is created only if an error is detected.
- * In such case, the error message is given by {@link #message(String, Object)}.
+ * In such case, the error message is given by {@link #message(Map, String, Object)}.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4 (derived from geotk-2.0)
@@ -56,7 +57,8 @@ final class Verifier {
      * The current implementation relies on the following invariants:
      *
      * <ul>
-     *   <li>The first element in this array is always the parameter name.</li>
+     *   <li>The first element in this array will be the parameter name. Before the name is known,
+     *       this element is either {@code null} or the index to append to the name.</li>
      *   <li>The last element shall be set to the erroneous value if {@link #needsValue} is {@code true}.</li>
      * </ul>
      */
@@ -73,7 +75,7 @@ final class Verifier {
 
     /**
      * Ensures that the given value is valid according the specified parameter descriptor.
-     * This convenience method ensures that {@code value} is assignable to the
+     * This method ensures that {@code value} is assignable to the
      * {@linkplain ParameterDescriptor#getValueClass() expected class}, is between the
      * {@linkplain ParameterDescriptor#getMinimumValue() minimum} and
      * {@linkplain ParameterDescriptor#getMaximumValue() maximum} values and is one of the
@@ -86,9 +88,8 @@ final class Verifier {
      * @param  descriptor The parameter descriptor to check against.
      * @param  value      The value to check, or {@code null}.
      * @param  unit       The unit of the value to check, or {@code null}.
-     * @return The value casted to the descriptor parameterized type, or the
-     *         {@linkplain ParameterDescriptor#getDefaultValue() default value}
-     *         if the given value was null.
+     * @return The given value converted to the descriptor unit if any,
+     *         then casted to the descriptor parameterized type.
      * @throws InvalidParameterValueException if the parameter value is invalid.
      */
     @SuppressWarnings("unchecked")
@@ -98,8 +99,10 @@ final class Verifier {
         final Class<T> valueClass = descriptor.getValueClass();
         /*
          * Before to verify if the given value is inside the bounds, we need to convert the value
-         * to the units used by the parameter descriptor.
+         * to the units used by the parameter descriptor. The first part of this block verifies
+         * the validity of the unit argument, so we execute it even if 'value' is null.
          */
+        UnitConverter converter = null;
         Object convertedValue = value;
         if (unit != null) {
             final Unit<?> def = descriptor.getUnit();
@@ -112,45 +115,85 @@ final class Verifier {
                 if (getUnitMessageID(unit) != expectedID) {
                     throw new IllegalArgumentException(Errors.format(expectedID, unit));
                 }
-                if (value != null && Number.class.isAssignableFrom(valueClass)) {
-                    final UnitConverter converter;
+                /*
+                 * Verify the type of the user's value before to perform the unit conversion,
+                 * because the conversion will create a new object not necessarily of the same type.
+                 */
+                if (value != null) {
+                    if (!valueClass.isInstance(value)) {
+                        final String name = getName(descriptor);
+                        throw new InvalidParameterValueException(
+                                Errors.format(Errors.Keys.IllegalParameterValueClass_3,
+                                name, valueClass, value.getClass()), name, value);
+                    }
+                    /*
+                     * From this point we will perform the actual unit conversion. The value may be either
+                     * a Number instance, or an array of numbers (typically an array of type double[]). In
+                     * the array case, we will store the converted values in a new array of the same type.
+                     */
                     try {
                         converter = unit.getConverterToAny(def);
                     } catch (ConversionException e) {
                         throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2, unit, def), e);
                     }
-                    Number n = (Number) value; // Given value.
-                    n = converter.convert(n.doubleValue()); // Value in units that we can compare.
-                    try {
-                        convertedValue = Numbers.cast(n, (Class<? extends Number>) valueClass);
-                    } catch (IllegalArgumentException e) {
-                        throw new InvalidParameterValueException(e.getLocalizedMessage(), getName(descriptor), value);
+                    Class<?> componentType = valueClass.getComponentType();
+                    if (componentType == null) {
+                        /*
+                         * Usual case where the value is not an array. Convert the value directly.
+                         * Note that the value can only be a number because the unit is associated
+                         * to MeasurementRange, which accepts only numbers.
+                         */
+                        Number n = converter.convert(((Number) value).doubleValue());
+                        try {
+                            convertedValue = Numbers.cast(n, (Class<? extends Number>) valueClass);
+                        } catch (IllegalArgumentException e) {
+                            throw new InvalidParameterValueException(e.getLocalizedMessage(), getName(descriptor), value);
+                        }
+                    } else {
+                        /*
+                         * The value is an array. Creates a new array and store the converted values
+                         * using Array reflection.
+                         */
+                        final int length = Array.getLength(value);
+                        convertedValue = Array.newInstance(componentType, length);
+                        componentType = Numbers.primitiveToWrapper(componentType);
+                        for (int i=0; i<length; i++) {
+                            Number n = (Number) Array.get(value, i);
+                            n = converter.convert(n.doubleValue()); // Value in units that we can compare.
+                            try {
+                                n = Numbers.cast(n, (Class<? extends Number>) componentType);
+                            } catch (IllegalArgumentException e) {
+                                throw new InvalidParameterValueException(e.getLocalizedMessage(),
+                                        getName(descriptor) + '[' + i + ']', value);
+                            }
+                            Array.set(convertedValue, i, n);
+                        }
                     }
                 }
             }
         }
-        if (convertedValue == null) {
-            return descriptor.getDefaultValue();
-        }
-        final Verifier error;
-        final Set<T> validValues = descriptor.getValidValues();
-        if (descriptor instanceof DefaultParameterDescriptor<?>) {
-            error = ensureValidValue(valueClass, validValues,
-                    ((DefaultParameterDescriptor<?>) descriptor).getValueDomain(), convertedValue);
-        } else {
-            error = ensureValidValue(valueClass, validValues,
-                    descriptor.getMinimumValue(), descriptor.getMaximumValue(), convertedValue);
-        }
-        if (error != null) {
-            final String name = getName(descriptor);
-            throw new InvalidParameterValueException(error.message(null, name, value), name, value);
-        }
         /*
-         * Passed every tests - the value is valid.
-         * Really returns the original value, not the converted one, because we store the given
-         * unit as well. Conversions will be applied on the fly by the getter method if needed.
+         * At this point the user's value has been fully converted to the unit of measurement specified
+         * by the ParameterDescriptor. Now compares the converted value to the restricting given by the
+         * descriptor (set of valid values and range of value domain).
          */
-        return (T) value;
+        if (convertedValue != null) {
+            final Verifier error;
+            final Set<T> validValues = descriptor.getValidValues();
+            if (descriptor instanceof DefaultParameterDescriptor<?>) {
+                error = ensureValidValue(valueClass, validValues,
+                        ((DefaultParameterDescriptor<?>) descriptor).getValueDomain(), convertedValue);
+            } else {
+                error = ensureValidValue(valueClass, validValues,
+                        descriptor.getMinimumValue(), descriptor.getMaximumValue(), convertedValue);
+            }
+            if (error != null) {
+                error.convertRange(converter);
+                final String name = getName(descriptor);
+                throw new InvalidParameterValueException(error.message(null, name, value), name, value);
+            }
+        }
+        return (T) convertedValue;
     }
 
     /**
@@ -164,17 +207,32 @@ final class Verifier {
     static <T> Verifier ensureValidValue(final Class<T> valueClass, final Set<T> validValues,
             final Range<?> valueDomain, final Object convertedValue)
     {
-        Verifier verifier = ensureValidValue(valueClass, validValues, convertedValue);
+        final Verifier verifier = ensureValidValue(valueClass, validValues, null, null, convertedValue);
         if (verifier == null && valueDomain != null) {
-            /*
-             * Following assertion should never fail with DefaultParameterDescriptor instances.
-             * It could fail if the user override DefaultParameterDescriptor.getValueDomain()
-             * in a way that break the method contract.
-             */
-            assert valueDomain.getElementType() == valueClass : valueDomain;
-            if (!((Range) valueDomain).contains((Comparable<?>) convertedValue)) {
-                return new Verifier(Errors.Keys.ValueOutOfRange_4, true, null,
-                        valueDomain.getMinValue(), valueDomain.getMaxValue(), convertedValue);
+            if (!valueClass.isArray()) {
+                /*
+                 * Following assertion should never fail with DefaultParameterDescriptor instances.
+                 * It could fail if the user overrides DefaultParameterDescriptor.getValueDomain()
+                 * in a way that break the method contract.
+                 */
+                assert valueDomain.getElementType() == valueClass : valueDomain;
+                if (!((Range) valueDomain).contains((Comparable<?>) convertedValue)) {
+                    return new Verifier(Errors.Keys.ValueOutOfRange_4, true, null,
+                            valueDomain.getMinValue(), valueDomain.getMaxValue(), convertedValue);
+                }
+            } else {
+                /*
+                 * Following assertion should never fail under the same condition than above.
+                 */
+                assert valueDomain.getElementType() == Numbers.primitiveToWrapper(valueClass.getComponentType()) : valueDomain;
+                final int length = Array.getLength(convertedValue);
+                for (int i=0; i<length; i++) {
+                    final Object e = Array.get(convertedValue, i);
+                    if (!((Range) valueDomain).contains((Comparable<?>) e)) {
+                        return new Verifier(Errors.Keys.ValueOutOfRange_4, true, i,
+                                valueDomain.getMinValue(), valueDomain.getMaxValue(), e);
+                    }
+                }
             }
         }
         return verifier;
@@ -184,38 +242,17 @@ final class Verifier {
      * Same as {@link #ensureValidValue(Class, Set, Range, Object)}, used as a fallback when
      * the descriptor is not an instance of {@link DefaultParameterDescriptor}.
      *
-     * @param convertedValue The value <em>converted to the units specified by the descriptor</em>.
-     *        This is not necessarily the user-provided value.
-     */
-    @SuppressWarnings("unchecked")
-    private static <T> Verifier ensureValidValue(final Class<T> valueClass, final Set<T> validValues,
-            final Comparable<T> minimum, final Comparable<T> maximum, final Object convertedValue)
-    {
-        Verifier verifier = ensureValidValue(valueClass, validValues, convertedValue);
-        if (verifier == null) {
-            if ((minimum != null && minimum.compareTo((T) convertedValue) > 0) ||
-                (maximum != null && maximum.compareTo((T) convertedValue) < 0))
-            {
-                verifier = new Verifier(Errors.Keys.ValueOutOfRange_4, true, null, minimum, maximum, convertedValue);
-            }
-        }
-        return verifier;
-    }
-
-    /**
-     * Ensures that the given value has valid type and is a member of the set of valid values.
-     * If the value is valid, returns {@code null}. Otherwise returns an object that can be used
-     * for formatting the error message.
-     *
-     * <p>If this method returns {@code null}, then {@code convertedValue} can safely be casted to
-     * type {@code <T>}.</p>
+     * <div class="note"><b>Implementation note:</b>
+     * At the difference of {@code ensureValidValue(…, Range, …)}, this method does not need to verify array elements
+     * because the type returned by {@link ParameterDescriptor#getMinimumValue()} and {@code getMaximumValue()}
+     * methods (namely {@code Comparable<T>}) does not allow usage with arrays.</div>
      *
      * @param convertedValue The value <em>converted to the units specified by the descriptor</em>.
      *        This is not necessarily the user-provided value.
      */
     @SuppressWarnings("unchecked")
     private static <T> Verifier ensureValidValue(final Class<T> valueClass, final Set<T> validValues,
-            final Object convertedValue)
+            final Comparable<T> minimum, final Comparable<T> maximum, final Object convertedValue)
     {
         if (!valueClass.isInstance(convertedValue)) {
             return new Verifier(Errors.Keys.IllegalParameterValueClass_3, false, null, valueClass, convertedValue.getClass());
@@ -223,17 +260,46 @@ final class Verifier {
         if (validValues != null && !validValues.contains(convertedValue)) {
             return new Verifier(Errors.Keys.IllegalParameterValue_2, true, null, convertedValue);
         }
+        if ((minimum != null && minimum.compareTo((T) convertedValue) > 0) ||
+            (maximum != null && maximum.compareTo((T) convertedValue) < 0))
+        {
+            return new Verifier(Errors.Keys.ValueOutOfRange_4, true, null, minimum, maximum, convertedValue);
+        }
         return null;
     }
 
     /**
+     * Converts the information about an "value out of range" error. The range in the error message will be formatted
+     * in the unit given by the user, which is not necessarily the same than the unit of the parameter descriptor.
+     *
+     * @param converter The conversion from user unit to descriptor unit, or {@code null} if none. This method
+     *        uses the inverse of that conversion for converting the given minimum and maximum values.
+     */
+    private void convertRange(UnitConverter converter) {
+        if (converter != null && errorKey == Errors.Keys.ValueOutOfRange_4) {
+            converter = converter.inverse();
+            Object minimumValue = arguments[1];
+            Object maximumValue = arguments[2];
+            minimumValue = (minimumValue != null) ? converter.convert(((Number) minimumValue).doubleValue()) : "-∞";
+            maximumValue = (maximumValue != null) ? converter.convert(((Number) maximumValue).doubleValue()) :  "∞";
+            arguments[1] = minimumValue;
+            arguments[2] = maximumValue;
+        }
+    }
+
+    /**
      * Returns an error message for the error detected by
      * {@link #ensureValidValue(Class, Set, Range, Object)}.
      *
      * @param name  The parameter name.
      * @param value The user-supplied value (not necessarily equals to the converted value).
      */
-    String message(final Map<?,?> properties, final String name, final Object value) {
+    String message(final Map<?,?> properties, String name, Object value) {
+        final Object index = arguments[0];
+        if (index != null) {
+            name = name + '[' + index + ']';
+            value = Array.get(value, (Integer) index);
+        }
         arguments[0] = name;
         if (needsValue) {
             arguments[arguments.length - 1] = value;

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorTest.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -65,7 +65,7 @@ public final strictfp class DefaultParam
      * @return The parameter descriptor.
      */
     static <T> DefaultParameterDescriptor<T> createSimpleOptional(final String name, final Class<T> type) {
-        return new DefaultParameterDescriptor<>(properties(name), type, null, null, false);
+        return new DefaultParameterDescriptor<>(properties(name), type, null, null, null, false);
     }
 
     /**
@@ -81,7 +81,7 @@ public final strictfp class DefaultParam
             final int minimumValue, final int maximumValue, final int defaultValue)
     {
         return new DefaultParameterDescriptor<>(properties(name), Integer.class,
-                NumberRange.create(minimumValue, true, maximumValue, true), Integer.valueOf(defaultValue), true);
+                NumberRange.create(minimumValue, true, maximumValue, true), null, Integer.valueOf(defaultValue), true);
     }
 
     /**
@@ -98,7 +98,7 @@ public final strictfp class DefaultParam
             final double minimumValue, final double maximumValue, final double defaultValue, final Unit<?> unit)
     {
         return new DefaultParameterDescriptor<>(properties(name), Double.class,
-                MeasurementRange.create(minimumValue, true, maximumValue, true, unit),
+                MeasurementRange.create(minimumValue, true, maximumValue, true, unit), null,
                 Double.isNaN(defaultValue) ? null : Double.valueOf(defaultValue), true);
     }
 
@@ -115,9 +115,23 @@ public final strictfp class DefaultParam
     static <T> DefaultParameterDescriptor<T> create(final String name, final Class<T> type,
             final T[] validValues, final T defaultValue)
     {
-        final Map<String,Object> properties = properties(name);
-        assertNull(properties.put(DefaultParameterDescriptor.VALID_VALUES_KEY, validValues));
-        return new DefaultParameterDescriptor<>(properties, type, null, defaultValue, true);
+        return new DefaultParameterDescriptor<>(properties(name), type, null, validValues, defaultValue, true);
+    }
+
+    /**
+     * Creates a descriptor for an array of {@code double[] values.
+     *
+     * @param  name         The parameter name.
+     * @param  minimumValue The minimum parameter value, or {@link Double#NEGATIVE_INFINITY} if none.
+     * @param  maximumValue The maximum parameter value, or {@link Double#POSITIVE_INFINITY} if none.
+     * @param  unit         The unit for minimum and maximum values.
+     * @return The parameter descriptor for the given range of values.
+     */
+    static DefaultParameterDescriptor<double[]> createForArray(final String name,
+            final double minimumValue, final double maximumValue, final Unit<?> unit)
+    {
+        final MeasurementRange<Double> valueDomain = MeasurementRange.create(minimumValue, true, maximumValue, true, unit);
+        return new DefaultParameterDescriptor<>(properties(name), double[].class, valueDomain, null, null, true);
     }
 
     /**
@@ -170,7 +184,7 @@ public final strictfp class DefaultParam
             create("Test default", 4, 20, 3);
             fail("defaultValue < minimum");
         } catch (IllegalArgumentException exception) {
-            assertEquals("Value ‘Test default’=3 is invalid. Expected a value in the [4 … 20] range.", exception.getMessage());
+            assertEquals("Value ‘Test default’ = 3 is invalid. Expected a value in the [4 … 20] range.", exception.getMessage());
         }
     }
 
@@ -198,7 +212,7 @@ public final strictfp class DefaultParam
     public void testStringType() {
         final Range<String> valueDomain = new Range<>(String.class, "AAA", true, "BBB", true);
         final DefaultParameterDescriptor<String> descriptor = new DefaultParameterDescriptor<>(
-                properties("String param"), String.class, valueDomain, "ABC", false);
+                properties("String param"), String.class, valueDomain, null, "ABC", false);
         assertEquals("name", "String param",     descriptor.getName().getCode());
         assertEquals("valueClass", String.class, descriptor.getValueClass());
         assertNull  ("validValues",              descriptor.getValidValues());
@@ -208,6 +222,7 @@ public final strictfp class DefaultParam
         assertEquals("maximumValue",  "BBB",     descriptor.getMaximumValue());
         assertEquals("minimumOccurs", 0,         descriptor.getMinimumOccurs());
         assertEquals("maximumOccurs", 1,         descriptor.getMaximumOccurs());
+        assertNull  ("unit",                     descriptor.getUnit());
     }
 
     /**
@@ -227,6 +242,7 @@ public final strictfp class DefaultParam
         assertNull       ("maximumValue",              descriptor.getMaximumValue());
         assertEquals     ("minimumOccurs", 1,          descriptor.getMinimumOccurs());
         assertEquals     ("maximumOccurs", 1,          descriptor.getMaximumOccurs());
+        assertNull       ("unit",                      descriptor.getUnit());
         /*
          * Invalid operation: element not in the list of valid elements.
          */
@@ -236,4 +252,37 @@ public final strictfp class DefaultParam
             assertEquals("Parameter “Enumeration param” can not take the “Pear” value.", e.getMessage());
         }
     }
+
+    /**
+     * Tests a descriptor for a parameter value of kind {@code double[]}.
+     */
+    @Test
+    @DependsOnMethod("testDoubleType")
+    public void testArrayType() {
+        final DefaultParameterDescriptor<double[]> descriptor = createForArray("Array param", 4, 9, SI.METRE);
+        assertEquals("name",       "Array param",  descriptor.getName().getCode());
+        assertEquals("valueClass", double[].class, descriptor.getValueClass());
+        assertEquals("unit",       SI.METRE,       descriptor.getUnit());
+        assertNull  ("validValues",                descriptor.getValidValues());
+        assertNull  ("defaultValue",               descriptor.getDefaultValue());
+        assertNull  ("minimumValue",               descriptor.getMinimumValue());
+        assertNull  ("maximumValue",               descriptor.getMaximumValue());
+        assertEquals("minimumOccurs", 1,           descriptor.getMinimumOccurs());
+        assertEquals("maximumOccurs", 1,           descriptor.getMaximumOccurs());
+
+        final Range<?> valueDomain = descriptor.getValueDomain();
+        assertNotNull("valueDomain", valueDomain);
+        assertEquals(Double.class,      valueDomain.getElementType());
+        assertEquals(Double.valueOf(4), valueDomain.getMinValue());
+        assertEquals(Double.valueOf(9), valueDomain.getMaxValue());
+        /*
+         * Invalid operation: wrong type of range value.
+         */
+        try {
+            new DefaultParameterDescriptor<>(properties("Array param"), double[].class,
+                    NumberRange.create(4, true, 9, true), null, null, false);
+        } catch (IllegalArgumentException e) {
+            assertEquals("Argument ‘valueDomain’ can not be an instance of ‘Range<Integer>’.", e.getMessage());
+        }
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueTest.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueTest.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -21,7 +21,6 @@ import javax.measure.unit.NonSI;
 import javax.measure.unit.Unit;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.datum.VerticalDatumType;
-import org.opengis.parameter.ParameterValue;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.InvalidParameterTypeException;
 import org.opengis.parameter.InvalidParameterValueException;
@@ -59,6 +58,38 @@ public final strictfp class DefaultParam
     private static final double EPS = 1E-10;
 
     /**
+     * A subclass of {@code DefaultParameterValue} which store the value converted by {@link Verifier}.
+     * This allows {@link DefaultParameterValueTest} methods to verify the conversion result.
+     */
+    @SuppressWarnings("serial")
+    private static final strictfp class Watcher<T> extends DefaultParameterValue<T> {
+        /** The value converted by {@link Verifier}. */
+        T convertedValue;
+
+        /** Creates a new parameter value for testing purpose. */
+        Watcher(final DefaultParameterDescriptor<T> descriptor) {
+            super(descriptor);
+        }
+
+        /** Automatically invoked when a new value is set. */
+        @Override protected void validate(final T value) {
+            convertedValue = value;
+        }
+
+        /** Asserts that the value and the converted value are equal to the expected one. */
+        void assertValueEquals(final Object expected) {
+            assertEquals("value",          expected, getValue());
+            assertEquals("convertedValue", expected, convertedValue);
+        }
+
+        /** Asserts that the value and the converted value are equal to the expected ones. */
+        void assertValueEquals(final Object expected, final Object converted) {
+            assertEquals("value",          expected,  getValue());
+            assertEquals("convertedValue", converted, convertedValue);
+        }
+    }
+
+    /**
      * Constructs an optional parameter initialized to the given value.
      * The descriptor has no default value, no minimum and no maximum.
      *
@@ -66,8 +97,8 @@ public final strictfp class DefaultParam
      * @param  value The parameter value.
      * @return A new parameter instance for the given name and value.
      */
-    private static DefaultParameterValue<Integer> createOptional(final String name, final int value) {
-        final DefaultParameterValue<Integer> parameter = new DefaultParameterValue<>(
+    private static Watcher<Integer> createOptional(final String name, final int value) {
+        final Watcher<Integer> parameter = new Watcher<>(
                 DefaultParameterDescriptorTest.createSimpleOptional(name, Integer.class));
         parameter.setValue(value, null);
         return parameter;
@@ -81,10 +112,9 @@ public final strictfp class DefaultParam
      * @param unit  The unit for the parameter value.
      * @return A new parameter instance for the given name and value.
      */
-    private static DefaultParameterValue<Double> create(final String name, final double value, final Unit<?> unit) {
-        final ParameterDescriptor<Double> descriptor = DefaultParameterDescriptorTest.create(
-                name, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, unit);
-        final DefaultParameterValue<Double> parameter = new DefaultParameterValue<>(descriptor);
+    private static Watcher<Double> create(final String name, final double value, final Unit<?> unit) {
+        final Watcher<Double> parameter = new Watcher<>(DefaultParameterDescriptorTest.create(
+                name, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, unit));
         parameter.setValue(value, unit);
         return parameter;
     }
@@ -96,7 +126,7 @@ public final strictfp class DefaultParam
      */
     @Test
     public void testInteger() {
-        final ParameterValue<Integer> parameter = createOptional("Integer param", 14);
+        final Watcher<Integer> parameter = createOptional("Integer param", 14);
         final ParameterDescriptor<Integer> descriptor = parameter.getDescriptor();
         validate(parameter);
 
@@ -139,7 +169,7 @@ public final strictfp class DefaultParam
          * the value should stay null (not substituted by the default value).
          */
         parameter.setValue(-15);
-        assertEquals(Integer.valueOf(-15), parameter.getValue());
+        parameter.assertValueEquals(Integer.valueOf(-15));
         parameter.setValue(null);
         assertNull(parameter.getValue());
         validate(parameter);
@@ -153,7 +183,7 @@ public final strictfp class DefaultParam
     @Test
     @DependsOnMethod("testInteger")
     public void testBoundedInteger() {
-        final DefaultParameterValue<Integer> parameter = new DefaultParameterValue<>(
+        final Watcher<Integer> parameter = new Watcher<>(
                 DefaultParameterDescriptorTest.create("Bounded param", -30, +40, 15));
         assertEquals(Integer.class, parameter.getDescriptor().getValueClass());
         assertEquals(      "value", Integer.valueOf(15), parameter.getValue());
@@ -164,7 +194,7 @@ public final strictfp class DefaultParam
          * Set a value inside the range of valid values.
          */
         parameter.setValue(12);
-        assertEquals(      "value", Integer.valueOf(12), parameter.getValue());
+        parameter.assertValueEquals(Integer.valueOf(12));
         assertEquals(   "intValue", 12, parameter.intValue());
         assertEquals("doubleValue", 12, parameter.doubleValue(), STRICT);
         validate(parameter);
@@ -203,7 +233,7 @@ public final strictfp class DefaultParam
          * the value can be converted to an integer.
          */
         parameter.setValue(10.0);
-        assertEquals(      "value", Integer.valueOf(10), parameter.getValue());
+        parameter.assertValueEquals(Integer.valueOf(10));
         assertEquals(   "intValue", 10, parameter.intValue());
         assertEquals("doubleValue", 10, parameter.doubleValue(), STRICT);
         validate(parameter);
@@ -226,7 +256,7 @@ public final strictfp class DefaultParam
      */
     @Test
     public void testMeasure() {
-        final DefaultParameterValue<Double> parameter = create("Numerical param", 3, SI.METRE);
+        final Watcher<Double> parameter = create("Numerical param", 3, SI.METRE);
         final ParameterDescriptor<Double> descriptor = parameter.getDescriptor();
         validate(parameter);
 
@@ -258,6 +288,7 @@ public final strictfp class DefaultParam
          * Sets a value in centimetres.
          */
         parameter.setValue(400, SI.CENTIMETRE);
+        parameter.assertValueEquals(Double.valueOf(400), Double.valueOf(4));
         assertEquals("unit",        SI.CENTIMETRE, parameter.getUnit());
         assertEquals("doubleValue", 400, parameter.doubleValue(),              STRICT);
         assertEquals("doubleValue", 400, parameter.doubleValue(SI.CENTIMETRE), STRICT);
@@ -272,7 +303,7 @@ public final strictfp class DefaultParam
     @Test
     @DependsOnMethod("testMeasure")
     public void testBoundedDouble() {
-        final DefaultParameterValue<Double> parameter = new DefaultParameterValue<>(
+        final Watcher<Double> parameter = new Watcher<>(
                 DefaultParameterDescriptorTest.create("Bounded param", -30.0, +40.0, 15.0, null));
         assertEquals(Double.class, parameter.getDescriptor().getValueClass());
         assertEquals(      "value", Double.valueOf(15), parameter.getValue());
@@ -281,7 +312,7 @@ public final strictfp class DefaultParam
         validate(parameter);
 
         parameter.setValue(12.0);
-        assertEquals(      "value", Double.valueOf(12), parameter.getValue());
+        parameter.assertValueEquals(Double.valueOf(12));
         assertEquals(   "intValue", 12, parameter.intValue());
         assertEquals("doubleValue", 12, parameter.doubleValue(), STRICT);
         validate(parameter);
@@ -316,7 +347,7 @@ public final strictfp class DefaultParam
     @Test
     @DependsOnMethod({"testMeasure", "testBoundedDouble"})
     public void testBoundedMeasure() {
-        final DefaultParameterValue<Double> parameter = new DefaultParameterValue<>(
+        final Watcher<Double> parameter = new Watcher<>(
                 DefaultParameterDescriptorTest.create("Length measure", 4, 20, 12, SI.METRE));
         assertEquals("value",    Double.valueOf(12), parameter.getValue());
         assertEquals("intValue", 12,                 parameter.intValue());
@@ -325,7 +356,7 @@ public final strictfp class DefaultParam
 
         for (int i=4; i<=20; i++) {
             parameter.setValue(i);
-            assertEquals("value", Double.valueOf(i), parameter.getValue());
+            parameter.assertValueEquals(Double.valueOf(i));
             assertEquals("unit",  SI.METRE,          parameter.getUnit());
             assertEquals("value", i,                 parameter.doubleValue(SI.METRE), STRICT);
             assertEquals("value", 100*i,             parameter.doubleValue(SI.CENTIMETRE), STRICT);
@@ -337,16 +368,61 @@ public final strictfp class DefaultParam
             assertEquals("Length measure", exception.getParameterName());
         }
         try {
+            parameter.setValue(10.0, SI.KILOMETRE); // Out of range only after unit conversion.
+            fail("setValue(> max)");
+        } catch (InvalidParameterValueException exception) {
+            assertEquals("Length measure", exception.getParameterName());
+        }
+        try {
             parameter.setValue("12");
             fail("setValue(Sring)");
         } catch (InvalidParameterValueException exception) {
             assertEquals("Length measure", exception.getParameterName());
         }
         for (int i=400; i<=2000; i+=100) {
+            final double metres = i / 100.0;
             parameter.setValue(i, SI.CENTIMETRE);
-            assertEquals("value", Double.valueOf(i), parameter.getValue());
-            assertEquals("unit",  SI.CENTIMETRE,     parameter.getUnit());
-            assertEquals("value", i/100,             parameter.doubleValue(SI.METRE), EPS);
+            parameter.assertValueEquals(Double.valueOf(i), Double.valueOf(metres));
+            assertEquals("unit",  SI.CENTIMETRE, parameter.getUnit());
+            assertEquals("value", metres,        parameter.doubleValue(SI.METRE), EPS);
+        }
+    }
+
+    /**
+     * Tests a parameter for values of type {@code double[]}.
+     */
+    @Test
+    public void testArray() {
+        double[] values = {5, 10, 15};
+        final Watcher<double[]> parameter = new Watcher<>(
+                DefaultParameterDescriptorTest.createForArray("myValues", 4, 4000, SI.METRE));
+        parameter.setValue(values);
+        assertArrayEquals(values, parameter.getValue(), 0);
+        assertArrayEquals(values, parameter.convertedValue, 0);
+        assertArrayEquals(values, parameter.doubleValueList(), 0);
+        assertArrayEquals(new double[] {500, 1000, 1500}, parameter.doubleValueList(SI.CENTIMETRE), 0);
+        /*
+         * New values in kilometres.
+         */
+        values = new double[] {3, 2, 4};
+        final double[] metres = new double[] {3000, 2000, 4000};
+        parameter.setValue(values, SI.KILOMETRE);
+        assertArrayEquals(values, parameter.getValue(), 0);
+        assertArrayEquals(metres, parameter.convertedValue, 0);
+        assertArrayEquals(values, parameter.doubleValueList(), 0);
+        assertArrayEquals(metres, parameter.doubleValueList(SI.METRE), 0);
+        /*
+         * Values out of range.
+         */
+        try {
+            parameter.setValue(new double[] {5, 10, -5}, SI.METRE);
+        } catch (InvalidParameterValueException e) {
+            assertTrue(e.getMessage().contains("myValues[2]"));
+        }
+        try {
+            parameter.setValue(new double[] {4, 5}, SI.KILOMETRE); // Out of range only after unit conversion.
+        } catch (InvalidParameterValueException e) {
+            assertTrue(e.getMessage().contains("myValues[1]"));
         }
     }
 

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Range.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Range.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Range.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/Range.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -126,12 +126,17 @@ public class Range<E extends Comparable<
         isMinIncluded = range.isMinIncluded;
         maxValue      = range.maxValue;
         isMaxIncluded = range.isMaxIncluded;
-        assert validate();
+        assert validate() : elementType;
     }
 
     /**
      * Creates a new range bounded by the given endpoint values.
      *
+     * <div class="note"><b>Assertion:</b>
+     * This constructor verifies the {@code minValue} and {@code maxValue} arguments type if Java assertions
+     * are enabled. This verification is not performed in normal execution because theoretically unnecessary
+     * unless Java generic types have been tricked.</div>
+     *
      * @param elementType    The base type of the range elements.
      * @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.
@@ -152,7 +157,7 @@ public class Range<E extends Comparable<
         this.isMinIncluded = isMinIncluded && (minValue != null);
         this.maxValue      = maxValue;
         this.isMaxIncluded = isMaxIncluded && (maxValue != null);
-        assert validate();
+        assert validate() : elementType;
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/Classes.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -321,8 +321,7 @@ public final class Classes extends Stati
      */
     private static Set<Class<?>> getInterfaceSet(final Class<?> type, Set<Class<?>> addTo) {
         final Class<?>[] interfaces = type.getInterfaces();
-        for (int i=0; i<interfaces.length; i++) {
-            final Class<?> candidate = interfaces[i];
+        for (final Class<?> candidate : interfaces) {
             if (addTo == null) {
                 addTo = new LinkedHashSet<>(hashMapCapacity(interfaces.length));
             }

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Mon Mar  3 20:42:34 2014
@@ -788,12 +788,12 @@ public final class Errors extends Indexe
         public static final short ValueAlreadyDefined_1 = 131;
 
         /**
-         * Value ‘{0}’={1} is invalid. Expected a number greater than 0.
+         * Value ‘{0}’ = {1} is invalid. Expected a number greater than 0.
          */
         public static final short ValueNotGreaterThanZero_2 = 132;
 
         /**
-         * Value ‘{0}’={3} is invalid. Expected a value in the [{1} … {2}] range.
+         * Value ‘{0}’ = {3} is invalid. Expected a value in the [{1} … {2}] range.
          */
         public static final short ValueOutOfRange_4 = 133;
     }

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Mon Mar  3 20:42:34 2014
@@ -169,5 +169,5 @@ UnsupportedOperation_1            = The 
 UnsupportedType_1                 = The \u2018{0}\u2019 type is unsupported.
 UnsupportedVersion_1              = Version {0} is not supported.
 ValueAlreadyDefined_1             = A value is already defined for \u201c{0}\u201d.
-ValueNotGreaterThanZero_2         = Value \u2018{0}\u2019={1} is invalid. Expected a number greater than 0.
-ValueOutOfRange_4                 = Value \u2018{0}\u2019={3} is invalid. Expected a value in the [{1} \u2026 {2}] range.
+ValueNotGreaterThanZero_2         = Value \u2018{0}\u2019 = {1} is invalid. Expected a number greater than 0.
+ValueOutOfRange_4                 = Value \u2018{0}\u2019 = {3} is invalid. Expected a value in the [{1} \u2026 {2}] range.

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1573740&r1=1573739&r2=1573740&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Mon Mar  3 20:42:34 2014
@@ -158,5 +158,5 @@ UnsupportedOperation_1            = L\u2
 UnsupportedType_1                 = Le type \u2018{0}\u2019 n\u2019est pas support\u00e9.
 UnsupportedVersion_1              = La version {0} n\u2019est pas support\u00e9e.
 ValueAlreadyDefined_1             = Une valeur est d\u00e9j\u00e0 d\u00e9finie pour \u00ab\u202f{0}\u202f\u00bb.
-ValueNotGreaterThanZero_2         = La valeur \u2018{0}\u2019={1} n\u2019est pas valide. On attendait un nombre positif non-nul.
-ValueOutOfRange_4                 = La valeur \u2018{0}\u2019={3} est invalide. Une valeur dans la plage [{1} \u2026 {2}] \u00e9tait attendue.
+ValueNotGreaterThanZero_2         = La valeur \u2018{0}\u2019 = {1} n\u2019est pas valide. On attendait un nombre positif non-nul.
+ValueOutOfRange_4                 = La valeur \u2018{0}\u2019 = {3} est invalide. Une valeur dans la plage [{1} \u2026 {2}] \u00e9tait attendue.



Mime
View raw message