sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1575442 - in /sis/branches/JDK7/core/sis-referencing/src: main/java/org/apache/sis/parameter/ test/java/org/apache/sis/parameter/
Date Fri, 07 Mar 2014 22:48:35 GMT
Author: desruisseaux
Date: Fri Mar  7 22:48:35 2014
New Revision: 1575442

URL: http://svn.apache.org/r1575442
Log:
Improve implementation of parameter groups.

Added:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java   (with props)
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/parameter/ParameterValueList.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=1575442&r1=1575441&r2=1575442&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] Fri Mar  7 22:48:35 2014
@@ -19,7 +19,6 @@ package org.apache.sis.parameter;
 import java.util.Map;
 import java.util.Set;
 import java.util.List;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Collections;
 import org.opengis.parameter.ParameterValueGroup;
@@ -68,13 +67,7 @@ public class DefaultParameterDescriptorG
     /**
      * The {@linkplain #descriptors() parameter descriptors} for this group.
      */
-    private final GeneralParameterDescriptor[] parameters;
-
-    /**
-     * A view of {@link #parameters} as an immutable list.
-     * Will be constructed only when first needed.
-     */
-    private transient List<GeneralParameterDescriptor> asList;
+    private final List<GeneralParameterDescriptor> descriptors;
 
     /**
      * Constructs a parameter group from a set of properties. The properties map is given unchanged to the
@@ -139,7 +132,7 @@ public class DefaultParameterDescriptorG
                 }
             }
         }
-        this.parameters = parameters;
+        descriptors = asList(parameters);
     }
 
     /**
@@ -156,7 +149,53 @@ public class DefaultParameterDescriptorG
         minimumOccurs = descriptor.getMinimumOccurs();
         maximumOccurs = descriptor.getMaximumOccurs();
         final List<GeneralParameterDescriptor> c = descriptor.descriptors();
-        parameters = c.toArray(new GeneralParameterDescriptor[c.size()]);
+        if (descriptor instanceof DefaultParameterDescriptorGroup &&
+                ((DefaultParameterDescriptorGroup) descriptor).descriptors == c)
+        {
+            descriptors = c; // Share the immutable instance (no need to clone).
+        } else {
+            descriptors = asList(c.toArray(new GeneralParameterDescriptor[c.size()]));
+        }
+    }
+
+    /**
+     * Returns the given array of parameters as an unmodifiable list.
+     */
+    private static List<GeneralParameterDescriptor> asList(final GeneralParameterDescriptor[] parameters) {
+        switch (parameters.length) {
+            case 0:  return Collections.emptyList();
+            case 1:  return Collections.singletonList(parameters[0]);
+            case 2:  // fall through
+            case 3:  return UnmodifiableArrayList.wrap(parameters);
+            default: return new AsList(parameters);
+        }
+    }
+
+    /**
+     * The {@link DefaultParameterDescriptorGroup#descriptors} as an unmodifiable list.
+     * This class overrides {@link #contains(Object)} with a faster implementation based on {@link HashSet}.
+     * This optimizations is helpful for map projection implementations, which test often for a parameter validity.
+     */
+    private static final class AsList extends UnmodifiableArrayList<GeneralParameterDescriptor> {
+        /** For compatibility with different versions. */
+        private static final long serialVersionUID = -2116304004367396735L;
+
+        /** The element as a set, created when first needed. */
+        private transient volatile Set<GeneralParameterDescriptor> asSet;
+
+        /** Constructs a list for the specified array. */
+        public AsList(final GeneralParameterDescriptor[] array) {
+            super(array);
+        }
+
+        /** Tests for the inclusion of the specified descriptor. */
+        @Override public boolean contains(final Object object) {
+            Set<GeneralParameterDescriptor> s = asSet;
+            if (s == null) {
+                asSet = s = new HashSet<>(this); // No synchronization: not a big problem if created twice.
+            }
+            return s.contains(object);
+        }
     }
 
     /**
@@ -209,46 +248,13 @@ public class DefaultParameterDescriptorG
     }
 
     /**
-     * A view of {@link #parameters} as an unmodifiable list. This class overrides {@link #contains(Object)}
-     * with a faster implementation based on {@link HashSet}. This optimizations is helpful for map projection
-     * implementations, which test often for a parameter validity.
-     */
-    private static final class AsList extends UnmodifiableArrayList<GeneralParameterDescriptor> {
-        /** For compatibility with different versions. */
-        private static final long serialVersionUID = -2116304004367396735L;
-
-        /** The element as a set. */
-        private final Set<GeneralParameterDescriptor> asSet;
-
-        /** Constructs a list for the specified array. */
-        public AsList(final GeneralParameterDescriptor[] array) {
-            super(array);
-            asSet = new HashSet<>(this);
-        }
-
-        /** Tests for the inclusion of the specified descriptor. */
-        @Override public boolean contains(final Object object) {
-            return asSet.contains(object);
-        }
-    }
-
-    /**
      * Returns all parameters in this group.
      *
      * @return The parameter descriptors in this group.
      */
     @Override
     public List<GeneralParameterDescriptor> descriptors() {
-        if (asList == null) {
-            switch (parameters.length) {
-                case 0:  asList = Collections.emptyList();                  break;
-                case 1:  asList = Collections.singletonList(parameters[0]); break;
-                case 2:  // fall through
-                case 3:  asList = UnmodifiableArrayList.wrap(parameters);   break;
-                default: asList = new AsList(parameters);                   break;
-            }
-        }
-        return asList;
+        return descriptors;
     }
 
     /**
@@ -264,7 +270,7 @@ public class DefaultParameterDescriptorG
     public GeneralParameterDescriptor descriptor(final String name) throws ParameterNotFoundException {
         ArgumentChecks.ensureNonNull("name", name);
         GeneralParameterDescriptor fallback = null, ambiguity = null;
-        for (final GeneralParameterDescriptor param : parameters) {
+        for (final GeneralParameterDescriptor param : descriptors) {
             if (IdentifiedObjects.isHeuristicMatchForName(param, name)) {
                 if (name.equals(param.getName().getCode())) {
                     return param;
@@ -296,7 +302,7 @@ public class DefaultParameterDescriptorG
                     final DefaultParameterDescriptorGroup that = (DefaultParameterDescriptorGroup) object;
                     return minimumOccurs == that.minimumOccurs &&
                            maximumOccurs == that.maximumOccurs &&
-                           Arrays.equals(parameters, that.parameters);
+                           descriptors.equals(that.descriptors);
                 }
                 default: {
                     final ParameterDescriptorGroup that = (ParameterDescriptorGroup) object;
@@ -316,6 +322,6 @@ public class DefaultParameterDescriptorG
      */
     @Override
     protected long computeHashCode() {
-        return Arrays.hashCode(parameters) + super.computeHashCode();
+        return super.computeHashCode() + descriptors.hashCode();
     }
 }

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=1575442&r1=1575441&r2=1575442&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] Fri Mar  7 22:48:35 2014
@@ -69,7 +69,7 @@ public class DefaultParameterValueGroup 
      *
      * <p>Consider this field as final. It is not for the purpose of {@link #clone()}.</p>
      */
-    private ParameterValueList content;
+    private ParameterValueList values;
 
     /**
      * Constructs a parameter group from the specified descriptor.
@@ -78,14 +78,13 @@ public class DefaultParameterValueGroup 
      */
     public DefaultParameterValueGroup(final ParameterDescriptorGroup descriptor) {
         ArgumentChecks.ensureNonNull("descriptor", descriptor);
-        final List<GeneralParameterDescriptor> parameters = descriptor.descriptors();
-        final List<GeneralParameterValue> values = new ArrayList<>(parameters.size());
-        for (final GeneralParameterDescriptor element : parameters) {
-            for (int count=element.getMinimumOccurs(); --count>=0;) {
-                values.add(element.createValue());
+        final List<GeneralParameterDescriptor> elements = descriptor.descriptors();
+        values = new ParameterValueList(descriptor, elements.size());
+        for (final GeneralParameterDescriptor child : elements) {
+            for (int count=child.getMinimumOccurs(); --count>=0;) {
+                values.addUnchecked(new UninitializedParameter(child));
             }
         }
-        content = new ParameterValueList(descriptor, values);
     }
 
     /**
@@ -95,16 +94,28 @@ public class DefaultParameterValueGroup 
      */
     @Override
     public ParameterDescriptorGroup getDescriptor() {
-        return content.descriptor;
+        return values.descriptor;
     }
 
     /**
-     * Returns the values in this group. Changes in this list are reflected on this {@code ParameterValueGroup}.
-     * The returned list supports the {@code add(…)} and {@code remove(…)} operations.
+     * Returns the values in this group. The returned list is <cite>live</cite>:
+     * changes in this list are reflected on this {@code ParameterValueGroup}, and conversely.
+     *
+     * {@section Restrictions}
+     * All write operations must comply to the following conditions:
+     *
+     * <ul>
+     *   <li>Parameters added to the list shall have one of the descriptors listed by {@link #getDescriptor()}.</li>
+     *   <li>Adding or removing parameters shall not violate the parameter cardinality constraints.</li>
+     * </ul>
+     *
+     * The list will verify those conditions and throws {@link org.opengis.parameter.InvalidParameterNameException},
+     * {@link org.opengis.parameter.InvalidParameterCardinalityException} or other runtime exceptions if a condition
+     * is not meet.
      */
     @Override
     public List<GeneralParameterValue> values() {
-        return content;
+        return values;
     }
 
     /**
@@ -113,19 +124,23 @@ public class DefaultParameterValueGroup 
      *
      * <ul>
      *   <li>If this group contains a parameter value of the given name, then that parameter is returned.</li>
-     *   <li>Otherwise if the {@linkplain #getDescriptor() descriptor} contains a definition for a parameter
-     *       of the given name, then a new {@code ParameterValue} instance is
-     *       {@linkplain DefaultParameterDescriptor#createValue() created}, added to this group then returned.</li>
+     *   <li>Otherwise if a {@linkplain DefaultParameterDescriptorGroup#descriptor(String) descriptor} of the
+     *       given name exists, then a new {@code ParameterValue} instance is
+     *       {@linkplain DefaultParameterDescriptor#createValue() created}, added to this group and returned.</li>
      *   <li>Otherwise a {@code ParameterNotFoundException} is thrown.</li>
      * </ul>
      *
-     * This convenience method provides a way to get and set parameter values by name. For example
-     * the following idiom fetches a floating point value for the {@code "false_easting"} parameter:
+     * This convenience method provides a way to get and set parameter values by name.
+     * For example the following idiom fetches a floating point value for the <cite>False easting</cite>
+     * and <cite>False northing</cite> parameters and set a new value for the <cite>False easting</cite> one:
      *
      * {@preformat java
-     *     double value = parameter("false_easting").doubleValue();
+     *     double easting  = parameter("False easting" ).doubleValue();
+     *     double northing = parameter("False northing").doubleValue();
+     *     parameter("False easting").setValue(500000.0);
      * }
      *
+     * {@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
      * {@linkplain #groups(String) query all subgroups} and select explicitly the appropriate one.
@@ -139,42 +154,42 @@ public class DefaultParameterValueGroup 
     @Override
     public ParameterValue<?> parameter(final String name) throws ParameterNotFoundException {
         ArgumentChecks.ensureNonNull("name", name);
-        final List<GeneralParameterValue> values = content.values;
-        ParameterValue<?> fallback = null, ambiguity = null;
-        for (final GeneralParameterValue value : values) {
-            if (value instanceof ParameterValue<?>) {
-                final GeneralParameterDescriptor descriptor = value.getDescriptor();
+        final ParameterValueList values = this.values; // Protect against accidental changes.
+        int fallback = -1, ambiguity = -1;
+        final int size = values.size();
+        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<?>) value;
-                    } else if (fallback == null) {
-                        fallback = (ParameterValue<?>) value;
+                        return (ParameterValue<?>) values.get(i);
+                    } else if (fallback < 0) {
+                        fallback = i;
                     } else {
-                        ambiguity = (ParameterValue<?>) value;
+                        ambiguity = i;
                     }
                 }
             }
         }
-        if (fallback != null) {
-            if (ambiguity == null) {
-                return fallback;
+        if (fallback >= 0) {
+            if (ambiguity < 0) {
+                return (ParameterValue<?>) values.get(fallback);
             }
             throw new ParameterNotFoundException(Errors.format(Errors.Keys.AmbiguousName_3,
-                    fallback.getDescriptor().getName(), ambiguity.getDescriptor().getName(), name), name);
+                    values.descriptor(fallback).getName(), values.descriptor(ambiguity).getName(), name), name);
         }
         /*
-         * No existing parameter found. Check if an optional parameter exists.
-         * If such a descriptor is found, create it, add it to the list of values
-         * and returns it.
+         * No existing parameter found. The parameter may be optional. Check if a descriptor exists.
+         * If such a descriptor is found, create the parameter, add it to the values list and returns it.
          */
-        final GeneralParameterDescriptor descriptor = content.descriptor.descriptor(name);
-        if (descriptor instanceof ParameterDescriptor<?>) {
+        final GeneralParameterDescriptor descriptor = values.descriptor.descriptor(name);
+        if (descriptor instanceof ParameterDescriptor<?> && descriptor.getMaximumOccurs() != 0) {
             final ParameterValue<?> value = ((ParameterDescriptor<?>) descriptor).createValue();
-            values.add(value);
+            values.addUnchecked(value);
             return value;
         }
         throw new ParameterNotFoundException(Errors.format(Errors.Keys.ParameterNotFound_2,
-                content.descriptor.getName(), name), name);
+                values.descriptor.getName(), name), name);
     }
 
     /**
@@ -191,11 +206,14 @@ public class DefaultParameterValueGroup 
     @Override
     public List<ParameterValueGroup> groups(final String name) throws ParameterNotFoundException {
         ArgumentChecks.ensureNonNull("name", name);
+        final ParameterValueList values = this.values; // Protect against accidental changes.
         final List<ParameterValueGroup> groups = new ArrayList<>(4);
-        for (final GeneralParameterValue value : content.values) {
-            if (value instanceof ParameterValueGroup) {
-                if (isHeuristicMatchForName(value.getDescriptor(), name)) {
-                    groups.add((ParameterValueGroup) value);
+        final int size = values.size();
+        for (int i=0; i<size; i++) {
+            final GeneralParameterDescriptor descriptor = values.descriptor(i);
+            if (descriptor instanceof ParameterDescriptorGroup) {
+                if (isHeuristicMatchForName(descriptor, name)) {
+                    groups.add((ParameterValueGroup) values.get(i));
                 }
             }
         }
@@ -205,7 +223,7 @@ public class DefaultParameterValueGroup 
          * it is simply an optional group not yet defined), then returns an empty list.
          */
         if (groups.isEmpty()) {
-            final ParameterDescriptorGroup descriptor = content.descriptor;
+            final ParameterDescriptorGroup descriptor = values.descriptor;
             if (!(descriptor.descriptor(name) instanceof ParameterDescriptorGroup)) {
                 throw new ParameterNotFoundException(Errors.format(
                         Errors.Keys.ParameterNotFound_2, descriptor.getName(), name), name);
@@ -235,19 +253,15 @@ public class DefaultParameterValueGroup 
     public ParameterValueGroup addGroup(final String name)
             throws ParameterNotFoundException, InvalidParameterCardinalityException
     {
-        final ParameterDescriptorGroup descriptor = content.descriptor;
+        final ParameterValueList values = this.values; // Protect against accidental changes.
+        final ParameterDescriptorGroup descriptor = values.descriptor;
         final GeneralParameterDescriptor child = descriptor.descriptor(name);
         if (!(child instanceof ParameterDescriptorGroup)) {
             throw new ParameterNotFoundException(Errors.format(
                     Errors.Keys.ParameterNotFound_2, descriptor.getName(), name), name);
         }
-        final int count = content.count(child.getName());
-        if (count >= child.getMaximumOccurs()) {
-            throw new InvalidParameterCardinalityException(Errors.format(
-                    Errors.Keys.TooManyOccurrences_2, count, name), name);
-        }
         final ParameterValueGroup value = ((ParameterDescriptorGroup) child).createValue();
-        content.values.add(value);
+        values.add(value);
         return value;
     }
 
@@ -264,8 +278,8 @@ public class DefaultParameterValueGroup 
         }
         if (object != null && getClass() == object.getClass()) {
             final DefaultParameterValueGroup that = (DefaultParameterValueGroup) object;
-            return Objects.equals(content.descriptor, that.content.descriptor) &&
-                   Objects.equals(content.values,     that.content.values);
+            return Objects.equals(values.descriptor, that.values.descriptor) &&
+                   Objects.equals(values, that.values);
         }
         return false;
     }
@@ -278,7 +292,7 @@ public class DefaultParameterValueGroup 
      */
     @Override
     public int hashCode() {
-        return content.descriptor.hashCode() ^ content.values.hashCode();
+        return values.descriptor.hashCode() ^ values.hashCode();
     }
 
     /**
@@ -296,11 +310,7 @@ public class DefaultParameterValueGroup 
         } catch (CloneNotSupportedException e) {
             throw new AssertionError(e);
         }
-        copy.content = new ParameterValueList(content.descriptor, new ArrayList<>(content.values));
-        final List<GeneralParameterValue> values = copy.content.values;
-        for (int i=values.size(); --i>=0;) {
-            values.set(i, values.get(i).clone());
-        }
+        copy.values = new ParameterValueList(copy.values);
         return copy;
     }
 }

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=1575442&r1=1575441&r2=1575442&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] Fri Mar  7 22:48:35 2014
@@ -28,7 +28,7 @@ import static org.apache.sis.util.Argume
 
 
 /**
- * Provides convenience methods for easier {@code DefaultParameterDescriptor} instantiations.
+ * Provides convenience methods for easier {@code ParameterDescriptorGroup} 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
@@ -53,8 +53,9 @@ import static org.apache.sis.util.Argume
  * Parameter descriptors are typically grouped in a {@link ParameterDescriptorGroup}.
  * All parameters usually have the same namespace, which can be declared only once.
  * The following example creates parameters for "<cite>Mercator (variant A)</cite>" projection method (EPSG:9804)
- * with all parameter names in the "EPSG" namespace. The default values define a projection centered on (0°,0°)
- * with no scale factor and no false easting/northing.
+ * with all parameter names in the "EPSG" namespace. The default values define a projection centered on (0°,0°),
+ * with no scale factor and no false easting/northing. The projection is valid from 80°S to 84°N and on all the
+ * longitude range (±180°).
  *
  * {@preformat java
  *   ParameterBuilder builder = new ParameterBuilder();
@@ -76,6 +77,7 @@ import static org.apache.sis.util.Argume
  *   builder.addName("Longitude of natural origin")        // Primary name in builder default namespace.
  *          .addName(Citations.OGC, "central_meridian")    // First alias in "OGC" namespace.
  *          .addName(Citations.GEOTIFF, "NatOriginLong")   // Second alias in "GeoTIFF" namespace.
+ *          .addIdentifier("8802")                         // Primary key in EPSG database.
  *          .createBounded(-80, +84, 0, NonSI.DEGREE_ANGLE);
  * }
  *

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterValueList.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterValueList.java?rev=1575442&r1=1575441&r2=1575442&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterValueList.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterValueList.java [UTF-8] Fri Mar  7 22:48:35 2014
@@ -19,7 +19,10 @@ package org.apache.sis.parameter;
 import java.util.List;
 import java.util.AbstractList;
 import java.util.RandomAccess;
+import java.util.Arrays;
 import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
 import org.opengis.parameter.ParameterValue;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.GeneralParameterValue;
@@ -28,13 +31,20 @@ import org.opengis.parameter.GeneralPara
 import org.opengis.parameter.InvalidParameterNameException;
 import org.opengis.parameter.InvalidParameterCardinalityException;
 import org.opengis.referencing.ReferenceIdentifier;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 
 
 /**
  * The list to be returned by {@link DefaultParameterValueGroup#values()}.
  * This class performs checks on the parameter values to be added or removed.
- * This implementation supports {@code add(…)} and {@code remove(…)} operations.
+ * This implementation supports {@code set(…)}, {@code add(…)} and {@code remove(…)} operations.
+ *
+ * <p><b>Implementation note:</b> This class reproduces some {@link java.util.ArrayList} functionalities.
+ * However we do <strong>not</strong> extend {@code ArrayList} because we really need the default method
+ * implementations provided by {@code AbstractList} — the optimizations performed by {@code ArrayList}
+ * are not suitable here.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.4 (derived from geotk-2.1)
@@ -50,63 +60,159 @@ final class ParameterValueList extends A
     /**
      * The descriptor for the list as a whole.
      *
-     * <p>This descriptor will not be used in {@link #equals(Object)} and {@link #hashCode()}
+     * <p>This descriptor shall not be used in {@link #equals(Object)} and {@link #hashCode()}
      * implementations in order to stay consistent with the {@link List} contract.</p>
      */
     final ParameterDescriptorGroup descriptor;
 
     /**
-     * The unchecked list of parameter values for this list.
+     * The parameter values in the group. The length of this array is the list capacity.
+     */
+    private GeneralParameterValue[] values;
+
+    /**
+     * Number of valid elements in the {@link #values} array.
      */
-    final List<GeneralParameterValue> values;
+    private int size;
 
     /**
-     * Constructs a parameter list.
+     * Constructs an initially empty parameter list.
      *
      * @param descriptor The descriptor for this list.
-     * @param values The parameter values for this list.
+     * @param capacity The initial capacity.
      */
-    ParameterValueList(final ParameterDescriptorGroup descriptor, final List<GeneralParameterValue> values) {
+    ParameterValueList(final ParameterDescriptorGroup descriptor, final int capacity) {
         this.descriptor = descriptor;
-        this.values     = values;
+        values = new GeneralParameterValue[capacity];
+    }
+
+    /**
+     * Constructs a parameter list initialized to a copy of the given one.
+     */
+    ParameterValueList(final ParameterValueList other) {
+        descriptor = other.descriptor;
+        values = new GeneralParameterValue[size = other.size];
+        for (int i=0; i<size; i++) {
+            values[i] = other.values[i].clone();
+        }
+    }
+
+    /**
+     * Returns the number of parameters in this list.
+     */
+    @Override
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns the descriptor at the given index. This method is preferable to {@code get(i).getDescriptor()}
+     * when the caller does not need the replacement of {@link UninitializedParameter} instances.
+     */
+    final GeneralParameterDescriptor descriptor(final int index) {
+        return values[index].getDescriptor();
+    }
+
+    /**
+     * Returns the parameter value at the given index. If the parameter at the given index is a
+     * mandatory parameter pending creation of the actual value, the value will be created now.
+     */
+    @Override
+    public GeneralParameterValue get(int index) {
+        ArgumentChecks.ensureValidIndex(size, index);
+        GeneralParameterValue value = values[index];
+        if (value instanceof UninitializedParameter) {
+            values[index] = value = value.getDescriptor().createValue();
+        }
+        return value;
     }
 
-    /*
-     * CAUTION: Some methods are NOT forwarded to 'values', and this is on purpose!
-     *          This include all modification methods (add, set, remove, etc.).
-     *          We must rely on the default AbstractList implementation for them.
-     */
-    @Override public boolean               isEmpty    ()         {return values.isEmpty    ( );}
-    @Override public int                   size       ()         {return values.size       ( );}
-    @Override public GeneralParameterValue get        (int i)    {return values.get        (i);}
-    @Override public int                   indexOf    (Object o) {return values.indexOf    (o);}
-    @Override public int                   lastIndexOf(Object o) {return values.lastIndexOf(o);}
-    @Override public boolean               equals     (Object o) {return values.equals     (o);}
-    @Override public int                   hashCode   ()         {return values.hashCode   ( );}
-    @Override public String                toString   ()         {return values.toString   ( );}
+    /**
+     * Sets the parameter at the given index. The descriptor of the given parameter must be one of those
+     * in the {@link DefaultParameterDescriptorGroup#descriptors()} list, and storing that parameter must
+     * be allowed by the cardinality constraints.
+     */
+    @Override
+    public GeneralParameterValue set(final int index, final GeneralParameterValue parameter) {
+        ArgumentChecks.ensureValidIndex(size, index);
+        final GeneralParameterValue value = values[index];
+        ArgumentChecks.ensureNonNull("parameter", parameter);
+        final GeneralParameterDescriptor desc = parameter.getDescriptor();
+        if (!value.getDescriptor().equals(desc)) {
+            ensureDescriptorExists(desc);
+            ensureCanRemove(desc);
+            ensureCanAdd(desc);
+        }
+        values[index] = parameter;
+        return value;
+    }
 
     /**
      * Adds a {@link ParameterValue} or an other {@link ParameterValueGroup} to this list.
      * If an existing parameter is already included for the same name and adding the new
      * parameter would increase the number past what is allowable by {@code maximumOccurs},
-     * then an {@link IllegalStateException} will be thrown.
+     * then an {@link InvalidParameterCardinalityException} will be thrown.
      *
      * @param  parameter New parameter to be added to this group.
-     * @return {@code true} if this object changed as a result of this call.
+     * @return Always {@code true} since this object changes as a result of this call.
      * @throws IllegalArgumentException if the specified parameter is not allowable by the groups descriptor.
      * @throws InvalidParameterCardinalityException if adding this parameter would result in more parameters
      *         than allowed by {@code maximumOccurs}.
      */
     @Override
     public boolean add(final GeneralParameterValue parameter) {
-        final GeneralParameterDescriptor type = parameter.getDescriptor();
-        final ReferenceIdentifier name = type.getName();
+        ArgumentChecks.ensureNonNull("parameter", parameter);
+        final GeneralParameterDescriptor desc = parameter.getDescriptor();
+        ensureDescriptorExists(desc);
+        /*
+         * If we had an uninitialized parameter (a parameter created by the DefaultParameterValueGroup constructor
+         * and never been queried or set by the user), then the given parameter will replace the uninitialized.
+         * The intend is to allow users to set its own parameters by a call to group.values().addAll(myParam).
+         * Otherwise the given parameter will be added, in which case we need to check the cardinality.
+         */
+        final ReferenceIdentifier name = desc.getName();
+        int count = 0;
+        for (int i=0; i<size; i++) {
+            final GeneralParameterValue value = values[i];
+            if (name.equals(value.getDescriptor().getName())) {
+                if (value instanceof UninitializedParameter) {
+                    values[i] = parameter;
+                    return true;
+                }
+                count++;
+            }
+        }
+        if (count > desc.getMaximumOccurs()) {
+            throw new InvalidParameterCardinalityException(Errors.format(
+                    Errors.Keys.TooManyOccurrences_2, count, name), name.getCode());
+        }
+        addUnchecked(parameter);
+        modCount++;
+        return true;
+    }
+
+    /**
+     * Unconditionally adds the given parameter to this list without any validity check.
+     * The internal array will growth as needed.
+     */
+    final void addUnchecked(final GeneralParameterValue parameter) {
+        if (size == values.length) {
+            values = Arrays.copyOf(values, size*2);
+        }
+        values[size++] = parameter;
+    }
+
+    /**
+     * Verifies the given descriptor exists in the {@link DefaultParameterDescriptorGroup#descriptors()} list.
+     */
+    final void ensureDescriptorExists(final GeneralParameterDescriptor desc) {
         final List<GeneralParameterDescriptor> descriptors = descriptor.descriptors();
-        if (!descriptors.contains(type)) {
+        if (!descriptors.contains(desc)) {
             /*
              * For a more accurate error message, check if the operation failed because the
              * parameter name was not found, or the parameter descriptor does not matches.
              */
+            final ReferenceIdentifier name = desc.getName();
             for (final GeneralParameterDescriptor descriptor : descriptors) {
                 if (name.equals(descriptor.getName())) {
                     throw new IllegalArgumentException(Errors.format(
@@ -116,32 +222,47 @@ final class ParameterValueList extends A
             throw new InvalidParameterNameException(Errors.format(
                     Errors.Keys.ParameterNotFound_2, descriptor.getName(), name), name.getCode());
         }
-        /*
-         * Before to add the parameter, check the cardinality.
-         */
-        final int count = count(name);
-        if (count >= type.getMaximumOccurs()) {
+    }
+
+    /**
+     * Verifies if adding a parameter with the given descriptor is allowed by the cardinality constraints. If adding
+     * the parameter would result in more occurrences than {@link DefaultParameterDescriptor#getMaximumOccurs()},
+     * then this method throws an {@link InvalidParameterCardinalityException}.
+     */
+    private void ensureCanAdd(final GeneralParameterDescriptor desc) {
+        final ReferenceIdentifier name = desc.getName();
+        int count = 0;
+        for (int i=0; i<size; i++) {
+            if (name.equals(values[i].getDescriptor().getName())) {
+                count++;
+            }
+        }
+        if (count >= desc.getMaximumOccurs()) {
             throw new InvalidParameterCardinalityException(Errors.format(
                     Errors.Keys.TooManyOccurrences_2, count, name), name.getCode());
         }
-        modCount++;
-        return values.add(parameter);
     }
 
     /**
-     * Count the number of parameter having the given name.
-     *
-     * @param  name The name to search.
-     * @return Number of parameter having the given name.
+     * Verifies if removing the given value is allowed by the cardinality constraints. If removing the parameter
+     * would result in less occurrences than {@link DefaultParameterDescriptor#getMinimumOccurs()},
+     * then this method throws an {@link InvalidParameterCardinalityException}.
      */
-    final int count(final ReferenceIdentifier name) {
-        int count = 0;
-        for (final GeneralParameterValue value : values) {
-            if (name.equals(value.getDescriptor().getName())) {
-                count++;
+    private void ensureCanRemove(final GeneralParameterDescriptor desc) {
+        final int min = desc.getMinimumOccurs();
+        if (min != 0) { // Optimization for a common case.
+            final ReferenceIdentifier name = desc.getName();
+            int count = 0;
+            for (int i=0; i<size; i++) {
+                if (name.equals(values[i].getDescriptor().getName())) {
+                    if (++count > min) {
+                        return;
+                    }
+                }
             }
+            throw new InvalidParameterCardinalityException(Errors.format(
+                    Errors.Keys.TooFewOccurrences_2, min, name), name.getCode());
         }
-        return count;
     }
 
     /**
@@ -153,22 +274,36 @@ final class ParameterValueList extends A
      */
     @Override
     public GeneralParameterValue remove(final int index) {
-        final GeneralParameterDescriptor type = values.get(index).getDescriptor();
-        final int min = type.getMinimumOccurs();
-        if (min != 0) {
-            int count = 0;
-            final ReferenceIdentifier name = type.getName();
-            for (final GeneralParameterValue value : values) {
-                if (name.equals(value.getDescriptor().getName())) {
-                    if (++count > min) break;
-                }
-            }
-            if (count <= min) {
-                throw new InvalidParameterCardinalityException(Errors.format(
-                        Errors.Keys.TooFewOccurrences_2, min, name), name.getCode());
-            }
-        }
+        ArgumentChecks.ensureValidIndex(size, index);
+        final GeneralParameterValue value = values[index];
+        ensureCanRemove(value.getDescriptor());
+        System.arraycopy(values, index + 1, values, index, --size - index);
+        values[size] = null;
         modCount++;
-        return values.remove(index);
+        return value;
+    }
+
+    /**
+     * Returns a string representation of this list.
+     */
+    @Override
+    public String toString() {
+        if (size == 0) {
+            return "[]";
+        }
+        final String lineSeparator = System.lineSeparator();
+        final StringBuilder buffer = new StringBuilder();
+        for (int i=0; i<size; i++) {
+            buffer.append(values[i]).append(lineSeparator);
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Trims the array to its capacity before to serialize.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        values = ArraysExt.resize(values, size);
+        out.defaultWriteObject();
     }
 }

Added: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java?rev=1575442&view=auto
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java (added)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java [UTF-8] Fri Mar  7 22:48:35 2014
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.parameter;
+
+import java.io.Serializable;
+import org.opengis.parameter.GeneralParameterDescriptor;
+import org.opengis.parameter.GeneralParameterValue;
+import org.apache.sis.referencing.IdentifiedObjects;
+
+
+/**
+ * Placeholder for a mandatory parameter value which has not yet been initialized.
+ * {@code UninitializedParameter} are immutable and contains only the descriptor of
+ * the parameter to initialize. {@code UninitializedParameter} are replaced by the
+ * actual parameter when first needed.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.4
+ * @version 0.4
+ * @module
+ */
+final class UninitializedParameter implements GeneralParameterValue, Serializable {
+    /**
+     * For cross-version serialization compatibility.
+     */
+    private static final long serialVersionUID = 4664809449434987422L;
+
+    /**
+     * The descriptor of the parameter to initialize.
+     */
+    private final GeneralParameterDescriptor descriptor;
+
+    /**
+     * Creates a new {@code UninitializedParameter} for the given descriptor.
+     */
+    UninitializedParameter(final GeneralParameterDescriptor descriptor) {
+        this.descriptor = descriptor;
+    }
+
+    /**
+     * Returns the descriptor of the parameter to initialize.
+     */
+    @Override
+    public GeneralParameterDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Returns {@code this} since there is no need to clone this object.
+     */
+    @Override
+    public GeneralParameterValue clone() {
+        return this;
+    }
+
+    /**
+     * Returns a string representation of this parameter.
+     */
+    @Override
+    public String toString() {
+        return "Parameter[\"" + IdentifiedObjects.toString(descriptor.getName()) + "\"]";
+    }
+}

Propchange: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/UninitializedParameter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

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=1575442&r1=1575441&r2=1575442&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] Fri Mar  7 22:48:35 2014
@@ -17,18 +17,8 @@
 package org.apache.sis.parameter;
 
 import java.util.Map;
-import java.util.HashMap;
-import java.util.Locale;
+import java.util.List;
 import org.opengis.parameter.GeneralParameterDescriptor;
-import javax.measure.unit.SI;
-import javax.measure.unit.Unit;
-import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.parameter.ParameterValueGroup;
-import org.apache.sis.measure.Range;
-import org.apache.sis.measure.NumberRange;
-import org.apache.sis.measure.MeasurementRange;
-import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -56,14 +46,14 @@ public final strictfp class DefaultParam
      * The very last parameter has a maximum number of occurrence of 2, which is illegal
      * according ISO 19111 but nevertheless supported by Apache SIS.
      */
-    static final DefaultParameterDescriptorGroup createGroupOfIntegers() {
-        final Integer DEFAULT = 10;
+    static final DefaultParameterDescriptorGroup createGroup_2M_2O() {
+        final Integer DEFAULT_VALUE = 10;
         final Class<Integer> type = Integer.class;
         return new DefaultParameterDescriptorGroup(name("The group"), 0, 1,
-            new DefaultParameterDescriptor<>(name("Mandatory 1"), type, null, null, DEFAULT, true),
-            new DefaultParameterDescriptor<>(name("Mandatory 2"), type, null, null, DEFAULT, true),
-            new DefaultParameterDescriptor<>(name( "Optional 3"), type, null, null, DEFAULT, false),
-            new MultiOccurrenceDescriptor <>(name( "Optional 4"), type, null, null, DEFAULT, false)
+            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)
         );
     }
 
@@ -75,11 +65,11 @@ public final strictfp class DefaultParam
     }
 
     /**
-     * Tests descriptor validation.
+     * Validates the test parameter descriptors created by {@link #createGroup_2M_2O()}.
      */
     @Test
-    public void testValidate() {
-        for (final GeneralParameterDescriptor descriptor : createGroupOfIntegers().descriptors()) {
+    public void validateTestObjects() {
+        for (final GeneralParameterDescriptor descriptor : createGroup_2M_2O().descriptors()) {
             AssertionError error = null;
             try {
                 validate(descriptor);
@@ -93,4 +83,32 @@ public final strictfp class DefaultParam
             }
         }
     }
+
+    /**
+     * Tests {@link DefaultParameterDescriptorGroup#descriptor(String)}.
+     */
+    @Test
+    public void testDescriptor() {
+        final DefaultParameterDescriptorGroup group = createGroup_2M_2O();
+        final List<GeneralParameterDescriptor> descriptors = group.descriptors();
+        assertEquals("name", "The 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"));
+    }
+
+    /**
+     * Tests {@code DefaultParameterDescriptorGroup.descriptors().contains(Object)}.
+     * The list returned by {@code descriptors()} provides a fast implementation based on {@code HashSet},
+     * because this operation is requested everytime a new parameter is added or modified.
+     */
+    @Test
+    public void testContains() {
+        final List<GeneralParameterDescriptor> descriptors = createGroup_2M_2O().descriptors();
+        for (final GeneralParameterDescriptor p : descriptors) {
+            assertTrue(descriptors.contains(p));
+        }
+    }
 }

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=1575442&r1=1575441&r2=1575442&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] Fri Mar  7 22:48:35 2014
@@ -16,20 +16,13 @@
  */
 package org.apache.sis.parameter;
 
-import java.util.Map;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
+import java.util.Arrays;
 import org.opengis.parameter.GeneralParameterDescriptor;
-import javax.measure.unit.SI;
-import javax.measure.unit.Unit;
+import org.opengis.parameter.GeneralParameterValue;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterValueGroup;
-import org.apache.sis.measure.Range;
-import org.apache.sis.measure.NumberRange;
-import org.apache.sis.measure.MeasurementRange;
-import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -68,13 +61,12 @@ public final strictfp class DefaultParam
     }
 
     /**
-     * Tests parameter validation.
+     * Validates the test parameter values created by {@link #createValues(List, int)}.
      */
     @Test
-    public void testValidate() {
-        for (final DefaultParameterValue<?> param : createValues(
-                DefaultParameterDescriptorGroupTest.createGroupOfIntegers().descriptors(), 10))
-        {
+    public void validateTestObjects() {
+        final DefaultParameterDescriptorGroup group = DefaultParameterDescriptorGroupTest.createGroup_2M_2O();
+        for (final DefaultParameterValue<?> param : createValues(group.descriptors(), 10)) {
             AssertionError error = null;
             try {
                 validate(param);
@@ -90,6 +82,24 @@ public final strictfp class DefaultParam
     }
 
     /**
+     * Tests {@code DefaultParameterValueGroup.values().addAll(…)}.
+     */
+    @Test
+    public void testAddAll() {
+        final DefaultParameterDescriptorGroup descriptor = DefaultParameterDescriptorGroupTest.createGroup_2M_2O();
+        final DefaultParameterValueGroup group = new DefaultParameterValueGroup(descriptor);
+        final List<GeneralParameterValue> values = group.values();
+        assertEquals("Initial size", 2, values.size());
+
+        final DefaultParameterValue<?>[] external = createValues(descriptor.descriptors(), 10);
+        assertTrue(values.addAll(Arrays.asList(external)));
+        assertEquals("Final size", external.length, values.size());
+        for (int i=0; i<external.length; i++) {
+            assertSame(external[i], values.get(i));
+        }
+    }
+
+    /**
      * Tests the {@link DefaultParameterValueGroup#addGroup(String)} method.
      * Ensures the descriptor is found and the new value correctly inserted.
      */



Mime
View raw message