Parameter.java
/**
* Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.commons.parameters;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.config.MapModuleConfig;
import com.powsybl.commons.config.ModuleConfig;
import com.powsybl.commons.config.ModuleConfigUtil;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
*/
public class Parameter {
private final List<String> names = new ArrayList<>();
private final ParameterType type;
private final String description;
private final Object defaultValue;
private final List<Object> possibleValues;
private final ParameterScope scope;
private final String categoryKey;
public Parameter(String name, ParameterType type, String description, Object defaultValue,
List<Object> possibleValues, ParameterScope scope, String categoryKey) {
names.add(Objects.requireNonNull(name));
this.type = Objects.requireNonNull(type);
this.description = Objects.requireNonNull(description);
this.defaultValue = checkDefaultValue(type, defaultValue);
this.possibleValues = checkPossibleValues(type, possibleValues, defaultValue);
this.scope = scope;
this.categoryKey = categoryKey;
}
public Parameter(List<String> names, ParameterType type, String description, Object defaultValue,
List<Object> possibleValues, ParameterScope scope, String categoryKey) {
Objects.requireNonNull(names);
if (names.isEmpty()) {
throw new IllegalArgumentException("At least one name is expected.");
}
Objects.requireNonNull(names.get(0));
this.names.addAll(names);
this.type = Objects.requireNonNull(type);
this.description = Objects.requireNonNull(description);
this.defaultValue = checkDefaultValue(type, defaultValue);
this.possibleValues = checkPossibleValues(type, possibleValues, defaultValue);
this.scope = scope;
this.categoryKey = categoryKey;
}
public Parameter(String name, ParameterType type, String description, Object defaultValue,
List<Object> possibleValues, ParameterScope scope) {
this(name, type, description, defaultValue, possibleValues, scope, null);
}
public Parameter(String name, ParameterType type, String description, Object defaultValue, ParameterScope scope,
String categoryKey) {
this(name, type, description, defaultValue, null, scope, categoryKey);
}
public Parameter(String name, ParameterType type, String description, Object defaultValue,
List<Object> possibleValues) {
this(name, type, description, defaultValue, possibleValues, ParameterScope.FUNCTIONAL);
}
public Parameter(String name, ParameterType type, String description, Object defaultValue) {
this(name, type, description, defaultValue, null);
}
private static void checkValue(Class<?> typeClass, Object value) {
if (value != null && !typeClass.isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("Bad default value type " + value.getClass() + ", " + typeClass + " was expected");
}
}
private static void checkPossibleValuesContainsValue(List<Object> possibleValues, Object value,
Function<Object, IllegalArgumentException> exceptionCreator) {
Objects.requireNonNull(possibleValues);
Objects.requireNonNull(exceptionCreator);
if (value != null) {
if (value instanceof List) {
for (Object valueElement : (List<?>) value) {
if (!possibleValues.contains(valueElement)) {
throw exceptionCreator.apply(valueElement);
}
}
} else {
if (!possibleValues.contains(value)) {
throw exceptionCreator.apply(value);
}
}
}
}
private static List<Object> checkPossibleValues(ParameterType type, List<Object> possibleValues, Object defaultValue) {
if (possibleValues != null) {
possibleValues.forEach(value -> checkValue(type.getElementClass(), value));
checkPossibleValuesContainsValue(possibleValues, defaultValue, v -> {
throw new IllegalArgumentException("Parameter possible values " + possibleValues + " should contain default value " + v);
});
}
return possibleValues;
}
private static Object checkDefaultValue(ParameterType type, Object defaultValue) {
checkValue(type.getTypeClass(), defaultValue);
if (type == ParameterType.BOOLEAN && defaultValue == null) {
throw new PowsyblException("With Boolean parameter you are not allowed to pass a null default value");
}
if (type == ParameterType.DOUBLE && defaultValue == null) {
throw new PowsyblException("With Double parameter you are not allowed to pass a null default value");
}
if (type == ParameterType.INTEGER && defaultValue == null) {
throw new PowsyblException("With Integer parameter you are not allowed to pass a null default value");
}
return defaultValue;
}
public static Object read(String prefix, Properties parameters, Parameter configuredParameter, ParameterDefaultValueConfig defaultValueConfig) {
Objects.requireNonNull(configuredParameter);
switch (configuredParameter.getType()) {
case BOOLEAN:
return readBoolean(prefix, parameters, configuredParameter, defaultValueConfig);
case STRING:
return readString(prefix, parameters, configuredParameter, defaultValueConfig);
case STRING_LIST:
return readStringList(prefix, parameters, configuredParameter, defaultValueConfig);
case DOUBLE:
return readDouble(prefix, parameters, configuredParameter, defaultValueConfig);
case INTEGER:
return readInteger(prefix, parameters, configuredParameter, defaultValueConfig);
default:
throw new IllegalStateException("Unknown parameter type: " + configuredParameter.getType());
}
}
public static Object read(String prefix, Properties paramaters, Parameter configuredParameter) {
return read(prefix, paramaters, configuredParameter, ParameterDefaultValueConfig.INSTANCE);
}
public static boolean readBoolean(String prefix, Properties parameters, Parameter configuredParameter, ParameterDefaultValueConfig defaultValueConfig) {
return read(parameters, configuredParameter, defaultValueConfig.getBooleanValue(prefix, configuredParameter), ModuleConfigUtil::getOptionalBooleanProperty);
}
public static boolean readBoolean(String prefix, Properties parameters, Parameter configuredParameter) {
return readBoolean(prefix, parameters, configuredParameter, ParameterDefaultValueConfig.INSTANCE);
}
public static String readString(String prefix, Properties parameters, Parameter configuredParameter, ParameterDefaultValueConfig defaultValueConfig) {
return read(parameters, configuredParameter, defaultValueConfig.getStringValue(prefix, configuredParameter), ModuleConfigUtil::getOptionalStringProperty);
}
public static String readString(String prefix, Properties parameters, Parameter configuredParameter) {
return readString(prefix, parameters, configuredParameter, ParameterDefaultValueConfig.INSTANCE);
}
public static List<String> readStringList(String prefix, Properties parameters, Parameter configuredParameter, ParameterDefaultValueConfig defaultValueConfig) {
return read(parameters, configuredParameter, defaultValueConfig.getStringListValue(prefix, configuredParameter), ModuleConfigUtil::getOptionalStringListProperty);
}
public static List<String> readStringList(String prefix, Properties parameters, Parameter configuredParameter) {
return readStringList(prefix, parameters, configuredParameter, ParameterDefaultValueConfig.INSTANCE);
}
public static double readDouble(String prefix, Properties parameters, Parameter configuredParameter, ParameterDefaultValueConfig defaultValueConfig) {
return read(parameters, configuredParameter, defaultValueConfig.getDoubleValue(prefix, configuredParameter),
(moduleConfig, names) -> ModuleConfigUtil.getOptionalDoubleProperty(moduleConfig, names).orElse(Double.NaN), value -> value != null && !Double.isNaN(value));
}
public static int readInteger(String prefix, Properties parameters, Parameter configuredParameter, ParameterDefaultValueConfig defaultValueConfig) {
return read(parameters, configuredParameter, defaultValueConfig.getIntegerValue(prefix, configuredParameter),
(moduleConfig, names) -> ModuleConfigUtil.getOptionalIntProperty(moduleConfig, names).stream().boxed().findFirst().orElse(null), Objects::nonNull);
}
public static double readDouble(String prefix, Properties parameters, Parameter configuredParameter) {
return readDouble(prefix, parameters, configuredParameter, ParameterDefaultValueConfig.INSTANCE);
}
private static <T> T read(Properties parameters, Parameter configuredParameter, T defaultValue,
BiFunction<ModuleConfig, List<String>, T> supplier, Predicate<T> isPresent) {
Objects.requireNonNull(configuredParameter);
T value = null;
// priority on passed parameters
if (parameters != null) {
MapModuleConfig moduleConfig = new MapModuleConfig(parameters);
value = supplier.apply(moduleConfig, configuredParameter.getNames());
// check that if possible values are configured, value is contained in possible values
if (value != null
&& configuredParameter.getPossibleValues() != null) {
checkPossibleValuesContainsValue(configuredParameter.getPossibleValues(), value,
v -> new IllegalArgumentException("Value " + v + " of parameter " + configuredParameter.getName() +
" is not contained in possible values " + configuredParameter.getPossibleValues()));
}
}
// if none, use configured parameters
if (value != null && isPresent.test(value)) {
return value;
}
return defaultValue;
}
private static <T> T read(Properties parameters, Parameter configuredParameter, T defaultValue, BiFunction<ModuleConfig, List<String>, Optional<T>> supplier) {
return read(parameters, configuredParameter, defaultValue, (moduleConfig, strings) -> supplier.apply(moduleConfig, strings).orElse(null), Objects::nonNull);
}
public Parameter addAdditionalNames(String... names) {
Objects.requireNonNull(names);
this.names.addAll(Arrays.asList(names));
return this;
}
public String getName() {
return names.get(0);
}
public List<String> getNames() {
return Collections.unmodifiableList(names);
}
public ParameterType getType() {
return type;
}
public String getDescription() {
return description;
}
public Object getDefaultValue() {
return defaultValue;
}
public List<Object> getPossibleValues() {
return possibleValues;
}
public ParameterScope getScope() {
return scope;
}
public String getCategoryKey() {
return categoryKey;
}
}