Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameterGroup.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameterGroup.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameterGroup.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameterGroup.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -16,10 +16,18 @@ */ package org.apache.sis.internal.jaxb.referencing; +import java.util.Map; +import java.util.List; +import java.util.LinkedHashMap; import javax.xml.bind.annotation.XmlElement; +import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; +import org.opengis.parameter.GeneralParameterDescriptor; import org.apache.sis.internal.jaxb.gco.PropertyType; import org.apache.sis.parameter.DefaultParameterDescriptorGroup; +import org.apache.sis.util.CorruptedObjectException; +import org.apache.sis.util.collection.Containers; +import org.apache.sis.util.resources.Errors; /** @@ -89,4 +97,125 @@ public final class CC_OperationParameter public void setElement(final DefaultParameterDescriptorGroup parameter) { metadata = parameter; } + + /** + * Invoked by {@link DefaultParameterDescriptorGroup#setDescriptors(GeneralParameterDescriptor[])} + * for merging into a single set the descriptors which are repeated twice in a GML document. + * + * <p>The {@code descriptors} argument gives the descriptors listed explicitely inside a + * {@code <gml:OperationParameterGroup>} or {@code <gml:OperationMethod>} element. Those + * descriptors are said "incomplete" (from SIS point of view) because they are missing the + * {@link ParameterDescriptor#getValueClass()} property, which does not exist in GML but + * is mandatory for us. However an exception to this "incompleteness" happen when SIS has + * been able to match the {@code <gml:OperationMethod>} parent to one of the pre-defined + * operations in the {@link org.apache.sis.internal.referencing.provider} package.</p> + * + * <p>The {@code fromValues} argument gives the descriptors declared in each {@code <gml:ParameterValue>} + * instances of a {@code <gml:ParameterValueGroup>} or {@code <gml:AbstractSingleOperation>} element. + * Contrarily to the {@code descriptors} argument, the {@code fromValues} instances should have non-null + * {@link ParameterDescriptor#getValueClass()} property inferred by SIS from the parameter value.</p> + * + * <p>So the preferred descriptors from more complete to less complete are:</p> + * <ol> + * <li>{@code descriptors} if and only if they contain pre-defined parameters inferred by SIS from the {@code <gml:OperationMethod>} name.</li> + * <li>{@code fromValues}, which contain the descriptors declared in the {@code <gml:ParameterValue>} instances.</li> + * <li>{@code descriptors}, which contain the descriptor listed in {@code <gml:OperationParameterGroup>} or {@code <gml:OperationMethod>}.</li> + * </ol> + * + * <div class="note"><b>Note:</b> + * this code is defined in this {@code CC_OperationParameterGroup} class instead than in the + * {@link DefaultParameterDescriptorGroup} class in the hope to reduce the amount of code + * processed by the JVM in the common case where JAXB (un)marshalling is not needed.</div> + * + * @param descriptors The descriptors declared in the {@code ParameterDescriptorGroup}. + * @param fromValues The descriptors declared in the {@code ParameterValue} instances. + * They are said "valid" because they contain the mandatory {@code valueClass} property. + * @param replacements An {@code IdentityHashMap} where to store the replacements that the caller needs + * to apply in the {@code GeneralParameterValue} instances. + * @return A sequence containing the merged set of parameter descriptors. + * + * @see <a href="http://issues.apache.org/jira/browse/SIS-290">SIS-290</a> + */ + public static GeneralParameterDescriptor[] merge( + final List<GeneralParameterDescriptor> descriptors, + final GeneralParameterDescriptor[] fromValues, + final Map<GeneralParameterDescriptor,GeneralParameterDescriptor> replacements) + { + if (descriptors.isEmpty()) { + return fromValues; + } + final Map<String,GeneralParameterDescriptor> union = + new LinkedHashMap<>(Containers.hashMapCapacity(descriptors.size())); + /* + * Collect the descriptors declared explicitely in the ParameterDescriptorGroup. We should never have + * two descriptors of the same name since the DefaultParameterDescriptorGroup constructor checked for + * name ambiguity. If a name collision is nevertheless detected, this would mean that a descriptor's + * name mutated. + */ + for (final GeneralParameterDescriptor p : descriptors) { + final String name = p.getName().getCode(); + if (union.put(name, p) != null) { + throw new CorruptedObjectException(name); + } + } + /* + * Verify if any descriptors found in the ParameterValue instances could replace the descriptors in the group. + * We give precedence to the descriptors having a non-null 'valueClass' property, which normally appear in the + * 'fromValues' array. + */ + for (final GeneralParameterDescriptor valueDescriptor : fromValues) { + final String name = valueDescriptor.getName().getCode(); + GeneralParameterDescriptor complete = valueDescriptor; + GeneralParameterDescriptor previous = union.put(name, complete); + if (previous != null) { + if (previous instanceof ParameterDescriptor<?>) { + verifyEquivalence(name, complete instanceof ParameterDescriptor<?>); + final Class<?> valueClass = ((ParameterDescriptor<?>) previous).getValueClass(); + if (valueClass != null) { + /* + * This may happen if the 'descriptors' argument contain the parameters of a pre-defined + * method from the 'org.apache.sis.internal.referencing.provider' package instead than a + * descriptor from the GML file. In such case, presume that 'previous' is actually more + * complete than 'complete'. + * + * Note that 'r' should never be null unless JAXB unmarshalled the elements in reverse + * order (e.g. <gml:ParameterValue> before <gml:OperationMethod>). Since this behavior + * may depend on JAXB implementation, we are better to check for such case. + */ + final Class<?> r = ((ParameterDescriptor<?>) complete).getValueClass(); + if (r != null) { + verifyEquivalence(name, valueClass == r); + } + // Restore the previous value in the map and swap 'previous' with 'replacement'. + previous = union.put(name, complete = previous); + } + } else if (previous instanceof ParameterDescriptorGroup) { + verifyEquivalence(name, complete instanceof ParameterDescriptorGroup); + } + /* + * Verify that the replacement contains at least all the information provided by the previous + * descriptor. The replacement is allowed to contain more information however. + */ + final GeneralParameterDescriptor replacement = CC_GeneralOperationParameter.merge(previous, complete); + if (replacement != valueDescriptor) { + union.put(name, replacement); + if (replacements.put(valueDescriptor, replacement) != null) { + // Should never happen, unless the parameter name changed during execution of this loop. + throw new CorruptedObjectException(name); + } + } + } + } + return union.values().toArray(new GeneralParameterDescriptor[union.size()]); + } + + /** + * Throws an exception for mismatched descriptor if a condition is false. + * This is used for verifying that a descriptors has the expected properties. + */ + private static void verifyEquivalence(final String name, final boolean condition) { + if (!condition) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedParameterDescriptor_1, name)); + } + } }
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -27,6 +27,7 @@ import org.opengis.referencing.datum.Ell import org.opengis.referencing.datum.PrimeMeridian; import org.apache.sis.util.Static; import org.apache.sis.util.Utilities; +import org.apache.sis.util.CharSequences; import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.jaxb.Context; import org.apache.sis.referencing.CommonCRS; @@ -224,6 +225,41 @@ public final class ReferencingUtilities } /** + * Returns the XML property name of the given interface. + * + * For {@link CoordinateSystem} base type, the returned value shall be one of + * {@code affineCS}, {@code cartesianCS}, {@code cylindricalCS}, {@code ellipsoidalCS}, {@code linearCS}, + * {@code parametricCS}, {@code polarCS}, {@code sphericalCS}, {@code timeCS} or {@code verticalCS}. + * + * @param base The abstract base interface. + * @param type The interface or classes for which to get the XML property name. + * @return The XML property name for the given class or interface, or {@code null} if none. + * + * @since 0.6 + */ + public static StringBuilder toPropertyName(final Class<?> base, final Class<?> type) { + final UML uml = type.getAnnotation(UML.class); + if (uml != null && uml.specification() == Specification.ISO_19111) { + final String name = uml.identifier(); + final int length = name.length(); + final StringBuilder buffer = new StringBuilder(length).append(name, name.indexOf('_') + 1, length); + if (buffer.length() != 0) { + buffer.setCharAt(0, Character.toLowerCase(buffer.charAt(0))); + return buffer; + } + } + for (final Class<?> c : type.getInterfaces()) { + if (base.isAssignableFrom(c)) { + final StringBuilder name = toPropertyName(base, c); + if (name != null) { + return name; + } + } + } + return null; + } + + /** * Returns the WKT type of the given interface. * * For {@link CoordinateSystem} base type, the returned value shall be one of @@ -236,28 +272,18 @@ public final class ReferencingUtilities */ public static String toWKTType(final Class<?> base, final Class<?> type) { if (type != base) { - final UML uml = type.getAnnotation(UML.class); - if (uml != null && uml.specification() == Specification.ISO_19111) { - String name = uml.identifier(); - final int length = name.length() - 5; // Length without "CS_" and "CS". - if (length >= 1 && name.startsWith("CS_") && name.endsWith("CS")) { - final StringBuilder buffer = new StringBuilder(length).append(name, 3, 3 + length); - if (!name.regionMatches(3, "Cartesian", 0, 9)) { - buffer.setCharAt(0, Character.toLowerCase(buffer.charAt(0))); + final StringBuilder name = toPropertyName(base, type); + if (name != null) { + int end = name.length() - 2; + if (CharSequences.regionMatches(name, end, "CS")) { + name.setLength(end); + if ("time".contentEquals(name)) { + return "temporal"; } - name = buffer.toString(); - if (name.equals("time")) { - name = "temporal"; - } - return name; - } - } - for (final Class<?> c : type.getInterfaces()) { - if (base.isAssignableFrom(c)) { - final String name = toWKTType(base, c); - if (name != null) { - return name; + if (CharSequences.regionMatches(name, 0, "cartesian")) { + name.setCharAt(0, 'C'); // "Cartesian" } + return name.toString(); } } } @@ -265,9 +291,9 @@ public final class ReferencingUtilities } /** - * Ensures that the given argument value is {@code false}. This method is invoked by private setter methods, - * which are themselves invoked by JAXB at unmarshalling time. Invoking this method from those setter methods - * serves three purposes: + * Invoked by private setter methods (themselves invoked by JAXB at unmarshalling time) + * when an element is already set. Invoking this method from those setter methods serves + * three purposes: * * <ul> * <li>Make sure that a singleton property is not defined twice in the XML document.</li> @@ -277,23 +303,17 @@ public final class ReferencingUtilities * warning or error messages in future SIS versions.</li> * </ul> * - * @param classe The caller class, used only in case of warning message to log. - * @param method The caller method, used only in case of warning message to log. - * @param name The property name, used only in case of error message to format. - * @param isDefined Whether the property in the caller object is current defined. - * @return {@code true} if the caller can set the property. + * @param classe The caller class, used only in case of warning message to log. + * @param method The caller method, used only in case of warning message to log. + * @param name The property name, used only in case of error message to format. * @throws IllegalStateException If {@code isDefined} is {@code true} and we are not unmarshalling an object. */ - public static boolean canSetProperty(final Class<?> classe, final String method, - final String name, final boolean isDefined) throws IllegalStateException + public static void propertyAlreadySet(final Class<?> classe, final String method, final String name) + throws IllegalStateException { - if (!isDefined) { - return true; - } final Context context = Context.current(); if (context != null) { Context.warningOccured(context, classe, method, Errors.class, Errors.Keys.ElementAlreadyPresent_1, name); - return false; } else { throw new IllegalStateException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, name)); } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -114,12 +114,13 @@ abstract class AbstractProvider extends } /** - * Creates a descriptor for a constant value in degrees. + * Creates a descriptor for a 0 constant value in degrees. * * @see MapProjection#validate(ParameterDescriptor, double) */ - static ParameterDescriptor<Double> createConstant(final ParameterBuilder builder, final Double constant) { - return builder.createBounded(MeasurementRange.create(constant, true, constant, true, NonSI.DEGREE_ANGLE), constant); + static ParameterDescriptor<Double> createZeroConstant(final ParameterBuilder builder) { + final Double zero = +0.0; + return builder.createBounded(MeasurementRange.create(-0.0, true, zero, true, NonSI.DEGREE_ANGLE), zero); } /** Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -163,7 +163,7 @@ public final class Equirectangular exten * still see it in use sometime. However, taking inspiration from the practice done in "Mercator (1SP)" * projection, we require that the parameter value must be zero. */ - LATITUDE_OF_ORIGIN = createConstant(builder // Was used by EPSG:9823 (also EPSG:9842). + LATITUDE_OF_ORIGIN = createZeroConstant(builder // Was used by EPSG:9823 (also EPSG:9842). .addIdentifier("8801") .addName("Latitude of natural origin") .addName(Citations.OGC, "latitude_of_origin") @@ -172,7 +172,7 @@ public final class Equirectangular exten .addName(Citations.GEOTIFF, "ProjCenterLat") .addName(Citations.PROJ4, "lat_0") .setRemarks(Messages.formatInternational(Messages.Keys.ConstantProjParameterValue_1, 0)) - .setRequired(false), 0.0); + .setRequired(false)); // Do not declare the ESRI "Equidistant_Cylindrical" projection name below, // for avoiding confusion with EPSG "Equidistant Cylindrical" ellipsoidal projection. Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -47,7 +47,6 @@ import org.apache.sis.util.resources.Mes import static org.opengis.metadata.Identifier.AUTHORITY_KEY; // Branch-dependent imports -import java.util.Objects; /** @@ -133,7 +132,7 @@ public abstract class MapProjection exte * @param value The parameter value in the units given by the descriptor. * @throws IllegalArgumentException if the given value is out of bounds. * - * @see #createConstant(ParameterBuilder, Double) + * @see #createZeroConstant(ParameterBuilder) */ public static void validate(final ParameterDescriptor<? extends Number> descriptor, final double value) throws IllegalArgumentException @@ -144,7 +143,9 @@ public abstract class MapProjection exte } final Comparable<? extends Number> min = descriptor.getMinimumValue(); final Comparable<? extends Number> max = descriptor.getMaximumValue(); - if (!Objects.equals(min, max)) { + final double minValue = (min instanceof Number) ? ((Number) min).doubleValue() : Double.NaN; + final double maxValue = (max instanceof Number) ? ((Number) max).doubleValue() : Double.NaN; + if (value < minValue || value > maxValue) { /* * RATIONAL: why we do not check the bounds if (min == max): * The only case when our descriptor have (min == max) is when a parameter can only be zero, @@ -152,11 +153,9 @@ public abstract class MapProjection exte * But in some cases, it would be possible to deal with non-zero values, even if in principle * we should not. In such case we let the caller decides. * - * Above check should be revisited if createConstant(ParameterBuilder, Double) is modified. + * Above check should be revisited if createZeroConstant(ParameterBuilder) is modified. */ - if ((min instanceof Number && !(value >= ((Number) min).doubleValue())) || - (max instanceof Number && !(value <= ((Number) max).doubleValue()))) - { + if (minValue != maxValue) { // Compare as 'double' because we want (-0 == +0) to be true. throw new IllegalArgumentException(Errors.format(Errors.Keys.ValueOutOfRange_4, descriptor.getName(), min, max, value)); } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -66,9 +66,9 @@ public final class Mercator1SP extends A static final ParameterDescriptorGroup PARAMETERS; static { final ParameterBuilder builder = builder(); - LATITUDE_OF_ORIGIN = createConstant(builder.addNamesAndIdentifiers(Equirectangular.LATITUDE_OF_ORIGIN) + LATITUDE_OF_ORIGIN = createZeroConstant(builder.addNamesAndIdentifiers(Equirectangular.LATITUDE_OF_ORIGIN) .rename(Citations.GEOTIFF, "NatOriginLat") - .setRemarks(Equirectangular.LATITUDE_OF_ORIGIN.getRemarks()), 0.0); + .setRemarks(Equirectangular.LATITUDE_OF_ORIGIN.getRemarks())); LONGITUDE_OF_ORIGIN = createLongitude(builder.addNamesAndIdentifiers(Equirectangular.LONGITUDE_OF_ORIGIN) .rename(Citations.GEOTIFF, "NatOriginLong")); Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -77,9 +77,9 @@ public final class Mercator2SP extends A */ builder.setRequired(false); // Will apply to all remaining parameters. final InternationalString remarks = notFormalParameter("Mercator (variant A)"); - final ParameterDescriptor<Double> latitudeOfOrigin = createConstant(builder + final ParameterDescriptor<Double> latitudeOfOrigin = createZeroConstant(builder .addNamesAndIdentifiers(Mercator1SP.LATITUDE_OF_ORIGIN) - .setRemarks(remarks), 0.0); + .setRemarks(remarks)); /* * Remove the EPSG name and identifier at least for the scale factor, because its meaning does not fit well * in this context. The EPSG name is "Scale factor at natural origin" while actually the scale factor applied Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -34,6 +34,7 @@ import org.apache.sis.util.ComparisonMod import org.apache.sis.util.Debug; import static org.apache.sis.util.Utilities.deepEquals; +import static org.apache.sis.internal.jaxb.referencing.CC_GeneralOperationParameter.DEFAULT_OCCURRENCE; // Branch-dependent imports import java.util.Objects; @@ -134,17 +135,6 @@ public abstract class AbstractParameterD private short maximumOccurs; /** - * Constructs a new object in which every attributes are set to a null value. - * <strong>This is not a valid object.</strong> This constructor is strictly - * reserved to JAXB, which will assign values to the fields using reflexion. - */ - AbstractParameterDescriptor() { - super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE); - minimumOccurs = 1; // Default value is XML element is omitted. - maximumOccurs = 1; - } - - /** * Constructs a parameter 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: @@ -368,7 +358,30 @@ public abstract class AbstractParameterD return WKTKeywords.Parameter; } - // ---- XML SUPPORT ---------------------------------------------------- + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Constructs a new object in which every attributes are set to a null value. + * <strong>This is not a valid object.</strong> This constructor is strictly + * reserved to JAXB, which will assign values to the fields using reflexion. + */ + AbstractParameterDescriptor() { + super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE); + minimumOccurs = DEFAULT_OCCURRENCE; // Default value if XML element is omitted. + maximumOccurs = DEFAULT_OCCURRENCE; + } /** * Invoked by JAXB for marshalling the {@link #minimumOccurs} value. Omit marshalling of this @@ -378,7 +391,7 @@ public abstract class AbstractParameterD @XmlSchemaType(name = "nonNegativeInteger") private Integer getNonDefaultMinimumOccurs() { final int n = getMinimumOccurs(); - return (n != 1) ? n : null; + return (n != DEFAULT_OCCURRENCE) ? n : null; } /** @@ -395,20 +408,20 @@ public abstract class AbstractParameterD @XmlSchemaType(name = "nonNegativeInteger") private Integer getNonDefaultMaximumOccurs() { final int n = getMaximumOccurs(); - return (n != 1) ? n : null; + return (n != DEFAULT_OCCURRENCE) ? n : null; } /** * Invoked by JAXB for unmarshalling the {@link #minimumOccurs} value. */ private void setNonDefaultMinimumOccurs(final Integer n) { - minimumOccurs = (n != null) ? crop(n) : 1; + minimumOccurs = (n != null) ? crop(n) : DEFAULT_OCCURRENCE; } /** * Invoked by JAXB for unmarshalling the {@link #maximumOccurs} value. */ private void setNonDefaultMaximumOccurs(final Integer n) { - maximumOccurs = (n != null) ? crop(n) : 1; + maximumOccurs = (n != null) ? crop(n) : DEFAULT_OCCURRENCE; } } 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=1701516&r1=1701515&r2=1701516&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] Sun Sep 6 19:10:30 2015 @@ -121,37 +121,6 @@ public class DefaultParameterDescriptor< private final T defaultValue; /** - * Constructs a new object in which every attributes are set to a null value. - * <strong>This is not a valid object.</strong> This constructor is strictly - * reserved to JAXB, which will assign values to the fields using reflexion. - * - * <p>This constructor fetches the value class and the unit of measurement from the enclosing - * {@link DefaultParameterValue}, if presents, because those information are not presents in GML. - * They are GeoAPI additions.</p> - */ - @SuppressWarnings("unchecked") - private DefaultParameterDescriptor() { - final PropertyType<?,?> wrapper = Context.getWrapper(Context.current()); - if (wrapper instanceof CC_OperationParameter) { - final CC_OperationParameter param = (CC_OperationParameter) wrapper; - /* - * This unsafe cast would be forbidden if this constructor was public or used in any context where the - * user can choose the value of <T>. But this constructor should be invoked only during unmarshalling, - * after the creation of the ParameterValue (this is the reverse creation order than what we normally - * do through the public API). The 'valueClass' should be compatible with DefaultParameterValue.value, - * and the parameterized type visible to the user should be only <?>. - */ - valueClass = (Class) param.valueClass; - valueDomain = param.valueDomain; - } else { - valueClass = null; - valueDomain = null; - } - validValues = null; - defaultValue = null; - } - - /** * Constructs a descriptor from the given properties. The properties map is given unchanged to the * {@linkplain AbstractParameterDescriptor#AbstractParameterDescriptor(Map, int, int) super-class constructor}. * The following table is a reminder of main (not all) properties: @@ -537,4 +506,50 @@ public class DefaultParameterDescriptor< protected long computeHashCode() { return Arrays.deepHashCode(new Object[] {valueClass, valueDomain, defaultValue}) + super.computeHashCode(); } + + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + + /** + * Constructs a new object in which every attributes are set to a null value. + * <strong>This is not a valid object.</strong> This constructor is strictly + * reserved to JAXB, which will assign values to the fields using reflexion. + * + * <p>This constructor fetches the value class and the unit of measurement from the enclosing + * {@link DefaultParameterValue}, if presents, because those information are not presents in GML. + * They are GeoAPI additions.</p> + */ + @SuppressWarnings("unchecked") + private DefaultParameterDescriptor() { + final PropertyType<?,?> wrapper = Context.getWrapper(Context.current()); + if (wrapper instanceof CC_OperationParameter) { + final CC_OperationParameter param = (CC_OperationParameter) wrapper; + /* + * This unsafe cast would be forbidden if this constructor was public or used in any context where the + * user can choose the value of <T>. But this constructor should be invoked only during unmarshalling, + * after the creation of the ParameterValue (this is the reverse creation order than what we normally + * do through the public API). The 'valueClass' should be compatible with DefaultParameterValue.value, + * and the parameterized type visible to the user should be only <?>. + */ + valueClass = (Class) param.valueClass; + valueDomain = param.valueDomain; + } else { + valueClass = null; + valueDomain = null; + } + validValues = null; + defaultValue = null; + } } 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=1701516&r1=1701515&r2=1701516&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 Sep 6 19:10:30 2015 @@ -21,7 +21,6 @@ import java.util.Set; import java.util.List; import java.util.HashSet; import java.util.Collections; -import java.util.LinkedHashMap; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -31,9 +30,10 @@ import org.opengis.parameter.ParameterDe import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.parameter.InvalidParameterNameException; +import org.apache.sis.internal.jaxb.referencing.CC_OperationParameterGroup; +import org.apache.sis.internal.referencing.ReferencingUtilities; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.internal.util.UnmodifiableArrayList; -import org.apache.sis.util.collection.Containers; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ComparisonMode; @@ -110,15 +110,6 @@ public class DefaultParameterDescriptorG private List<GeneralParameterDescriptor> descriptors; /** - * Constructs a new object in which every attributes are set to a null value or an empty list. - * <strong>This is not a valid object.</strong> This constructor is strictly reserved to JAXB, - * which will assign values to the fields using reflexion. - */ - private DefaultParameterDescriptorGroup() { - descriptors = Collections.emptyList(); - } - - /** * Constructs a parameter group from a set of properties. The properties map is given unchanged to the * {@linkplain AbstractParameterDescriptor#AbstractParameterDescriptor(Map, int, int) super-class constructor}. * The following table is a reminder of main (not all) properties: @@ -419,7 +410,30 @@ public class DefaultParameterDescriptorG return super.computeHashCode() + descriptors.hashCode(); } - // ---- XML SUPPORT ---------------------------------------------------- + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Constructs a new object in which every attributes are set to a null value or an empty list. + * <strong>This is not a valid object.</strong> This constructor is strictly reserved to JAXB + * and to {@link DefaultParameterValueGroup}, which will assign values later. + * + * @see #setDescriptors(GeneralParameterDescriptor[]) + */ + DefaultParameterDescriptorGroup() { + descriptors = Collections.emptyList(); + } /** * Invoked by JAXB for getting the parameters to marshal. @@ -431,35 +445,39 @@ public class DefaultParameterDescriptorG } /** - * Invoked by JAXB or by {@link DefaultParameterValueGroup} for setting the unmarshalled parameters. - * If parameters already exist, them this method computes the union of the two parameter collections - * with the new parameters having precedence over the old ones. + * Invoked by JAXB for setting the unmarshalled parameter descriptors. + */ + private void setDescriptors(final GeneralParameterDescriptor[] parameters) { + if (descriptors.isEmpty()) { + verifyNames(null, parameters); + descriptors = asList(parameters); + } else { + ReferencingUtilities.propertyAlreadySet(DefaultParameterValue.class, "setDescriptors", "parameter"); + } + } + + /** + * Merges the given parameter descriptors with the descriptors currently in this group. + * The descriptors are set twice during {@link DefaultParameterValueGroup} unmarshalling: * - * <div class="note"><b>Rational:</b> - * this method is invoked twice during {@link DefaultParameterValueGroup} unmarshalling: * <ol> - * <li>First, this method is invoked during unmarshalling of this {@code DefaultParameterDescriptorGroup}. + * <li>First, the descriptors are set during unmarshalling of this {@code DefaultParameterDescriptorGroup}. * But the value class of {@code ParameterDescriptor} components are unknown because this information * is not part of GML.</li> * <li>Next, this method is invoked during unmarshalling of the {@code DefaultParameterValueGroup} enclosing * element with the descriptors found inside the {@code ParameterValue} components. The later do have the * {@code valueClass} information, so we want to use them in replacement of descriptors of step 1.</li> * </ol> - * </div> + * + * @param fromValues Descriptors declared in the {@code ParameterValue} instances of a {@code ParameterValueGroup}. + * @param replacements An {@code IdentityHashMap} where to store the replacements that the caller needs + * to apply in the {@code GeneralParameterValue} instances. */ - final void setDescriptors(GeneralParameterDescriptor[] parameters) { - verifyNames(null, parameters); - if (!descriptors.isEmpty()) { - final Map<String,GeneralParameterDescriptor> union = - new LinkedHashMap<>(Containers.hashMapCapacity(descriptors.size())); - for (final GeneralParameterDescriptor p : descriptors) { - union.put(p.getName().getCode(), p); - } - for (final GeneralParameterDescriptor p : parameters) { - union.put(p.getName().getCode(), p); - } - parameters = union.values().toArray(new GeneralParameterDescriptor[union.size()]); - } - descriptors = asList(parameters); + final void merge(GeneralParameterDescriptor[] fromValues, + final Map<GeneralParameterDescriptor,GeneralParameterDescriptor> replacements) + { + fromValues = CC_OperationParameterGroup.merge(descriptors, fromValues, replacements); + verifyNames(null, fromValues); + descriptors = asList(fromValues); } } 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=1701516&r1=1701515&r2=1701516&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] Sun Sep 6 19:10:30 2015 @@ -161,13 +161,6 @@ public class DefaultParameterValue<T> ex private Unit<?> unit; /** - * Default constructor for JAXB only. The descriptor is initialized to {@code null}, - * but will be assigned a value after XML unmarshalling. - */ - private DefaultParameterValue() { - } - - /** * Creates a parameter value from the specified descriptor. * The value will be initialized to the default value, if any. * @@ -1002,7 +995,38 @@ public class DefaultParameterValue<T> ex formatter.hasContextualUnit(2); // In WKT2 } - // ---- XML SUPPORT ---------------------------------------------------- + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Default constructor for JAXB only. The descriptor is initialized to {@code null}, + * but will be assigned a value after XML unmarshalling. + */ + private DefaultParameterValue() { + } + + /** + * Invoked by JAXB at unmarshalling time. + * May also be invoked by {@link DefaultParameterValueGroup} if the descriptor as been completed + * with additional information provided in the {@code <gml:group>} element of a descriptor group. + * + * @see #getDescriptor() + */ + final void setDescriptor(final ParameterDescriptor<T> descriptor) { + this.descriptor = descriptor; + assert (value == null) || descriptor.getValueClass().isInstance(value) : this; + } /** * Invoked by JAXB for obtaining the object to marshal. @@ -1052,9 +1076,7 @@ public class DefaultParameterValue<T> ex */ @SuppressWarnings("unchecked") private void setXmlValue(Object xmlValue) { - if (ReferencingUtilities.canSetProperty(DefaultParameterValue.class, - "setXmlValue", "value", value != null || unit != null)) - { + if (value == null && unit == null) { if (xmlValue instanceof Measure) { final Measure measure = (Measure) xmlValue; xmlValue = measure.value; @@ -1085,16 +1107,8 @@ public class DefaultParameterValue<T> ex */ value = (T) xmlValue; } + } else { + ReferencingUtilities.propertyAlreadySet(DefaultParameterValue.class, "setXmlValue", "value"); } } - - /** - * Invoked by JAXB at unmarshalling time. - * - * @see #getDescriptor() - */ - private void setDescriptor(final ParameterDescriptor<T> descriptor) { - this.descriptor = descriptor; - assert (value == null) || descriptor.getValueClass().isInstance(value) : this; - } } 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=1701516&r1=1701515&r2=1701516&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 Sep 6 19:10:30 2015 @@ -18,7 +18,8 @@ package org.apache.sis.parameter; import java.util.List; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Map; +import java.util.IdentityHashMap; import java.io.Serializable; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlElement; @@ -132,13 +133,6 @@ public class DefaultParameterValueGroup private ParameterValueList values; /** - * Default constructor for JAXB only. The values list is initialized to {@code null}, - * but will be assigned a value after XML unmarshalling. - */ - private DefaultParameterValueGroup() { - } - - /** * Creates a parameter group from the specified descriptor. * * <p><b>Usage note:</b> {@code ParameterValueGroup} are usually not instantiated directly. Instead, consider @@ -504,7 +498,42 @@ public class DefaultParameterValueGroup ParameterFormat.print(this); } - // ---- XML SUPPORT ---------------------------------------------------- + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Default constructor for JAXB only. The values list is initialized to {@code null}, + * but will be assigned a value after XML unmarshalling. + */ + private DefaultParameterValueGroup() { + } + + /** + * Invoked by JAXB for setting the group parameter descriptor. Those parameter are redundant with + * the parameters associated to the values given to {@link #setValues(GeneralParameterValue[])}, + * except the the group identification (name, <i>etc.</i>) and for any optional parameters which + * were not present in the above {@code GeneralParameterValue} array. + * + * @see #getDescriptor() + */ + private void setDescriptor(final ParameterDescriptorGroup descriptor) { + if (values == null) { + values = new ParameterValueList(descriptor); + } else { + ReferencingUtilities.propertyAlreadySet(DefaultParameterValue.class, "setDescriptor", "group"); + } + } /** * Invoked by JAXB for getting the parameters to marshal. @@ -519,38 +548,56 @@ public class DefaultParameterValueGroup * Invoked by JAXB for setting the unmarshalled parameters. This method should be invoked last * (after {@link #setDescriptor(ParameterDescriptorGroup)}) even if the {@code parameterValue} * elements were first in the XML document. This is the case at least with the JAXB reference - * implementation. + * implementation, because the property type is an array (it would not work with a list). + * + * <p><b>Maintenance note:</b> the {@code "setValues"} method name is also hard-coded in + * {@link org.apache.sis.internal.jaxb.referencing.CC_GeneralOperationParameter} for logging purpose.</p> */ private void setValues(final GeneralParameterValue[] parameters) { - final GeneralParameterDescriptor[] descriptors = new GeneralParameterDescriptor[parameters.length]; - for (int i=0; i<descriptors.length; i++) { - descriptors[i] = parameters[i].getDescriptor(); - } - if (values == null) { + ParameterValueList addTo = values; + if (addTo == null) { // Should never happen, unless the XML document is invalid and does not have a 'group' element. - - } else { - // We known that the descriptor is an instance of our DefaultParameterDescriptorGroup - // implementation because this is what we declare to the JAXBContext and in adapters. - ((DefaultParameterDescriptorGroup) values.descriptor).setDescriptors(descriptors); - values.clear(); // Because references to parameter descriptors have changed. + addTo = new ParameterValueList(new DefaultParameterDescriptorGroup()); } - values.addAll(Arrays.asList(parameters)); + /* + * Merge the descriptors declared in the <gml:group> element with the descriptors given in each + * <gml:parameterValue> element. The implementation is known to be DefaultParameterDescriptorGroup + * because this is the type declared in the JAXBContext and in adapters. + */ + final Map<GeneralParameterDescriptor,GeneralParameterDescriptor> replacements = new IdentityHashMap<>(4); + ((DefaultParameterDescriptorGroup) addTo.descriptor).merge(getDescriptors(parameters), replacements); + addTo.clear(); // Because references to parameter descriptors have changed. + setValues(parameters, replacements, addTo); } /** - * Invoked by JAXB for setting the group parameter descriptor. Those parameter are redundant with - * the parameters associated to the values given to {@link #setValues(GeneralParameterValue[])}, - * except the the group identification (name, <i>etc.</i>) and for any optional parameters which - * were not present in the above {@code GeneralParameterValue} array. + * Appends all parameter values. In this process, we may need to update the descriptor of some values + * if those descriptors changed as a result of the above merge process. * - * @see #getDescriptor() + * @param parameters The parameters to add, or {@code null} for {@link #values}. + * @param replacements The replacements to apply in the {@code GeneralParameterValue} instances. + * @param addTo Where to store the new values. */ - private void setDescriptor(final ParameterDescriptorGroup descriptor) { - if (ReferencingUtilities.canSetProperty(DefaultParameterValue.class, - "setDescriptor", "group", values != null)) - { - values = new ParameterValueList(descriptor); + @SuppressWarnings({"unchecked", "AssignmentToCollectionOrArrayFieldFromParameter"}) + private void setValues(GeneralParameterValue[] parameters, + final Map<GeneralParameterDescriptor,GeneralParameterDescriptor> replacements, + final ParameterValueList addTo) + { + if (parameters == null) { + parameters = values.toArray(); + } + for (final GeneralParameterValue p : parameters) { + final GeneralParameterDescriptor replacement = replacements.get(p.getDescriptor()); + if (replacement != null) { + if (p instanceof DefaultParameterValue<?>) { + ((DefaultParameterValue<?>) p).setDescriptor((ParameterDescriptor) replacement); + } else if (p instanceof DefaultParameterValueGroup) { + ((DefaultParameterValueGroup) p).setValues(null, replacements, + new ParameterValueList((ParameterDescriptorGroup) replacement)); + } + } + addTo.add(p); } + values = addTo; } } 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=1701516&r1=1701515&r2=1701516&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] Sun Sep 6 19:10:30 2015 @@ -34,6 +34,7 @@ import org.opengis.metadata.Identifier; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.resources.Errors; +import org.apache.sis.referencing.IdentifiedObjects; /** @@ -238,14 +239,15 @@ final class ParameterValueList extends A * parameter name was not found, or the parameter descriptor does not matches. */ final Identifier name = desc.getName(); + final String code = name.getCode(); for (final GeneralParameterDescriptor descriptor : descriptors) { - if (name.equals(descriptor.getName())) { + if (IdentifiedObjects.isHeuristicMatchForName(descriptor, code)) { throw new IllegalArgumentException(Errors.format( Errors.Keys.MismatchedParameterDescriptor_1, name)); } } throw new InvalidParameterNameException(Errors.format(Errors.Keys.ParameterNotFound_2, - Verifier.getDisplayName(descriptor), name), name.getCode()); + Verifier.getDisplayName(descriptor), name), code); } } @@ -310,6 +312,14 @@ final class ParameterValueList extends A } /** + * Returns the parameters in an array. + */ + @Override + public GeneralParameterValue[] toArray() { + return Arrays.copyOf(values, size); + } + + /** * Returns a string representation of this list. */ @Override Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -220,6 +220,35 @@ public abstract class Parameters impleme } /** + * Returns the descriptors of the given parameters, in the same order. + * Special cases: + * + * <ul> + * <li>If the given array is {@code null}, then this method returns {@code null}. + * <li>If an element of the given array is {@code null}, then the corresponding + * element of the returned array is also {@code null}.</li> + * </ul> + * + * @param parameters The parameter values from which to get the descriptors, or {@code null}. + * @return The descriptors of the given parameter values, or {@code null} if the {@code parameters} argument was null. + * + * @since 0.6 + */ + public static GeneralParameterDescriptor[] getDescriptors(final GeneralParameterValue... parameters) { + if (parameters == null) { + return null; + } + final GeneralParameterDescriptor[] descriptors = new GeneralParameterDescriptor[parameters.length]; + for (int i=0; i<parameters.length; i++) { + final GeneralParameterValue p = parameters[i]; + if (p != null) { + descriptors[i] = p.getDescriptor(); + } + } + return descriptors; + } + + /** * Gets the parameter name as an instance of {@code MemberName}. * This method performs the following checks: * Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -29,6 +29,7 @@ import javax.xml.bind.annotation.XmlType import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; import org.opengis.util.GenericName; @@ -223,16 +224,6 @@ public class AbstractIdentifiedObject ex private transient int hashCode; /** - * Constructs a new object in which every attributes are set to a null value. - * <strong>This is not a valid object.</strong> This constructor is strictly - * reserved to JAXB, which will assign values to the fields using reflexion. - */ - AbstractIdentifiedObject() { - remarks = null; - deprecated = false; - } - - /** * Constructs an object from the given properties. Keys are strings from the table below. * The map given in argument shall contain an entry at least for the * {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} or @@ -500,6 +491,7 @@ public class AbstractIdentifiedObject ex * since it may depends on the marshalling context.</p> */ @XmlID + @XmlSchemaType(name = "ID") @XmlAttribute(name = "id", namespace = Namespaces.GML, required = true) @XmlJavaTypeAdapter(CollapsedStringAdapter.class) final String getID() { @@ -532,144 +524,6 @@ public class AbstractIdentifiedObject ex } /** - * Returns a single element from the {@code Set<Identifier>} collection, or {@code null} if none. - * We have to define this method because ISO 19111 defines the {@code identifiers} property as a collection - * while GML 3.2 defines it as a singleton. - * - * <p>This method searches for the following identifiers, in preference order:</p> - * <ul> - * <li>The first identifier having a code that begin with {@code "urn:"}.</li> - * <li>The first identifier having a code that begin with {@code "http:"}.</li> - * <li>The first identifier, converted to the {@code "urn:} syntax if possible.</li> - * </ul> - */ - @XmlElement(name = "identifier") - final Code getIdentifier() { - return Code.forIdentifiedObject(getClass(), identifiers); - } - - /** - * Invoked by JAXB at unmarshalling time for setting the identifier. - */ - private void setIdentifier(final Code identifier) { - if (identifier != null) { - final Identifier id = identifier.getIdentifier(); - if (id != null && ReferencingUtilities.canSetProperty(AbstractIdentifiedObject.class, - "setIdentifier", "identifier", identifiers != null)) - { - identifiers = Collections.singleton(id); - } - } - } - - /** - * A writable view over the {@linkplain AbstractIdentifiedObject#getName() name} of the enclosing object followed - * by all {@linkplain AbstractIdentifiedObject#getAlias() aliases} which are instance of {@link Identifier}. - * Used by JAXB only at (un)marshalling time because GML merges the name and aliases in a single {@code <gml:name>} - * property. - */ - private final class Names extends AbstractCollection<Identifier> { - /** - * Invoked by JAXB before to write in the collection at unmarshalling time. - * Do nothing since our object is already empty. - */ - @Override - public void clear() { - } - - /** - * Returns the number of name and aliases that are instance of {@link Identifier}. - */ - @Override - public int size() { - return NameIterator.count(AbstractIdentifiedObject.this); - } - - /** - * Returns an iterator over the name and aliases that are instance of {@link Identifier}. - */ - @Override - public Iterator<Identifier> iterator() { - return new NameIterator(AbstractIdentifiedObject.this); - } - - /** - * Invoked by JAXB at unmarshalling time for each identifier. The first identifier will be taken - * as the name and all other identifiers (if any) as aliases. - * - * <p>Some (but not all) JAXB implementations never invoke setter method for collections. - * Instead they invoke {@link AbstractIdentifiedObject#getNames()} and add directly the identifiers - * in the returned collection. Consequently this method must writes directly in the enclosing object. - * See <a href="https://java.net/jira/browse/JAXB-488">JAXB-488</a> for more information.</p> - */ - @Override - public boolean add(final Identifier id) { - addName(id); - return true; - } - } - - /** - * Implementation of {@link Names#add(Identifier)}, defined in the enclosing class - * for access to private fields without compiler-generated bridge methods. - */ - final void addName(final Identifier id) { - if (name == NilReferencingObject.UNNAMED) { - name = id; - } else { - /* - * Our Code and RS_Identifier implementations should always create NamedIdentifier instance, - * so the 'instanceof' check should not be necessary. But we do a paranoiac check anyway. - */ - final GenericName n = id instanceof GenericName ? (GenericName) id : new NamedIdentifier(id); - if (alias == null) { - alias = Collections.singleton(n); - } else { - /* - * This implementation is inefficient since each addition copies the array, but we rarely - * have more than two aliases. This implementation is okay for a small number of aliases - * and ensures that the enclosing AbstractIdentifiedObject is unmodifiable except by this - * add(…) method. - * - * Note about alternative approaches - * --------------------------------- - * An alternative approach could be to use an ArrayList and replace it by an unmodifiable - * list only after unmarshalling (using an afterUnmarshal(Unmarshaller, Object) method), - * but we want to avoid Unmarshaller dependency (for reducing classes loading for users - * who are not interrested in XML) and it may actually be less efficient for the vast - * majority of cases where there is less than 3 aliases. - */ - final int size = alias.size(); - final GenericName[] names = alias.toArray(new GenericName[size + 1]); - names[size] = n; - alias = UnmodifiableArrayList.wrap(names); - } - } - } - - /** - * Returns the {@link #name} and all aliases which are also instance of {@link Identifier}. - * The later happen often in SIS implementation since many aliases are instance of {@link NamedIdentifier}. - * - * <p>The returned collection is <cite>live</cite>: adding elements in that collection will modify this - * {@code AbstractIdentifiedObject} instance. This is needed for unmarshalling with JAXB and should not - * be used in other context.</p> - * - * <div class="section">Why there is no <code>setNames(…)</code> method</div> - * Some JAXB implementations never invoke setter method for collections. Instead they invoke the getter and - * add directly the identifiers in the returned collection. Whether JAXB will perform or not a final call to - * {@code setNames(…)} is JAXB-implementation dependent (JDK7 does but JDK6 and JDK8 early access do not). - * It seems a more portable approach (at least for JAXB reference implementations) to design our class - * without setter method, in order to have the same behavior on all supported JDK versions. - * - * @see <a href="https://java.net/jira/browse/JAXB-488">JAXB-488</a> - */ - @XmlElement(name = "name", required = true) - final Collection<Identifier> getNames() { - return new Names(); - } - - /** * Returns the primary name by which this object is identified. * * @return The primary name. @@ -1066,4 +920,168 @@ public class AbstractIdentifiedObject ex WKTUtilities.appendName(this, formatter, ElementKind.forType(getClass())); return null; } + + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Constructs a new object in which every attributes are set to a null value. + * <strong>This is not a valid object.</strong> This constructor is strictly + * reserved to JAXB, which will assign values to the fields using reflexion. + */ + AbstractIdentifiedObject() { + remarks = null; + deprecated = false; + } + + /** + * Returns a single element from the {@code Set<Identifier>} collection, or {@code null} if none. + * We have to define this method because ISO 19111 defines the {@code identifiers} property as a collection + * while GML 3.2 defines it as a singleton. + * + * <p>This method searches for the following identifiers, in preference order:</p> + * <ul> + * <li>The first identifier having a code that begin with {@code "urn:"}.</li> + * <li>The first identifier having a code that begin with {@code "http:"}.</li> + * <li>The first identifier, converted to the {@code "urn:} syntax if possible.</li> + * </ul> + */ + @XmlElement(name = "identifier") + final Code getIdentifier() { + return Code.forIdentifiedObject(getClass(), identifiers); + } + + /** + * Invoked by JAXB at unmarshalling time for setting the identifier. + */ + private void setIdentifier(final Code identifier) { + if (identifiers == null) { + if (identifier != null) { + final Identifier id = identifier.getIdentifier(); + if (id != null) { + identifiers = Collections.singleton(id); + } + } + } else { + ReferencingUtilities.propertyAlreadySet(AbstractIdentifiedObject.class, "setIdentifier", "identifier"); + } + } + + /** + * Returns the {@link #name} and all aliases which are also instance of {@link Identifier}. + * The later happen often in SIS implementation since many aliases are instance of {@link NamedIdentifier}. + * + * <p>The returned collection is <cite>live</cite>: adding elements in that collection will modify this + * {@code AbstractIdentifiedObject} instance. This is needed for unmarshalling with JAXB and should not + * be used in other context.</p> + * + * <div class="section">Why there is no <code>setNames(…)</code> method</div> + * Some JAXB implementations never invoke setter method for collections. Instead they invoke the getter and + * add directly the identifiers in the returned collection. Whether JAXB will perform or not a final call to + * {@code setNames(…)} is JAXB-implementation dependent (JDK7 does but JDK6 and JDK8 early access do not). + * It seems a more portable approach (at least for JAXB reference implementations) to design our class + * without setter method, in order to have the same behavior on all supported JDK versions. + * + * @see <a href="https://java.net/jira/browse/JAXB-488">JAXB-488</a> + */ + @XmlElement(name = "name", required = true) + final Collection<Identifier> getNames() { + return new Names(); + } + + /** + * A writable view over the {@linkplain AbstractIdentifiedObject#getName() name} of the enclosing object followed + * by all {@linkplain AbstractIdentifiedObject#getAlias() aliases} which are instance of {@link Identifier}. + * Used by JAXB only at (un)marshalling time because GML merges the name and aliases in a single {@code <gml:name>} + * property. + */ + private final class Names extends AbstractCollection<Identifier> { + /** + * Invoked by JAXB before to write in the collection at unmarshalling time. + * Do nothing since our object is already empty. + */ + @Override + public void clear() { + } + + /** + * Returns the number of name and aliases that are instance of {@link Identifier}. + */ + @Override + public int size() { + return NameIterator.count(AbstractIdentifiedObject.this); + } + + /** + * Returns an iterator over the name and aliases that are instance of {@link Identifier}. + */ + @Override + public Iterator<Identifier> iterator() { + return new NameIterator(AbstractIdentifiedObject.this); + } + + /** + * Invoked by JAXB at unmarshalling time for each identifier. The first identifier will be taken + * as the name and all other identifiers (if any) as aliases. + * + * <p>Some (but not all) JAXB implementations never invoke setter method for collections. + * Instead they invoke {@link AbstractIdentifiedObject#getNames()} and add directly the identifiers + * in the returned collection. Consequently this method must writes directly in the enclosing object. + * See <a href="https://java.net/jira/browse/JAXB-488">JAXB-488</a> for more information.</p> + */ + @Override + public boolean add(final Identifier id) { + addName(id); + return true; + } + } + + /** + * Implementation of {@link Names#add(Identifier)}, defined in the enclosing class + * for access to private fields without compiler-generated bridge methods. + */ + final void addName(final Identifier id) { + if (name == NilReferencingObject.UNNAMED) { + name = id; + } else { + /* + * Our Code and RS_Identifier implementations should always create NamedIdentifier instance, + * so the 'instanceof' check should not be necessary. But we do a paranoiac check anyway. + */ + final GenericName n = id instanceof GenericName ? (GenericName) id : new NamedIdentifier(id); + if (alias == null) { + alias = Collections.singleton(n); + } else { + /* + * This implementation is inefficient since each addition copies the array, but we rarely + * have more than two aliases. This implementation is okay for a small number of aliases + * and ensures that the enclosing AbstractIdentifiedObject is unmodifiable except by this + * add(…) method. + * + * Note about alternative approaches + * --------------------------------- + * An alternative approach could be to use an ArrayList and replace it by an unmodifiable + * list only after unmarshalling (using an afterUnmarshal(Unmarshaller, Object) method), + * but we want to avoid Unmarshaller dependency (for reducing classes loading for users + * who are not interrested in XML) and it may actually be less efficient for the vast + * majority of cases where there is less than 3 aliases. + */ + final int size = alias.size(); + final GenericName[] names = alias.toArray(new GenericName[size + 1]); + names[size] = n; + alias = UnmodifiableArrayList.wrap(names); + } + } + } } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -89,16 +89,6 @@ public class AbstractReferenceSystem ext private final InternationalString scope; /** - * Constructs a new object in which every attributes are set to a null value. - * <strong>This is not a valid object.</strong> This constructor is strictly - * reserved to JAXB, which will assign values to the fields using reflexion. - */ - AbstractReferenceSystem() { - domainOfValidity = null; - scope = null; - } - - /** * Constructs a reference system from the given properties. * The properties given in argument follow the same rules than for the * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}. @@ -252,4 +242,28 @@ public class AbstractReferenceSystem ext protected long computeHashCode() { return super.computeHashCode() + Objects.hash(domainOfValidity, scope); } + + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Constructs a new object in which every attributes are set to a null value. + * <strong>This is not a valid object.</strong> This constructor is strictly + * reserved to JAXB, which will assign values to the fields using reflexion. + */ + AbstractReferenceSystem() { + domainOfValidity = null; + scope = null; + } } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java?rev=1701516&r1=1701515&r2=1701516&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractCRS.java [UTF-8] Sun Sep 6 19:10:30 2015 @@ -122,15 +122,6 @@ public class AbstractCRS extends Abstrac private transient Map<AxesConvention,AbstractCRS> forConvention; /** - * Constructs a new object in which every attributes are set to a null value. - * <strong>This is not a valid object.</strong> This constructor is strictly - * reserved to JAXB, which will assign values to the fields using reflexion. - */ - AbstractCRS() { - super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE); - } - - /** * Creates a coordinate reference system from the given properties and coordinate system. * The properties given in argument follow the same rules than for the * {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) super-class constructor}. @@ -290,47 +281,6 @@ public class AbstractCRS extends Abstrac } /** - * Sets the coordinate system to the given value. This method is invoked only by JAXB at - * unmarshalling time and can be invoked only if the coordinate system has never been set. - * - * <div class="note"><b>Implementation note:</b> - * It was easy to put JAXB annotations directly on datum fields in subclasses because each CRS type - * can be associated to only one datum type. But we do not have this convenience for coordinate systems, - * where the same CRS may accept different kinds of CS. In GML, the different kinds of CS are marshalled - * as different XML elements. The usual way to handle such {@code <xs:choice>} with JAXB is to annotate - * a single method like below: - * - * {@preformat java - * @Override - * @XmlElements({ - * @XmlElement(name = "cartesianCS", type = DefaultCartesianCS.class), - * @XmlElement(name = "affineCS", type = DefaultAffineCS.class), - * @XmlElement(name = "cylindricalCS", type = DefaultCylindricalCS.class), - * @XmlElement(name = "linearCS", type = DefaultLinearCS.class), - * @XmlElement(name = "polarCS", type = DefaultPolarCS.class), - * @XmlElement(name = "sphericalCS", type = DefaultSphericalCS.class), - * @XmlElement(name = "userDefinedCS", type = DefaultUserDefinedCS.class) - * }) - * public CoordinateSystem getCoordinateSystem() { - * return super.getCoordinateSystem(); - * } - * } - * - * However our attempts to apply this approach have not been conclusive. - * For an unknown reason, the unmarshalled CS object was empty.</div> - * - * @param name The property name, used only in case of error message to format. - * @throws IllegalStateException If the coordinate system has already been set. - */ - final void setCoordinateSystem(final String name, final CoordinateSystem cs) { - if (cs != null && ReferencingUtilities.canSetProperty(AbstractCRS.class, - "setCoordinateSystem", name, coordinateSystem != null)) - { - coordinateSystem = cs; - } - } - - /** * Returns the existing CRS for the given convention, or {@code null} if none. */ final AbstractCRS getCached(final AxesConvention convention) { @@ -537,4 +487,45 @@ public class AbstractCRS extends Abstrac formatter.restoreContextualUnit(unit, oldUnit); formatter.newLine(); // For writing the ID[…] element on its own line. } + + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + //////// //////// + //////// XML support with JAXB //////// + //////// //////// + //////// The following methods are invoked by JAXB using reflection (even if //////// + //////// they are private) or are helpers for other methods invoked by JAXB. //////// + //////// Those methods can be safely removed if Geographic Markup Language //////// + //////// (GML) support is not needed. //////// + //////// //////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Constructs a new object in which every attributes are set to a null value. + * <strong>This is not a valid object.</strong> This constructor is strictly + * reserved to JAXB, which will assign values to the fields using reflexion. + */ + AbstractCRS() { + super(org.apache.sis.internal.referencing.NilReferencingObject.INSTANCE); + } + + /** + * Sets the coordinate system to the given value. + * This method is indirectly invoked by JAXB at unmarshalling time. + * + * @param name The property name, used only in case of error message to format. Can be null for auto-detect. + * @throws IllegalStateException If the coordinate system has already been set. + */ + final void setCoordinateSystem(String name, final CoordinateSystem cs) { + if (coordinateSystem == null) { + coordinateSystem = cs; + } else { + if (name == null) { + name = String.valueOf(ReferencingUtilities.toPropertyName(CoordinateSystem.class, cs.getClass())); + } + ReferencingUtilities.propertyAlreadySet(AbstractCRS.class, "setCoordinateSystem", name); + } + } }
