/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*/
package org.geotools.parameter;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.measure.unit.Unit;
import org.geotools.referencing.AbstractIdentifiedObject;
import org.geotools.referencing.NamedIdentifier;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.util.Utilities;
import org.opengis.metadata.citation.Citation;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.util.CodeList;
import org.opengis.util.InternationalString;
/**
* The definition of a parameter used by an operation method.
* For {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem Coordinate
* Reference Systems} most parameter values are numeric, but other types
* of parameter values are possible.
* <P>
* For numeric values, the {@linkplain #getValueClass value class} is usually
* <code>{@linkplain Double}.class</code>, <code>{@linkplain Integer}.class</code> or
* some other Java wrapper class.
* <P>
* This class contains numerous convenience constructors. But all of them ultimately invoke
* {@linkplain #DefaultParameterDescriptor(Map,Class,Object[],Object,Comparable,Comparable,Unit,boolean)
* a single, full-featured constructor}. All other constructors are just shortcuts.
*
* @param <T> The type of elements to be returned by {@link ParameterValue#getValue}.
*
* @since 2.1
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*
* @see Parameter
* @see DefaultParameterDescriptorGroup
*/
public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor
implements ParameterDescriptor<T>
{
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = -295668622297737705L;
/**
* The class that describe the type of the parameter.
* This is the value class that the user specified at construction time.
*/
private final Class<T> valueClass;
/**
* A immutable, finite set of valid values (usually from a {linkplain org.opengis.util.CodeList
* code list}) or {@code null} if it doesn't apply. This set is immutable.
*/
private final Set<T> validValues;
/**
* The default value for the parameter, or {@code null}.
*/
private final T defaultValue;
/**
* The minimum parameter value, or {@code null}.
*/
private final Comparable<T> minimum;
/**
* The maximum parameter value, or {@code null}.
*/
private final Comparable<T> maximum;
/**
* The unit for default, minimum and maximum values, or {@code null}.
*/
private final Unit<?> unit;
/**
* Constructs a descriptor with the same values than the specified one. This copy constructor
* may be used in order to wraps an arbitrary implementation into a Geotools one.
*
* @param descriptor The descriptor to copy.
*
* @since 2.2
*/
public DefaultParameterDescriptor(final ParameterDescriptor<T> descriptor) {
super(descriptor);
valueClass = descriptor.getValueClass();
validValues = descriptor.getValidValues();
defaultValue = descriptor.getDefaultValue();
minimum = descriptor.getMinimumValue();
maximum = descriptor.getMaximumValue();
unit = descriptor.getUnit();
}
/**
* Constructs a mandatory parameter for a set of predefined values.
*
* @param name The parameter name.
* @param valueClass The class that describe the type of the parameter.
* @param validValues A finite set of valid values (usually from a
* {linkplain org.opengis.util.CodeList code list}) or {@code null}
* if it doesn't apply.
* @param defaultValue The default value for the parameter, or {@code null}.
*/
public DefaultParameterDescriptor(final String name,
final Class<T> valueClass,
final T[] validValues,
final T defaultValue)
{
this(Collections.singletonMap(NAME_KEY, name),
valueClass, validValues, defaultValue, null, null, null, true);
}
/**
* Constructs a parameter from an authority and a name.
*
* @param authority The authority (e.g.
* {@link org.geotools.metadata.iso.citation.Citations#OGC OGC}).
* @param name The parameter name.
* @param valueClass The class that describe the type of the parameter.
* @param validValues A finite set of valid values (usually from a
* {linkplain org.opengis.util.CodeList code list}) or {@code null}
* if it doesn't apply.
* @param defaultValue The default value for the parameter, or {@code null}.
* @param minimum The minimum parameter value, or {@code null}.
* @param maximum The maximum parameter value, or {@code null}.
* @param unit The unit for default, minimum and maximum values.
* @param required {@code true} if this parameter is required,
* or {@code false} if it is optional.
*
* @since 2.2
*/
public DefaultParameterDescriptor(final Citation authority,
final String name,
final Class<T> valueClass,
final T[] validValues,
final T defaultValue,
final Comparable<T> minimum,
final Comparable<T> maximum,
final Unit<?> unit,
final boolean required)
{
this(Collections.singletonMap(NAME_KEY, new NamedIdentifier(authority, name)),
valueClass, validValues, defaultValue, minimum, maximum, unit, required);
}
/**
* Constructs a parameter from a set of properties. The properties map is
* given unchanged to the {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map)
* super-class constructor}.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
* @param valueClass The class that describe the type of the parameter.
* @param validValues A finite set of valid values (usually from a
* {linkplain org.opengis.util.CodeList code list}) or {@code null}
* if it doesn't apply.
* @param defaultValue The default value for the parameter, or {@code null}.
* @param minimum The minimum parameter value, or {@code null}.
* @param maximum The maximum parameter value, or {@code null}.
* @param unit The unit for default, minimum and maximum values.
* @param required {@code true} if this parameter is required,
* or {@code false} if it is optional.
*/
public DefaultParameterDescriptor(final Map<String,?> properties,
final Class<T> valueClass,
final T[] validValues,
final T defaultValue,
final Comparable<T> minimum,
final Comparable<T> maximum,
final Unit<?> unit,
final boolean required)
{
this(properties, required, valueClass, validValues, defaultValue, minimum, maximum, unit);
}
/**
* Constructs a parameter from a set of properties. The properties map is given unchanged to the
* {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
* <p>
* This constructor assumes that minimum, maximum and default values are
* already replaced by their cached values, if available.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
* @param required {@code true} if this parameter is required, or {@code false}
* if it is optional.
* @param valueClass The class that describe the type of the parameter.
* @param validValues A finite set of valid values (usually from a
* {linkplain org.opengis.util.CodeList code list}) or {@code null}
* if it doesn't apply.
* @param defaultValue The default value for the parameter, or {@code null}.
* @param minimum The minimum parameter value, or {@code null}.
* @param maximum The maximum parameter value, or {@code null}.
* @param unit The unit for default, minimum and maximum values.
*/
private DefaultParameterDescriptor(final Map<String,?> properties,
final boolean required,
final Class<T> valueClass,
final T[] validValues,
final T defaultValue,
final Comparable<T> minimum,
final Comparable<T> maximum,
final Unit<?> unit)
{
super(properties, required ? 1 : 0, 1);
this.valueClass = valueClass;
this.defaultValue = defaultValue;
this.minimum = minimum;
this.maximum = maximum;
this.unit = unit;
ensureNonNull("valueClass", valueClass);
AbstractParameter.ensureValidClass(valueClass, defaultValue);
AbstractParameter.ensureValidClass(valueClass, minimum);
AbstractParameter.ensureValidClass(valueClass, maximum);
if (minimum!=null && maximum!=null) {
if (minimum.compareTo(valueClass.cast(maximum)) > 0) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.BAD_RANGE_$2, minimum, maximum));
}
}
if (validValues != null) {
final Set<T> valids = new HashSet<T>(Math.max(validValues.length*4/3 + 1, 8), 0.75f);
for (int i=0; i<validValues.length; i++) {
final T value = validValues[i];
AbstractParameter.ensureValidClass(valueClass, value);
valids.add(value);
}
this.validValues = Collections.unmodifiableSet(valids);
} else {
this.validValues = null;
}
if (defaultValue != null) {
Parameter.ensureValidValue(this, defaultValue);
}
}
/**
* Constructs a descriptor for a mandatory parameter in a range of integer values.
*
* @param name The parameter name.
* @param defaultValue The default value for the parameter.
* @param minimum The minimum parameter value, or {@link Integer#MIN_VALUE} if none.
* @param maximum The maximum parameter value, or {@link Integer#MAX_VALUE} if none.
* @return The parameter descriptor for the given range of values.
*
* @since 2.5
*/
public static DefaultParameterDescriptor<Integer> create(final String name,
final int defaultValue, final int minimum, final int maximum)
{
return create(Collections.singletonMap(NAME_KEY, name),
defaultValue, minimum, maximum, true);
}
/**
* Constructs a descriptor for a parameter in a range of integer values.
*
* @param properties The parameter properties (name, identifiers, alias...).
* @param defaultValue The default value for the parameter.
* @param minimum The minimum parameter value, or {@link Integer#MIN_VALUE} if none.
* @param maximum The maximum parameter value, or {@link Integer#MAX_VALUE} if none.
* @param required {@code true} if this parameter is required, {@code false} otherwise.
* @return The parameter descriptor for the given range of values.
*
* @since 2.5
*/
public static DefaultParameterDescriptor<Integer> create(final Map<String,?> properties,
final int defaultValue, final int minimum, final int maximum, final boolean required)
{
return new DefaultParameterDescriptor<Integer>(properties, required,
Integer.class, null, Integer.valueOf(defaultValue),
minimum == Integer.MIN_VALUE ? null :Integer.valueOf( minimum),
maximum == Integer.MAX_VALUE ? null : Integer.valueOf(maximum),
null);
}
/**
* Constructs a descriptor for a mandatory parameter in a range of floating point values.
*
* @param name The parameter name.
* @param defaultValue The default value for the parameter, or {@link Double#NaN} if none.
* @param minimum The minimum parameter value, or {@link Double#NEGATIVE_INFINITY} if none.
* @param maximum The maximum parameter value, or {@link Double#POSITIVE_INFINITY} if none.
* @param unit The unit for default, minimum and maximum values.
* @return The parameter descriptor for the given range of values.
*
* @since 2.5
*/
public static DefaultParameterDescriptor<Double> create(final String name,
final double defaultValue, final double minimum, final double maximum, final Unit<?> unit)
{
return create(Collections.singletonMap(NAME_KEY, name),
defaultValue, minimum, maximum, unit, true);
}
/**
* Constructs a descriptor for a parameter in a range of floating point values.
*
* @param properties The parameter properties (name, identifiers, alias...).
* @param defaultValue The default value for the parameter, or {@link Double#NaN} if none.
* @param minimum The minimum parameter value, or {@link Double#NEGATIVE_INFINITY} if none.
* @param maximum The maximum parameter value, or {@link Double#POSITIVE_INFINITY} if none.
* @param unit The unit of measurement for default, minimum and maximum values.
* @param required {@code true} if this parameter is required, {@code false} otherwise.
* @return The parameter descriptor for the given range of values.
*
* @since 2.5
*/
public static DefaultParameterDescriptor<Double> create(final Map<String,?> properties,
final double defaultValue, final double minimum, final double maximum,
final Unit<?> unit, final boolean required)
{
return new DefaultParameterDescriptor<Double>(properties, required, Double.class, null,
Double.isNaN(defaultValue) ? null : Double.valueOf(defaultValue),
minimum == Double.NEGATIVE_INFINITY ? null : Double.valueOf(minimum),
maximum == Double.POSITIVE_INFINITY ? null : Double.valueOf(maximum), unit);
}
/**
* Constructs a descriptor from a name and a default value.
*
* @param <T> The parameter type.
* @param name The parameter name.
* @param remarks An optional description as a {@link String} or an {@link InternationalString}.
* @param valueClass The parameter type.
* @param defaultValue The default value.
* @param required {@code true} if this parameter is required, {@code false} otherwise.
* @return The parameter descriptor for the given default value.
*
* @since 2.5
*/
public static <T> DefaultParameterDescriptor<T> create(final String name,
final CharSequence remarks, final Class<T> valueClass,
final T defaultValue, final boolean required)
{
T[] codeList = null;
if (CodeList.class.isAssignableFrom(valueClass)) {
try {
@SuppressWarnings("unchecked") // Type checked with reflection.
final T[] tmp = (T[]) valueClass.getMethod("values",
(Class<?>[]) null).invoke(null, (Object[]) null);
codeList = tmp;
} catch (Exception exception) {
// No code list defined. Not a problem; we will just
// not provide any set of code to check against.
}
}
final Map<String,CharSequence> properties;
if (remarks == null ){
properties = Collections.singletonMap(NAME_KEY, (CharSequence) name);
} else {
properties = new HashMap<String,CharSequence>(4);
properties.put(NAME_KEY, name);
properties.put(REMARKS_KEY, remarks);
}
return new DefaultParameterDescriptor<T>(properties, valueClass,
codeList, defaultValue, null, null, null, required);
}
/**
* The maximum number of times that values for this parameter group or
* parameter can be included. For a {@linkplain DefaultParameterDescriptor
* single parameter}, the value is always 1.
*
* @return The maximum occurence.
*
* @see #getMinimumOccurs
*/
public int getMaximumOccurs() {
return 1;
}
/**
* Creates a new instance of {@linkplain org.geotools.parameter.Parameter parameter value}
* initialized with the {@linkplain #getDefaultValue default value}.
* The {@linkplain org.geotools.parameter.Parameter#getDescriptor parameter value
* descriptor} for the created parameter value will be {@code this} object.
*
* @return A parameter initialized to the default value.
*/
@SuppressWarnings("unchecked")
public ParameterValue<T> createValue() {
if (Double.class.equals(valueClass) && unit == null) {
return (ParameterValue) new FloatParameter((ParameterDescriptor) this);
}
return new Parameter<T>(this);
}
/**
* Returns the class that describe the type of the parameter.
*
* @return The parameter value class.
*/
public Class<T> getValueClass() {
return valueClass;
}
/**
* If this parameter allows only a finite set of values, returns this set.
* This set is usually a {linkplain org.opengis.util.CodeList code list} or
* enumerations. This method returns {@code null} if this parameter
* doesn't limits values to a finite set.
*
* @return A finite set of valid values (usually from a
* {linkplain org.opengis.util.CodeList code list}),
* or {@code null} if it doesn't apply.
*/
public Set<T> getValidValues() {
return validValues;
}
/**
* Returns the default value for the parameter. The return type can be any type
* including a {@link Number} or a {@link String}. If there is no default value,
* then this method returns {@code null}.
*
* @return The default value, or {@code null} in none.
*/
public T getDefaultValue() {
return defaultValue;
}
/**
* Returns the minimum parameter value. If there is no minimum value, or if minimum
* value is inappropriate for the {@linkplain #getValueClass parameter type}, then
* this method returns {@code null}.
*
* @return The minimum parameter value (often an instance of {@link Double}), or {@code null}.
*/
public Comparable<T> getMinimumValue() {
return minimum;
}
/**
* Returns the maximum parameter value. If there is no maximum value, or if maximum
* value is inappropriate for the {@linkplain #getValueClass parameter type}, then
* this method returns {@code null}.
*
* @return The minimum parameter value (often an instance of {@link Double}), or {@code null}.
*/
public Comparable<T> getMaximumValue() {
return maximum;
}
/**
* Returns the unit for
* {@linkplain #getDefaultValue default},
* {@linkplain #getMinimumValue minimum} and
* {@linkplain #getMaximumValue maximum} values.
* This attribute apply only if the values is of numeric type (usually an instance
* of {@link Double}).
*
* @return The unit for numeric value, or {@code null} if it
* doesn't apply to the value type.
*/
public Unit<?> getUnit() {
return unit;
}
/**
* Compares the specified object with this parameter for equality.
*
* @param object The object to compare to {@code this}.
* @param compareMetadata {@code true} for performing a strict comparaison, or
* {@code false} for comparing only properties relevant to transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) {
if (object == this) {
return true;
}
if (super.equals(object, compareMetadata)) {
if (!compareMetadata) {
/*
* Tests for name, since parameters with different name have
* completly different meaning. For example there is no difference
* between "semi_major" and "semi_minor" parameters except the name.
* We don't perform this comparaison if the user asked for metadata
* comparaison, because in such case the names have already been
* compared by the subclass.
*/
if (!nameMatches(object. getName().getCode()) &&
!nameMatches(object, getName().getCode()))
{
return false;
}
}
final DefaultParameterDescriptor that = (DefaultParameterDescriptor) object;
return Utilities.equals(this.validValues, that.validValues) &&
Utilities.equals(this.defaultValue, that.defaultValue) &&
Utilities.equals(this.minimum, that.minimum) &&
Utilities.equals(this.maximum, that.maximum) &&
Utilities.equals(this.unit, that.unit);
}
return false;
}
/**
* Returns a hash value for this parameter.
*
* @return The hash code value. This value doesn't need to be the same
* in past or future versions of this class.
*/
@Override
public int hashCode() {
int code = super.hashCode()*37 + valueClass.hashCode();
if (defaultValue != null) code += (37) *defaultValue.hashCode();
if (minimum != null) code += (37*37) *minimum .hashCode();
if (maximum != null) code += (37*37*37)*maximum .hashCode();
if (unit != null) code += unit .hashCode();
return code;
}
}