BehavioralCharacteristics.java
package org.opentrafficsim.core.gtu.behavioralcharacteristics;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.djunits.unit.DimensionlessUnit;
import org.djunits.value.vdouble.scalar.Dimensionless;
import org.djunits.value.vdouble.scalar.DoubleScalarInterface;
import nl.tudelft.simulation.language.Throw;
import nl.tudelft.simulation.language.reflection.ClassUtil;
/**
* In this class a set of behavioral characteristics in the form of parameters can be stored for use in behavioral models.
* <p>
* Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
* <p>
* @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 13, 2016 <br>
* @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
* @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
*/
public class BehavioralCharacteristics implements Serializable
{
/** */
private static final long serialVersionUID = 20160400L;
/**
* Object to recognize that no value was set previously.
*/
private static final Empty EMPTY = new Empty();
/** Whether to copy internal data on write. */
private boolean copyOnWrite = false;
/** List of parameters. */
private Map<AbstractParameterType<?>, DoubleScalarInterface> parameters;
/** List of parameters with values before last set. */
private Map<AbstractParameterType<?>, DoubleScalarInterface> previous;
/**
* Empty constructor.
*/
public BehavioralCharacteristics()
{
this.parameters = new HashMap<>();
this.previous = new HashMap<>();
}
/**
* Constructor which creates a copy of the input set.
* @param behavioralCharacteristics input set
*/
public BehavioralCharacteristics(final BehavioralCharacteristics behavioralCharacteristics)
{
this.parameters = behavioralCharacteristics.parameters;
this.previous = behavioralCharacteristics.previous;
this.copyOnWrite = true;
behavioralCharacteristics.copyOnWrite = true;
}
/**
* Set parameter value of given parameter type.
* @param parameterType Parameter type.
* @param value Value.
* @param <T> Class of value.
* @throws ParameterException If the value does not comply with value type constraints.
*/
public final <T extends DoubleScalarInterface> void setParameter(final ParameterType<T> parameterType, final T value)
throws ParameterException
{
Throw.when(value == null, ParameterException.class,
"Parameter of type '%s' was assigned a null value, this is not allowed.", parameterType.getId());
parameterType.check(value, this);
saveSetParameter(parameterType, value);
}
/**
* Set parameter value of given parameter type.
* @param parameterType Parameter type.
* @param value Value.
*/
public final void setParameter(final ParameterTypeBoolean parameterType, final boolean value)
{
try
{
saveSetParameter(parameterType, new Dimensionless(value ? 1.0 : 0.0, DimensionlessUnit.SI));
}
catch (ParameterException pe)
{
// This cannot occur as the ParameterTypeBoolean constructor does not allow a default check.
throw new RuntimeException("ParameterTypeBoolean default check throws a ParameterException.", pe);
}
}
/**
* Set parameter value of given parameter type.
* @param parameterType Parameter type.
* @param value Value.
* @throws ParameterException If the value does not comply with value type constraints.
*/
public final void setParameter(final ParameterTypeDouble parameterType, final double value) throws ParameterException
{
parameterType.check(value, this);
saveSetParameter(parameterType, new Dimensionless(value, DimensionlessUnit.SI));
}
/**
* Set parameter value of given parameter type.
* @param parameterType Parameter type.
* @param value Value.
* @throws ParameterException If the value does not comply with value type constraints.
*/
public final void setParameter(final ParameterTypeInteger parameterType, final int value) throws ParameterException
{
parameterType.check(value, this);
saveSetParameter(parameterType, new Dimensionless(value, DimensionlessUnit.SI));
}
/**
* Remembers the current value, or if it is not given, for possible reset.
* @param parameterType Parameter type.
* @param value Value.
* @param <T> Class of the value.
* @throws ParameterException If the value does not comply with constraints.
*/
private <T extends DoubleScalarInterface> void saveSetParameter(final AbstractParameterType<T> parameterType, final T value)
throws ParameterException
{
parameterType.checkCheck(value);
checkCopyOnWrite();
if (this.parameters.containsKey(parameterType))
{
this.previous.put(parameterType, this.parameters.get(parameterType));
}
else
{
// remember that there was no value before this set
this.previous.put(parameterType, EMPTY);
}
this.parameters.put(parameterType, value);
}
/**
* Resets the parameter value to the value from before the last set. This goes only a single value back.
* @param parameterType Parameter type.
* @throws ParameterException If the parameter was never set.
*/
public final void resetParameter(final AbstractParameterType<?> parameterType) throws ParameterException
{
Throw.when(!this.previous.containsKey(parameterType), ParameterException.class,
"Reset on parameter of type '%s' could not be performed, it was not set.", parameterType.getId());
checkCopyOnWrite();
if (this.previous.get(parameterType) instanceof Empty)
{
// no value was set before last set, so make parameter type not set
this.parameters.remove(parameterType);
}
else
{
this.parameters.put(parameterType, this.previous.get(parameterType));
}
this.previous.remove(parameterType); // prevent consecutive resets
}
/**
* Check if internal data needs to be copied.
*/
private void checkCopyOnWrite()
{
if (this.copyOnWrite)
{
this.parameters = new HashMap<>(this.parameters);
this.previous = new HashMap<>(this.previous);
this.copyOnWrite = false;
}
}
/**
* Get parameter of given type.
* @param parameterType Parameter type.
* @param <T> Class of value.
* @return Parameter of given type.
* @throws ParameterException If parameter was never set.
*/
@SuppressWarnings("checkstyle:designforextension")
public <T extends DoubleScalarInterface> T getParameter(final ParameterType<T> parameterType) throws ParameterException
{
checkContains(parameterType);
@SuppressWarnings("unchecked")
// set methods guarantee matching of parameter type and value
T result = (T) this.parameters.get(parameterType);
return result;
}
/**
* Get parameter of given type.
* @param parameterType Parameter type.
* @return Parameter of given type.
* @throws ParameterException If parameter was never set.
*/
public final boolean getParameter(final ParameterTypeBoolean parameterType) throws ParameterException
{
checkContains(parameterType);
return this.parameters.get(parameterType).getSI() != 0.0;
}
/**
* Get parameter of given type.
* @param parameterType Parameter type.
* @return Parameter of given type.
* @throws ParameterException If parameter was never set.
*/
public final int getParameter(final ParameterTypeInteger parameterType) throws ParameterException
{
checkContains(parameterType);
return (int) this.parameters.get(parameterType).getSI();
}
/**
* Get parameter of given type.
* @param parameterType Parameter type.
* @return Parameter of given type.
* @throws ParameterException If parameter was never set.
*/
public final double getParameter(final ParameterTypeDouble parameterType) throws ParameterException
{
checkContains(parameterType);
return this.parameters.get(parameterType).getSI();
}
/**
* Check whether parameter has been set.
* @param parameterType Parameter type.
* @throws ParameterException If parameter is not present.
*/
private void checkContains(final AbstractParameterType<?> parameterType) throws ParameterException
{
Throw.when(!contains(parameterType), ParameterException.class,
"Could not get parameter of type '%s' as it was not set.", parameterType.getId());
}
/**
* Whether the given parameter type has been set.
* @param parameterType Parameter type.
* @return Whether the given parameter type has been set.
*/
public final boolean contains(final AbstractParameterType<?> parameterType)
{
return this.parameters.containsKey(parameterType);
}
/**
* Returns a safe copy of the parameters.
* @return Safe copy of the parameters, e.g., for printing.
*/
public final Map<AbstractParameterType<?>, DoubleScalarInterface> getParameters()
{
return new HashMap<>(this.parameters);
}
/**
* Sets the default value of a parameter.
* @param parameter parameter to set the default value of
* @param <T> Class of the value.
* @return this set of behavioral characteristics (for method chaining)
* @throws ParameterException if the parameter type has no default value
*/
@SuppressWarnings("unchecked")
public final <T extends DoubleScalarInterface> BehavioralCharacteristics setDefaultParameter(
final AbstractParameterType<T> parameter) throws ParameterException
{
T defaultValue;
if (parameter.getDefaultValue() instanceof DoubleScalarInterface)
{
// all types based on DJUNITS
defaultValue = (T) parameter.getDefaultValue();
}
else if (parameter.getDefaultValue() instanceof Boolean)
{
// boolean
defaultValue = (T) new Dimensionless((boolean) parameter.getDefaultValue() ? 1.0 : 0.0, DimensionlessUnit.SI);
}
else
{
// double or integer
defaultValue = (T) new Dimensionless(((Number) parameter.getDefaultValue()).doubleValue(), DimensionlessUnit.SI);
}
try
{
saveSetParameter(parameter, defaultValue);
}
catch (ParameterException pe)
{
// should not happen, default value and parameter type are connected
throw new RuntimeException(pe);
}
return this;
}
/**
* Sets the default values of all accessible parameters defined in the given class.
* @param clazz class with parameters
* @return this set of behavioral characteristics (for method chaining)
*/
public final BehavioralCharacteristics setDefaultParameters(final Class<?> clazz)
{
return setDefaultParametersLocal(clazz);
}
/**
* Sets the default values of all accessible parameters defined in the given class.
* @param clazz class with parameters
* @param <T> Class of the value.
* @return this set of behavioral characteristics (for method chaining)
*/
@SuppressWarnings("unchecked")
private <T extends DoubleScalarInterface> BehavioralCharacteristics setDefaultParametersLocal(final Class<?> clazz)
{
// set all default values using reflection
Set<Field> fields = ClassUtil.getAllFields(clazz);
for (Field field : fields)
{
if (AbstractParameterType.class.isAssignableFrom(field.getType()))
{
try
{
field.setAccessible(true);
AbstractParameterType<T> p = (AbstractParameterType<T>) field.get(clazz);
T defaultValue;
if (p.getDefaultValue() instanceof DoubleScalarInterface)
{
// all types based on DJUNITS
defaultValue = (T) p.getDefaultValue();
}
else if (p.getDefaultValue() instanceof Boolean)
{
// boolean
defaultValue = (T) new Dimensionless((boolean) p.getDefaultValue() ? 1.0 : 0.0, DimensionlessUnit.SI);
}
else
{
// double or integer
defaultValue =
(T) new Dimensionless(((Number) p.getDefaultValue()).doubleValue(), DimensionlessUnit.SI);
}
saveSetParameter(p, defaultValue);
}
catch (IllegalArgumentException iare)
{
// should not happen, field and clazz are related
throw new RuntimeException(iare);
}
catch (IllegalAccessException iace)
{
// parameter type not public
}
catch (ParameterException pe)
{
// do not set parameter without default value
}
}
}
return this;
}
/**
* Sets all behavioral characteristics from the given set in this set.
* @param behavioralCharacteristics set of behavioral characteristics to include in this set
*/
public final void setAll(final BehavioralCharacteristics behavioralCharacteristics)
{
for (AbstractParameterType<?> key : behavioralCharacteristics.parameters.keySet())
{
this.parameters.put(key, behavioralCharacteristics.parameters.get(key));
}
}
/** {@inheritDoc} */
public final String toString()
{
StringBuilder out = new StringBuilder("BehavioralCharacteristics [");
String sep = "";
for (AbstractParameterType<?> apt : this.parameters.keySet())
{
try
{
out.append(sep).append(apt.getId()).append("=").append(apt.printValue(this));
sep = ", ";
}
catch (ParameterException pe)
{
// We know the parameter has been set as we get the keySet from parameters
}
}
out.append("]");
return out.toString();
}
/**
* Class to put in a HashMap to recognize that no value was set at some point.
* <p>
* Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* <br>
* BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
* <p>
* @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 14, 2016 <br>
* @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
* @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
*/
private static class Empty extends Dimensionless
{
/** */
private static final long serialVersionUID = 20160414L;
/**
* Empty constructor.
*/
Empty()
{
super(Double.NaN, DimensionlessUnit.SI);
}
}
}