/* * Geotools 2 - OpenSource mapping toolkit * (C) 2004, Geotools Project Managment Committee (PMC) * (C) 2004, Institut de Recherche pour le Développement * * 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; either * version 2.1 of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.parameter; // J2SE dependencies and extensions import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.units.Unit; // OpenGIS dependencies import org.opengis.util.CodeList; import org.opengis.util.InternationalString; import org.opengis.metadata.citation.Citation; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.GeneralParameterValue; // Geotools dependencies import org.geotools.metadata.iso.IdentifierImpl; import org.geotools.referencing.AbstractIdentifiedObject; import org.geotools.resources.ClassChanger; import org.geotools.resources.Utilities; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * 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. *

* For numeric values, the {@linkplain #getValueClass value class} is usually * {@linkplain Double}.class, {@linkplain Integer}.class or * some other Java wrapper class. *

* 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. * * @since 2.1 * @source $URL$ * @version $Id$ * @author Martin Desruisseaux * * @see Parameter * @see DefaultParameterDescriptorGroup */ public class DefaultParameterDescriptor extends AbstractParameterDescriptor implements ParameterDescriptor { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -295668622297737705L; /** * The class that describe the type of the parameter. * This class can never be a primitive. */ private final Class valueClass; /** * The class that describe the type of the parameter, maybe as a primitive. This is the * value class that the user specified at construction time. This is usually identical * to {@code valueClass}. However, some optimization may be done for some primitive * types, for example a special implementation of {@link Parameter} for the * {@code double} type. */ private final Class primitiveClass; /** * 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 validValues; /** * The default value for the parameter, or {@code null}. */ private final Object defaultValue; /** * The minimum parameter value, or {@code null}. */ private final Comparable minimum; /** * The maximum parameter value, or {@code null}. */ private final Comparable 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. * * @since 2.2 */ public DefaultParameterDescriptor(final ParameterDescriptor descriptor) { super(descriptor); valueClass = descriptor.getValueClass(); validValues = descriptor.getValidValues(); defaultValue = descriptor.getDefaultValue(); minimum = descriptor.getMinimumValue(); maximum = descriptor.getMaximumValue(); unit = descriptor.getUnit(); primitiveClass = (descriptor instanceof DefaultParameterDescriptor) ? ((DefaultParameterDescriptor) descriptor).primitiveClass : valueClass; } /** * Constructs a mandatory parameter for 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. */ public DefaultParameterDescriptor(final String name, final int defaultValue, final int minimum, final int maximum) { this(Collections.singletonMap(NAME_KEY, name), defaultValue, minimum, maximum, true); } /** * Constructs a parameter for 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. */ public DefaultParameterDescriptor(final Map properties, final int defaultValue, final int minimum, final int maximum, final boolean required) { this(properties, required, Integer.class, null, Parameter.wrap(defaultValue), minimum == Integer.MIN_VALUE ? null : Parameter.wrap(minimum), maximum == Integer.MAX_VALUE ? null : Parameter.wrap(maximum), null); } /** * Constructs a mandatory parameter for 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. */ public DefaultParameterDescriptor(final String name, final double defaultValue, final double minimum, final double maximum, final Unit unit) { this(Collections.singletonMap(NAME_KEY, name), defaultValue, minimum, maximum, unit, true); } /** * Constructs a parameter for 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 for default, minimum and maximum values. * @param required {@code true} if this parameter is required, {@code false} otherwise. */ public DefaultParameterDescriptor(final Map properties, final double defaultValue, final double minimum, final double maximum, final Unit unit, final boolean required) { this(properties, required, Double.class, null, Double.isNaN(defaultValue) ? null : Parameter.wrap(defaultValue), minimum == Double.NEGATIVE_INFINITY ? null : Parameter.wrap(minimum), maximum == Double.POSITIVE_INFINITY ? null : Parameter.wrap(maximum), unit); } /** * Constructs a parameter for a name and a default value. The parameter type will * be assumed the same than the default value class. * * @param name The parameter name. * @param remarks An optional description as a {@link String} or an * {@link InternationalString}, or {@code null} if none. * @param defaultValue The default value. * @param required {@code true} if this parameter is required, {@code false} otherwise. */ public DefaultParameterDescriptor(final String name, final CharSequence remarks, final Object defaultValue, final boolean required) { this(toMap(name, remarks), defaultValue.getClass(), (defaultValue instanceof CodeList) ? getCodeLists(defaultValue.getClass()) : null, defaultValue, null, null, null, required); } /** * Work around for RFE #4093999 in Sun's bug database * ("Relax constraint on placement of this()/super() call in constructors"). */ private static final Map toMap(final String name, final CharSequence remarks) { if (remarks == null ){ return Collections.singletonMap(NAME_KEY, name); } final Map properties = new HashMap(4); properties.put(NAME_KEY, name); properties.put(REMARKS_KEY, remarks); return properties; } /** * Constructs a parameter for a {@linkplain CodeList code list} (or enumeration). * * @param name The parameter name. * @param defaultValue The default value. */ public DefaultParameterDescriptor(final String name, final CodeList defaultValue) { this(name, defaultValue.getClass(), defaultValue); } /** * Constructs a parameter for a {@linkplain CodeList code list} (or enumeration). This * constructor is used by the {@link #DefaultParameterDescriptor(String,CodeList)} constructor. * * @param name The parameter name. * @param valueClass The class that describe the type of the parameter. * Must be a subclass of {@link CodeList}. * @param defaultValue The default value, or {@code null}. */ DefaultParameterDescriptor(final String name, final Class valueClass, final CodeList defaultValue) { this(name, valueClass, getCodeLists(valueClass), defaultValue); } /** * Returns the enumeration found in the specified {@code CodeList} class. * Returns {@code null} if no values were found. */ private static CodeList[] getCodeLists(final Class type) { try { return (CodeList[]) type.getMethod("values", (Class[])null) .invoke(null, (Object[]) null); } catch (Exception exception) { // No code list defined. Not a problem; we will just // not provide any set of code to check against. return null; } } /** * 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 valueClass, final Object[] validValues, final Object 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 valueClass, final Object[] validValues, final Object defaultValue, final Comparable minimum, final Comparable maximum, final Unit unit, final boolean required) { this(Collections.singletonMap(NAME_KEY, new IdentifierImpl(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 "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 properties, final Class valueClass, final Object[] validValues, final Object defaultValue, final Comparable minimum, final Comparable maximum, final Unit unit, final boolean required) { this(properties, required, valueClass, validValues, defaultValue, Parameter.replace(minimum), Parameter.replace(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}. *

* 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 "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 properties, final boolean required, Class valueClass, final Object[] validValues, final Object defaultValue, final Comparable minimum, final Comparable maximum, final Unit unit) { super(properties, required ? 1 : 0, 1); this.primitiveClass = valueClass; this.defaultValue = defaultValue; this.minimum = minimum; this.maximum = maximum; this.unit = unit; ensureNonNull("valueClass", valueClass); if (valueClass.isPrimitive()) { valueClass = ClassChanger.toWrapper(valueClass); } this.valueClass = valueClass; AbstractParameter.ensureValidClass(valueClass, defaultValue); AbstractParameter.ensureValidClass(valueClass, minimum); AbstractParameter.ensureValidClass(valueClass, maximum); if (minimum!=null && maximum!=null) { if (minimum.compareTo(maximum) > 0) { throw new IllegalArgumentException(Errors.format( ErrorKeys.BAD_RANGE_$2, minimum, maximum)); } } if (validValues != null) { final Set valids = new HashSet(Math.max(validValues.length*4/3 + 1, 8), 0.75f); for (int i=0; i * If the {@linkplain #getValueClass value class} specified at construction time was * a primitive type (e.g. Double.{@linkplain Double#TYPE TYPE} instead * of {@linkplain Double}.class), then this method may returns a specialized * parameter value implementation for this primitive type. Specialized implementations may * use less storage space and be more flexible during conversions (for example from * {@code float} to {@link String}), but this flexibility is not always wanted. */ public GeneralParameterValue createValue() { if (Double.TYPE.equals(primitiveClass)) { return new FloatParameter(this); } return new Parameter(this); } /** * Returns the class that describe the type of the parameter. If the value class specified * at construction time was a primitive type (e.g. {@code double}), it is converted to * the corresponding wrapper class (e.g. {@link Double}). * * @return The parameter value class (never a primitive type). */ public Class 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 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 Object 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 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 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. */ 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.primitiveClass, that.primitiveClass) && 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. */ 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; } }