001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    Copyright © 2003-2024 Open Geospatial Consortium, Inc.
004 *    http://www.geoapi.org
005 *
006 *    Licensed under the Apache License, Version 2.0 (the "License");
007 *    you may not use this file except in compliance with the License.
008 *    You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *    Unless required by applicable law or agreed to in writing, software
013 *    distributed under the License is distributed on an "AS IS" BASIS,
014 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 *    See the License for the specific language governing permissions and
016 *    limitations under the License.
017 */
018package org.opengis.parameter;
019
020import java.net.URI;
021import javax.measure.Unit;
022import javax.measure.UnitConverter;
023import javax.measure.IncommensurableException;
024import org.opengis.annotation.UML;
025import org.opengis.annotation.Classifier;
026import org.opengis.annotation.Stereotype;
027import org.opengis.metadata.citation.Citation;
028import org.opengis.metadata.Identifier;
029import org.opengis.geometry.Geometry;
030
031import static org.opengis.annotation.Obligation.*;
032import static org.opengis.annotation.Specification.*;
033
034
035/**
036 * A single parameter value used by an operation method. Most CRS parameter values are numeric and can be obtained
037 * by the {@link #intValue()} or {@link #doubleValue()} methods. But other types of parameter values are possible
038 * and can be handled by the more generic {@link #getValue()} and {@link #setValue(Object)} methods.
039 *
040 * <p>All {@code xxxValue()} methods in this interface are convenience methods converting the value from {@code Object}
041 * to some commonly used types. Those types are specified in ISO 19111 as a union of attributes, listed below with the
042 * corresponding getter and setter methods:</p>
043 *
044 * <table class="ogc">
045 * <caption>Common value types</caption>
046 *   <tr><th>ISO attribute</th>     <th>Java type</th>        <th>Getter method</th>                  <th>Setter method</th></tr>
047 *   <tr><td></td>                  <td>{@link Object}</td>   <td>{@link #getValue()}</td>            <td>{@link #setValue(Object)}</td></tr>
048 *   <tr><td>stringValue</td>       <td>{@link String}</td>   <td>{@link #stringValue()}</td>         <td>{@link #setValue(Object)}</td></tr>
049 *   <tr><td>value</td>             <td>{@code double}</td>   <td>{@link #doubleValue()}</td>         <td>{@link #setValue(double)}</td></tr>
050 *   <tr><td></td>                  <td>{@code double}</td>   <td>{@link #doubleValue(Unit)}</td>     <td>{@link #setValue(double, Unit)}</td></tr>
051 *   <tr><td>valueList</td>         <td>{@code double[]}</td> <td>{@link #doubleValueList()}</td>     <td>{@link #setValue(Object)}</td></tr>
052 *   <tr><td></td>                  <td>{@code double[]}</td> <td>{@link #doubleValueList(Unit)}</td> <td>{@link #setValue(double[], Unit)}</td></tr>
053 *   <tr><td>integerValue</td>      <td>{@code int}</td>      <td>{@link #intValue()}</td>            <td>{@link #setValue(int)}</td></tr>
054 *   <tr><td>integerValueList</td>  <td>{@code int[]}</td>    <td>{@link #intValueList()}</td>        <td>{@link #setValue(Object)}</td></tr>
055 *   <tr><td>booleanValue</td>      <td>{@code boolean}</td>  <td>{@link #booleanValue()}</td>        <td>{@link #setValue(boolean)}</td></tr>
056 *   <tr><td>valueFile</td>         <td>{@link URI}</td>      <td>{@link #valueFile()}</td>           <td>{@link #setValue(Object)}</td></tr>
057 *   <tr><td>valueFileCitation</td> <td>{@link Citation}</td> <td>{@link #getValue()}</td>            <td>{@link #setValue(Object)}</td></tr>
058 *   <tr><td>geographicObject</td>  <td>{@link Geometry}</td> <td>{@link #getValue()}</td>            <td>{@link #setValue(Object)}</td></tr>
059 * </table>
060 *
061 * The type and constraints on parameter values are given by the {@linkplain #getDescriptor() descriptor},
062 * Instances of {@code ParameterValue} are created by the {@link ParameterDescriptor#createValue()} method.
063 *
064 * @departure integration
065 *   This interface merges the {@code OperationParameterValue} interface and {@code ParameterValue} union.
066 *   The {@code valueFileCitation} and {@code geographicObject} properties were omitted because those more
067 *   complex objects can be specified by setting the {@code <T>} parameterized type to
068 *   {@link Citation} and {@link Geometry} respectively.
069 *
070 * @param  <T>  the type of parameter values.
071 *
072 * @author  OGC Topic 2 (for abstract model and documentation)
073 * @author  Martin Desruisseaux (IRD, Geomatys)
074 * @author  Jody Garnett (Refractions Research)
075 * @version 3.1
076 * @since   1.0
077 *
078 * @see ParameterDescriptor
079 * @see ParameterValueGroup
080 */
081@Classifier(Stereotype.UNION)
082@UML(identifier="OperationParameterValue", specification=ISO_19111)
083public interface ParameterValue<T> extends GeneralParameterValue {
084    /**
085     * Returns the abstract definition of this parameter value.
086     *
087     * @return the abstract definition of this parameter value.
088     */
089    @Override
090    @UML(identifier="parameter", obligation=MANDATORY, specification=ISO_19111)
091    ParameterDescriptor<T> getDescriptor();
092
093    /**
094     * Returns the name of this parameter, for error messages only.
095     * All parameter shall have a name since it is a mandatory property,
096     * but if this parameter is nevertheless unnamed, an arbitrary value is returned.
097     *
098     * @return name of this parameter for error message.
099     */
100    private String getName() {
101        ParameterDescriptor<?> descriptor = getDescriptor();
102        if (descriptor != null) {
103            Identifier name = descriptor.getName();
104            if (name != null) {
105                String code = name.getCode();
106                if (code != null) {
107                    return code;
108                }
109            }
110        }
111        return "unnamed";
112    }
113
114    /**
115     * Returns a message saying that the value of this parameter cannot be converted to the specified unit.
116     *
117     * @param  unit  the illegal unit of measurement.
118     * @return a message saying that this parameter value cannot be converted.
119     */
120    private String cannotConvert(final Unit<?> unit) {
121        return "The “" + getName() + "” parameter value cannot be converted to “" + unit + "” units.";
122    }
123
124    /**
125     * Returns the unit of measure of the parameter value.
126     * If the parameter value has no unit (for example because it is a {@link String} type),
127     * then this method returns {@code null}. Note that "no unit" doesn't means
128     * "dimensionless".
129     *
130     * @return the unit of measure of the parameter value.
131     *
132     * @see #doubleValue()
133     * @see #doubleValueList()
134     * @see #getValue()
135     */
136    Unit<?> getUnit();
137
138    /**
139     * Returns the numeric value of this parameter in the specified unit of measure.
140     * This convenience method applies unit conversion on the fly as needed.
141     * The default implementation invokes {@link #getValue()} and {@link #getUnit()},
142     * then tries to cast and convert the value.
143     *
144     * @param  unit  the unit of measure for the value to be returned.
145     * @return the numeric value represented by this parameter after conversion to type
146     *         {@code double} and conversion to {@code unit}.
147     * @throws IllegalArgumentException if the specified unit is invalid for this parameter.
148     * @throws InvalidParameterTypeException if the value is not a numeric type.
149     * @throws IllegalStateException if the value cannot be returned for another reason.
150     *
151     * @see #getUnit()
152     * @see #setValue(double,Unit)
153     * @see #doubleValueList(Unit)
154     */
155    default double doubleValue(final Unit<?> unit) throws IllegalArgumentException, IllegalStateException {
156        final T value = getValue();
157        final Number number;
158        try {
159            number = (Number) value;
160        } catch (ClassCastException cause) {
161            throw new InvalidParameterTypeException(cause, getName());
162        }
163        final Unit<?> source = getUnit();
164        try {
165            return source.getConverterToAny(unit).convert(number).doubleValue();
166        } catch (IncommensurableException cause) {
167            throw new IllegalArgumentException(cannotConvert(unit), cause);
168        }
169    }
170
171    /**
172     * Returns the numeric value of this operation parameter.
173     * The unit of measurement is specified by {@link #getUnit()}.
174     * The default implementation invokes {@link #getValue()}, then tries to cast the value.
175     *
176     * @return the numeric value represented by this parameter after conversion to type {@code double}.
177     * @throws InvalidParameterTypeException if the value is not a numeric type.
178     * @throws IllegalStateException if the value cannot be returned for another reason.
179     * @unitof Measure
180     *
181     * @departure rename
182     *   Renamed the method from "{@code value}" to "{@code doubleValue}" for consistency
183     *   with {@code Number.doubleValue()} and the other "{@code *Value}" methods defined
184     *   in this interface.
185     *
186     * @see #getUnit()
187     * @see #setValue(double)
188     * @see #doubleValueList()
189     */
190    @UML(identifier="ParameterValue.value", obligation=CONDITIONAL, specification=ISO_19111)
191    default double doubleValue() throws IllegalStateException {
192        final T value = getValue();
193        try {
194            return ((Number) value).doubleValue();
195        } catch (NullPointerException | ClassCastException cause) {
196            throw new InvalidParameterTypeException(cause, getName());
197        }
198    }
199
200    /**
201     * Returns the integer value of this parameter, usually used for a count.
202     * An integer value does not have an associated unit of measure.
203     * The default implementation invokes {@link #getValue()}, then tries to cast the value.
204     *
205     * @return the numeric value represented by this parameter after conversion to type {@code int}.
206     * @throws InvalidParameterTypeException if the value is not an integer type.
207     * @throws IllegalStateException if the value cannot be returned for another reason.
208     *
209     * @departure rename
210     *   Renamed the method from "{@code integerValue}" to "{@code intValue}" for
211     *   consistency with {@code Number.intValue()} and the {@code int} Java primitive type.
212     *
213     * @see #setValue(int)
214     * @see #intValueList()
215     */
216    @UML(identifier="ParameterValue.integerValue", obligation=CONDITIONAL, specification=ISO_19111)
217    default int intValue() throws IllegalStateException {
218        final T value = getValue();
219        try {
220            return Math.toIntExact(((Number) value).longValue());
221        } catch (NullPointerException | ClassCastException | ArithmeticException cause) {
222            throw new InvalidParameterTypeException(cause, getName());
223        }
224    }
225
226    /**
227     * Returns the Boolean value of this parameter.
228     * A Boolean value does not have an associated unit of measure.
229     * The default implementation invokes {@link #getValue()}, then tries to cast the value.
230     *
231     * @return the Boolean value represented by this parameter.
232     * @throws InvalidParameterTypeException if the value is not a Boolean type.
233     * @throws IllegalStateException if the value cannot be returned for another reason.
234     *
235     * @see #setValue(boolean)
236     */
237    @UML(identifier="ParameterValue.booleanValue", obligation=CONDITIONAL, specification=ISO_19111)
238    default boolean booleanValue() throws IllegalStateException {
239        final T value = getValue();
240        try {
241            return (Boolean) value;
242        } catch (NullPointerException | ClassCastException cause) {
243            throw new InvalidParameterTypeException(cause, getName());
244        }
245    }
246
247    /**
248     * Returns the string value of this parameter.
249     * A string value does not have an associated unit of measure.
250     * The default implementation invokes {@link #getValue()}, then tries to cast the value.
251     *
252     * @return the string value represented by this parameter.
253     * @throws InvalidParameterTypeException if the value is not a string.
254     * @throws IllegalStateException if the value cannot be returned for another reason.
255     *
256     * @see #getValue()
257     * @see #setValue(Object)
258     */
259    @UML(identifier="ParameterValue.stringValue", obligation=CONDITIONAL, specification=ISO_19111)
260    default String stringValue() throws IllegalStateException {
261        final T value = getValue();
262        try {
263            return (String) value;
264        } catch (ClassCastException cause) {
265            throw new InvalidParameterTypeException(cause, getName());
266        }
267    }
268
269    /**
270     * Returns an ordered sequence of numeric values in the specified unit of measure.
271     * This convenience method applies unit conversions on the fly as needed.
272     * The default implementation invokes {@link #doubleValueList()}, then tries to convert the values.
273     *
274     * @param  unit  the unit of measure for the value to be returned.
275     * @return the sequence of values represented by this parameter after conversion to type
276     *         {@code double} and conversion to {@code unit}.
277     * @throws IllegalArgumentException if the specified unit is invalid for this parameter.
278     * @throws InvalidParameterTypeException if the value is not an array of {@code double}s.
279     * @throws IllegalStateException if the value cannot be returned for another reason.
280     *
281     * @see #getUnit()
282     * @see #setValue(double[],Unit)
283     * @see #doubleValue(Unit)
284     */
285    default double[] doubleValueList(Unit<?> unit) throws IllegalArgumentException, IllegalStateException {
286        double[] values = doubleValueList();
287        final Unit<?> source = getUnit();
288        try {
289            final UnitConverter c = source.getConverterToAny(unit);
290            if (!c.isIdentity()) {
291                values = values.clone();
292                for (int i=0; i < values.length; i++) {
293                    values[i] = c.convert(values[i]);
294                }
295            }
296        } catch (IncommensurableException cause) {
297            throw new InvalidParameterTypeException(cause, getName());
298        }
299        return values;
300    }
301
302    /**
303     * Returns an ordered sequence of two or more numeric values of this parameter,
304     * where each value has the same associated unit of measure.
305     * The default implementation invokes {@link #getValue()}, then tries to cast the value.
306     *
307     * @return the sequence of values represented by this parameter.
308     * @throws InvalidParameterTypeException if the value is not an array of {@code double}s.
309     * @throws IllegalStateException if the value cannot be returned for another reason.
310     * @unitof Measure
311     *
312     * @departure rename
313     *   Renamed the method from "{@code valueList}" to "{@code doubleValueList}" both for
314     *   consistency with {@code doubleValue()} and also because, like {@code doubleValue()},
315     *   this method returns an array of {@code double} values rather than a {@code Measure}
316     *   object.
317     *
318     * @see #getUnit()
319     * @see #setValue(Object)
320     * @see #doubleValue()
321     */
322    @UML(identifier="ParameterValue.valueList", obligation=CONDITIONAL, specification=ISO_19111)
323    default double[] doubleValueList() throws IllegalStateException {
324        final T value = getValue();
325        try {
326            return (double[]) value;
327        } catch (ClassCastException cause) {
328            throw new InvalidParameterTypeException(cause, getName());
329        }
330    }
331
332    /**
333     * Returns an ordered sequence of two or more integer values of this parameter, usually used for counts.
334     * These integer values do not have an associated unit of measure.
335     * The default implementation invokes {@link #getValue()}, then tries to cast the value.
336     *
337     * @return the sequence of values represented by this parameter.
338     * @throws InvalidParameterTypeException if the value is not an array of {@code int}s.
339     * @throws IllegalStateException if the value cannot be returned for another reason.
340     *
341     * @departure rename
342     *   Renamed the attribute from "{@code integerValueList}" to "{@code intValueList}"
343     *   for consistency with {@code intValue()}.
344     *
345     * @see #setValue(Object)
346     * @see #intValue()
347     */
348    @UML(identifier="ParameterValue.integerValueList", obligation=CONDITIONAL, specification=ISO_19111)
349    default int[] intValueList() throws IllegalStateException {
350        final T value = getValue();
351        try {
352            return (int[]) value;
353        } catch (ClassCastException cause) {
354            throw new InvalidParameterTypeException(cause, getName());
355        }
356    }
357
358    /**
359     * Returns a reference to a file or a part of a file containing one or more parameter values.
360     * When referencing a part of a file, that file must contain multiple identified parts, such
361     * as an XML encoded document. Furthermore, the referenced file or part of a file can reference
362     * another part of the same or different files, as allowed in XML documents.
363     *
364     * <p>The default implementation invokes {@link #getValue()}, then tries to cast the value.</p>
365     *
366     * @return the reference to a file containing parameter values.
367     * @throws InvalidParameterTypeException if the value is not a reference to a file or a URI.
368     * @throws IllegalStateException if the value cannot be returned for another reason.
369     *
370     * @see #getValue()
371     * @see #setValue(Object)
372     */
373    @UML(identifier="ParameterValue.valueFile", obligation=CONDITIONAL, specification=ISO_19111)
374    default URI valueFile() throws IllegalStateException {
375        final T value = getValue();
376        try {
377            return (URI) value;
378        } catch (ClassCastException cause) {
379            throw new InvalidParameterTypeException(cause, getName());
380        }
381    }
382
383    /**
384     * Returns the parameter value as an object. The object type is typically (but not restricted to)
385     * {@link Double}, {@link Integer}, {@link Boolean}, {@link String}, {@link URI}, {@code double[]} or {@code int[]}.
386     * If no value has been set, then this method returns the
387     * {@linkplain ParameterDescriptor#getDefaultValue() default value} (which may be null).
388     *
389     * @return the parameter value as an object, or {@code null} if no value has been set and
390     *         there is no default value.
391     *
392     * @see #setValue(Object)
393     */
394    T getValue();
395
396    /**
397     * Sets the parameter value as an array of floating point and their associated unit.
398     *
399     * @param  values  the parameter values.
400     * @param  unit    the unit for the specified value.
401     * @throws InvalidParameterValueException if the floating point type is inappropriate for this
402     *         parameter, or if the value is illegal for some other reason (for example a value out
403     *         of range).
404     */
405    void setValue(double[] values, Unit<?> unit) throws InvalidParameterValueException;
406
407    /**
408     * Sets the parameter value as a floating point and its associated unit.
409     *
410     * @param  value  the parameter value.
411     * @param  unit   the unit for the specified values.
412     * @throws InvalidParameterValueException if the floating point type is inappropriate for this
413     *         parameter, or if the value is illegal for some other reason (for example a value out
414     *         of range).
415     *
416     * @see #setValue(double)
417     * @see #doubleValue(Unit)
418     */
419    void setValue(double value, Unit<?> unit) throws InvalidParameterValueException;
420
421    /**
422     * Sets the parameter value as a floating point.
423     * The default implementation delegates to {@link #setValue(Object)}.
424     *
425     * @param  value  the parameter value.
426     * @throws InvalidParameterValueException if the floating point type is inappropriate for this
427     *         parameter, or if the value is illegal for some other reason (for example a value out
428     *         of range).
429     *
430     * @see #setValue(double,Unit)
431     * @see #doubleValue()
432     */
433    default void setValue(double value) throws InvalidParameterValueException {
434        setValue((Object) value);
435    }
436
437    /**
438     * Sets the parameter value as an integer.
439     * The default implementation delegates to {@link #setValue(Object)}.
440     *
441     * @param  value  the parameter value.
442     * @throws InvalidParameterValueException if the integer type is inappropriate for this parameter,
443     *         or if the value is illegal for some other reason (for example a value out of range).
444     *
445     * @see #intValue()
446     */
447    default void setValue(int value) throws InvalidParameterValueException {
448        setValue((Object) value);
449    }
450
451    /**
452     * Sets the parameter value as a Boolean.
453     * The default implementation delegates to {@link #setValue(Object)}.
454     *
455     * @param  value  the parameter value.
456     * @throws InvalidParameterValueException if the Boolean type is inappropriate for this parameter.
457     *
458     * @see #booleanValue()
459     */
460    default void setValue(boolean value) throws InvalidParameterValueException {
461        setValue((Object) value);
462    }
463
464    /**
465     * Sets the parameter value as an object. The object type is typically (but not restricted to)
466     * {@link Double}, {@link Integer}, {@link Boolean}, {@link String}, {@link URI}, {@code double[]} or {@code int[]}.
467     *
468     * <p>The argument is not restricted to the parameterized type {@code T} because the type
469     * is typically unknown (as in <code>group.{@linkplain ParameterValueGroup#parameter(String)
470     * parameter}("<var>name</var>").setValue(<var>value</var>)</code>) and
471     * because some implementations may choose to convert a wider range of types.</p>
472     *
473     * @param  value  the parameter value.
474     * @throws InvalidParameterValueException if the type of {@code value} is inappropriate
475     *         for this parameter, or if the value is illegal for some other reason (for example
476     *         the value is numeric and out of range).
477     *
478     * @see #getValue()
479     */
480    void setValue(Object value) throws InvalidParameterValueException;
481
482    /**
483     * Returns a copy of this parameter value.
484     *
485     * @return a copy of this parameter value.
486     */
487    @Override
488    ParameterValue<T> clone();
489}