InputParameterSelectionMap.java
package nl.tudelft.simulation.dsol.model.inputparameters;
import java.util.SortedMap;
import java.util.TreeMap;
import org.djutils.exceptions.Throw;
/**
* InputParameterSelectionMap contains a list of key values to select from, each leading to another value to be selected as the
* value. An example would be a list of human readable ISO-3166 2-letter country codes to choose from (String), whereas the
* ISO3166 numeric code would be stored as an int. The InputParameterSelectionMap extends AbstractInputParameter<T> and
* not AbstractInputParameter<Map<K, T>> because the value it can return is the value-item in the map and not
* the map itself.<br>
* <br>
* Copyright (c) 2003-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>. The
* source code and binary code of this software is proprietary information of Delft University of Technology.
* @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
* @param <K> the key value to select the values in the map
* @param <T> the type of parameter stored in the map
*/
public class InputParameterSelectionMap<K, T> extends AbstractInputParameter<T, T>
{
/** */
private static final long serialVersionUID = 1L;
/** The options of the selection list. */
private SortedMap<K, T> options;
/**
* Construct a new InputParameterSelectionMap.
* @param key String; unique (within the parent's input parameter map) name of the new InputParameterSelectionMap
* @param shortName String; concise description of the input parameter
* @param description String; long description of the input parameter (may use HTML markup)
* @param options SortedMap<K,T>; the list of selection options
* @param defaultValue T; the default value of this input parameter
* @param displayPriority double; sorting order when properties are displayed to the user
* @throws NullPointerException when key, shortName, defaultValue, or description is null
* @throws IllegalArgumentException when displayPriority is NaN
* @throws InputParameterException in case the default value is not part of the list
*/
public InputParameterSelectionMap(final String key, final String shortName, final String description,
final SortedMap<K, T> options, final T defaultValue, final double displayPriority) throws InputParameterException
{
super(key, shortName, description, defaultValue, displayPriority);
this.options = new TreeMap<>(options);
if (!options.containsValue(defaultValue))
{
throw new InputParameterException(
"Default value " + defaultValue + " not part of selectionMap options for key " + getKey());
}
}
/** {@inheritDoc} */
@Override
public T getCalculatedValue()
{
return getValue();
}
/**
* @return the options for the selection
*/
public SortedMap<K, T> getOptions()
{
return this.options;
}
/**
* Return the index of the current value.
* @return K; the key in the options belonging to the current value, or null if the list does not contain the value
*/
public K getKeyforValue()
{
return getKeyforValue(getValue());
}
/**
* Return the index of the given value in the options.
* @param value T; the value to calculate the index for
* @return K; the key in the options belonging to the given value, or null if the list does not contain the value
*/
public K getKeyforValue(final T value)
{
for (K key : this.options.keySet())
{
if (this.options.get(key).equals(value))
{
return key;
}
}
return null;
}
/**
* Set a new value by providing one of the map value options.
* @param newValue T; the new value (must be one of the map value options)
* @throws InputParameterException when the new value is not part of selectionMap options, or when the map is read-only
*/
public void setMapValue(final T newValue) throws InputParameterException
{
if (getKeyforValue(newValue) == null)
{
throw new InputParameterException("Value " + newValue + " not part of selectionMap options");
}
super.setValue(newValue);
}
/**
* Change the value of the input parameter, as an object when the generics are not known (e.g., in a user interface).
* @param objectValue Object; the new value for the input parameter
* @throws InputParameterException when this InputParameter is read-only, or newValue is not valid
*/
public void setObjectValue(final Object objectValue) throws InputParameterException
{
@SuppressWarnings("unchecked")
T newValue = (T) objectValue;
if (getKeyforValue(newValue) == null)
{
throw new InputParameterException("Value " + newValue + " not part of selectionMap options");
}
super.setValue(newValue);
}
/**
* Return the index of the given value in the options.
* @param key K; the index of the new value
* @throws InputParameterException when index out of bounds
*/
public void setKeyforValue(final K key) throws InputParameterException
{
Throw.when(key == null || !this.options.containsKey(key), InputParameterException.class,
"InputParameterSelectionMap.setKeyforValue() - key not part of options");
setValue(this.options.get(key));
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
public InputParameterSelectionMap<K, T> clone()
{
InputParameterSelectionMap<K, T> ipsm = (InputParameterSelectionMap<K, T>) super.clone();
SortedMap<K, T> clonedMap = new TreeMap<>(this.options.comparator());
for (K key : this.options.keySet())
{
T item = this.options.get(key);
// needed because the ArrayList.clone() returns a shallow copy
if (item instanceof InputParameter<?, ?>)
{
clonedMap.put(key, (T) ((InputParameter<?, ?>) item).clone());
}
else
{
clonedMap.put(key, item); // shallow; we cannot see the clone() method for other objects...
}
}
try
{
boolean readOnly = ipsm.isReadOnly();
ipsm.setReadOnly(false);
ipsm.setValue(getValue());
ipsm.setReadOnly(readOnly);
}
catch (InputParameterException exception)
{
throw new RuntimeException(exception);
}
return ipsm;
}
}