blob: caff949e0ec72b4fe54c83d620e28e9f55728f11 [file] [log] [blame]
/*
* 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.util.Map;
import java.util.HashMap;
import java.util.List;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlTransient;
import javax.measure.Unit;
import org.opengis.util.MemberName;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.parameter.*; // We use almost all types from this package.
import org.apache.sis.internal.jaxb.metadata.replace.ServiceParameter;
import org.apache.sis.internal.referencing.Resources;
import org.apache.sis.measure.Range;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.MeasurementRange;
import org.apache.sis.referencing.IdentifiedObjects;
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.Debug;
/**
* Convenience methods for fetching parameter values despite the variations in parameter names, value types and units.
* See {@link DefaultParameterValueGroup} javadoc for a description of the standard way to get and set a particular
* parameter in a group. The remaining of this javadoc is specific to Apache SIS.
*
* <h2>Convenience methods</h2>
* This class provides the following convenience static methods:
* <ul>
* <li>{@link #cast(ParameterValue, Class) cast(…, Class)} for type safety with parameterized types.</li>
* <li>{@link #getMemberName(ParameterDescriptor)} for inter-operability between ISO 19111 and ISO 19115.</li>
* <li>{@link #getValueDomain(ParameterDescriptor)} for information purpose.</li>
* <li>{@link #copy(ParameterValueGroup, ParameterValueGroup)} for copying values into an existing instance.</li>
* </ul>
*
* Most instance methods in this class follow the same naming pattern
* than the methods provided by the {@link ParameterValue} interface.
* Those methods are themselves inspired by JDK methods:
*
* <table class="sis">
* <caption>Methods fetching parameter value</caption>
* <tr><th>{@code Parameters} method</th> <th>{@code ParameterValue} method</th> <th>JDK methods</th></tr>
* <tr><td>{@link #getValue(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#getValue() getValue()}</td> <td></td></tr>
* <tr><td>{@link #booleanValue(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#booleanValue() booleanValue()}</td> <td>{@link Boolean#booleanValue()}</td></tr>
* <tr><td>{@link #intValue(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#intValue() intValue()}</td> <td>{@link Number#intValue()}</td></tr>
* <tr><td>{@link #intValueList(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#intValueList() intValueList()}</td> <td></td></tr>
* <tr><td>{@link #doubleValue(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#doubleValue() doubleValue()}</td> <td>{@link Number#doubleValue()}</td></tr>
* <tr><td>{@link #doubleValueList(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#doubleValueList() doubleValueList()}</td> <td></td></tr>
* <tr><td>{@link #stringValue(ParameterDescriptor)}</td> <td>{@link DefaultParameterValue#stringValue() stringValue()}</td> <td></td></tr>
* </table>
*
*
* <h2>Fetching parameter values despite different names, types or units</h2>
* The common way to get a parameter is to invoke the {@link #parameter(String)} method.
* This {@code Parameters} class provides alternative ways, using a {@link ParameterDescriptor} argument
* instead than a {@code String} argument. Those descriptors provide additional information like the various
* {@linkplain DefaultParameterDescriptor#getAlias() aliases} under which the same parameter may be known.
* By using this information, {@code Parameters} can choose the most appropriate parameter name or alias
* (by searching for a common {@linkplain org.apache.sis.referencing.ImmutableIdentifier#getAuthority() authority})
* when it delegates its work to the {@code parameter(String)} method.
*
* <div class="note"><b>Example:</b>
* The same parameter may be known under different names. For example the
* {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getSemiMajorAxis()
* length of the semi-major axis of the ellipsoid} is commonly known as {@code "semi_major"}.
* But that parameter can also be named {@code "semi_major_axis"}, {@code "earth_radius"} or simply {@code "a"}
* in other libraries. When fetching parameter values, we do not always know in advance which of the above-cited
* names is recognized by an arbitrary {@code ParameterValueGroup} instance.</div>
*
* {@code Parameters} uses also the descriptor information for applying type and unit conversions
* (i.e. returned values are converted to the units of measurement specified by the given parameter descriptor).
*
*
* <h2>Note for subclass implementers</h2>
* This class does not implement any method from the {@link ParameterValueGroup} interface
* (this class is not named “{@code AbstractParameterValueGroup}” for that reason).
* Extending this class or extending {@link Object} make almost no difference for implementers;
* {@code Parameters} purpose is mostly to extend the API for users convenience.
* All methods in this class get their information from the {@link ParameterValueGroup} methods.
* In addition, unless otherwise specified, methods in this class is isolated from all others:
* overriding one method has no impact on other methods.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.0
* @since 0.4
* @module
*/
@XmlTransient
public abstract class Parameters implements ParameterValueGroup, Cloneable {
/**
* For subclass constructors only.
*/
protected Parameters() {
}
/**
* Returns the given parameter value group as an unmodifiable {@code Parameters} instance.
* If the given parameters is already an unmodifiable instance of {@code Parameters},
* then it is returned as-is. Otherwise this method copies all parameter values in a new,
* unmodifiable, parameter group instance.
*
* @param parameters the parameters to make unmodifiable, or {@code null}.
* @return an unmodifiable group with the same parameters than the given group,
* or {@code null} if the given argument was null.
*
* @see DefaultParameterValue#unmodifiable(ParameterValue)
*
* @since 0.7
*/
public static Parameters unmodifiable(final ParameterValueGroup parameters) {
return UnmodifiableParameterValueGroup.create(parameters);
}
/**
* Returns the given parameter value group as a {@code Parameters} instance.
* If the given parameters is already an instance of {@code Parameters}, then it is returned as-is.
* Otherwise this method returns a wrapper which delegate all method invocations to the given instance.
*
* <p>This method provides a way to get access to the non-static {@code Parameters} methods, like
* {@link #getValue(ParameterDescriptor)}, for an arbitrary {@code ParameterValueGroup} instance.</p>
*
* @param parameters the object to cast or wrap, or {@code null}.
* @return the given argument as an instance of {@code Parameters} (may be the same reference),
* or {@code null} if the given argument was null.
*/
public static Parameters castOrWrap(final ParameterValueGroup parameters) {
if (parameters == null || parameters instanceof Parameters) {
return (Parameters) parameters;
} else {
return new Wrapper(parameters);
}
}
/** Wrappers used as a fallback by {@link Parameters#castOrWrap(ParameterValueGroup)}. */
@SuppressWarnings("CloneDoesntCallSuperClone")
private static final class Wrapper extends Parameters implements Serializable {
private static final long serialVersionUID = -5491790565456920471L;
private final ParameterValueGroup delegate;
Wrapper(final ParameterValueGroup delegate) {this.delegate = delegate;}
@Override public ParameterDescriptorGroup getDescriptor() {return delegate.getDescriptor();}
@Override public List<GeneralParameterValue> values() {return delegate.values();}
@Override public ParameterValue<?> parameter(String name) {return delegate.parameter(name);}
@Override public List<ParameterValueGroup> groups (String name) {return delegate.groups(name);}
@Override public ParameterValueGroup addGroup (String name) {return delegate.addGroup(name);}
@Override public Parameters clone() {return new Wrapper(delegate.clone());}
}
/**
* Casts the given parameter descriptor to the given type.
* An exception is thrown immediately if the parameter does not have the expected
* {@linkplain DefaultParameterDescriptor#getValueClass() value class}.
*
* @param <T> the expected value class.
* @param descriptor the descriptor to cast, or {@code null}.
* @param valueClass the expected value class.
* @return the descriptor casted to the given value class, or {@code null} if the given descriptor was null.
* @throws ClassCastException if the given descriptor does not have the expected value class.
*
* @see Class#cast(Object)
*
* @category verification
*/
@SuppressWarnings("unchecked")
public static <T> ParameterDescriptor<T> cast(final ParameterDescriptor<?> descriptor, final Class<T> valueClass)
throws ClassCastException
{
ArgumentChecks.ensureNonNull("valueClass", valueClass);
if (descriptor != null) {
final Class<?> actual = descriptor.getValueClass();
/*
* We require a strict equality - not type.isAssignableFrom(actual) - because in
* the later case we could have (to be strict) to return a <? extends T> type.
*/
if (!valueClass.equals(actual)) {
throw new ClassCastException(Resources.format(Resources.Keys.IllegalParameterType_2,
Verifier.getDisplayName(descriptor), actual));
}
}
return (ParameterDescriptor<T>) descriptor;
}
/**
* Casts the given parameter value to the given type.
* An exception is thrown immediately if the parameter does not have the expected value class.
*
* @param <T> the expected value class.
* @param parameter the parameter to cast, or {@code null}.
* @param valueClass the expected value class.
* @return the value casted to the given type, or {@code null} if the given value was null.
* @throws ClassCastException if the given value doesn't have the expected value class.
*
* @see Class#cast(Object)
*
* @category verification
*/
@SuppressWarnings("unchecked")
public static <T> ParameterValue<T> cast(final ParameterValue<?> parameter, final Class<T> valueClass)
throws ClassCastException
{
ArgumentChecks.ensureNonNull("valueClass", valueClass);
if (parameter != null) {
final ParameterDescriptor<?> descriptor = parameter.getDescriptor();
final Class<?> actual = descriptor.getValueClass();
if (!valueClass.equals(actual)) { // Same comment than cast(ParameterDescriptor).
throw new ClassCastException(Resources.format(Resources.Keys.IllegalParameterType_2,
Verifier.getDisplayName(descriptor), actual));
}
}
return (ParameterValue<T>) parameter;
}
/**
* Returns the descriptors of the given parameters, in the same order.
* Special cases:
*
* <ul>
* <li>If the given array is {@code null}, then this method returns {@code null}.
* <li>If an element of the given array is {@code null}, then the corresponding
* element of the returned array is also {@code null}.</li>
* </ul>
*
* @param parameters the parameter values from which to get the descriptors, or {@code null}.
* @return the descriptors of the given parameter values, or {@code null} if the {@code parameters} argument was null.
*
* @since 0.6
*/
public static GeneralParameterDescriptor[] getDescriptors(final GeneralParameterValue... parameters) {
if (parameters == null) {
return null;
}
final GeneralParameterDescriptor[] descriptors = new GeneralParameterDescriptor[parameters.length];
for (int i=0; i<parameters.length; i++) {
final GeneralParameterValue p = parameters[i];
if (p != null) {
descriptors[i] = p.getDescriptor();
}
}
return descriptors;
}
/**
* Gets the parameter name as an instance of {@code MemberName}.
* This method performs the following checks:
*
* <ul>
* <li>If the {@linkplain DefaultParameterDescriptor#getName() primary name} is an instance of {@code MemberName},
* returns that primary name.</li>
* <li>Otherwise this method searches for the first {@linkplain DefaultParameterDescriptor#getAlias() alias}
* which is an instance of {@code MemberName}. If found, that alias is returned.</li>
* <li>If no alias is found, then this method tries to build a member name from the primary name and the
* {@linkplain DefaultParameterDescriptor#getValueClass() value class}, using the mapping defined in
* {@link org.apache.sis.util.iso.DefaultTypeName} javadoc.</li>
* </ul>
*
* This method can be used as a bridge between the parameter object
* defined by ISO 19111 (namely {@code CC_OperationParameter}) and the one
* defined by ISO 19115 (namely {@code SV_Parameter}).
*
* @param parameter the parameter from which to get the name (may be {@code null}).
* @return the member name, or {@code null} if none.
*
* @see org.apache.sis.util.iso.Names#createMemberName(CharSequence, String, CharSequence, Class)
*
* @since 0.5
*/
public static MemberName getMemberName(final ParameterDescriptor<?> parameter) {
return ServiceParameter.getMemberName(parameter);
}
/**
* Returns the domain of valid values defined by the given descriptor, or {@code null} if none.
* This method performs the following operations:
*
* <ul>
* <li>If the given parameter is an instance of {@code DefaultParameterDescriptor},
* delegate to {@link DefaultParameterDescriptor#getValueDomain()}.</li>
* <li>Otherwise builds the range from the {@linkplain DefaultParameterDescriptor#getMinimumValue() minimum value},
* {@linkplain DefaultParameterDescriptor#getMaximumValue() maximum value} and, if the values are numeric, from
* the {@linkplain DefaultParameterDescriptor#getUnit() unit}.</li>
* </ul>
*
* @param descriptor the parameter descriptor, or {@code null}.
* @return the domain of valid values, or {@code null} if none.
*
* @see DefaultParameterDescriptor#getValueDomain()
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static Range<?> getValueDomain(final ParameterDescriptor<?> descriptor) {
if (descriptor != null) {
if (descriptor instanceof DefaultParameterDescriptor<?>) {
return ((DefaultParameterDescriptor<?>) descriptor).getValueDomain();
}
final Class<?> valueClass = descriptor.getValueClass();
final Comparable<?> minimumValue = descriptor.getMinimumValue();
final Comparable<?> maximumValue = descriptor.getMaximumValue();
if ((minimumValue == null || valueClass.isInstance(minimumValue)) &&
(maximumValue == null || valueClass.isInstance(maximumValue)))
{
if (Number.class.isAssignableFrom(valueClass)) {
final Unit<?> unit = descriptor.getUnit();
if (unit != null) {
return new MeasurementRange((Class) valueClass,
(Number) minimumValue, true, (Number) maximumValue, true, unit);
} else if (minimumValue != null || maximumValue != null) {
return new NumberRange((Class) valueClass,
(Number) minimumValue, true, (Number) maximumValue, true);
}
} else if (minimumValue != null || maximumValue != null) {
return new Range(valueClass, minimumValue, true, maximumValue, true);
}
}
}
return null;
}
/**
* Returns the name or alias of the given parameter for the authority code space expected by this group.
* If no name or alias for this group's authority can be found, then the primary name will be returned.
*
* @param source the parameter for which the name is wanted.
* @return the name of the given parameter. May be {@code null} if there is no name at all,
* but such nameless descriptors are not legal.
*/
private String getName(final GeneralParameterDescriptor source) {
final ParameterDescriptorGroup descriptor = getDescriptor();
if (descriptor != null) { // Paranoiac check (should never be null)
final Identifier group = descriptor.getName();
if (group != null) { // Paranoiac check (should never be null)
final Citation authority = group.getAuthority();
final String name = IdentifiedObjects.getName(source, authority);
if (name != null || authority == null) {
return name;
}
}
}
return IdentifiedObjects.getName(source, null);
}
/**
* Returns the parameter of the given name, or {@code null} if it does not exist.
* The default implementation iterates over the {@link #values()} and compares the descriptor names.
* The {@link DefaultParameterValueGroup} subclass will override this method with a more efficient
* implementation which avoid creating some deferred parameters.
*/
@SuppressWarnings("null")
ParameterValue<?> parameterIfExist(final String name) throws ParameterNotFoundException {
int i1 = 0, i2 = 0;
ParameterValue<?> first = null;
ParameterValue<?> ambiguity = null;
final List<GeneralParameterValue> values = values();
final int size = values.size();
for (int i=0; i<size; i++) {
final GeneralParameterValue value = values.get(i);
if (value instanceof ParameterValue<?>) {
final ParameterValue<?> param = (ParameterValue<?>) value;
if (IdentifiedObjects.isHeuristicMatchForName(param.getDescriptor(), name)) {
if (first == null) {
first = param;
i1 = i;
} else {
ambiguity = param;
i2 = i;
}
}
}
}
/*
* If there is no ambiguity, we are done. In case of ambiguity we should throw an exception.
* However we will not throw the exception if this method is invoked from the getParameter(…)
* method of a Parameters instance wrapping a non-SIS implementation. The reason is that for
* foreigner implementations, the package-private getParameter(…) method will conservatively
* delegate to the public parameter(…) method, in case the implementer overrides it. But for
* Apache SIS implementations in this package, we rely on the exception being thrown.
*
* Note that all classes in this package except UnmodifiableParameterValueGroup override this
* method in a way that unconditionally throw the exception. UnmodifiableParameterValueGroup
* is the class that needs the exception to be thrown.
*/
if (ambiguity == null || !isKnownImplementation()) {
return first;
}
final GeneralParameterDescriptor d1 = first .getDescriptor();
final GeneralParameterDescriptor d2 = ambiguity.getDescriptor();
final String message;
if (d1 == d2) {
message = Errors.format(Errors.Keys.MultiOccurenceValueAtIndices_3, name, i1, i2);
} else {
message = Errors.format(Errors.Keys.AmbiguousName_3,
IdentifiedObjects.toString(d1.getName()),
IdentifiedObjects.toString(d2.getName()), name);
}
throw new ParameterNotFoundException(message, name);
}
/**
* Returns the parameter value for the specified operation parameter.
* This method tries to do the same work than {@link #parameter(String)} but without
* instantiating optional parameters if that parameter was not already instantiated.
*
* @param parameter the parameter to search.
* @return the requested parameter value, or {@code null} if none.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
*/
private ParameterValue<?> getParameter(final ParameterDescriptor<?> parameter) throws ParameterNotFoundException {
ArgumentChecks.ensureNonNull("parameter", parameter);
/*
* Search for an identifier matching this group's authority. For example if this ParameterValueGroup
* was created from an EPSG database, then we want to use the EPSG names instead than the OGC names.
*/
final String name = getName(parameter);
/*
* We do not want to invoke 'parameter(name)' because we do not want to create a new parameter
* if the user did not supplied one. We search the parameter ourself (so we don't create any)
* and return null if we do not find any.
*
* If we find a parameter, we can return it directly only if this object is an instance of a known
* implementation (currently DefaultParameterValueGroup and MapProjectionParameters), otherwise we
* do not know if the user overrode the 'parameter' method in a way incompatible with this method.
* We do not use Class.getMethod(…).getDeclaringClass() because it is presumed not worth the cost.
* In case of doubt, we delegate to 'parameter(name)'.
*/
final ParameterValue<?> value = parameterIfExist(name);
if (value == null || isKnownImplementation()) {
return value;
} else {
return parameter(name);
}
}
/**
* Returns {@code true} if this class is an implementation of an instance which is known to not override
* {@link #parameter(String)} in a way incompatible with {@link #parameterIfExist(String)}.
* The {@link DefaultParameterValueGroup} class needs to override this method.
*/
boolean isKnownImplementation() {
return false;
}
/**
* Returns the value of the parameter identified by the given descriptor, or {@code null} if none.
* This method uses the following information from the given {@code parameter} descriptor:
*
* <ul>
* <li>The most appropriate {@linkplain DefaultParameterDescriptor#getName() name} or
* {@linkplain DefaultParameterDescriptor#getAlias() alias} to use for searching
* in this {@code ParameterValueGroup}, chosen as below:
* <ul>
* <li>a name or alias defined by the same
* {@linkplain org.apache.sis.referencing.ImmutableIdentifier#getAuthority() authority}, if any;</li>
* <li>an arbitrary name or alias otherwise.</li>
* </ul>
* </li>
* <li>The {@linkplain DefaultParameterDescriptor#getDefaultValue() default value}
* to return if there is no value associated to the above-cited name or alias.</li>
* <li>The {@linkplain DefaultParameterDescriptor#getUnit() unit of measurement}
* (if any) of numerical value to return.</li>
* <li>The {@linkplain DefaultParameterDescriptor#getValueClass() type} of value to return.</li>
* </ul>
*
* This method can be useful when the {@code ParameterDescriptor} are known in advance, for example in the
* implementation of some {@linkplain org.apache.sis.referencing.operation.DefaultOperationMethod coordinate
* operation method}. If the caller has no such {@code ParameterDescriptor} at hand, then the
* {@link DefaultParameterValueGroup#parameter(String) parameter(String)} method is probably more convenient.
*
* @param <T> the type of the parameter value.
* @param parameter the name or alias of the parameter to look for, together with the desired type and unit of value.
* @return the requested parameter value if it exists, or the {@linkplain DefaultParameterDescriptor#getDefaultValue()
* default value} otherwise (which may be {@code null}).
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws UnconvertibleObjectException if the parameter value can not be converted to the expected type.
*
* @see #getMandatoryValue(ParameterDescriptor)
* @see #getOrCreate(ParameterDescriptor)
* @see DefaultParameterValueGroup#parameter(String)
* @see DefaultParameterValue#getValue()
*
* @since 0.6
*/
public <T> T getValue(final ParameterDescriptor<T> parameter) throws ParameterNotFoundException {
final ParameterValue<?> p = getParameter(parameter);
if (p != null) {
final Class<T> type = parameter.getValueClass();
final Unit<?> unit = parameter.getUnit();
Object value = p.getValue();
if (value != null) { // Tested first for avoiding IllegalStateException in call to doubleValue().
if (unit != null) {
if (type.isArray()) {
value = p.doubleValueList(unit);
} else {
value = p.doubleValue(unit);
}
}
return ObjectConverters.convert(value, type);
}
}
return parameter.getDefaultValue(); // Returning null is allowed here.
}
/**
* Returns the value of the parameter identified by the given descriptor, or throws an exception if none.
* The default implementation performs the same work than {@link #getValue(ParameterDescriptor)} and verifies
* that the returned value is non-null.
*
* @param <T> the type of the parameter value.
* @param parameter the name or alias of the parameter to look for, together with the desired type and unit of value.
* @return the requested parameter value if it exists, or the {@linkplain DefaultParameterDescriptor#getDefaultValue()
* default value} otherwise provided that it is not {@code null}.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see #getValue(ParameterDescriptor)
* @see #getOrCreate(ParameterDescriptor)
*
* @since 0.7
*/
public <T> T getMandatoryValue(final ParameterDescriptor<T> parameter) throws ParameterNotFoundException {
final ParameterValue<?> p = getParameter(parameter);
if (p != null) {
final Class<T> type = parameter.getValueClass();
final Unit<?> unit = parameter.getUnit();
final Object value;
if (unit == null) {
value = p.getValue();
} else if (type.isArray()) {
value = p.doubleValueList(unit);
} else {
value = p.doubleValue(unit);
}
final T result;
if (value != null) {
result = ObjectConverters.convert(value, type);
} else {
result = parameter.getDefaultValue();
}
if (result != null) {
return result;
}
}
throw new IllegalStateException(Resources.format(Resources.Keys.MissingValueForParameter_1,
Verifier.getDisplayName(parameter)));
}
/**
* Returns the default value of the given descriptor, or throws an exception if the
* descriptor does not define a default value. This check should be kept consistent
* with the {@link DefaultParameterValue#missingOrIncompatibleValue(Object)} check.
*/
private static <T> T defaultValue(final ParameterDescriptor<T> parameter) throws IllegalStateException {
final T value = parameter.getDefaultValue();
if (value != null) {
return value;
} else {
throw new IllegalStateException(Resources.format(Resources.Keys.MissingValueForParameter_1,
Verifier.getDisplayName(parameter)));
}
}
/**
* Returns the boolean value of the parameter identified by the given descriptor.
* See {@link #getValue(ParameterDescriptor)} for more information about how this
* method uses the given {@code parameter} argument.
*
* @param parameter the name or alias of the parameter to look for.
* @return the requested parameter value if it exists, or the <strong>non-null</strong>
* {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} otherwise.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see DefaultParameterValue#booleanValue()
*
* @since 0.6
*/
public boolean booleanValue(final ParameterDescriptor<Boolean> parameter) throws ParameterNotFoundException {
final ParameterValue<?> value = getParameter(parameter);
return (value != null) ? value.booleanValue() : defaultValue(parameter);
}
/**
* Returns the integer value of the parameter identified by the given descriptor.
* See {@link #getValue(ParameterDescriptor)} for more information about how this
* method uses the given {@code parameter} argument.
*
* @param parameter the name or alias of the parameter to look for.
* @return the requested parameter value if it exists, or the <strong>non-null</strong>
* {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} otherwise.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see DefaultParameterValue#intValue()
*
* @since 0.6
*/
public int intValue(final ParameterDescriptor<? extends Number> parameter) throws ParameterNotFoundException {
final ParameterValue<?> value = getParameter(parameter);
return (value != null) ? value.intValue() : defaultValue(parameter).intValue();
}
/**
* Returns the integer values of the parameter identified by the given descriptor.
* See {@link #getValue(ParameterDescriptor)} for more information about how this
* method uses the given {@code parameter} argument.
*
* @param parameter the name or alias of the parameter to look for.
* @return the requested parameter values if they exist, or the <strong>non-null</strong>
* {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} otherwise.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see DefaultParameterValue#intValueList()
*
* @since 0.6
*/
public int[] intValueList(final ParameterDescriptor<int[]> parameter) throws ParameterNotFoundException {
final ParameterValue<?> value = getParameter(parameter);
return (value != null) ? value.intValueList() : defaultValue(parameter);
}
/**
* Returns the floating point value of the parameter identified by the given descriptor.
* See {@link #getValue(ParameterDescriptor)} for more information about how this method
* uses the given {@code parameter} argument.
*
* <p>If the given descriptor supplies a {@linkplain DefaultParameterDescriptor#getUnit()
* unit of measurement}, then the returned value will be converted into that unit.</p>
*
* @param parameter the name or alias of the parameter to look for.
* @return the requested parameter value if it exists, or the <strong>non-null</strong>
* {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} otherwise.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see DefaultParameterValue#doubleValue(Unit)
*
* @since 0.6
*/
public double doubleValue(final ParameterDescriptor<? extends Number> parameter) throws ParameterNotFoundException {
final ParameterValue<?> value = getParameter(parameter);
if (value != null) {
final Unit<?> unit = parameter.getUnit();
return (unit != null) ? value.doubleValue(unit) : value.doubleValue();
} else {
return defaultValue(parameter).doubleValue();
}
}
/**
* Returns the floating point values of the parameter identified by the given descriptor.
* See {@link #getValue(ParameterDescriptor)} for more information about how this method
* uses the given {@code parameter} argument.
*
* <p>If the given descriptor supplies a {@linkplain DefaultParameterDescriptor#getUnit()
* unit of measurement}, then the returned values will be converted into that unit.</p>
*
* @param parameter the name or alias of the parameter to look for.
* @return the requested parameter values if they exists, or the <strong>non-null</strong>
* {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} otherwise.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see DefaultParameterValue#doubleValueList(Unit)
*
* @since 0.6
*/
public double[] doubleValueList(final ParameterDescriptor<double[]> parameter) throws ParameterNotFoundException {
final ParameterValue<?> value = getParameter(parameter);
if (value != null) {
final Unit<?> unit = parameter.getUnit();
return (unit != null) ? value.doubleValueList(unit) : value.doubleValueList();
} else {
return defaultValue(parameter);
}
}
/**
* Returns the string value of the parameter identified by the given descriptor.
* See {@link #getValue(ParameterDescriptor)} for more information about how this
* method uses the given {@code parameter} argument.
*
* @param parameter the name or alias of the parameter to look for.
* @return the requested parameter value if it exists, or the <strong>non-null</strong>
* {@linkplain DefaultParameterDescriptor#getDefaultValue() default value} otherwise.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
* @throws IllegalStateException if the value is not defined and there is no default value.
*
* @see DefaultParameterValue#stringValue()
*
* @since 0.6
*/
public String stringValue(final ParameterDescriptor<? extends CharSequence> parameter) throws ParameterNotFoundException {
final ParameterValue<?> value = getParameter(parameter);
return (value != null) ? value.stringValue() : defaultValue(parameter).toString();
}
/**
* Returns the parameter identified by the given descriptor.
* If the identified parameter is optional and not yet created, then it will be created now.
*
* <p>The default implementation is equivalent to:</p>
*
* {@preformat java
* return cast(parameter(name), parameter.getValueClass());
* }
*
* where {@code name} is a {@code parameter} {@linkplain DefaultParameterDescriptor#getName() name}
* or {@linkplain DefaultParameterDescriptor#getAlias() alias} chosen by the same algorithm than
* {@link #getValue(ParameterDescriptor)}.
*
* @param <T> the type of the parameter value.
* @param parameter the parameter to look for.
* @return the requested parameter instance.
* @throws ParameterNotFoundException if the given {@code parameter} name or alias is not legal for this group.
*
* @see #getValue(ParameterDescriptor)
* @see #getMandatoryValue(ParameterDescriptor)
* @see DefaultParameterValueGroup#parameter(String)
*
* @since 0.6
*/
public <T> ParameterValue<T> getOrCreate(final ParameterDescriptor<T> parameter) throws ParameterNotFoundException {
return cast(parameter(getName(parameter)), parameter.getValueClass());
}
/**
* Returns a copy of this group of parameter values.
* The default implementation performs a <em>shallow</em> copy,
* but subclasses are encouraged to perform a <em>deep</em> copy.
*
* @return a copy of this group of parameter values.
*
* @see #copy(ParameterValueGroup, ParameterValueGroup)
*/
@Override
public Parameters clone() {
try {
return (Parameters) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e); // Should never happen since we are Cloneable.
}
}
/**
* Copies the values of a parameter group into another parameter group.
* All values in the {@code source} group shall be valid for the {@code destination} group,
* but the {@code destination} may have more parameters.
* Sub-groups are copied recursively.
*
* <p>A typical usage of this method is for transferring values from an arbitrary implementation
* to some specific implementation, or to a parameter group using a different but compatible
* {@linkplain DefaultParameterValueGroup#getDescriptor() descriptor}.</p>
*
* @param values the parameters values to copy.
* @param destination where to copy the values.
* @throws InvalidParameterNameException if a {@code source} parameter name is unknown to the {@code destination}.
* @throws InvalidParameterValueException if the value of a {@code source} parameter is invalid for the {@code destination}.
*
* @see #clone()
*
* @since 0.5
*/
public static void copy(final ParameterValueGroup values, final ParameterValueGroup destination)
throws InvalidParameterNameException, InvalidParameterValueException
{
final Integer ZERO = 0;
final Map<String,Integer> occurrences = new HashMap<>();
for (final GeneralParameterValue value : values.values()) {
final String name = value.getDescriptor().getName().getCode();
final int occurrence = occurrences.getOrDefault(name, ZERO);
if (value instanceof ParameterValueGroup) {
/*
* Contains sub-group - invokes 'copy' recursively.
* The target group may exist, but not necessarily.
*/
final List<ParameterValueGroup> groups = destination.groups(name);
copy((ParameterValueGroup) value, (occurrence < groups.size())
? groups.get(occurrence) : destination.addGroup(name));
} else {
/*
* Single parameter - copy the value, with special care for value with units
* and for multi-occurrences. Not that the later is not allowed by ISO 19111
* but supported by SIS implementation.
*/
final ParameterValue<?> source = (ParameterValue<?>) value;
final ParameterValue<?> target;
if (occurrence == 0) {
try {
target = destination.parameter(name);
} catch (ParameterNotFoundException cause) {
throw (InvalidParameterNameException) new InvalidParameterNameException(Errors.format(
Errors.Keys.UnexpectedParameter_1, name), name).initCause(cause);
}
} else {
target = (ParameterValue<?>) getOrCreate(destination, name, occurrence);
}
final Object v = source.getValue();
final Unit<?> unit = source.getUnit();
if (unit == null) {
target.setValue(v);
} else if (v instanceof Number) {
target.setValue(((Number) v).doubleValue(), unit);
} else if (v instanceof double[]) {
target.setValue((double[]) v, unit);
} else if (v != target.getValue()) { // Accept null value if the target value is already null.
throw new InvalidParameterValueException(Errors.format(
Errors.Keys.IllegalArgumentValue_2, name, v), name, v);
}
}
occurrences.put(name, occurrence + 1);
}
}
/**
* Returns the <var>n</var>th occurrence of the parameter of the given name.
* This method is not public because ISO 19111 does not allow multi-occurrences of parameter values
* (this is a SIS-specific flexibility). Current implementation is not very efficient, but it should
* not be an issue if this method is rarely invoked.
*
* @param values the group from which to get or create a value
* @param name the name of the parameter to fetch. An exact match will be required.
* @param n number of occurrences to skip before to return or create the parameter.
* @return the <var>n</var>th occurrence (zero-based) of the parameter of the given name.
* @throws IndexOutOfBoundsException if {@code n} is greater than the current number of
* parameters of the given name.
*/
private static GeneralParameterValue getOrCreate(final ParameterValueGroup values, final String name, int n) {
for (final GeneralParameterValue value : values.values()) {
if (name.equals(value.getDescriptor().getName().getCode())) {
if (--n < 0) {
return value;
}
}
}
if (n == 0) {
final GeneralParameterValue value = values.getDescriptor().descriptor(name).createValue();
values.values().add(value);
return value;
} else {
/*
* We do not botter formatting a good error message for now, because
* this method is currently invoked only with increasing index values.
*/
throw new IndexOutOfBoundsException(name);
}
}
/**
* Returns a string representation of this group.
* The default implementation delegates to {@link ParameterFormat}.
*
* <p>This method is for information purpose only and may change in future SIS version.</p>
*
* @since 0.7
*/
@Override
public String toString() {
return ParameterFormat.sharedFormat(this);
}
/**
* Prints a string representation of this group to the {@linkplain System#out standard output stream}.
* If a {@linkplain java.io.Console console} is attached to the running JVM (i.e. if the application
* is run from the command-line and the output is not redirected to a file) and if Apache SIS thinks
* that the console supports the ANSI escape codes (a.k.a. X3.64), then a syntax coloring will be applied.
*
* <p>This is a convenience method for debugging purpose and for console applications.</p>
*
* @since 0.7
*/
@Debug
public void print() {
ParameterFormat.print(this);
}
}