You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2022/11/15 09:23:24 UTC
[sis] 01/04: Bug fixes related to unmarshalling of GML documents. Those bugs were identified by OGC TestBed 18 D025 scenario.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git
commit eef4dfff3178e743e6baf2174d75ea72327fb77b
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Oct 28 17:48:13 2022 +0200
Bug fixes related to unmarshalling of GML documents.
Those bugs were identified by OGC TestBed 18 D025 scenario.
---
.../sis/internal/jaxb/IdentifierMapAdapter.java | 30 ++++++++-
.../org/apache/sis/internal/jaxb/package-info.java | 2 +-
.../java/org/apache/sis/xml/NilObjectHandler.java | 2 +-
.../java/org/apache/sis/xml/ReferenceResolver.java | 8 +--
.../jaxb/referencing/CC_OperationParameter.java | 54 +++++++++++-----
.../internal/jaxb/referencing/package-info.java | 2 +-
.../sis/internal/referencing/AxisDirections.java | 54 +++++++++++++---
.../sis/parameter/DefaultParameterDescriptor.java | 74 ++++++++++++++++++----
.../sis/parameter/DefaultParameterValue.java | 14 ++--
.../sis/parameter/DefaultParameterValueGroup.java | 8 +--
.../org/apache/sis/parameter/ParameterFormat.java | 3 +-
.../java/org/apache/sis/parameter/Parameters.java | 9 ++-
.../sis/parameter/UnmodifiableParameterValue.java | 13 ++--
.../sis/referencing/cs/CoordinateSystems.java | 7 ++
.../org/apache/sis/referencing/cs/Normalizer.java | 26 ++++----
.../operation/AbstractSingleOperation.java | 6 ++
.../operation/DefaultConcatenatedOperation.java | 2 +-
.../operation/DefaultOperationMethod.java | 26 +++++---
.../storage/csv/MovingFeatureIterator.java | 3 +-
.../org/apache/sis/internal/storage/csv/Store.java | 2 +-
20 files changed, 260 insertions(+), 85 deletions(-)
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
index 95dcf08a4c..2dc9ae3436 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
@@ -18,8 +18,10 @@ package org.apache.sis.internal.jaxb;
import java.net.URI;
import java.util.Set;
+import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
@@ -81,7 +83,7 @@ import static org.apache.sis.util.collection.Containers.hashMapCapacity;
* This class is thread safe if the underlying identifier collection is thread safe.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.3
*
* @see org.apache.sis.xml.IdentifiedObject
*
@@ -101,7 +103,10 @@ public class IdentifierMapAdapter extends AbstractMap<Citation,String> implement
/**
* The identifiers to wrap in a map view.
+ *
+ * @see #getIdentifiers(Class)
*/
+ @SuppressWarnings("serial") // Not statically typed as Serializable.
public final Collection<Identifier> identifiers;
/**
@@ -113,6 +118,29 @@ public class IdentifierMapAdapter extends AbstractMap<Citation,String> implement
this.identifiers = identifiers;
}
+ /**
+ * Returns the identifiers as a collection of the specified type.
+ * The given type is the return type of the {@code getIdentifiers()} method which is delegating to this method.
+ * The returned collection is modifiable only if {@link #identifiers} is already of the desired type.
+ * This is the case for {@link org.apache.sis.metadata.iso.ISOMetadata#getIdentifiers()},
+ * which is the API for which we want modifiable collections.
+ *
+ * @param type {@code Collection.class}, {@code List.class} or {@code Set.class}.
+ * @return the identifiers as a collection of the specified type.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public final Collection<Identifier> getIdentifiers(final Class<?> type) {
+ if (!type.isInstance(identifiers)) {
+ if (type.isAssignableFrom(Set.class)) {
+ return new HashSet<>(identifiers); // TODO: use Set.copyOf in JDK10.
+ }
+ if (type.isAssignableFrom(List.class)) {
+ return new ArrayList<>(identifiers); // TODO: use List.copyOf in JDK10.
+ }
+ }
+ return identifiers;
+ }
+
/**
* If the given authority is a special case, returns its {@link NonMarshalledAuthority} integer enum.
* Otherwise returns -1. See javadoc for more information about special cases.
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java
index 24080ec8e7..ba864f8ea2 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java
@@ -35,7 +35,7 @@
* @author Cédric Briançon (Geomatys)
* @author Cullen Rombach (Image Matters)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.3
* @since 0.3
* @module
*/
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java
index bf4ce28745..4cb46aeeb6 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java
@@ -134,7 +134,7 @@ final class NilObjectHandler implements InvocationHandler {
}
case "getIdentifiers": {
return (attribute instanceof IdentifierMapAdapter) ?
- ((IdentifierMapAdapter) attribute).identifiers : null;
+ ((IdentifierMapAdapter) attribute).getIdentifiers(method.getReturnType()) : null;
}
case "toString": {
return Strings.bracket(getInterface(proxy), attribute);
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java
index 11b8ee14f8..cca68226f5 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java
@@ -68,8 +68,8 @@ public class ReferenceResolver {
* <li>{@link IdentifiedObject#getIdentifierMap()} will return a {@link java.util.Map}
* view over the given identifiers.</li>
* <li>All other methods except the ones inherited from the {@link Object} class will return
- * an empty collection, an empty array, {@code null}, {@link Double#NaN NaN}, 0 or
- * {@code false}, depending on the method return type.</li>
+ * an empty collection, an empty array, {@code null}, {@link Double#NaN}, 0 or {@code false},
+ * depending on the method return type.</li>
* </ul>
*
* @param <T> the compile-time type of the {@code type} argument.
@@ -135,10 +135,10 @@ public class ReferenceResolver {
return type.cast(object);
} else {
final short key;
- final Object args;
+ final Object[] args;
if (object == null) {
key = Errors.Keys.NotABackwardReference_1;
- args = id;
+ args = new Object[] {id};
} else {
key = Errors.Keys.UnexpectedTypeForReference_3;
args = new Object[] {id, type, object.getClass()};
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java
index a00d2a31f0..7970ef1f42 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java
@@ -37,7 +37,7 @@ import org.apache.sis.parameter.DefaultParameterDescriptor;
* infer it from the actual value.</p>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.3
* @since 0.6
* @module
*/
@@ -118,6 +118,40 @@ public final class CC_OperationParameter extends PropertyType<CC_OperationParame
metadata = parameter;
}
+ /**
+ * Returns the base class of parameter values, or {@code null} if unknown.
+ * This method assumes that the parameter value does not yet have a descriptor
+ * (which happens at GML unmarshalling time) and that the type must be derived
+ * from the actual value.
+ *
+ * @param param the parameter from which to get the value class.
+ * @return base class of values, or {@code null} if unknown.
+ */
+ public static Class<?> valueClass(final ParameterValue<?> param) {
+ final Object value = param.getValue();
+ return (value != null) ? value.getClass() : null;
+ }
+
+ /**
+ * Saves the unit of measurement in a boundless range. This method should be invoked only when
+ * {@link #valueClass} is {@link Double} or {@code double[]}. It is the case in a well-formed GML.
+ *
+ * @param param the parameter from which to get the unit of measurement.
+ * @return unit of measurement wrapped in a boundless range, or {@code null} if none.
+ */
+ public static MeasurementRange<?> valueDomain(final ParameterValue<?> param) {
+ Unit<?> unit = param.getUnit();
+ if (unit == null) {
+ return null;
+ }
+ unit = unit.getSystemUnit();
+ if (Units.RADIAN.equals(unit)) {
+ unit = Units.DEGREE;
+ }
+ return MeasurementRange.create(Double.NEGATIVE_INFINITY, false,
+ Double.POSITIVE_INFINITY, false, unit);
+ }
+
/**
* Invoked by JAXB during unmarshalling of the enclosing {@code <gml:OperationParameter>},
* before the child {@link DefaultParameterDescriptor}. This method stores the class and
@@ -129,21 +163,9 @@ public final class CC_OperationParameter extends PropertyType<CC_OperationParame
*/
private void beforeUnmarshal(final Unmarshaller unmarshaller, final Object parent) {
if (parent instanceof ParameterValue<?>) {
- final Object value = ((ParameterValue<?>) parent).getValue();
- if (value != null) {
- valueClass = value.getClass();
- Unit<?> unit = ((ParameterValue<?>) parent).getUnit();
- if (unit != null) {
- unit = unit.getSystemUnit();
- if (Units.RADIAN.equals(unit)) {
- unit = Units.DEGREE;
- }
- assert (valueClass == Double.class) || (valueClass == double[].class) : valueClass;
- valueDomain = MeasurementRange.create(Double.NEGATIVE_INFINITY, false,
- Double.POSITIVE_INFINITY, false, unit);
- }
- Context.setWrapper(Context.current(), this);
- }
+ valueClass = valueClass ((ParameterValue<?>) parent);
+ valueDomain = valueDomain((ParameterValue<?>) parent);
+ Context.setWrapper(Context.current(), this);
}
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java
index 385610001b..d081072712 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java
@@ -24,7 +24,7 @@
* @author Guilhem Legal (Geomatys)
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.3
*
* @see javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter
*
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
index 32d5e1cab3..05326fc7c9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
@@ -44,7 +44,7 @@ import static org.apache.sis.util.CharSequences.*;
* Utilities methods related to {@link AxisDirection}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.3
* @since 0.4
* @module
*/
@@ -76,17 +76,28 @@ public final class AxisDirections extends Static {
private static final int LAST_ORDINAL = DISPLAY_DOWN.ordinal();
/**
- * Distance from the origin in a polar coordinate system.
- * Specified in ISO 19162 but not yet in ISO 19111.
+ * Forward direction.
+ * For an observer at the centre of the object this is will be towards its front, bow or nose.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
*
- * @since 0.7
+ * @since 1.3
*/
- @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=UNSPECIFIED)
- public static final AxisDirection AWAY_FROM = AxisDirection.valueOf("AWAY_FROM");
+ @UML(identifier="forward", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection FORWARD = AxisDirection.valueOf("FORWARD");
+
+ /**
+ * Starboard direction.
+ * For an observer at the centre of the object this will be towards its right.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ *
+ * @since 1.3
+ */
+ @UML(identifier="starboard", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection STARBOARD = AxisDirection.valueOf("STARBOARD");
/**
* Direction of geographic angles (bearing).
- * Specified in ISO 19162 but not yet in ISO 19111.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
*
* @since 0.7
*/
@@ -95,13 +106,22 @@ public final class AxisDirections extends Static {
/**
* Direction of arithmetic angles. Used in polar coordinate systems.
- * Specified in ISO 19162 but not yet in ISO 19111.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
*
* @since 0.7
*/
@UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=UNSPECIFIED)
public static final AxisDirection COUNTER_CLOCKWISE = AxisDirection.valueOf("COUNTER_CLOCKWISE");
+ /**
+ * Distance from the origin in a polar coordinate system.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ *
+ * @since 0.7
+ */
+ @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection AWAY_FROM = AxisDirection.valueOf("AWAY_FROM");
+
/**
* For each direction, the opposite direction.
* This map shall be immutable after construction.
@@ -345,6 +365,20 @@ public final class AxisDirections extends Static {
return ordinal >= COLUMN_POSITIVE.ordinal() && ordinal <= ROW_NEGATIVE.ordinal();
}
+ /**
+ * Arithmetic angle between forward/aft/port/starboard directions only.
+ * This is the angle as viewed from above the vehicle.
+ *
+ * @param source the start direction.
+ * @param target the final direction.
+ * @return the angle as a multiple of 90°, or {@link Integer#MIN_VALUE} if none.
+ */
+ public static int angleForVehicle(final AxisDirection source, final AxisDirection target) {
+ if (source == STARBOARD && target == FORWARD) return +1;
+ if (source == FORWARD && target == STARBOARD) return -1;
+ return Integer.MIN_VALUE;
+ }
+
/**
* Angle between geocentric directions only.
*
@@ -367,7 +401,7 @@ public final class AxisDirections extends Static {
}
/**
- * Angle between compass directions only (not for angle between direction along meridians).
+ * Arithmetic angle between compass directions only (not for angle between direction along meridians).
*
* @param source the start direction.
* @param target the final direction.
@@ -394,7 +428,7 @@ public final class AxisDirections extends Static {
}
/**
- * Angle between display directions only.
+ * Arithmetic angle between display directions only.
*
* @param source the start direction.
* @param target the final direction.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
index 67a62c0c3d..9fadb81fe2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java
@@ -65,7 +65,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureCanCast;
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.8
+ * @version 1.3
*
* @param <T> the type of elements to be returned by {@link DefaultParameterValue#getValue()}.
*
@@ -85,10 +85,12 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
/**
* The class that describe the type of parameter values.
+ * This field should be considered final after construction.
+ * This is declared non-final only for GML unmarshalling.
*
* @see #getValueClass()
*/
- private final Class<T> valueClass;
+ private Class<T> valueClass;
/**
* A set of valid values (usually from a {@linkplain CodeList code list})
@@ -110,9 +112,12 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
* <code>valueClass.{@linkplain Class#getComponentType() getComponentType()}</code>.</li>
* </ul>
*
+ * This field should be considered final after construction.
+ * This is declared non-final only for GML unmarshalling.
+ *
* @see #getValueDomain()
*/
- private final Range<?> valueDomain;
+ private Range<?> valueDomain;
/**
* The default value for the parameter, or {@code null}.
@@ -270,7 +275,6 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
*
* @see #castOrCopy(ParameterDescriptor)
*/
- @SuppressWarnings("unchecked")
protected DefaultParameterDescriptor(final ParameterDescriptor<T> descriptor) {
super(descriptor);
valueClass = descriptor.getValueClass();
@@ -374,6 +378,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
@Override
@SuppressWarnings("unchecked")
public Comparable<T> getMinimumValue() {
+ final Range<?> valueDomain = this.valueDomain;
return (valueDomain != null && valueDomain.getElementType() == valueClass)
? (Comparable<T>) valueDomain.getMinValue() : null;
}
@@ -392,6 +397,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
@Override
@SuppressWarnings("unchecked")
public Comparable<T> getMaximumValue() {
+ final Range<?> valueDomain = this.valueDomain;
return (valueDomain != null && valueDomain.getElementType() == valueClass)
? (Comparable<T>) valueDomain.getMaxValue() : null;
}
@@ -422,6 +428,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
*/
@Override
public Unit<?> getUnit() {
+ final Range<?> valueDomain = this.valueDomain;
return (valueDomain instanceof MeasurementRange<?>) ? ((MeasurementRange<?>) valueDomain).unit() : null;
}
@@ -491,10 +498,10 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
}
case STRICT: {
final DefaultParameterDescriptor<?> that = (DefaultParameterDescriptor<?>) object;
- return this.valueClass == that.valueClass &&
- Objects. equals(this.validValues, that.validValues) &&
- Objects. equals(this.valueDomain, that.valueDomain) &&
- Objects.deepEquals(this.defaultValue, that.defaultValue);
+ return valueClass == that.valueClass &&
+ Objects. equals(validValues, that.validValues) &&
+ Objects. equals(valueDomain, that.valueDomain) &&
+ Objects.deepEquals(defaultValue, that.defaultValue);
}
}
}
@@ -527,7 +534,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
/**
- * Constructs a new object in which every attributes are set to a null value.
+ * Constructs a new object in which attributes may be 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.
*
@@ -544,16 +551,57 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i
* 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,
+ * 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;
}
+
+ /**
+ * Invoked by {@link DefaultParameterValue} when the descriptor is set after the value at unmarshalling time.
+ * There is two scenarios in a valid GML document. The first scenario is when the descriptor is defined inside
+ * the parameter value element, like below. In such case, {@link #valueClass} is defined at construction time
+ * by {@link #DefaultParameterDescriptor()} because the value is before the descriptor.
+ *
+ * {@preformat xml
+ * <gml:ParameterValue>
+ * <gml:value uom="…">…</gml:value>
+ * <gml:operationParameter>
+ * <gml:OperationParameter>
+ * …
+ * </gml:OperationParameter>
+ * </gml:operationParameter>
+ * </gml:ParameterValue>
+ * }
+ *
+ * In the second scenario shows below, the descriptor was defined before the value and is referenced by a link.
+ * In that case, {@link #valueClass} is {@code null} the first time that this method is invoked. It may become
+ * non-null if the same parameter descriptor is reused for many parameter values.
+ *
+ * {@preformat xml
+ * <gml:ParameterValue>
+ * <gml:value uom="…">…</gml:value>
+ * <gml:operationParameter xlink:href="#LongitudeRotation"/>
+ * </gml:ParameterValue>
+ * }
+ *
+ * This method modifies the state of this class despite the fact that it should be immutable.
+ * It is okay because we are updating an instance created during GML unmarshalling, and that
+ * instance should not have been given to user yet.
+ *
+ * @param param the parameter value from which to infer the value type.
+ * @return the parameter descriptor to assign to the given parameter value.
+ */
+ @SuppressWarnings("unchecked")
+ final DefaultParameterDescriptor<T> setValueClass(final DefaultParameterValue<?> param) {
+ valueClass = (Class) Classes.findCommonClass(valueClass, CC_OperationParameter.valueClass(param));
+ if (valueDomain == null) {
+ valueDomain = CC_OperationParameter.valueDomain(param);
+ }
+ return this;
+ }
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
index 88e41ac63b..7defaf722e 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
@@ -118,7 +118,7 @@ import static org.apache.sis.util.Utilities.deepEquals;
* for modifying the behavior of all getter and setter methods.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.2
+ * @version 1.3
*
* @param <T> the type of the value stored in this parameter.
*
@@ -1129,14 +1129,18 @@ convert: if (componentType != null) {
/**
* Invoked by JAXB at unmarshalling time.
- * May also be invoked by {@link DefaultParameterValueGroup} if the descriptor as been completed
+ * May also be invoked by {@link DefaultParameterValueGroup} if the descriptor has 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;
+ final void setDescriptor(final ParameterDescriptor<T> p) {
+ descriptor = DefaultParameterDescriptor.castOrCopy(p).setValueClass(this);
+ /*
+ * A previous version was doing `assert descriptor.getValueClass().isInstance(value)`
+ * where the value class was inferred by `DefaultParameterDescriptor()`. But it does
+ * not always work, and the `NullPointerException` seems to be caught by JAXB.
+ */
}
/**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
index fe091065c6..cf7de09327 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java
@@ -559,7 +559,7 @@ scan: for (final GeneralParameterValue param : actual.values()) {
/**
* 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
+ * except for the group identification (name, <i>etc.</i>) and for any optional parameters which
* were not present in the above {@code GeneralParameterValue} array.
*
* @see #getDescriptor()
@@ -611,9 +611,9 @@ scan: for (final GeneralParameterValue param : actual.values()) {
* 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.
*
- * @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.
+ * @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.
*/
@SuppressWarnings({"unchecked", "AssignmentToCollectionOrArrayFieldFromParameter"})
private void setValues(GeneralParameterValue[] parameters,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
index a2912b2ef4..3f9e127424 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java
@@ -693,7 +693,8 @@ public class ParameterFormat extends TabularFormat<Object> {
/*
* Writes the values, each on its own line, together with their unit of measurement.
*/
- final byte alignment = Number.class.isAssignableFrom(valueClass) ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT;
+ final byte alignment = (valueClass != null && Number.class.isAssignableFrom(valueClass))
+ ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT;
table.setCellAlignment(alignment);
final int length = row.values.size();
for (int i=0; i<length; i++) {
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java
index e31bec5252..ccfade6322 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java
@@ -38,6 +38,7 @@ import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.Classes;
import org.apache.sis.util.Debug;
@@ -355,9 +356,15 @@ public abstract class Parameters implements ParameterValueGroup, Cloneable {
if (descriptor instanceof DefaultParameterDescriptor<?>) {
return ((DefaultParameterDescriptor<?>) descriptor).getValueDomain();
}
- final Class<?> valueClass = descriptor.getValueClass();
+ Class<?> valueClass = descriptor.getValueClass();
final Comparable<?> minimumValue = descriptor.getMinimumValue();
final Comparable<?> maximumValue = descriptor.getMaximumValue();
+ if (valueClass == null) { // Should never be null, but invalid objects exist.
+ valueClass = Classes.findCommonClass(Classes.getClass(minimumValue), Classes.getClass(maximumValue));
+ if (valueClass == null) {
+ valueClass = Object.class;
+ }
+ }
if ((minimumValue == null || valueClass.isInstance(minimumValue)) &&
(maximumValue == null || valueClass.isInstance(maximumValue)))
{
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
index a5ef86b428..0858703c2b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
@@ -49,7 +49,7 @@ import org.apache.sis.util.resources.Errors;
* </div>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.6
+ * @version 1.3
*
* @param <T> the type of the value stored in this parameter.
*
@@ -100,10 +100,13 @@ final class UnmodifiableParameterValue<T> extends DefaultParameterValue<T> {
@Override
public T getValue() {
T value = super.getValue();
- if (value instanceof Cloneable) try {
- value = getDescriptor().getValueClass().cast(Cloner.cloneIfPublic(value));
- } catch (CloneNotSupportedException e) {
- throw new UnsupportedOperationException(Errors.format(Errors.Keys.CloneNotSupported_1, value.getClass()), e);
+ if (value instanceof Cloneable) {
+ final Class<T> type = getDescriptor().getValueClass(); // May be null after GML unmarshalling.
+ if (type != null) try {
+ value = type.cast(Cloner.cloneIfPublic(value));
+ } catch (CloneNotSupportedException e) {
+ throw new UnsupportedOperationException(Errors.format(Errors.Keys.CloneNotSupported_1, value.getClass()), e);
+ }
}
return value;
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
index 1102f49e87..ea0b5bfe80 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java
@@ -239,6 +239,13 @@ public final class CoordinateSystems extends Static {
if (c != Integer.MIN_VALUE) {
return new Angle(c * 90);
}
+ /*
+ * Check for FORWARD, AFT, PORT, STARBOARD.
+ */
+ c = AxisDirections.angleForVehicle(source, target);
+ if (c != Integer.MIN_VALUE) {
+ return new Angle(c * 90);
+ }
/*
* Check for DISPLAY_UP, DISPLAY_DOWN, etc. assuming a flat screen.
* Note that we do not check for grid directions (COLUMN_POSITIVE,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java
index f1c38a8d8b..96ad930a6f 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java
@@ -106,7 +106,7 @@ final class Normalizer implements Comparable<Normalizer> {
*
* @see #order(AxisDirection)
*/
- private static final int SHIFT = 2;
+ private static final int SHIFT = 3;
/**
* Custom code list values to handle as if the where defined between two GeoAPI values.
@@ -115,12 +115,15 @@ final class Normalizer implements Comparable<Normalizer> {
*/
private static final Map<AxisDirection,Integer> ORDER = new HashMap<>();
static {
- final Map<AxisDirection,Integer> m = ORDER;
// Get ordinal of last compass direction defined by GeoAPI. We will continue on the horizontal plane.
- final int horizontal = (AxisDirection.NORTH.ordinal() + (AxisDirections.COMPASS_COUNT - 1)) << SHIFT;
- m.put(AxisDirections.AWAY_FROM, horizontal + 1);
- m.put(AxisDirections.COUNTER_CLOCKWISE, horizontal + 2);
- m.put(AxisDirections.CLOCKWISE, horizontal + 3);
+ int code = (AxisDirection.NORTH.ordinal() + (AxisDirections.COMPASS_COUNT - 1)) << SHIFT;
+ for (final AxisDirection d : new AxisDirection[] {
+ AxisDirections.FORWARD,
+ AxisDirections.STARBOARD,
+ AxisDirections.COUNTER_CLOCKWISE,
+ AxisDirections.CLOCKWISE,
+ AxisDirections.AWAY_FROM
+ }) ORDER.put(d, ++code);
}
/**
@@ -170,8 +173,9 @@ final class Normalizer implements Comparable<Normalizer> {
if (d == 0) {
final AxisDirection d1 = this.axis.getDirection();
final AxisDirection d2 = that.axis.getDirection();
- d = AxisDirections.angleForCompass(d2, d1);
- if (d == Integer.MIN_VALUE) {
+ if ((d = AxisDirections.angleForCompass(d2, d1)) == Integer.MIN_VALUE &&
+ (d = AxisDirections.angleForVehicle(d2, d1)) == Integer.MIN_VALUE)
+ {
if (meridian != null) {
if (that.meridian != null) {
d = meridian.compareTo(that.meridian);
@@ -445,10 +449,10 @@ final class Normalizer implements Comparable<Normalizer> {
*/
static AbstractCS forConvention(final CoordinateSystem cs, final AxesConvention convention) {
switch (convention) {
- case NORMALIZED: // Fall through
+ case NORMALIZED: // Fall through
case DISPLAY_ORIENTED: return normalize(cs, convention, true);
- case RIGHT_HANDED: return normalize(cs, null, true);
- case POSITIVE_RANGE: return shiftAxisRange(cs);
+ case RIGHT_HANDED: return normalize(cs, null, true);
+ case POSITIVE_RANGE: return shiftAxisRange(cs);
default: throw new AssertionError(convention);
}
}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
index 7be7a93f96..8d89447e1c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java
@@ -463,6 +463,12 @@ class AbstractSingleOperation extends AbstractCoordinateOperation implements Sin
@Override
final void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
super.afterUnmarshal(unmarshaller, parent);
+ if (parameters == null && method != null) {
+ final ParameterDescriptorGroup descriptor = method.getParameters();
+ if (descriptor != null && descriptor.descriptors().isEmpty()) {
+ parameters = descriptor.createValue();
+ }
+ }
final CoordinateReferenceSystem sourceCRS = super.getSourceCRS();
final CoordinateReferenceSystem targetCRS = super.getTargetCRS();
if (transform == null && sourceCRS != null && targetCRS != null && parameters != null) try {
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
index afa8ae1da8..c6ae10396a 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java
@@ -243,7 +243,7 @@ final class DefaultConcatenatedOperation extends AbstractCoordinateOperation imp
} else if (!step.isIdentity()) {
flattened.add(op);
}
- if (mtFactory != null && step != null) {
+ if (mtFactory != null) {
transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step;
}
/*
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
index 1232c0aad6..1660e4f234 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Collections;
+import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@@ -117,7 +118,7 @@ import static org.apache.sis.util.ArgumentChecks.*;
* {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory}.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.1
+ * @version 1.3
*
* @see DefaultConversion
* @see DefaultTransformation
@@ -192,7 +193,8 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* The set of parameters, or {@code null} if none.
*
* <p><b>Consider this field as final!</b>
- * This field is modified only at unmarshalling time by {@link #setDescriptors(GeneralParameterDescriptor[])}</p>
+ * This field is modified only at unmarshalling time by {@link #setDescriptors(GeneralParameterDescriptor[])}
+ * or {@link #afterUnmarshal(Unmarshaller, Object)}.</p>
*/
@SuppressWarnings("serial") // Not statically typed as Serializable.
private ParameterDescriptorGroup parameters;
@@ -303,10 +305,7 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
targetDimensions = transform.getTargetDimensions();
if (transform instanceof Parameterized) {
parameters = ((Parameterized) transform).getParameterDescriptors();
- } else {
- parameters = null;
}
- formula = null;
}
/**
@@ -632,9 +631,8 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
* Returns the set of parameters.
*
* <div class="note"><b>Departure from the ISO 19111 standard:</b>
- * this property is mandatory according ISO 19111, but may be null in Apache SIS if the
- * {@link #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it
- * or if this {@code OperationMethod} has been read from an incomplete GML document.</div>
+ * this property is mandatory according ISO 19111, but may be {@code null} in Apache SIS if the
+ * {@link #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it.</div>
*
* @return the parameters, or {@code null} if unknown.
*
@@ -971,4 +969,16 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements
parameters = new DefaultParameterDescriptorGroup(IdentifiedObjects.getProperties(previous),
previous.getMinimumOccurs(), previous.getMaximumOccurs(), descriptors);
}
+
+ /**
+ * Invoked by JAXB after unmarshalling. If the {@code <gml:OperationMethod>} element does not contain
+ * any {@code <gml:parameter>}, we assume that this is a valid parameterless operation (as opposed to
+ * an operation with unknown parameters). We need this assumption because, contrarily to GeoAPI model,
+ * the GML schema does not differentiate "no parameters" from "unspecified parameters".
+ */
+ private void afterUnmarshal(final Unmarshaller unmarshaller, final Object parent) {
+ if (parameters == null) {
+ parameters = CC_OperationMethod.group(super.getName(), new GeneralParameterDescriptor[0]);
+ }
+ }
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java
index 06cc1f2de2..3478fbc723 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java
@@ -114,7 +114,8 @@ final class MovingFeatureIterator extends FeatureIterator implements Consumer<Lo
/**
* Executes the given action for the next moving feature or for all remaining moving features.
- * This method assumes that the 4 first columns are as documented in the code inside constructor.
+ * This method assumes that the 4 first columns are identifier, start time, end time and
+ * optional attributes in that order.
*
* @param action the action to execute as soon as the {@code mfidref} change, or {@code null} if none.
* @param all {@code true} for executing the given action on all remaining features.
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
index a1a0ef0750..a169618dbb 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
@@ -261,7 +261,7 @@ final class Store extends URIDataStore implements FeatureSet {
throw new DataStoreContentException(Resources.forLocale(getLocale())
.getString(Resources.Keys.ShallBeDeclaredBefore_2, "@columns", "@stboundedby"));
}
- envelope = parseEnvelope(elements); // Also set 'timeEncoding' and 'spatialDimensionCount'.
+ envelope = parseEnvelope(elements); // Also set `timeEncoding` and `spatialDimensionCount`.
dissociate |= (timeEncoding == null); // Need to be updated before parseFeatureType(…) execution.
break;
}