You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2014/03/09 21:50:44 UTC

svn commit: r1575751 - in /sis/branches/JDK7/core/sis-referencing/src: main/java/org/apache/sis/parameter/ main/java/org/apache/sis/referencing/ test/java/org/apache/sis/parameter/

Author: desruisseaux
Date: Sun Mar  9 20:50:44 2014
New Revision: 1575751

URL: http://svn.apache.org/r1575751
Log:
Added tests and javadoc.

Modified:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java
    sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueGroupTest.java

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java?rev=1575751&r1=1575750&r2=1575751&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java [UTF-8] Sun Mar  9 20:50:44 2014
@@ -38,6 +38,43 @@ import static org.apache.sis.util.Utilit
 
 /**
  * The definition of a group of related parameters used by an operation method.
+ * {@code DefaultParameterDescriptorGroup} instances are immutable and thus thread-safe.
+ * Each map projection or process will typically defines a single static {@code ParameterDescriptorGroup},
+ * to be shared by all users of that projection or process.
+ *
+ * {@section Instantiation}
+ * Map projection or process <em>implementors</em> may use the {@link ParameterBuilder} class for making
+ * their task easier. The following example creates a <cite>Mercator (variant A)</cite> projection valid
+ * from 80°S to 84°N on all the longitude range (±180°).
+ *
+ * {@preformat java
+ *     public class MercatorProjection {
+ *         public static final ParameterDescriptorGroup PARAMETERS;
+ *         static {
+ *             ParameterBuilder builder = new ParameterBuilder();
+ *             builder.setCodeSpace(Citations.OGP, "EPSG").setRequired(true);
+ *             ParameterDescriptor<?>[] parameters = {
+ *                 builder.addName("Latitude of natural origin")    .createBounded( -80,  +84, 0, NonSI.DEGREE_ANGLE),
+ *                 builder.addName("Longitude of natural origin")   .createBounded(-180, +180, 0, NonSI.DEGREE_ANGLE),
+ *                 builder.addName("Scale factor at natural origin").createStrictlyPositive(1, Unit.ONE),
+ *                 builder.addName("False easting")                 .create(0, SI.METRE),
+ *                 builder.addName("False northing")                .create(0, SI.METRE)
+ *             };
+ *             builder.addIdentifier("9804")                    // Primary key in EPSG database.
+ *                    .addName("Mercator (variant A)")          // EPSG name since October 2010.
+ *                    .addName("Mercator (1SP)")                // EPSG name prior October 2010.
+ *                    .addName(Citations.OGC, "Mercator_1SP");  // Name found in some OGC specifications.
+ *             PARAMETERS = builder.createGroup(parameters);
+ *         }
+ *     }
+ * }
+ *
+ * Users can simply reference the descriptor provided par projection or process providers like below:
+ *
+ * {@preformat java
+ *     ParameterValueGroup parameters = MercatorProjection.PARAMETERS.createValue();
+ *     // See DefaultParameterValueGroup for more information on 'parameters' usage.
+ * }
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
@@ -268,19 +305,27 @@ public class DefaultParameterDescriptorG
     @Override
     @SuppressWarnings("null")
     public GeneralParameterDescriptor descriptor(final String name) throws ParameterNotFoundException {
+        // Quick search for an exact match.
         ArgumentChecks.ensureNonNull("name", name);
+        for (final GeneralParameterDescriptor param : descriptors) {
+            if (name.equals(param.getName().getCode())) {
+                return param;
+            }
+        }
+        // More costly search before to give up.
         GeneralParameterDescriptor fallback = null, ambiguity = null;
         for (final GeneralParameterDescriptor param : descriptors) {
             if (IdentifiedObjects.isHeuristicMatchForName(param, name)) {
-                if (name.equals(param.getName().getCode())) {
-                    return param;
-                } else if (fallback == null) {
+                if (fallback == null) {
                     fallback = param;
                 } else {
                     ambiguity = param;
                 }
             }
         }
+        if (fallback != null && ambiguity == null) {
+            return fallback;
+        }
         throw new ParameterNotFoundException(ambiguity != null
                 ? Errors.format(Errors.Keys.AmbiguousName_3, fallback.getName(), ambiguity.getName(), name)
                 : Errors.format(Errors.Keys.ParameterNotFound_2, getName(), name), name);

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java?rev=1575751&r1=1575750&r2=1575751&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java [UTF-8] Sun Mar  9 20:50:44 2014
@@ -46,9 +46,24 @@ import java.util.Objects;
  *   <li>{@link #addGroup(String)} for creating a new subgroup and adding it to the list of subgroups.</li>
  * </ul>
  *
- * <div class="note"><b>API note:</b> there is no <code>parameter<b><u>s</u></b>(String)</code> method
- * returning a list of parameter values because the ISO 19111 standard fixes the {@code ParameterValue}
- * {@linkplain DefaultParameterDescriptor#getMaximumOccurs() maximum occurrence} to 1.</div>
+ * {@section Instantiation}
+ * {@code ParameterValueGroup} are usually not instantiated directly. Instead, new instances are created by calling
+ * <code>descriptor.{@linkplain DefaultParameterDescriptorGroup#createValue() createValue()}</code> on a descriptor
+ * supplied by a map projection or process provider. After a {@code ParameterValueGroup} instance has been created,
+ * the parameter values can be set by chaining calls to {@link #parameter(String)} with one of the {@code setValue(…)}
+ * methods defined in the {@linkplain DefaultParameterValue parameter value} class.
+ *
+ * <div class="note"><b>Example:</b>
+ * Assuming a descriptor defined as in the {@link DefaultParameterDescriptorGroup} javadoc,
+ * one can set <cite>Mercator (variant A)</cite> projection parameters as below:
+ *
+ * {@preformat java
+ *     ParameterValueGroup mercator = MercatorProjection.PARAMETERS.createValue();
+ *     mercator.parameter("Longitude of natural origin").setValue(-60, NonSI.DEGREE_ANGLE);  // 60°W
+ *     mercator.parameter("Latitude of natural origin") .setValue( 40, NonSI.DEGREE_ANGLE);  // 40°N
+ *     // Keep default values for other parameters.
+ * }
+ * </div>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4 (derived from geotk-2.0)
@@ -140,6 +155,10 @@ public class DefaultParameterValueGroup 
      *     parameter("False easting").setValue(500000.0);
      * }
      *
+     * <div class="note"><b>API note:</b> there is no <code>parameter<b><u>s</u></b>(String)</code> method
+     * returning a list of parameter values because the ISO 19111 standard fixes the {@code ParameterValue}
+     * {@linkplain DefaultParameterDescriptor#getMaximumOccurs() maximum occurrence} to 1.</div>
+     *
      * {@section Parameters subgroups}
      * This method does not search recursively in subgroups. This is because more than one subgroup
      * may exist for the same {@linkplain ParameterDescriptorGroup descriptor}. The user have to
@@ -155,15 +174,24 @@ public class DefaultParameterValueGroup 
     public ParameterValue<?> parameter(final String name) throws ParameterNotFoundException {
         ArgumentChecks.ensureNonNull("name", name);
         final ParameterValueList values = this.values; // Protect against accidental changes.
-        int fallback = -1, ambiguity = -1;
+
+        // Quick search for an exact match.
         final int size = values.size();
         for (int i=0; i<size; i++) {
             final GeneralParameterDescriptor descriptor = values.descriptor(i);
             if (descriptor instanceof ParameterDescriptor<?>) {
+                if (name.equals(descriptor.getName().toString())) {
+                    return (ParameterValue<?>) values.get(i);
+                }
+            }
+        }
+        // More costly search before to give up.
+        int fallback = -1, ambiguity = -1;
+        for (int i=0; i<size; i++) {
+            final GeneralParameterDescriptor descriptor = values.descriptor(i);
+            if (descriptor instanceof ParameterDescriptor<?>) {
                 if (isHeuristicMatchForName(descriptor, name)) {
-                    if (name.equals(descriptor.getName().toString())) {
-                        return (ParameterValue<?>) values.get(i);
-                    } else if (fallback < 0) {
+                    if (fallback < 0) {
                         fallback = i;
                     } else {
                         ambiguity = i;

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java?rev=1575751&r1=1575750&r2=1575751&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java [UTF-8] Sun Mar  9 20:50:44 2014
@@ -19,6 +19,7 @@ package org.apache.sis.parameter;
 import javax.measure.unit.Unit;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.GeneralParameterDescriptor;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Range;
@@ -28,7 +29,7 @@ import static org.apache.sis.util.Argume
 
 
 /**
- * Provides convenience methods for easier {@code ParameterDescriptorGroup} instantiations.
+ * Provides convenience methods for easier {@code ParameterDescriptor} instantiations.
  * This builder can be helpful for map projection <em>providers</em>, or for implementation of
  * any process that use parameters. Map projection or process <em>users</em> do not need this
  * builder since they can invoke {@link ParameterDescriptor#createValue()} on the descriptor
@@ -60,7 +61,7 @@ import static org.apache.sis.util.Argume
  * {@preformat java
  *   ParameterBuilder builder = new ParameterBuilder();
  *   builder.setCodeSpace(Citations.OGP, "EPSG").setRequired(true);
- *   ParameterDescriptor<Double>[] parameters = {
+ *   ParameterDescriptor<?>[] parameters = {
  *       builder.addName("Latitude of natural origin")    .createBounded( -80,  +84, 0, NonSI.DEGREE_ANGLE),
  *       builder.addName("Longitude of natural origin")   .createBounded(-180, +180, 0, NonSI.DEGREE_ANGLE),
  *       builder.addName("Scale factor at natural origin").createStrictlyPositive(1, Unit.ONE),
@@ -273,10 +274,49 @@ public class ParameterBuilder extends Bu
     private <T> ParameterDescriptor<T> create(final Class<T> valueClass, final Range<?> valueDomain,
             final T[] validValues, final T defaultValue)
     {
+        final ParameterDescriptor<T> descriptor;
         onCreate(false);
-        final ParameterDescriptor<T> descriptor = new DefaultParameterDescriptor<>(
-                properties, valueClass, valueDomain, validValues, defaultValue, required);
-        onCreate(true);
+        try {
+            descriptor = new DefaultParameterDescriptor<>(properties,
+                    valueClass, valueDomain, validValues, defaultValue, required);
+        } finally {
+            onCreate(true);
+        }
         return descriptor;
     }
+
+    /**
+     * Creates a descriptor group for the given parameters. This is a convenience method for
+     * {@link #createGroup(int, int, GeneralParameterDescriptor[])} with a cardinality of [0 … 1]
+     * or [1 … 1] depending on the value given to the last call to {@link #setRequired(boolean)}.
+     *
+     * @param  parameters The {@linkplain #descriptors() parameter descriptors} for the group to create.
+     * @return The parameter descriptor group.
+     */
+    public ParameterDescriptorGroup createGroup(final GeneralParameterDescriptor... parameters) {
+        return createGroup(required ? 1 : 0, 1, parameters);
+    }
+
+    /**
+     * Creates a descriptor group for the given cardinality and parameters.
+     *
+     * @param  minimumOccurs The {@linkplain DefaultParameterDescriptorGroup#getMinimumOccurs() minimum}
+     *                       number of times that values for this parameter group are required.
+     * @param  maximumOccurs The {@linkplain DefaultParameterDescriptorGroup#getMaximumOccurs() maximum}
+     *                       number of times that values for this parameter group are required.
+     * @param  parameters    The {@linkplain #descriptors() parameter descriptors} for the group to create.
+     * @return The parameter descriptor group.
+     */
+    public ParameterDescriptorGroup createGroup(final int minimumOccurs, final int maximumOccurs,
+            final GeneralParameterDescriptor... parameters)
+    {
+        final ParameterDescriptorGroup group;
+        onCreate(false);
+        try {
+            group = new DefaultParameterDescriptorGroup(properties, minimumOccurs, maximumOccurs, parameters);
+        } finally {
+            onCreate(true);
+        }
+        return group;
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java?rev=1575751&r1=1575750&r2=1575751&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] Sun Mar  9 20:50:44 2014
@@ -508,9 +508,13 @@ public abstract class Builder<B extends 
      *
      * {@preformat java
      *     public Foo createFoo() {
+     *         final Foo foo;
      *         onCreate(false);
-     *         Foo foo = factory.createFoo(properties);
-     *         onCreate(true);
+     *         try {
+     *             foo = factory.createFoo(properties);
+     *         } finally {
+     *             onCreate(true);
+     *         }
      *         return foo;
      *     }
      * }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java?rev=1575751&r1=1575750&r2=1575751&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterDescriptorGroupTest.java [UTF-8] Sun Mar  9 20:50:44 2014
@@ -18,15 +18,17 @@ package org.apache.sis.parameter;
 
 import java.util.Map;
 import java.util.List;
+import java.util.HashMap;
 import org.opengis.parameter.GeneralParameterDescriptor;
+import org.opengis.parameter.ParameterNotFoundException;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 import static org.opengis.test.Validators.*;
 import static java.util.Collections.singletonMap;
-import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
+import static org.opengis.referencing.IdentifiedObject.*;
 
 
 /**
@@ -50,19 +52,42 @@ public final strictfp class DefaultParam
     static {
         final Integer DEFAULT_VALUE = 10;
         final Class<Integer> type = Integer.class;
-        M1_M1_O1_O2 = new DefaultParameterDescriptorGroup(name("The group"), 0, 1,
-            new DefaultParameterDescriptor<>(name("Mandatory 1"), type, null, null, DEFAULT_VALUE, true),
-            new DefaultParameterDescriptor<>(name("Mandatory 2"), type, null, null, DEFAULT_VALUE, true),
-            new DefaultParameterDescriptor<>(name( "Optional 3"), type, null, null, DEFAULT_VALUE, false),
-            new MultiOccurrenceDescriptor <>(name( "Optional 4"), type, null, null, DEFAULT_VALUE, false)
+        final Map<String,Object> properties = new HashMap<>(4);
+        M1_M1_O1_O2 = new DefaultParameterDescriptorGroup(singletonMap(NAME_KEY, "Test group"), 0, 1,
+            new DefaultParameterDescriptor<>(name(properties, "Mandatory 1", "Ambiguity"), type, null, null, DEFAULT_VALUE, true),
+            new DefaultParameterDescriptor<>(name(properties, "Mandatory 2", "Alias 2"),   type, null, null, DEFAULT_VALUE, true),
+            new DefaultParameterDescriptor<>(name(properties,  "Optional 3", "Alias 3"),   type, null, null, DEFAULT_VALUE, false),
+            new MultiOccurrenceDescriptor <>(name(properties,  "Optional 4", "Ambiguity"), type, null, null, DEFAULT_VALUE, false)
         );
     }
 
     /**
-     * Returns a map with only one entry, which is {@code "name"}=<var>name</var>.
+     * Returns a map with {@code "name"}=<var>name</var> and {@code "alias"}=<var>alias</var> entries.
      */
-    static Map<String,String> name(final String name) {
-        return singletonMap(NAME_KEY, name);
+    private static Map<String,Object> name(final Map<String,Object> properties, final String name, final String alias) {
+        properties.put(NAME_KEY, name);
+        properties.put(ALIAS_KEY, alias);
+        return properties;
+    }
+
+    /**
+     * Ensures that the constructor detects duplicated names.
+     */
+    @Test
+    public void testConstruction() {
+        final Class<Integer> type = Integer.class;
+        final Map<String,Object> properties = new HashMap<>(4);
+        final DefaultParameterDescriptor<Integer> p1, p2;
+        p1 = new DefaultParameterDescriptor<>(name(properties, "Name", null), type, null, null, null, true);
+        p2 = new DefaultParameterDescriptor<>(name(properties, "  NAME ", null), type, null, null, null, true);
+        try {
+            new DefaultParameterDescriptorGroup(singletonMap(NAME_KEY, "Test group"), 0, 1, p1, p2);
+            fail("Constructor should have detected the duplicated names.");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("Name"));
+            assertTrue(message, message.contains("NAME"));
+        }
     }
 
     /**
@@ -92,12 +117,40 @@ public final strictfp class DefaultParam
     public void testDescriptor() {
         final DefaultParameterDescriptorGroup group = M1_M1_O1_O2;
         final List<GeneralParameterDescriptor> descriptors = group.descriptors();
-        assertEquals("name", "The group", group.getName().getCode());
+        assertEquals("name", "Test group", group.getName().getCode());
         assertEquals("size", 4, descriptors.size());
         assertSame("descriptor(“Mandatory 1”)",  descriptors.get(0), group.descriptor("Mandatory 1"));
         assertSame("descriptor(“Optional 3”)",   descriptors.get(2), group.descriptor("Optional 3"));
         assertSame("descriptor(“Optional 4”)",   descriptors.get(3), group.descriptor("Optional 4"));
         assertSame("descriptor(“Mandatory 2”)",  descriptors.get(1), group.descriptor("Mandatory 2"));
+        assertSame("descriptor(“Mandatory 2”)",  descriptors.get(1), group.descriptor("Alias 2"));
+        assertSame("descriptor(“Optional 3”)",   descriptors.get(2), group.descriptor("Alias 3"));
+        try {
+            group.descriptor("Alias 1");
+            fail("“Alias 1” is not a parameter for this group.");
+        } catch (ParameterNotFoundException e) {
+            final String message = e.getMessage();
+            assertEquals(message, "Alias 1", e.getParameterName());
+            assertTrue  (message, message.contains("Alias 1"));
+            assertTrue  (message, message.contains("Test group"));
+        }
+    }
+
+    /**
+     * Verifies that {@link DefaultParameterDescriptorGroup#descriptor(String)} can detect ambiguities.
+     */
+    @Test
+    public void testAmbiguityDetection() {
+        try {
+            M1_M1_O1_O2.descriptor("Ambiguity");
+            fail("“Ambiguity” shall not be accepted since 2 parameters have this alias.");
+        } catch (ParameterNotFoundException e) {
+            final String message = e.getMessage();
+            assertEquals(message, "Ambiguity", e.getParameterName());
+            assertTrue  (message, message.contains("Ambiguity"));
+            assertTrue  (message, message.contains("Mandatory 1"));
+            assertTrue  (message, message.contains("Optional 4"));
+        }
     }
 
     /**
@@ -112,4 +165,12 @@ public final strictfp class DefaultParam
             assertTrue(descriptors.contains(p));
         }
     }
+
+    /**
+     * Tests {@link DefaultParameterDescriptorGroup} serialization.
+     */
+    @Test
+    public void testSerialization() {
+        assertSerializedEquals(M1_M1_O1_O2);
+    }
 }

Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueGroupTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueGroupTest.java?rev=1575751&r1=1575750&r2=1575751&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueGroupTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/parameter/DefaultParameterValueGroupTest.java [UTF-8] Sun Mar  9 20:50:44 2014
@@ -31,9 +31,10 @@ import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 import static org.opengis.test.Validators.*;
-import static org.apache.sis.parameter.DefaultParameterDescriptorGroupTest.name;
+import static java.util.Collections.singletonMap;
+import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
 
 
 /**
@@ -113,18 +114,21 @@ public final strictfp class DefaultParam
      * Tests {@link DefaultParameterValueGroup#parameter(String)}.
      */
     @Test
-    @DependsOnMethod("testValuesAddAll")
     public void testParameter() {
-        final DefaultParameterValueGroup  group  = createGroup(10);
-        final List<GeneralParameterValue> values = group.values();
-        assertSame  ("parameter(“Mandatory 1”)", values.get(0), group.parameter("Mandatory 1"));
-        assertSame  ("parameter(“Mandatory 2”)", values.get(1), group.parameter("Mandatory 2"));
-        assertSame  ("parameter(“Optional 3”)",  values.get(2), group.parameter("Optional 3"));
-        assertSame  ("parameter(“Optional 4”)",  values.get(3), group.parameter("Optional 4"));
-        assertEquals("parameter(“Mandatory 1”)", 10, group.parameter("Mandatory 1").intValue());
-        assertEquals("parameter(“Mandatory 2”)", 20, group.parameter("Mandatory 2").intValue());
-        assertEquals("parameter(“Optional 3”)",  30, group.parameter("Optional 3") .intValue());
-        assertEquals("parameter(“Optional 4”)",  40, group.parameter("Optional 4") .intValue());
+        final List<GeneralParameterDescriptor> descriptors = descriptor.descriptors();
+        final GeneralParameterValue[] expected = {
+            descriptors.get(0).createValue(),
+            descriptors.get(1).createValue(),
+            descriptors.get(2).createValue(),
+            descriptors.get(3).createValue()
+        };
+        final DefaultParameterValueGroup  group  = new DefaultParameterValueGroup(descriptor);
+        assertEquals("parameter(“Mandatory 1”)", expected[0], group.parameter("Mandatory 1"));
+        assertEquals("parameter(“Mandatory 2”)", expected[1], group.parameter("Mandatory 2"));
+        assertEquals("parameter(“Optional 3”)",  expected[2], group.parameter("Optional 3"));
+        assertEquals("parameter(“Optional 4”)",  expected[3], group.parameter("Optional 4"));
+        assertEquals("parameter(“Alias 2”)",     expected[1], group.parameter("Alias 2"));
+        assertEquals("parameter(“Alias 3”)",     expected[2], group.parameter("Alias 3"));
     }
 
     /**
@@ -144,6 +148,35 @@ public final strictfp class DefaultParam
         } catch (IndexOutOfBoundsException e) {
             assertNotNull(e.getMessage());
         }
+        assertEquals(10, ((ParameterValue<?>) values.get(0)).intValue());
+        assertEquals(10, ((ParameterValue<?>) values.get(1)).intValue());
+        // 10 is the default value specified by the descriptor.
+    }
+
+    /**
+     * Tests {@code DefaultParameterValueGroup.values().set(…)}.
+     */
+    @Test
+    @DependsOnMethod("testValuesGet")
+    public void testValuesSet() {
+        final DefaultParameterValueGroup  group  = new DefaultParameterValueGroup(descriptor);
+        final List<GeneralParameterValue> values = group.values();
+        assertEquals("Initial size", 2, values.size());
+        final ParameterValue<?> p0 = (ParameterValue<?>) descriptor.descriptors().get(0).createValue();
+        final ParameterValue<?> p1 = (ParameterValue<?>) descriptor.descriptors().get(1).createValue();
+        p0.setValue(4);
+        p1.setValue(5);
+        assertEquals("Mandatory 1", values.set(0, p0).getDescriptor().getName().toString());
+        assertEquals("Mandatory 2", values.set(1, p1).getDescriptor().getName().toString());
+        try {
+            values.set(2, p1);
+            fail("Index 2 shall be out of bounds.");
+        } catch (IndexOutOfBoundsException e) {
+            assertNotNull(e.getMessage());
+        }
+        assertEquals("size", 2, values.size()); // Size should be unchanged.
+        assertEquals(4, ((ParameterValue<?>) values.get(0)).intValue());
+        assertEquals(5, ((ParameterValue<?>) values.get(1)).intValue());
     }
 
     /**
@@ -188,7 +221,7 @@ public final strictfp class DefaultParam
         final DefaultParameterValueGroup    group = createGroup(10);
         final List<GeneralParameterValue>  values = group.values();
         final ParameterValue<Integer> nonExistent = new DefaultParameterDescriptor<>(
-                name("Optional 5"), Integer.class, null, null, null, false).createValue();
+                singletonMap(NAME_KEY, "Optional 5"), Integer.class, null, null, null, false).createValue();
         try {
             values.add(nonExistent);
             fail("“Optional 5” is not a parameter for this group.");
@@ -196,7 +229,7 @@ public final strictfp class DefaultParam
             assertEquals("Optional 5", e.getParameterName());
             final String message = e.getMessage();
             assertTrue(message, message.contains("Optional 5"));
-            assertTrue(message, message.contains("The group"));
+            assertTrue(message, message.contains("Test group"));
         }
     }
 
@@ -278,8 +311,8 @@ public final strictfp class DefaultParam
      */
     @Test
     public void testAddGroup() {
-        descriptor = new DefaultParameterDescriptorGroup(name("theGroup"), 1, 1,
-                new DefaultParameterDescriptorGroup(name("theSubGroup"), 0, 10)
+        descriptor = new DefaultParameterDescriptorGroup(singletonMap(NAME_KEY, "theGroup"), 1, 1,
+                new DefaultParameterDescriptorGroup(singletonMap(NAME_KEY, "theSubGroup"), 0, 10)
         );
         validate(descriptor);
 
@@ -288,6 +321,7 @@ public final strictfp class DefaultParam
         final ParameterValueGroup subGroupValues = groupValues.addGroup("theSubGroup");
         assertEquals("Size after add.", 1, groupValues.values().size());
         assertSame(subGroupValues, groupValues.values().get(0));
+        assertArrayEquals(new Object[] {subGroupValues}, groupValues.groups("theSubGroup").toArray());
     }
 
     /**
@@ -299,9 +333,17 @@ public final strictfp class DefaultParam
         final DefaultParameterValueGroup g1 = createGroup( 10);
         final DefaultParameterValueGroup g2 = createGroup(-10);
         final DefaultParameterValueGroup g3 = createGroup( 10);
-        assertTrue ("equals", g1.equals(g1));
-        assertFalse("equals", g1.equals(g2));
-        assertTrue ("equals", g1.equals(g3));
+        assertTrue  ("equals", g1.equals(g1));
+        assertFalse ("equals", g1.equals(g2));
+        assertTrue  ("equals", g1.equals(g3));
         assertEquals("hashCode", g1.hashCode(), g3.hashCode());
     }
+
+    /**
+     * Tests {@link DefaultParameterValueGroup} serialization.
+     */
+    @Test
+    public void testSerialization() {
+        assertSerializedEquals(createGroup(10));
+    }
 }