/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2004-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2012, Geomatys * * 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.geotoolkit.parameter; import java.net.URI; import javax.measure.Unit; import javax.measure.IncommensurableException; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.InvalidParameterTypeException; import org.opengis.parameter.InvalidParameterValueException; import org.apache.sis.util.ArraysExt; import org.geotoolkit.util.Utilities; import org.geotoolkit.resources.Errors; import static org.apache.sis.util.ArgumentChecks.ensureNonNull; /** * A parameter value as a floating point (double precision) number. * This class provides the same functionalities than {@link Parameter}, except that: * <p> * <ul> * <li>Values are always floating point numbers of type {@code double}.</li> * </ul> * <p> * When those conditions are meet, {@code FloatParameter} is slightly more efficient * than {@code Parameter} since it avoid the creation of {@link Double} wrapper objects. * * {@section Implementation note for subclasses} * Except for the constructors, the {@link #equals(Object)} and the {@link #hashCode()} methods, * all read and write operations ultimately delegates to the following methods: * <p> * <ul> * <li>All getter methods will invoke {@link #doubleValue()} and - if needed - {@link #getUnit()}, * then performs their processing on the values returned by those methods.</li> * <li>All setter methods will first check the argument validity, * then pass the values to the {@link #setValue(double, Unit)} method.</li> * </ul> * <p> * Consequently, the above-cited methods provide single points that subclasses can override * for modifying the behavior of all getter and setter methods. * * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.20 * * @see DefaultParameterDescriptor * @see ParameterGroup * * @since 2.0 * @module * * @deprecated Replaced by {@link org.apache.sis.parameter.DefaultParameterValue}. */ @Deprecated public class FloatParameter extends AbstractParameterValue<Double> { /** * Serial number for inter-operability with different versions. */ private static final long serialVersionUID = 9027797654033417816L; /** * The value, or {@code NaN} if undefined. Except for the constructors, the * {@link #equals(Object)} and the {@link #hashCode()} methods, this field is * read only by {@link #doubleValue()} and written by {@link #setValue(double, Unit)}. */ private double value; /** * The unit of measure for the value, or {@code null} if it doesn't apply. Except for the * constructors, the {@link #equals(Object)} and the {@link #hashCode()} methods, this field * is read only by {@link #getUnit()} and written by {@link #setValue(double, Unit)}. */ private Unit<?> unit; /** * Constructs a parameter from the specified descriptor. * * @param descriptor The abstract definition of this parameter. * @throws IllegalArgumentException if the value class is not {@code Double.class}. */ public FloatParameter(final ParameterDescriptor<Double> descriptor) { super(descriptor); final Class<Double> type = descriptor.getValueClass(); final Class<Double> expected = Double.class; if (!expected.equals(type) && !Double.TYPE.equals(type)) { throw new IllegalArgumentException(Errors.format( Errors.Keys.IllegalClass_2, type, expected)); } value = defaultValue(descriptor); unit = descriptor.getUnit(); } /** * Returns the default value from the given parameter descriptor, or {@code NaN} if none. */ private static double defaultValue(final ParameterDescriptor<Double> descriptor) { final Number value = (Number) descriptor.getDefaultValue(); return (value != null) ? value.doubleValue() : Double.NaN; } /** * Constructs a parameter from the specified descriptor and value. This convenience * constructor is equivalents to the one-argument constructor followed by a call to * {@link #setValue(double)}. * * @param descriptor The abstract definition of this parameter. * @param value The parameter value. * @throws IllegalArgumentException if the value class is not {@code Double.class}. */ public FloatParameter(final ParameterDescriptor<Double> descriptor, final double value) { this(descriptor); setValue(value); } /** * Returns the unit of measure of the {@linkplain #doubleValue() parameter value}. * The default value is {@link ParameterDescriptor#getUnit()}. * * {@section Implementation note for subclasses} * All getter methods which need unit information will invoke this {@code getUnit()} method. * Subclasses can override this method if they need to compute the unit dynamically. * * @return The unit of measure, or {@code null} if none. */ @Override public Unit<?> getUnit() { return unit; } /** * Returns the numeric value of the coordinate operation parameter in the specified unit * of measure. This convenience method applies unit conversions on the fly as needed. * * @param unit The unit of measure for the value to be returned. * @return The numeric value represented by this parameter after conversion to {@code unit}, * or {@link Double#NaN} if none. * @throws IllegalArgumentException if the specified unit is invalid for this parameter. */ @Override public double doubleValue(final Unit<?> unit) throws IllegalArgumentException { ensureNonNull("unit", unit); final Unit<?> thisUnit = getUnit(); if (thisUnit == null) { throw unitlessParameter(descriptor); } final short expectedID = getUnitMessageID(thisUnit); if (getUnitMessageID(unit) != expectedID) { throw new IllegalArgumentException(Errors.format(expectedID, unit)); } try { return thisUnit.getConverterToAny(unit).convert(doubleValue()); } catch (IncommensurableException e) { throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnit_1, unit), e); } } /** * Returns the numeric value represented by this operation parameter. * The units of measurement are specified by {@link #getUnit()}. * * {@section Implementation note for subclasses} * All getter methods will invoke this {@code doubleValue()} method. Subclasses can override * this method if they need to compute the value dynamically. * * @return The numeric value represented by this parameter, or {@link Double#NaN} if none. */ @Override public double doubleValue() { return value; } /** * Returns the numeric value casted to integer. If the current {@linkplain #doubleValue() * double value} can not be casted without precision loss, then this method throws an * exception. * * @return The numeric value represented by this parameter after conversion to type {@code int}. * @throws InvalidParameterTypeException If the value can not be casted to integer. * @throws IllegalStateException if the value is not defined and there is no default value. */ @Override public int intValue() throws IllegalStateException { final double value = doubleValue(); final int integer = (int) value; if (integer == value) { return integer; } final String name = getName(descriptor); if (Double.isNaN(value)) { // If a default value existed, it should has been copied by the constructor or setter methods. throw new IllegalStateException(Errors.format(Errors.Keys.NoParameter_1, name)); } throw new InvalidParameterTypeException(getClassTypeError(), name); } /** * Returns {@code true} if the value is different from 0, or {@code false} otherwise. * * @return The boolean value represented by this parameter. * @throws IllegalStateException if the value is not defined and there is no default value. */ @Override public boolean booleanValue() throws IllegalStateException { final double value = doubleValue(); if (Double.isNaN(value)) { throw new IllegalStateException(Errors.format(Errors.Keys.NoParameter_1, getName(descriptor))); } return value != 0; } /** * Returns the string representation of the value. * * @return The string value represented by this parameter. * @throws IllegalStateException if the value is not defined and there is no default value. */ @Override public String stringValue() throws IllegalStateException { final double value = doubleValue(); if (Double.isNaN(value)) { throw new IllegalStateException(Errors.format(Errors.Keys.NoParameter_1, getName(descriptor))); } return String.valueOf(value); } /** * Wraps the value in an array of length 0 or 1. * * @param unit The unit of measure for the value to be returned. * @return The sequence of values represented by this parameter after conversion to type * {@code double} and conversion to {@code unit}. * @throws IllegalArgumentException if the specified unit is invalid for this parameter. */ @Override public double[] doubleValueList(final Unit<?> unit) throws IllegalArgumentException { final double value = doubleValue(unit); return Double.isNaN(value) ? ArraysExt.EMPTY_DOUBLE: new double[] {value}; } /** * Wraps the value in an array of length 0 or 1. * * @return The sequence of values represented by this parameter. */ @Override public double[] doubleValueList() { final double value = doubleValue(); return Double.isNaN(value) ? ArraysExt.EMPTY_DOUBLE: new double[] {value}; } /** * Wraps the value in an array of length 1. * * @return The sequence of values represented by this parameter. * @throws InvalidParameterTypeException If the value can not be casted to integer. */ @Override public int[] intValueList() throws InvalidParameterTypeException { return new int[] {intValue()}; } /** * Always throws an exception, since this parameter is not an URI. * * @return Never return. * @throws InvalidParameterTypeException The value is not a reference to a file or an URI. */ @Override public URI valueFile() throws InvalidParameterTypeException { throw new InvalidParameterTypeException(getClassTypeError(), getName(descriptor)); } /** * Format an error message for illegal method call for the current value type. */ private static String getClassTypeError() { return Errors.format(Errors.Keys.IllegalOperationForValueClass_1, Double.class); } /** * Returns the parameter value as a {@link Double}, or {@code null} if none. * * @return The parameter value as an object, or {@code null} * if the current value is {@link Double#NaN}. */ @Override public Double getValue() { final double value = doubleValue(); return Double.isNaN(value) ? null : Double.valueOf(value); } /** * Sets the parameter value as a floating point and its associated unit. * The default implementation verifies the arguments validity, then stores the given values. * * {@section Implementation note for subclasses} * This method is invoked by all other {@code setXXX(…)} methods. Subclasses can override * this method if they want to perform more processing on the value before its storage, * or to be notified about value changes. * * @param value The parameter value. * @param unit The new unit of measurement. * @throws InvalidParameterValueException if the value is illegal for some reason * (for example a value out of range). */ @Override public void setValue(final double value, final Unit<?> unit) throws InvalidParameterValueException { double converted = value; @SuppressWarnings("unchecked") // Checked by constructor. final ParameterDescriptor<Double> descriptor = (ParameterDescriptor<Double>) this.descriptor; final Unit<?> targetUnit = descriptor.getUnit(); if (targetUnit != unit) { if (targetUnit == null) { throw unitlessParameter(descriptor); } else { ensureNonNull("unit", unit); final short expectedID = getUnitMessageID(targetUnit); if (getUnitMessageID(unit) != expectedID) { throw new InvalidParameterValueException(Errors.format(expectedID, unit), descriptor.getName().getCode(), value); } try { converted = unit.getConverterToAny(targetUnit).convert(value); } catch (IncommensurableException e) { throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnit_1, unit), e); } } } /* * Really store the original value, not the converted one, because we store the given * unit as well. Conversions will be applied on the fly by the getter method if needed. */ if (Double.isNaN(value)) { this.value = defaultValue(descriptor); } else { ensureValidValue(descriptor, converted); this.value = value; } this.unit = unit; // Store only after successful validation. fireValueChanged(); } /** * Sets the parameter value as a floating point. * The default implementation delegates to {@link #setValue(double, Unit)}. * * @param value The parameter value. * @throws InvalidParameterValueException if the value is illegal for some reason * (for example a value out of range). */ @Override public void setValue(final double value) throws InvalidParameterValueException { setValue(value, unit); } /** * Sets the parameter value as an integer. * The default implementation delegates to {@link #setValue(double)}. * * @param value The parameter value. * @throws InvalidParameterValueException if the value is illegal for some reason * (for example a value out of range). */ @Override public void setValue(final int value) throws InvalidParameterValueException { setValue((double) value); } /** * Sets the parameter value as a boolean. The default implementation delegates to * {@link #setValue(double)} with value 1 for {@code true} or 0 or {@code false}. * * @param value The parameter value. * @throws InvalidParameterValueException if the boolean type is inappropriate for this parameter. */ @Override public void setValue(final boolean value) throws InvalidParameterValueException { setValue(value ? 1.0 : 0.0); } /** * Sets the parameter value as a {@link Double} object. The default implementation ensures * that the given value is a {@link Number}, then delegates to {@link #setValue(double)}. * * @param value The parameter value, or {@code null} for {@link Double#NaN}. * @throws InvalidParameterValueException if the type of {@code value} is inappropriate * for this parameter, or if the value is illegal for some other reason (for example * the value is numeric and out of range). */ @Override public void setValue(final Object value) throws InvalidParameterValueException { final double number; if (value == null) { number = Double.NaN; } else if (value instanceof Number) { number = ((Number) value).doubleValue(); } else { throw new InvalidParameterValueException(getClassTypeError(), getName(descriptor), value); } setValue(number); } /** * If the length of the given array is 1, delegates to {@link #setValue(double, Unit)}. * Otherwise throws an exception, since this parameter does not accept arbitrary arrays. */ @Override public void setValue(final double[] values, final Unit<?> unit) throws InvalidParameterValueException { ensureNonNull("values", values); if (values.length == 1) { setValue(values[0], unit); } else { throw new InvalidParameterValueException(getClassTypeError(), getName(descriptor), values); } } /** * Compares the specified object with this parameter for equality. * * @param object The object to compare to {@code this}. * @return {@code true} if both objects are equal. */ @Override public boolean equals(final Object object) { if (super.equals(object)) { final FloatParameter that = (FloatParameter) object; return Double.doubleToLongBits(this.value) == Double.doubleToLongBits(that.value); } 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() { return Utilities.hash(value, super.hashCode()); } /** * Returns a clone of this parameter. */ @Override public FloatParameter clone() { return (FloatParameter) super.clone(); } }