AbstractInputParameterMap.java
package nl.tudelft.simulation.dsol.model.inputparameters;
import java.util.Comparator;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* The InputParameterMap contains a number of InputParameters, each of which can also be an InputParameterMap again. The
* InputParameterMap provides functions to add and remove sub-parameters, to retrieve sub-parameters based on their key, and to
* return a sorted set of InputParameters based on their displayValue. <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 <CT> The calculated type
*/
public abstract class AbstractInputParameterMap<CT> extends AbstractInputParameter<SortedMap<String, InputParameter<?, ?>>, CT>
{
/** */
private static final long serialVersionUID = 1L;
/**
* Construct a new AbstractInputParameterMap.
* @param key String; unique (within the parent's input parameter map) name of the new InputParameterMap
* @param shortName String; concise description of the input parameter
* @param description String; long description of the input parameter (may use HTML markup)
* @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
*/
public AbstractInputParameterMap(final String key, final String shortName, final String description,
final double displayPriority)
{
super(key, shortName, description, new TreeMap<String, InputParameter<?, ?>>(), displayPriority);
setReadOnly(true);
}
/**
* Add an input parameter to this map of input parameters.
* @param inputParameter AbstractInputParameter<?,?>; the input parameter to add
* @throws InputParameterException in case an input parameter with the same key already exists
*/
public void add(final AbstractInputParameter<?, ?> inputParameter) throws InputParameterException
{
if (getValue().containsKey(inputParameter.getKey()))
{
throw new InputParameterException(
"Duplicate key " + inputParameter.getKey() + " for InputParameterMap " + getKey());
}
getValue().put(inputParameter.getKey(), inputParameter);
inputParameter.setParent(this);
}
/**
* Removes an input parameter from this map of input parameters. The input parameter can point to deeper maps using the
* dot-notation. E.g., when an InputParameterMap exists in this map with name 'server1', and the server1 map has an input
* parameter called 'iat', we can remove iat by calling remove("server1.iat");
* @param key String; the key of the input parameter to remove
* @throws InputParameterException in case the input parameter with this key does not exist
*/
public void remove(final String key) throws InputParameterException
{
String[] keys = key.split("\\.");
if (!getValue().containsKey(keys[0]))
{
throw new InputParameterException("Key " + key + " does not exist in InputParameterMap " + getKey());
}
if (keys.length == 1)
{
getValue().remove(key);
return;
}
InputParameter<?, ?> parent = getValue().get(keys[0]);
if (!(parent instanceof AbstractInputParameterMap))
{
throw new InputParameterException(
"Key " + key + " in InputParameterMap " + getKey() + " is not a leaf, but also not an InputParameterMap");
}
((AbstractInputParameterMap<?>) parent).remove(key.substring(key.indexOf('.') + 1));
}
/**
* Gets an input parameter from this map of input parameters. The input parameter can point to deeper maps using the
* dot-notation. E.g., when an InputParameterMap exists in this map with name 'server1', and the server1 map has an input
* parameter called 'iat', we can get the InputParameter iat by calling get("server1.iat");
* @param key String; the key of the input parameter to retrieve
* @return the input parameter belonging to the key
* @throws InputParameterException in case the input parameter with this key does not exist
*/
public InputParameter<?, ?> get(final String key) throws InputParameterException
{
String[] keys = key.split("\\.");
if (!getValue().containsKey(keys[0]))
{
throw new InputParameterException("Key " + key + " does not exist in InputParameterMap " + getKey());
}
if (keys.length == 1)
{
return getValue().get(key);
}
InputParameter<?, ?> parent = getValue().get(keys[0]);
if (!(parent instanceof AbstractInputParameterMap))
{
throw new InputParameterException(
"Key " + key + " in InputParameterMap " + getKey() + " is not a leaf, but also not an InputParameterMap");
}
return ((AbstractInputParameterMap<?>) parent).get(key.substring(key.indexOf('.') + 1));
}
/** {@inheritDoc} */
@Override
public AbstractInputParameterMap<?> clone()
{
AbstractInputParameterMap<?> ipm = (AbstractInputParameterMap<?>) super.clone();
SortedMap<String, InputParameter<?, ?>> clonedMap = new TreeMap<>();
for (String key : getValue().keySet())
{
// needed because the TreeMap.clone() returns a shallow copy
clonedMap.put(key, getValue().get(key).clone());
}
try
{
boolean readOnly = ipm.isReadOnly();
ipm.setReadOnly(false);
ipm.setValue(clonedMap);
ipm.setReadOnly(readOnly);
}
catch (InputParameterException exception)
{
throw new RuntimeException(exception);
}
return ipm;
}
/**
* Return a sorted set of sub-parameters of this InputParameterMap.
* @return SortedSet<InputParameter<?>>; a sorted set of sub-parameters of this InputParameterMap
*/
public SortedSet<InputParameter<?, ?>> getSortedSet()
{
SortedSet<InputParameter<?, ?>> set = new TreeSet<>(new InputParameterComparator());
set.addAll(getValue().values());
return set;
}
/**
* Return a formatted human readable list of keys and values, indented with spaces corresponding to the depth.
* @param map AbstractInputParameterMap<?>; the map to display
* @param depth int; the depth of the tree
* @return String; a formatted human readable list of keys and values, indented by depth
*/
protected String printValues(final AbstractInputParameterMap<?> map, final int depth)
{
StringBuffer out = new StringBuffer();
for (InputParameter<?, ?> param : map.getSortedSet())
{
out.append(" ".substring(0, depth));
if (param instanceof AbstractInputParameterTypedMap)
{
out.append(param.getKey());
out.append(" = ");
try
{
out.append(((AbstractInputParameterTypedMap<?>) param).getCalculatedValue().toString());
}
catch (InputParameterException exception)
{
out.append("!!ERROR!!");
}
out.append("\n");
}
else if (param instanceof AbstractInputParameterMap)
{
out.append("MAP: ");
out.append(param.getKey());
out.append("\n");
out.append(printValues((AbstractInputParameterMap<?>) param, depth + 2));
}
else
{
out.append(param.getKey());
out.append(" = ");
out.append(param.getValue());
out.append("\n");
}
}
return out.toString();
}
/**
* Return a formatted human readable list of keys and values.
* @return String; a formatted human readable list of keys and values
*/
public String printValues()
{
return printValues(this, 0);
}
/**
* InputParameterComparator provides the comparator based on the displayPriority of the entries. In case they are the same,
* the entries are ordered by the String value of the key. <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>
*/
class InputParameterComparator implements Comparator<InputParameter<?, ?>>
{
/** {@inheritDoc} */
@Override
public int compare(final InputParameter<?, ?> o1, final InputParameter<?, ?> o2)
{
if (o1.getDisplayPriority() != o2.getDisplayPriority())
{
return o1.getDisplayPriority() < o2.getDisplayPriority() ? -1 : 1;
}
return o1.getKey().compareTo(o2.getKey());
}
}
}