package uws.job.parameters;
/*
* This file is part of UWSLibrary.
*
* UWSLibrary 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 3 of the License, or
* (at your option) any later version.
*
* UWSLibrary 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 UWSLibrary. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2016 - Astronomisches Rechen Institut (ARI)
*/
import java.io.Serializable;
import uws.UWSException;
/**
* <p>
* Let controlling a numeric parameter. Thus it is possible to set a default but also a minimum and a maximum value.
* Moreover you can indicate whether the value of the parameter can be modified by the user or not after initialization.
* </p>
*
* <p>Here is the logic applied by this controller for a numeric parameter:</p>
* <ul>
* <li>If no value is specified by the UWS client, the default value is returned.
* But if no default value is returned, <code>null</code> will then be returned.</li>
* <li>If the given value is smaller than the minimum (if any is set), the minimum value is returned.</li>
* <li>If the given value is bigger than the maximum (if any is set), the maximum value is returned.</li>
* </ul>
* <p>
* This implementation aims to be generic enough to support most of the numeric parameters, but if the logic
* presented above is not enough or does not fit your needs, you are free to extend it and thus benefit of the
* implementation of most of the functions and attributes of this controller.
* </p>
*
* @author Grégory Mantelet (CDS;ARI)
* @version 4.2 (06/2016)
* @since 4.2
*/
public class NumericParamController implements InputParamController, Serializable {
private static final long serialVersionUID = 1L;
/** The maximum value. */
protected Number minValue = null;
/** The default value. <i>MUST be between {@link #minValue} and {@link #maxValue}</i> */
protected Number defaultValue = null;
/** The maximum value. */
protected Number maxValue = null;
/** Indicates whether the parameter can be modified. */
protected boolean allowModification = true;
/**
* Create a numeric controller with no restriction.
*
* <p>
* A default, minimum and/or maximum value can be set after creation using {@link #setDefault(Number)},
* {@link #setMinimum(Number)} and {@link #setMaximum(Number)}. By default this parameter can always be modified,
* but it can be forbidden using {@link #allowModification(boolean)}.
* </p>
*/
public NumericParamController(){}
/**
* <p>Create a controller for a numeric parameter.
* The default and the maximum value are initialized with the given parameters.
* The third parameter allows also to forbid the modification of the parameter value by the user,
* if set to <i>false</i>.</p>
*
* <p>
* A default and/or maximum value can be modified after creation using {@link #setDefault(Number)}
* and {@link #setMaximum(Number)}. The flag telling whether this parameter can be modified by the user
* can be changed using {@link #allowModification(boolean)}.
* </p>
*
* @param defaultValue Value set by default to the parameter, when none is specified.
* @param minValue Minimum value that can be set. If a smaller value is provided by the user, an exception will be thrown by {@link #check(Object)}.
* @param maxValue Maximum value that can be set. If a bigger value is provided by the user, an exception will be thrown by {@link #check(Object)}.
* @param allowModification <i>true</i> to allow the user to modify this value when creating a job, <i>false</i> otherwise.
*/
public NumericParamController(final Number defaultValue, final Number minValue, final Number maxValue, final boolean allowModification){
reset(defaultValue, minValue, maxValue);
allowModification(allowModification);
}
/* ***************** */
/* GETTERS & SETTERS */
/* ***************** */
/**
* Gets the minimum value of this parameter.
*
* @return The minimum value
* or <code>null</code> if none has been specified.
*/
public final Number getMinimum(){
return minValue;
}
/**
* Sets the minimum value of this parameter.
*
* <p><b>Warning !:</b>
* If the <em>given</em> value is <em>bigger</em> than the {@link #getMaximum() maximum} value,
* the minimum will be set automatically to the {@link #getMaximum() maximum} value ;
* the given one will then be ignored.
* </p>
*
* <p><b>Warning 2:</b>
* If the <em>default</em> value is <em>smaller</em> than the new {@link #getMinimum() minimum} value,
* the default will be set automatically to the new {@link #getMinimum() minimum} value.
* </p>
*
* @param newMinValue The new minimum value. <i><code>null</code> is allowed ; it will remove the constraint on the minimum</i>
*/
public void setMinimum(final Number newMinValue){
// If NULL, set it and return:
if (newMinValue == null)
minValue = null;
// Otherwise:
else{
// Set the minimum to the maximum value if the given value is BIGGER than the current maximum:
if (maxValue != null && newMinValue.doubleValue() > maxValue.doubleValue())
minValue = maxValue;
// Otherwise, set it exactly as provided:
else
minValue = newMinValue;
/* Ensure the default value is still bigger than the minimum.
* If not, set the default to the minimum: */
if (defaultValue != null && defaultValue.doubleValue() < minValue.doubleValue())
defaultValue = minValue;
}
}
/**
* Gets the maximum value of this parameter.
*
* @return The maximum value
* or <code>null</code> if none has been specified.
*/
public final Number getMaximum(){
return maxValue;
}
/**
* Sets the maximum value of this parameter.
*
* <p><b>Warning 1:</b>
* If the <em>given</em> value is <em>smaller</em> than the {@link #getMinimum() minimum} value,
* the maximum will be set automatically to the {@link #getMinimum() minimum} value ;
* the given one will then be ignored.
* </p>
*
* <p><b>Warning 2:</b>
* If the <em>default</em> value is <em>bigger</em> than the new {@link #getMaximum() maximum} value,
* the default will be set automatically to the new {@link #getMaximum() maximum} value.
* </p>
*
* @param newMaxValue The new maximum value. <i><code>null</code> is allowed ; it will remove the constraint on the maximum</i>
*/
public void setMaximum(final Number newMaxValue){
// If NULL, set it and return:
if (newMaxValue == null)
maxValue = null;
// Otherwise:
else{
// Set the maximum to the minimum value if the given value is SMALLER than the current minimum:
if (minValue != null && newMaxValue.doubleValue() < minValue.doubleValue())
maxValue = minValue;
// Otherwise, set it exactly as provided:
else
maxValue = newMaxValue;
/* Ensure the default value is still smaller than the maximum.
* If not, set the default to the maximum: */
if (defaultValue != null && defaultValue.doubleValue() > maxValue.doubleValue())
defaultValue = maxValue;
}
}
@Override
public Object getDefault(){
return defaultValue;
}
/**
* Set the default value that the parameter must have if none is specified by the user.
*
* <p><b>Warning:</b>
* If the given value is not between the {@link #getMinimum() minimum} and the {@link #getMaximum() maximum}
* of this controller, the default value will be automatically set to the {@link #getMinimum() minimum} value.
* </p>
*
* @param newDefaultValue The new default value for this controller.
* <i><code>null</code> allowed ; <code>null</code> will then be returned if a given parameter is not set by the user</i>
*/
public void setDefault(final Number newDefaultValue){
// If NULL, set it and return:
if (newDefaultValue == null)
defaultValue = null;
// Otherwise:
else{
// If SMALLER than the minimum of this controller, set the default to the minimum:
if (minValue != null && newDefaultValue.doubleValue() < minValue.doubleValue())
defaultValue = minValue;
// If BIGGER than the maximum of this controller, set the default to the maximum:
else if (maxValue != null && newDefaultValue.doubleValue() > maxValue.doubleValue())
defaultValue = maxValue;
// Otherwise, set it exactly as provided:
else
defaultValue = newDefaultValue;
}
}
/**
* Reset all fields in the same time.
*
* @param defaultVal Value set by default to the parameter, when none is specified.
* @param minVal Minimum value that can be set. If a smaller value is provided by the user, an exception will be thrown by {@link #check(Object)}.
* @param maxVal Maximum value that can be set. If a bigger value is provided by the user, an exception will be thrown by {@link #check(Object)}.
*/
public void reset(final Number defaultVal, final Number minVal, final Number maxVal){
// Reset manually all concerned fields to NULL in order to avoid any conflict between the new and the old values:
minValue = null;
defaultValue = null;
maxValue = null;
// Set the given values with the appropriate set functions:
setMinimum(minVal);
setMaximum(maxVal);
setDefault(defaultVal);
}
@Override
public boolean allowModification(){
return allowModification;
}
/**
* Lets indicating whether the value of the parameter can be modified after initialization.
*
* @param allowModification <i>true</i> if the parameter value can be modified,
* <i>false</i> otherwise.
*/
public void allowModification(final boolean allowModification){
this.allowModification = allowModification;
}
/* ***************** */
/* CHECKING FUNCTION */
/* ***************** */
@Override
public Object check(final Object value) throws UWSException{
// If no value, return the default one:
if (value == null)
return getDefault();
// Otherwise, parse the given numeric value:
Number numVal = null;
if (value instanceof Number)
numVal = (Number)value;
else if (value instanceof String){
String strValue = (String)value;
try{
numVal = Double.parseDouble(strValue);
}catch(NumberFormatException nfe1){
try{
numVal = Long.parseLong(strValue);
}catch(NumberFormatException nfe2){
throw new UWSException(UWSException.BAD_REQUEST, "Wrong format for a numeric parameter: \"" + strValue + "\"! It should be a double or a long value between " + (minValue == null ? Double.MIN_VALUE : minValue) + " and " + (maxValue == null ? Double.MAX_VALUE : maxValue) + " (Default value: " + (defaultValue == null ? "none" : defaultValue) + ").");
}
}
}else
throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Wrong type for a numeric parameter: class \"" + value.getClass().getName() + "\"! It should be a double, a long value or a string containing only a double or a long value.");
// If the value is SMALLER than the minimum, the minimum value will be returned:
if (minValue != null && numVal.doubleValue() < minValue.doubleValue())
return minValue;
// If the value is BIGGER than the maximum, the maximum value will be returned:
else if (maxValue != null && numVal.doubleValue() > maxValue.doubleValue())
return maxValue;
// Otherwise, return the parsed number:
else
return numVal;
}
}