| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.parameter; |
| |
| import java.lang.reflect.Array; |
| import java.util.Objects; |
| import java.nio.file.Path; |
| import java.io.Serializable; |
| import java.io.File; |
| import java.net.URL; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import javax.xml.bind.annotation.XmlType; |
| import javax.xml.bind.annotation.XmlElement; |
| import javax.xml.bind.annotation.XmlElements; |
| import javax.xml.bind.annotation.XmlRootElement; |
| import javax.measure.Unit; |
| import javax.measure.UnitConverter; |
| import javax.measure.IncommensurableException; |
| import org.opengis.metadata.citation.Citation; |
| import org.opengis.parameter.ParameterValue; |
| import org.opengis.parameter.ParameterDescriptor; |
| import org.opengis.parameter.InvalidParameterTypeException; |
| import org.opengis.parameter.InvalidParameterValueException; |
| import org.apache.sis.io.wkt.FormattableObject; |
| import org.apache.sis.io.wkt.Formatter; |
| import org.apache.sis.io.wkt.Convention; |
| import org.apache.sis.io.wkt.ElementKind; |
| import org.apache.sis.internal.jaxb.gml.Measure; |
| import org.apache.sis.internal.jaxb.gml.MeasureList; |
| import org.apache.sis.internal.referencing.Resources; |
| import org.apache.sis.internal.referencing.WKTUtilities; |
| import org.apache.sis.internal.metadata.MetadataUtilities; |
| import org.apache.sis.internal.referencing.WKTKeywords; |
| import org.apache.sis.internal.util.Numerics; |
| import org.apache.sis.internal.system.Loggers; |
| import org.apache.sis.math.DecimalFunctions; |
| import org.apache.sis.util.Numbers; |
| import org.apache.sis.util.ComparisonMode; |
| import org.apache.sis.util.LenientComparable; |
| import org.apache.sis.util.ObjectConverters; |
| import org.apache.sis.util.resources.Errors; |
| import org.apache.sis.util.logging.Logging; |
| import org.apache.sis.util.UnconvertibleObjectException; |
| |
| import static org.apache.sis.util.ArgumentChecks.ensureNonNull; |
| import static org.apache.sis.util.Utilities.deepEquals; |
| |
| |
| /** |
| * A single parameter value used by an operation method. {@code ParameterValue} instances are elements in |
| * a {@linkplain DefaultParameterValueGroup parameter value group}, in a way similar to {@code Map.Entry} |
| * instances in a {@code java.util.Map}. |
| * |
| * <p>In the context of coordinate operations, most parameter values are numeric and can be obtained by the |
| * {@link #intValue()} or {@link #doubleValue()} methods. But other types of parameter values are possible |
| * and can be handled by the more generic {@link #getValue()} and {@link #setValue(Object)} methods. |
| * All {@code xxxValue()} methods in this class are convenience methods converting the value from {@code Object} |
| * to some commonly used types. Those types are specified in ISO 19111 as an union of attributes, listed below with |
| * the corresponding getter and setter methods:</p> |
| * |
| * <table class="sis"> |
| * <caption>Mapping from ISO attributes to getters and setters</caption> |
| * <tr><th>ISO attribute</th> <th>Java type</th> <th>Getter method</th> <th>Setter method</th></tr> |
| * <tr><td></td> <td>{@link Object}</td> <td>{@link #getValue()}</td> <td>{@link #setValue(Object)}</td></tr> |
| * <tr><td>stringValue</td> <td>{@link String}</td> <td>{@link #stringValue()}</td> <td>{@link #setValue(Object)}</td></tr> |
| * <tr><td>value</td> <td>{@code double}</td> <td>{@link #doubleValue()}</td> <td>{@link #setValue(double)}</td></tr> |
| * <tr><td></td> <td>{@code double}</td> <td>{@link #doubleValue(Unit)}</td> <td>{@link #setValue(double, Unit)}</td></tr> |
| * <tr><td>valueList</td> <td>{@code double[]}</td> <td>{@link #doubleValueList()}</td> <td>{@link #setValue(Object)}</td></tr> |
| * <tr><td></td> <td>{@code double[]}</td> <td>{@link #doubleValueList(Unit)}</td> <td>{@link #setValue(double[], Unit)}</td></tr> |
| * <tr><td>integerValue</td> <td>{@code int}</td> <td>{@link #intValue()}</td> <td>{@link #setValue(int)}</td></tr> |
| * <tr><td>integerValueList</td> <td>{@code int[]}</td> <td>{@link #intValueList()}</td> <td>{@link #setValue(Object)}</td></tr> |
| * <tr><td>booleanValue</td> <td>{@code boolean}</td> <td>{@link #booleanValue()}</td> <td>{@link #setValue(boolean)}</td></tr> |
| * <tr><td>valueFile</td> <td>{@link URI}</td> <td>{@link #valueFile()}</td> <td>{@link #setValue(Object)}</td></tr> |
| * <tr><td>valueFileCitation</td> <td>{@link Citation}</td> <td>{@link #getValue()}</td> <td>{@link #setValue(Object)}</td></tr> |
| * </table> |
| * |
| * The type and constraints on parameter values are given by the {@linkplain #getDescriptor() descriptor}, |
| * which is specified at construction time. The parameter type can be fetch with the following idiom: |
| * |
| * {@preformat java |
| * Class<T> valueClass = parameter.getDescriptor().getValueClass(); |
| * } |
| * |
| * <div class="section">Instantiation</div> |
| * A {@linkplain DefaultParameterDescriptor parameter descriptor} must be defined before parameter value can be created. |
| * Descriptors are usually pre-defined by map projection or process providers. Given a descriptor, a parameter value can |
| * be created by a call to the {@link #DefaultParameterValue(ParameterDescriptor)} constructor or by a call to the |
| * {@link ParameterDescriptor#createValue()} method. The later is recommended since it allows descriptors to return |
| * specialized implementations. |
| * |
| * <div class="section">Implementation note for subclasses</div> |
| * All read and write operations (except constructors, {@link #equals(Object)} and {@link #hashCode()}) |
| * ultimately delegates to the following methods: |
| * |
| * <ul> |
| * <li>All getter methods will invoke {@link #getValue()} and {@link #getUnit()} (if needed), |
| * then perform their processing on the values returned by those methods.</li> |
| * <li>All setter methods delegate to the {@link #setValue(Object, Unit)} method.</li> |
| * </ul> |
| * |
| * Consequently, the above-cited methods provide single points that subclasses can override |
| * for modifying the behavior of all getter and setter methods. |
| * |
| * @author Martin Desruisseaux (IRD, Geomatys) |
| * @version 1.0 |
| * |
| * @param <T> the type of the value stored in this parameter. |
| * |
| * @see DefaultParameterDescriptor |
| * @see DefaultParameterValueGroup |
| * |
| * @since 0.4 |
| * @module |
| */ |
| @XmlType(name = "ParameterValueType", propOrder = { |
| "xmlValue", |
| "descriptor" |
| }) |
| @XmlRootElement(name = "ParameterValue") |
| public class DefaultParameterValue<T> extends FormattableObject implements ParameterValue<T>, |
| LenientComparable, Serializable, Cloneable |
| { |
| /** |
| * Serial number for inter-operability with different versions. |
| */ |
| private static final long serialVersionUID = -5837826787089486776L; |
| |
| /** |
| * The definition of this parameter. |
| * |
| * <p><b>Consider this field as final!</b> |
| * This field is modified only at unmarshalling time by {@link #setDescriptor(ParameterDescriptor)}</p> |
| * |
| * @see #getDescriptor() |
| */ |
| private ParameterDescriptor<T> descriptor; |
| |
| /** |
| * The value, or {@code null} if undefined. |
| * Except for the constructors, the {@link #equals(Object)} and the {@link #hashCode()} methods, |
| * this field should be read only by {@link #getValue()} and written only by {@link #setValue(Object, Unit)}. |
| * |
| * @since 0.7 |
| */ |
| protected T value; |
| |
| /** |
| * The unit of measure for the value, or {@code null} if it does not apply. |
| * Except for the constructors, the {@link #equals(Object)} and the {@link #hashCode()} methods, |
| * this field should be read only by {@link #getUnit()} and written only by {@link #setValue(Object, Unit)}. |
| * |
| * @since 0.7 |
| */ |
| protected Unit<?> unit; |
| |
| /** |
| * Creates a parameter value from the specified descriptor. |
| * The value will be initialized to the default value, if any. |
| * |
| * @param descriptor the abstract definition of this parameter. |
| */ |
| public DefaultParameterValue(final ParameterDescriptor<T> descriptor) { |
| ensureNonNull("descriptor", descriptor); |
| this.descriptor = descriptor; |
| this.value = descriptor.getDefaultValue(); |
| this.unit = descriptor.getUnit(); |
| } |
| |
| /** |
| * Creates a new instance initialized with the values from the specified parameter object. |
| * This is a <em>shallow</em> copy constructor, since the value contained in the given |
| * object is not cloned. |
| * |
| * @param parameter the parameter to copy values from. |
| * |
| * @see #clone() |
| * @see #unmodifiable(ParameterValue) |
| */ |
| public DefaultParameterValue(final ParameterValue<T> parameter) { |
| ensureNonNull("parameter", parameter); |
| descriptor = parameter.getDescriptor(); |
| value = parameter.getValue(); |
| unit = parameter.getUnit(); |
| } |
| |
| /** |
| * Returns the definition of this parameter. |
| * |
| * @return the definition of this parameter. |
| */ |
| @Override |
| @XmlElement(name = "operationParameter", required = true) |
| public ParameterDescriptor<T> getDescriptor() { |
| return descriptor; |
| } |
| |
| /** |
| * Returns the unit of measure of the parameter value. |
| * If the parameter value has no unit (for example because it is a {@link String} type), |
| * then this method returns {@code null}. Note that "no unit" does not mean "dimensionless". |
| * |
| * <div class="section">Implementation note for subclasses</div> |
| * All getter methods which need unit information will invoke this {@code getUnit()} method. |
| * Subclasses can override this method if they need to compute the unit dynamically. |
| * |
| * @return the unit of measure, or {@code null} if none. |
| * |
| * @see #doubleValue() |
| * @see #doubleValueList() |
| * @see #getValue() |
| */ |
| @Override |
| public Unit<?> getUnit() { |
| return unit; |
| } |
| |
| /** |
| * Returns the parameter value as an object. |
| * If no value has been set, then this method returns the |
| * {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} (which may be null). |
| * |
| * <div class="section">Implementation note for subclasses</div> |
| * All getter methods will invoke this {@code getValue()} method. |
| * Subclasses can override this method if they need to compute the value dynamically. |
| * |
| * @return the parameter value as an object, or {@code null} if no value has been set |
| * and there is no default value. |
| * |
| * @see #setValue(Object) |
| * @see Parameters#getValue(ParameterDescriptor) |
| */ |
| @Override |
| public T getValue() { |
| return value; |
| } |
| |
| /** |
| * Returns the boolean value of this parameter. |
| * A boolean value does not have an associated unit of measure. |
| * |
| * <p>The default implementation invokes {@link #getValue()} and casts the result if possible, |
| * or throws an exception otherwise.</p> |
| * |
| * @return the boolean value represented by this parameter. |
| * @throws InvalidParameterTypeException if the value is not a boolean type. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #setValue(boolean) |
| * @see Parameters#booleanValue(ParameterDescriptor) |
| */ |
| @Override |
| public boolean booleanValue() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof Boolean) { |
| return (Boolean) value; |
| } |
| throw missingOrIncompatibleValue(value); |
| } |
| |
| /** |
| * Returns the integer value of this parameter, usually used for a count. |
| * An integer value does not have an associated unit of measure. |
| * |
| * <p>The default implementation invokes {@link #getValue()} and casts the result if possible, |
| * or throws an exception otherwise.</p> |
| * |
| * @return the numeric value represented by this parameter after conversion to type {@code int}. |
| * @throws InvalidParameterTypeException if the value is not an integer type. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #setValue(int) |
| * @see #intValueList() |
| * @see Parameters#intValue(ParameterDescriptor) |
| */ |
| @Override |
| public int intValue() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof Number) { |
| final int integer = ((Number) value).intValue(); |
| if (integer == ((Number) value).doubleValue()) { |
| return integer; |
| } |
| } |
| throw missingOrIncompatibleValue(value); |
| } |
| |
| /** |
| * Returns an ordered sequence of two or more integer values of this parameter, usually used for counts. |
| * |
| * <p>The default implementation invokes {@link #getValue()} and casts the result if possible, |
| * or throws an exception otherwise. If the value can be casted, then the array is cloned before |
| * to be returned.</p> |
| * |
| * @return a copy of the sequence of values represented by this parameter. |
| * @throws InvalidParameterTypeException if the value is not an array of {@code int}s. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #setValue(Object) |
| * @see #intValue() |
| * @see Parameters#intValueList(ParameterDescriptor) |
| */ |
| @Override |
| public int[] intValueList() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof int[]) { |
| return ((int[]) value).clone(); |
| } |
| throw missingOrIncompatibleValue(value); |
| } |
| |
| /** |
| * Returns the numeric value of this parameter. |
| * The units of measurement are specified by {@link #getUnit()}. |
| * |
| * <p>The default implementation invokes {@link #getValue()} and casts the result if possible, |
| * or throws an exception otherwise.</p> |
| * |
| * @return the numeric value represented by this parameter after conversion to type {@code double}. |
| * This method returns {@link Double#NaN} only if such "value" has been explicitly set. |
| * @throws InvalidParameterTypeException if the value is not a numeric type. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #getUnit() |
| * @see #setValue(double) |
| * @see #doubleValueList() |
| * @see Parameters#doubleValue(ParameterDescriptor) |
| */ |
| @Override |
| public double doubleValue() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof Number) { |
| return ((Number) value).doubleValue(); |
| } |
| throw missingOrIncompatibleValue(value); |
| } |
| |
| /** |
| * Returns an ordered sequence of two or more numeric values of this parameter, |
| * where each value has the same associated unit of measure. |
| * |
| * <p>The default implementation invokes {@link #getValue()} and casts the result if possible, |
| * or throws an exception otherwise. If the value can be casted, then the array is cloned before |
| * to be returned.</p> |
| * |
| * @return a copy of the sequence of values represented by this parameter. |
| * @throws InvalidParameterTypeException if the value is not an array of {@code double}s. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #getUnit() |
| * @see #setValue(Object) |
| * @see #doubleValue() |
| */ |
| @Override |
| public double[] doubleValueList() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof double[]) { |
| return ((double[]) value).clone(); |
| } |
| throw missingOrIncompatibleValue(value); |
| } |
| |
| /** |
| * Returns the converter to be used by {@link #doubleValue(Unit)} and {@link #doubleValueList(Unit)}. |
| */ |
| private UnitConverter getConverterTo(final Unit<?> unit) { |
| final Unit<?> source = getUnit(); |
| if (source == null) { |
| throw new IllegalStateException(Resources.format(Resources.Keys.UnitlessParameter_1, Verifier.getDisplayName(descriptor))); |
| } |
| ensureNonNull("unit", unit); |
| final short expectedID = Verifier.getUnitMessageID(source); |
| if (Verifier.getUnitMessageID(unit) != expectedID) { |
| throw new IllegalArgumentException(Errors.format(expectedID, unit)); |
| } |
| try { |
| return source.getConverterToAny(unit); |
| } catch (IncommensurableException e) { |
| throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2, source, unit), e); |
| } |
| } |
| |
| /** |
| * Returns the numeric value of this parameter in the given unit of measure. |
| * This convenience method applies unit conversions on the fly as needed. |
| * |
| * <p>The default implementation invokes {@link #doubleValue()} and {@link #getUnit()}, |
| * then converts the values to the given unit of measurement.</p> |
| * |
| * @param unit the unit of measure for the value to be returned. |
| * @return the numeric value represented by this parameter after conversion to type |
| * {@code double} and conversion to {@code unit}. |
| * @throws IllegalArgumentException if the specified unit is invalid for this parameter. |
| * @throws InvalidParameterTypeException if the value is not a numeric type. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #getUnit() |
| * @see #setValue(double,Unit) |
| * @see #doubleValueList(Unit) |
| * @see Parameters#doubleValue(ParameterDescriptor) |
| */ |
| @Override |
| public double doubleValue(final Unit<?> unit) throws IllegalArgumentException, IllegalStateException { |
| final double value = doubleValue(); // Invoke first in case it throws an exception. |
| return getConverterTo(unit).convert(value); |
| } |
| |
| /** |
| * Returns an ordered sequence of numeric values in the specified unit of measure. |
| * This convenience method applies unit conversions on the fly as needed. |
| * |
| * <p>The default implementation invokes {@link #doubleValueList()} and {@link #getUnit()}, |
| * then converts the values to the given unit of measurement.</p> |
| * |
| * @param unit the unit of measure for the value to be returned. |
| * @return the sequence of values represented by this parameter after conversion to type |
| * {@code double} and conversion to {@code unit}. |
| * @throws IllegalArgumentException if the specified unit is invalid for this parameter. |
| * @throws InvalidParameterTypeException if the value is not an array of {@code double}s. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #getUnit() |
| * @see #setValue(double[],Unit) |
| * @see #doubleValue(Unit) |
| * @see Parameters#doubleValueList(ParameterDescriptor) |
| */ |
| @Override |
| public double[] doubleValueList(final Unit<?> unit) throws IllegalArgumentException, IllegalStateException { |
| final UnitConverter converter = getConverterTo(unit); |
| final double[] values = doubleValueList(); |
| for (int i=0; i<values.length; i++) { |
| values[i] = converter.convert(values[i]); |
| } |
| return values; |
| } |
| |
| /** |
| * Returns the string value of this parameter. |
| * A string value does not have an associated unit of measure. |
| * |
| * @return the string value represented by this parameter. |
| * @throws InvalidParameterTypeException if the value is not a string. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #getValue() |
| * @see #setValue(Object) |
| * @see Parameters#stringValue(ParameterDescriptor) |
| */ |
| @Override |
| public String stringValue() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof CharSequence) { |
| return value.toString(); |
| } |
| throw missingOrIncompatibleValue(value); |
| } |
| |
| /** |
| * Returns a reference to a file or a part of a file containing one or more parameter values. |
| * The default implementation can convert the following value types: |
| * {@link URI}, {@link URL}, {@link Path}, {@link File}. |
| * |
| * @return the reference to a file containing parameter values. |
| * @throws InvalidParameterTypeException if the value is not a reference to a file or a URI. |
| * @throws IllegalStateException if the value is not defined and there is no default value. |
| * |
| * @see #getValue() |
| * @see #setValue(Object) |
| */ |
| @Override |
| public URI valueFile() throws IllegalStateException { |
| final T value = getValue(); |
| if (value instanceof URI) return (URI) value; |
| if (value instanceof File) return ((File) value).toURI(); |
| if (value instanceof Path) return ((Path) value).toUri(); |
| Exception cause = null; |
| if (value instanceof URL) try { |
| return ((URL) value).toURI(); |
| } catch (URISyntaxException exception) { |
| cause = exception; |
| } |
| final String name = Verifier.getDisplayName(descriptor); |
| if (value != null) { |
| throw new InvalidParameterTypeException(getClassTypeError(), name); |
| } |
| throw new IllegalStateException(Resources.format(Resources.Keys.MissingValueForParameter_1, name), cause); |
| } |
| |
| /** |
| * Returns {@code true} if the given value is an instance of one of the types documented in {@link #valueFile()}. |
| */ |
| private static boolean isFile(final Object value) { |
| return (value instanceof URI || value instanceof URL || value instanceof File || value instanceof Path); |
| } |
| |
| /** |
| * Same as {@link #isFile(Object)}, but accepts also a {@link String} if the type specified |
| * in the parameter descriptor is one of the types documented in {@link #valueFile()}. |
| */ |
| private boolean isOrNeedFile(final Object newValue) { |
| if (newValue instanceof String) { |
| final Class<?> type = descriptor.getValueClass(); |
| return (type == URI.class) || (type == URL.class) |
| || Path.class.isAssignableFrom(type) |
| || File.class.isAssignableFrom(type); |
| } |
| return isFile(newValue); |
| } |
| |
| /** |
| * Returns the exception to throw when an incompatible method is invoked for the value type. |
| */ |
| private IllegalStateException missingOrIncompatibleValue(final Object newValue) { |
| final String name = Verifier.getDisplayName(descriptor); |
| if (newValue != null) { |
| return new InvalidParameterTypeException(getClassTypeError(), name); |
| } |
| return new IllegalStateException(Resources.format(Resources.Keys.MissingValueForParameter_1, name)); |
| } |
| |
| /** |
| * Formats an error message for illegal method call for the current value type. |
| */ |
| private String getClassTypeError() { |
| return Resources.format(Resources.Keys.IllegalOperationForValueClass_1, |
| (descriptor != null) ? ((ParameterDescriptor<?>) descriptor).getValueClass() : "?"); |
| } |
| |
| /** |
| * Converts the given number to the expected type, with a special case for conversion from float to double type. |
| * Widening conversions are aimed to be exact in base 10 instead than base 2. If {@code expectedClass} is not a |
| * {@link Number} subtype, then this method does nothing. If the cast would result in information lost, than |
| * this method returns the given value unchanged for allowing a more accurate error message to happen later. |
| * |
| * @param value the value to cast (can be {@code null}). |
| * @param expectedClass the desired class as a wrapper class (not a primitive type). |
| * @return value converted to the desired class, or {@code value} if no cast is needed or can be done. |
| */ |
| @SuppressWarnings("unchecked") |
| private static Number cast(final Number value, final Class<?> expectedClass) { |
| if (expectedClass == Double.class && value instanceof Float) { |
| return DecimalFunctions.floatToDouble(value.floatValue()); |
| } else if (Number.class.isAssignableFrom(expectedClass)) { |
| final Number n = Numbers.cast(value, (Class<? extends Number>) expectedClass); |
| if (Numerics.equals(n.doubleValue(), value.doubleValue())) { |
| return n; |
| } |
| } |
| return value; |
| } |
| |
| /** |
| * Sets the parameter value as an object. The object type is typically (but not limited to) {@link Double}, |
| * {@code double[]}, {@link Integer}, {@code int[]}, {@link Boolean}, {@link String} or {@link URI}. |
| * If the given value is {@code null}, then this parameter is set to the |
| * {@linkplain DefaultParameterDescriptor#getDefaultValue() default value}. |
| * If the given value is not an instance of the expected type, then this method may perform automatically |
| * a type conversion (for example from {@link Float} to {@link Double} or from {@link Path} to {@link URI}) |
| * if such conversion can be done without information lost. |
| * |
| * @param newValue the parameter value, or {@code null} to restore the default. |
| * @throws InvalidParameterValueException if the type of {@code value} is inappropriate for this parameter, |
| * or if the value is illegal for some other reason (for example the value is numeric and out of range). |
| * |
| * @see #getValue() |
| */ |
| @Override |
| public void setValue(Object newValue) throws InvalidParameterValueException { |
| /* |
| * Try to convert the value only for a limited amount of types. In particular we want to allow conversions |
| * between java.io.File and java.nio.file.Path for easier transition between JDK6 and JDK7. We do not want |
| * to allow too many conversions for reducing the risk of unexpected behavior. If we fail to convert, try |
| * to set the value anyway since the user may have redefined the `setValue(Object, Unit)` method. |
| */ |
| if (newValue != null) { |
| final Class<?> expectedClass = descriptor.getValueClass(); |
| if (!expectedClass.isInstance(newValue)) { |
| if (newValue instanceof Number) { |
| newValue = cast((Number) newValue, expectedClass); |
| } else if (isOrNeedFile(newValue)) try { |
| newValue = ObjectConverters.convert(newValue, expectedClass); |
| } catch (UnconvertibleObjectException e) { |
| /* |
| * Level.FINE (not WARNING) because this log duplicates the exception |
| * that `setValue(Object, Unit)` may throw (with a better message). |
| */ |
| Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION), |
| DefaultParameterValue.class, "setValue", e); |
| } else { |
| /* |
| * If the given value is an array, verify if array elements need to be converted |
| * for example from `float` to `double`. This is a "all or nothing" operation: |
| * if at least one element can not be converted, then the whole array is unchanged. |
| */ |
| Class<?> componentType = expectedClass.getComponentType(); |
| convert: if (componentType != null) { |
| final Object array = newValue.getClass().isArray() ? newValue : new Object[] {newValue}; |
| final int length = Array.getLength(array); |
| if (length > 0) { |
| final Object copy = Array.newInstance(componentType, length); |
| componentType = Numbers.primitiveToWrapper(componentType); |
| for (int i=0; i<length; i++) { |
| Object element = Array.get(array, i); |
| if (element != null) { |
| if (!(element instanceof Number)) break convert; |
| element = cast((Number) element, componentType); |
| if (!(componentType.isInstance(element))) break convert; |
| Array.set(copy, i, element); |
| } |
| } |
| newValue = copy; |
| } |
| } |
| } |
| } |
| } |
| /* |
| * Code below uses `unit` instead than `getUnit()` despite class Javadoc claim because units are not expected |
| * to be involved in this method. We access this field only as a matter of principle, for making sure that no |
| * property other than the value is altered by this method call. |
| */ |
| setValue(newValue, unit); |
| } |
| |
| /** |
| * Sets the parameter value as a boolean. |
| * |
| * @param newValue the parameter value. |
| * @throws InvalidParameterValueException if the boolean type is inappropriate for this parameter. |
| * |
| * @see #booleanValue() |
| */ |
| @Override |
| public void setValue(final boolean newValue) throws InvalidParameterValueException { |
| setValue(newValue, unit); |
| /* |
| * Above code used `unit` instead than `getUnit()` despite class Javadoc claim because units are not expected |
| * to be involved in this method. We access this field only as a matter of principle, for making sure that no |
| * property other than the value is altered by this method call. |
| */ |
| } |
| |
| /** |
| * Sets the parameter value as an integer. This method automatically wraps the given integer |
| * in an object of the type specified by the {@linkplain #getDescriptor() descriptor} if that |
| * conversion can be done without information lost. |
| * |
| * @param newValue the parameter value. |
| * @throws InvalidParameterValueException if the integer type is inappropriate for this parameter, |
| * or if the value is illegal for some other reason (for example a value out of range). |
| * |
| * @see #intValue() |
| */ |
| @Override |
| public void setValue(final int newValue) throws InvalidParameterValueException { |
| Number n = newValue; |
| Number c = cast(n, descriptor.getValueClass()); |
| if (c.intValue() == newValue) n = c; |
| setValue(n, unit); |
| /* |
| * Above code used `unit` instead than `getUnit()` despite class Javadoc claim because units are not expected |
| * to be involved in this method. We access this field only as a matter of principle, for making sure that no |
| * property other than the value is altered by this method call. |
| */ |
| } |
| |
| /** |
| * Wraps the given value in a type compatible with the expected value class, if possible. |
| * If the value can not be wrapped, then this method fallbacks on the {@link Double} class |
| * consistently with this method being invoked only by {@code setValue(double, …)} methods. |
| * |
| * @throws IllegalArgumentException if the given value can not be converted to the given type. |
| */ |
| @SuppressWarnings("unchecked") |
| private static Number wrap(final double value, final Class<?> valueClass) throws IllegalArgumentException { |
| if (Number.class.isAssignableFrom(valueClass)) { |
| return Numbers.wrap(value, (Class<? extends Number>) valueClass); |
| } else { |
| return Numerics.valueOf(value); |
| } |
| } |
| |
| /** |
| * Sets the parameter value as a floating point. The unit, if any, stay unchanged. This method automatically |
| * wraps the given number in an object of the type specified by the {@linkplain #getDescriptor() descriptor}. |
| * |
| * @param newValue the parameter value. |
| * @throws InvalidParameterValueException if the floating point type is inappropriate for this parameter, |
| * or if the value is illegal for some other reason (for example a value out of range). |
| * |
| * @see #setValue(double,Unit) |
| * @see #doubleValue() |
| */ |
| @Override |
| public void setValue(final double newValue) throws InvalidParameterValueException { |
| final Number n; |
| try { |
| n = wrap(newValue, descriptor.getValueClass()); |
| } catch (IllegalArgumentException e) { |
| throw (InvalidParameterValueException) new InvalidParameterValueException( |
| e.getLocalizedMessage(), Verifier.getDisplayName(descriptor), newValue).initCause(e); |
| } |
| setValue(n, unit); |
| /* |
| * Above code used `unit` instead than `getUnit()` despite class Javadoc claim because units are not expected |
| * to be involved in this method. We access this field only as a matter of principle, for making sure that no |
| * property other than the value is altered by this method call. |
| */ |
| } |
| |
| /** |
| * Sets the parameter value as a floating point and its associated unit. This method automatically wraps |
| * the given number in an object of the type specified by the {@linkplain #getDescriptor() descriptor}. |
| * |
| * @param newValue the parameter value. |
| * @param unit the unit for the specified value. |
| * @throws InvalidParameterValueException if the floating point type is inappropriate for this parameter, |
| * or if the value is illegal for some other reason (for example a value out of range). |
| * |
| * @see #setValue(double) |
| * @see #doubleValue(Unit) |
| */ |
| @Override |
| public void setValue(final double newValue, final Unit<?> unit) throws InvalidParameterValueException { |
| final Number n; |
| try { |
| n = wrap(newValue, descriptor.getValueClass()); |
| } catch (IllegalArgumentException e) { |
| throw (InvalidParameterValueException) new InvalidParameterValueException( |
| e.getLocalizedMessage(), Verifier.getDisplayName(descriptor), newValue).initCause(e); |
| } |
| setValue(n, unit); |
| } |
| |
| /** |
| * Sets the parameter value as an array of floating point and their associated unit. |
| * |
| * @param newValues the parameter values. |
| * @param unit the unit for the specified value. |
| * @throws InvalidParameterValueException if the floating point array type is inappropriate for this parameter, |
| * or if the value is illegal for some other reason (for example a value out of range). |
| */ |
| @Override |
| public void setValue(final double[] newValues, final Unit<?> unit) throws InvalidParameterValueException { |
| setValue((Object) newValues, unit); |
| } |
| |
| /** |
| * Sets the parameter value and its associated unit. |
| * If the given value is {@code null}, then this parameter is set to the |
| * {@linkplain DefaultParameterDescriptor#getDefaultValue() default value}. |
| * Otherwise the given value shall be an instance of the class expected by the {@linkplain #getDescriptor() descriptor}. |
| * |
| * <ul> |
| * <li>This method does not perform any type conversion. Type conversion, if desired, should be |
| * applied by the public {@code setValue(…)} methods before to invoke this protected method.</li> |
| * <li>This method does not clone the given value. In particular, references to {@code int[]} and |
| * {@code double[]} arrays are stored <cite>as-is</cite>.</li> |
| * </ul> |
| * |
| * <div class="section">Implementation note for subclasses</div> |
| * This method is invoked by all setter methods in this class, thus providing a single point that |
| * subclasses can override if they want to perform more processing on the value before its storage, |
| * or to be notified about value changes. |
| * |
| * @param newValue the parameter value, or {@code null} to restore the default. |
| * @param unit the unit associated to the new parameter value, or {@code null}. |
| * @throws InvalidParameterValueException if the type of {@code value} is inappropriate for this parameter, |
| * or if the value is illegal for some other reason (for example the value is numeric and out of range). |
| * |
| * @see #validate(Object) |
| */ |
| @SuppressWarnings("unchecked") |
| protected void setValue(final Object newValue, final Unit<?> unit) throws InvalidParameterValueException { |
| final T convertedValue = Verifier.ensureValidValue(descriptor, newValue, unit); |
| if (newValue != null) { |
| validate(convertedValue); |
| this.value = (T) newValue; // Type has been verified by Verifier.ensureValidValue(…). |
| } else { |
| this.value = descriptor.getDefaultValue(); |
| } |
| this.unit = unit; // Assign only on success. |
| } |
| |
| /** |
| * Invoked by {@link #setValue(Object, Unit)} after the basic verifications have been done and before |
| * the value is stored. Subclasses can override this method for performing additional verifications. |
| * |
| * <div class="section">Unit of measurement</div> |
| * If the user specified a unit of measurement, then the value given to this method has been converted |
| * to the unit specified by the {@linkplain #getDescriptor() descriptor}, for easier comparisons against |
| * standardized values. This converted value may be different than the value to be stored in this |
| * {@code ParameterValue}, since the later value will be stored in the unit specified by the user. |
| * |
| * <div class="section">Standard validations</div> |
| * The checks for {@linkplain DefaultParameterDescriptor#getValueClass() value class}, |
| * for {@linkplain DefaultParameterDescriptor#getValueDomain() value domain} and for |
| * {@linkplain DefaultParameterDescriptor#getValidValues() valid values} are performed |
| * before this method is invoked. The default implementation of this method does nothing. |
| * |
| * @param newValue the value converted to the unit of measurement specified by the descriptor. |
| * @throws InvalidParameterValueException if the given value is invalid for implementation-specific reasons. |
| */ |
| protected void validate(final T newValue) throws InvalidParameterValueException { |
| } |
| |
| /** |
| * Compares the specified object with this parameter for equality. |
| * The strictness level is controlled by the second argument. |
| * |
| * @param object the object to compare to {@code this}. |
| * @param mode the strictness level of the comparison. |
| * @return {@code true} if both objects are equal according the given comparison mode. |
| */ |
| @Override |
| public boolean equals(final Object object, final ComparisonMode mode) { |
| if (object == this) { |
| return true; // Slight optimization |
| } |
| if (object != null) { |
| if (mode == ComparisonMode.STRICT) { |
| if (getClass() == object.getClass()) { |
| final DefaultParameterValue<?> that = (DefaultParameterValue<?>) object; |
| return Objects.equals(descriptor, that.descriptor) && |
| Objects.equals(value, that.value) && |
| Objects.equals(unit, that.unit); |
| } |
| } else if (object instanceof ParameterValue<?>) { |
| final ParameterValue<?> that = (ParameterValue<?>) object; |
| return deepEquals(getDescriptor(), that.getDescriptor(), mode) && |
| deepEquals(getValue(), that.getValue(), mode) && |
| deepEquals(getUnit(), that.getUnit(), mode); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Compares the specified object with this parameter for equality. |
| * This method is implemented as below: |
| * |
| * {@preformat java |
| * return equals(other, ComparisonMode.STRICT); |
| * } |
| * |
| * Subclasses shall override {@link #equals(Object, ComparisonMode)} instead than this method. |
| * |
| * @param object the object to compare to {@code this}. |
| * @return {@code true} if both objects are equal. |
| */ |
| @Override |
| public final boolean equals(final Object object) { |
| return equals(object, ComparisonMode.STRICT); |
| } |
| |
| /** |
| * Returns a hash value for this parameter. |
| * This value does not need to be the same in past or future versions of this class. |
| * |
| * @return the hash code value. |
| */ |
| @Override |
| public int hashCode() { |
| int code = 37 * descriptor.hashCode(); |
| if (value != null) code += value.hashCode(); |
| if (unit != null) code += 31*unit.hashCode(); |
| return code; |
| } |
| |
| /** |
| * Returns a clone of this parameter value. |
| * |
| * @see #DefaultParameterValue(ParameterValue) |
| * @see #unmodifiable(ParameterValue) |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| public DefaultParameterValue<T> clone() { |
| try { |
| return (DefaultParameterValue<T>) super.clone(); |
| } catch (CloneNotSupportedException exception) { |
| throw new AssertionError(exception); // Should not happen, since we are cloneable |
| } |
| } |
| |
| /** |
| * Returns an unmodifiable implementation of the given parameter value. |
| * This method shall be used only with: |
| * |
| * <ul> |
| * <li>immutable {@linkplain #getDescriptor() descriptor},</li> |
| * <li>immutable or null {@linkplain #getUnit() unit}, and</li> |
| * <li>immutable or {@linkplain Cloneable cloneable} parameter {@linkplain #getValue() value}.</li> |
| * </ul> |
| * |
| * If the parameter value implements the {@link Cloneable} interface and has a public {@code clone()} method, |
| * then that value will be cloned every time the {@link #getValue()} method is invoked. |
| * The value is not cloned by this method however; it is caller's responsibility to not modify the value of |
| * the given {@code parameter} instance after this method call. |
| * |
| * <div class="section">Instances sharing</div> |
| * If this method is invoked more than once with equal {@linkplain #getDescriptor() descriptor}, |
| * {@linkplain #getValue() value} and {@linkplain #getUnit() unit}, then this method will return |
| * the same {@code DefaultParameterValue} instance on a <cite>best effort</cite> basis. |
| * |
| * <div class="note"><b>Rational:</b> |
| * the same parameter value is often used in many different coordinate operations. For example all <cite>Universal |
| * Transverse Mercator</cite> (UTM) projections use the same scale factor (0.9996) and false easting (500000 metres). |
| * </div> |
| * |
| * @param <T> the type of the value stored in the given parameter. |
| * @param parameter the parameter to make unmodifiable, or {@code null}. |
| * @return a unmodifiable implementation of the given parameter, or {@code null} if the given parameter was null. |
| * |
| * @see DefaultParameterValueGroup#unmodifiable(ParameterValueGroup) |
| * |
| * @since 0.6 |
| */ |
| public static <T> DefaultParameterValue<T> unmodifiable(final ParameterValue<T> parameter) { |
| return UnmodifiableParameterValue.create(parameter); |
| } |
| |
| /** |
| * Formats this parameter as a <cite>Well Known Text</cite> {@code Parameter[…]} element. |
| * Example: |
| * |
| * {@preformat wkt |
| * Parameter["False easting", 0.0, LengthUnit["metre", 1]] |
| * } |
| * |
| * <div class="section">Unit of measurement</div> |
| * The units of measurement were never specified in WKT 1 format, and are optional in WKT 2 format. |
| * If the units are not specified, then they are inferred from the context. |
| * Typically, parameter values that are lengths are given in the unit for the projected CRS axes |
| * while parameter values that are angles are given in the unit for the base geographic CRS. |
| * |
| * <div class="note"><b>Example:</b> |
| * The snippet below show WKT representations of the map projection parameters of a projected CRS |
| * (most other elements are omitted). The map projection uses a <cite>"Latitude of natural origin"</cite> |
| * parameters which is set to 52 <strong>grads</strong>, as defined in the {@code UNIT[…]} element of the |
| * enclosing CRS. A similar rule applies to <cite>“False easting”</cite> and <cite>“False northing”</cite> |
| * parameters, which are in kilometres in this example. |
| * |
| * <p><b>WKT 1:</b></p> |
| * {@preformat wkt |
| * PROJCS[…, |
| * GEOGCS[…, |
| * UNIT[“grad”, 0.015707963267948967]], // Unit for all angles |
| * PROJECTION[“Lambert_Conformal_Conic_1SP”] |
| * PARAMETER[“latitude_of_origin”, 52.0], // In grads |
| * PARAMETER[“scale_factor”, 0.99987742], |
| * PARAMETER[“false_easting”, 600.0], // In kilometres |
| * PARAMETER[“false_northing”, 2200.0], // In kilometres |
| * UNIT[“kilometre”, 1000]] // Unit for all lengths |
| * } |
| * |
| * <p><b>WKT 2:</b></p> |
| * {@preformat wkt |
| * ProjectedCRS[… |
| * BaseGeodCRS[… |
| * AngleUnit[“grad”, 0.015707963267948967]], |
| * Conversion[“Lambert zone II”, |
| * Method[“Lambert Conic Conformal (1SP)”], |
| * Parameter[“Latitude of natural origin”, 52.0], |
| * Parameter[“Scale factor at natural origin”, 0.99987742], |
| * Parameter[“False easting”, 600.0], |
| * Parameter[“False northing”, 2200.0]], |
| * CS[“Cartesian”, 2], |
| * LengthUnit[“kilometre”, 1000]] |
| * } |
| * </div> |
| * |
| * @param formatter the formatter where to format the inner content of this WKT element. |
| * @return {@code "Parameter"} or {@code "ParameterFile"}. |
| * |
| * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#119">WKT 2 specification §17.2.4</a> |
| */ |
| @Override |
| protected String formatTo(final Formatter formatter) { |
| final ParameterDescriptor<T> descriptor = getDescriptor(); // Gives to users a chance to override this property. |
| WKTUtilities.appendName(descriptor, formatter, ElementKind.PARAMETER); |
| final Convention convention = formatter.getConvention(); |
| final boolean isWKT1 = convention.majorVersion() == 1; |
| Unit<?> unit = getUnit(); // Gives to users a chance to override this property. |
| if (unit == null) { |
| final T value = getValue(); // Gives to users a chance to override this property. |
| if (!isWKT1 && isFile(value)) { |
| formatter.append(value.toString(), null); |
| return WKTKeywords.ParameterFile; |
| } else { |
| formatter.appendAny(value); |
| } |
| } else { |
| /* |
| * In the WKT 1 specification, the unit of measurement is given by the context. |
| * If this parameter value does not use the same unit, we will need to convert it. |
| * Otherwise we can write the value as-is. |
| * |
| * Note that we take the descriptor unit as a starting point instead than this parameter unit |
| * in order to give precedence to the descriptor units in Convention.WKT1_COMMON_UNITS mode. |
| */ |
| Unit<?> contextualUnit; |
| if (descriptor == null || (contextualUnit = descriptor.getUnit()) == null) { |
| // Should be very rare (probably a buggy descriptor), but we try to be safe. |
| contextualUnit = unit; |
| } |
| contextualUnit = formatter.toContextualUnit(contextualUnit); |
| boolean ignoreUnits; |
| if (isWKT1) { |
| unit = contextualUnit; |
| ignoreUnits = true; |
| } else { |
| if (convention != Convention.INTERNAL) { |
| unit = WKTUtilities.toFormattable(unit); |
| } |
| ignoreUnits = unit.equals(contextualUnit); |
| } |
| double value; |
| try { |
| value = doubleValue(unit); |
| } catch (IllegalStateException exception) { |
| /* |
| * May happen if a parameter is mandatory (e.g. "semi-major") |
| * but no value has been set for this parameter. |
| */ |
| if (descriptor != null) { |
| formatter.setInvalidWKT(descriptor, exception); |
| } else { |
| /* |
| * Null descriptor should be illegal but may happen after unmarshalling of invalid GML. |
| * We make this WKT formatting robust since it is used by 'toString()' implementation. |
| */ |
| formatter.setInvalidWKT(DefaultParameterValue.class, exception); |
| } |
| value = Double.NaN; |
| } |
| formatter.append(value); |
| /* |
| * In the WKT 2 specification, the unit and the identifier are optional but recommended. |
| * We follow that recommendation in strict WKT2 mode, but omit them in non-strict modes. |
| * The only exception is when the parameter unit is not the same than the contextual unit, |
| * in which case we have no choice: we must format that unit, unless the numerical value |
| * is identical in both units (typically the 0 value). |
| */ |
| if (!ignoreUnits && !Double.isNaN(value)) { |
| // Test equivalent to unit.equals(contextualUnit) but more aggressive. |
| ignoreUnits = Numerics.equals(value, doubleValue(contextualUnit)); |
| } |
| if (ignoreUnits && convention != Convention.INTERNAL) { |
| // One last check about if we are really allowed to ignore units. |
| ignoreUnits = convention.isSimplified() && hasContextualUnit(formatter); |
| } |
| if (!ignoreUnits) { |
| if (!isWKT1) { |
| formatter.append(unit); |
| } else if (!ignoreUnits) { |
| if (descriptor != null) { |
| formatter.setInvalidWKT(descriptor, null); |
| } else { |
| /* |
| * Null descriptor should be illegal but may happen after unmarshalling of invalid GML. |
| * We make this WKT formatting robust since it is used by 'toString()' implementation. |
| */ |
| formatter.setInvalidWKT(DefaultParameterValue.class, null); |
| } |
| } |
| } |
| } |
| // ID will be added by the Formatter itself. |
| return WKTKeywords.Parameter; |
| } |
| |
| /** |
| * Returns {@code true} if the given formatter has contextual units, in which case the WKT format can omit |
| * the unit of measurement. The contextual units may be defined either in the direct parent or in the parent |
| * of the parent, depending if we are formatting WKT1 or WKT2 respectively. This is because WKT2 wraps the |
| * parameters in an additional {@code CONVERSION[…]} element which is not present in WKT1. |
| * |
| * <p>Taking the example documented in {@link #formatTo(Formatter)}:</p> |
| * <ul> |
| * <li>in WKT 1, {@code PROJCS[…]} is the immediate parent of {@code PARAMETER[…]}, but</li> |
| * <li>in WKT 2, {@code ProjectedCRS[…]} is the parent of {@code Conversion[…]}, |
| * which is the parent of {@code Parameter[…]}.</li> |
| * </ul> |
| */ |
| private static boolean hasContextualUnit(final Formatter formatter) { |
| return formatter.hasContextualUnit(1) || // In WKT1 |
| formatter.hasContextualUnit(2); // In WKT2 |
| } |
| |
| |
| |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| //////// //////// |
| //////// 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. |
| * The property name depends on its type after conversion by this method. |
| */ |
| @XmlElements({ |
| @XmlElement(name = "value", type = Measure.class), |
| @XmlElement(name = "integerValue", type = Integer.class), |
| @XmlElement(name = "booleanValue", type = Boolean.class), |
| @XmlElement(name = "stringValue", type = String .class), |
| @XmlElement(name = "valueFile", type = URI .class), |
| @XmlElement(name = "integerValueList", type = IntegerList.class), |
| @XmlElement(name = "valueList", type = MeasureList.class) |
| }) |
| private Object getXmlValue() { |
| final Object value = getValue(); // Give to user a chance to override. |
| if (value != null) { |
| if (value instanceof Number) { |
| final Number n = (Number) value; |
| if (Numbers.isInteger(n.getClass())) { |
| final int xmlValue = n.intValue(); |
| if (xmlValue >= 0 && xmlValue == n.doubleValue()) { |
| return xmlValue; |
| } |
| } |
| return new Measure(((Number) value).doubleValue(), getUnit()); |
| } |
| if (value instanceof CharSequence) { |
| return value.toString(); |
| } |
| if (isFile(value)) { |
| return ObjectConverters.convert(value, URI.class); |
| } |
| final Class<?> type = Numbers.primitiveToWrapper(value.getClass().getComponentType()); |
| if (type != null && Number.class.isAssignableFrom(type)) { |
| if (Numbers.isInteger(type)) { |
| return new IntegerList(value); |
| } |
| return new MeasureList(value, type, getUnit()); |
| } |
| } |
| return value; |
| } |
| |
| /** |
| * Invoked by JAXB at unmarshalling time. |
| */ |
| @SuppressWarnings("unchecked") |
| private void setXmlValue(Object xmlValue) { |
| if (value == null && unit == null) { |
| if (xmlValue instanceof Measure) { |
| final Measure measure = (Measure) xmlValue; |
| xmlValue = measure.value; |
| unit = measure.unit; |
| } else if (xmlValue instanceof MeasureList) { |
| final MeasureList measure = (MeasureList) xmlValue; |
| xmlValue = measure.toArray(); |
| unit = measure.unit; |
| } else if (xmlValue instanceof IntegerList) { |
| xmlValue = ((IntegerList) xmlValue).toArray(); |
| } |
| if (descriptor != null) { |
| /* |
| * Should never happen with default SIS implementation, but may happen if the user created |
| * a sub-type of DefaultParameterValue with a default constructor providing the descriptor. |
| */ |
| value = ObjectConverters.convert(xmlValue, descriptor.getValueClass()); |
| } else { |
| /* |
| * Temporarily accept the value without checking its type. This is required because the |
| * descriptor is normally defined after the value in a GML document. The type will need |
| * to be verified when the descriptor will be set. |
| * |
| * There is no way we can prove that this cast is correct before the descriptor is set, |
| * and maybe that descriptor will never be set if the GML document is illegal. However |
| * this code is executed only during XML unmarshalling, in which case our unmarshalling |
| * process will construct a descriptor compatible with the value rather than the converse. |
| */ |
| value = (T) xmlValue; |
| } |
| } else { |
| MetadataUtilities.propertyAlreadySet(DefaultParameterValue.class, "setXmlValue", "value"); |
| } |
| } |
| } |