| /* |
| * 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); |
| } |
| } |