Skip to content

Commit

Permalink
Added implemation of the ConfigTree.
Browse files Browse the repository at this point in the history
  • Loading branch information
cfries committed Nov 26, 2023
1 parent 68bb938 commit 8147ae5
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 1 deletion.
97 changes: 97 additions & 0 deletions src/main/java/net/finmath/util/config/ConfigTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package net.finmath.util.config;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import net.finmath.util.config.nodes.ConfigNode;
import net.finmath.util.config.nodes.Node;
import net.finmath.util.config.nodes.SpecialNodes;
import net.finmath.util.config.nodes.ValueNode;

/**
* Config Tree: A tree based representation of configurations that can be selected with a key-value map describing the selector.
*
* A configuration is a value (Object) assigned to a selector (Map<String, Object>).
* A selector is a key-value map where certain properties (String) have certain values (Object).
* Properties of the selector are checked in a fixed order,
* such that a tree is formed, where each property corresponds to a level (depth) in the tree.
* Each level has a special branch for DEFAULT VALUES, if the selector value does not match any value of other nodes.
*
* @author Christian Fries
*/
public class ConfigTree {

private final Node root;

/**
* Construct the tree.
*
* @param keyOrder Order in which the string keys of a selector define the levels of the tree.
* @param configs A list, where each element is map of keys to object. The key "value" is interpreted as the configuration value. All other keys are interpreted as configuration properties.
*/
public ConfigTree(List<String> keyOrder, List<Map<String, Object>> configs) {
this.root = group(keyOrder, configs);
}

/**
* Get the configuration for a given specification of the properties (selector).
*
* @param selector Maps the name (String) of a property to its value (Object).
* @return The configuration value for the given selector.
*/
public Object getConfig(Map<String, Object> selector) {

Node node = this.root;

while(node instanceof ConfigNode) {
ConfigNode configNode = (ConfigNode)node;
if(selector.containsKey(configNode.getKey()) && configNode.getValueToConfig().keySet().contains(selector.get(configNode.getKey()))) {
node = configNode.getValueToConfig().get(selector.get(configNode.getKey()));
}
else {
node = configNode.getValueToConfig().get(SpecialNodes.DEFAULT_VALUE);
}
}

if(node instanceof ValueNode) {
ValueNode valueNode = (ValueNode)node;
return valueNode.getValue();
}
else {
throw new IllegalArgumentException("Unable to resolve configuration from the given properties.");
}
}

/**
* Helper for the constructor. Recursive contruction of the tree.
*
* @param keyOrder Given key order.
* @param configs List of configs.
* @return Node of the (sub-)tree for the given config key.
*/
private Node group(List<String> keyOrder, List<Map<String, Object>> configs) {
if(keyOrder.size() == 0) {
if(configs.size() == 1) {
Map<String, Object> config = configs.get(0);
Object value = config.get("value");
return new ValueNode( value);
}
else {
throw new IllegalArgumentException("Multiple configs for same values.");
}
}

// Group all elements by the first key....
String key = keyOrder.get(0);
Map<Object, List<Map<String, Object>>> grouped = configs.stream().collect(Collectors.groupingBy(map -> map.get(key)));

// ...call group (recursive) for all values below this key...
List<String> keyOrderRemain = keyOrder.subList(1, keyOrder.size());
Map<Object, Node> valueToConfig = grouped.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey, entry -> group(keyOrderRemain, entry.getValue())));

// ...create a ConfigNode for this key.
return new ConfigNode(key, valueToConfig);
}
}
23 changes: 23 additions & 0 deletions src/main/java/net/finmath/util/config/nodes/ConfigNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.finmath.util.config.nodes;

import java.util.Map;

public class ConfigNode implements Node {

private final String key;
private final Map<Object, Node> valueToConfig;

public ConfigNode(String key, Map<Object, Node> valueToConfig) {
super();
this.key = key;
this.valueToConfig = valueToConfig;
}

public String getKey() {
return key;
}

public Map<Object, Node> getValueToConfig() {
return valueToConfig;
}
}
3 changes: 3 additions & 0 deletions src/main/java/net/finmath/util/config/nodes/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package net.finmath.util.config.nodes;

public interface Node {}
5 changes: 5 additions & 0 deletions src/main/java/net/finmath/util/config/nodes/SpecialNodes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.finmath.util.config.nodes;

public enum SpecialNodes {
DEFAULT_VALUE;
}
15 changes: 15 additions & 0 deletions src/main/java/net/finmath/util/config/nodes/ValueNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.finmath.util.config.nodes;

public class ValueNode implements Node {

private final Object value;

public ValueNode(Object value) {
super();
this.value = value;
}

public Object getValue() {
return value;
}
}
6 changes: 6 additions & 0 deletions src/main/java/net/finmath/util/config/nodes/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Utilities to manage configurations.
*
* @author Christian Fries
*/
package net.finmath.util.config.nodes;
6 changes: 6 additions & 0 deletions src/main/java/net/finmath/util/config/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Node implementation for the {@link net.finmath.util.config.ConfigTree}.
*
* @author Christian Fries
*/
package net.finmath.util.config;
2 changes: 1 addition & 1 deletion src/main/java/net/finmath/util/package-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Provides utility classes.
* Some utilities.
*
* @author Christian Fries
*/
Expand Down
84 changes: 84 additions & 0 deletions src/test/java/net/finmath/util/config/ConfigTreeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package net.finmath.util.config;

import static org.junit.jupiter.api.Assertions.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import net.finmath.util.config.nodes.SpecialNodes;

/**
* Configuration Test
*
* @author Christian Fries
*/
class ConfigTreeTest {

@Test
void test() {

/**
* Create a list of map of configs (this is the config file - each map is a row, the keys are the columns)
*/
List<Object> prop1Values = List.of(0.5, 1.0, SpecialNodes.DEFAULT_VALUE);
List<Object> prop2Values = List.of(1, 2, SpecialNodes.DEFAULT_VALUE);
List<Object> prop3Values = List.of("a", "b", "c", SpecialNodes.DEFAULT_VALUE);

Double valueForConfig = 0.0;
List<Map<String, Object>> configs = new ArrayList<>();
for(Object prop1Value : prop1Values) {
for(Object prop2Value : prop2Values) {
for(Object prop3Value : prop3Values) {
configs.add(Map.of(
"prop1", prop1Value,
"prop2", prop2Value,
"prop3", prop3Value,
"value", valueForConfig
));
valueForConfig += 1.0;
}
}
}

System.out.println("Input Configurations");
configs.forEach(System.out::println);
System.out.println("_".repeat(79));

// Build configTree
ConfigTree configTree = new ConfigTree(List.of("prop1", "prop2", "prop3"), configs);

// Fetch some stuff

print(configTree, Map.of(
"prop1", Double.valueOf(0.5),
"prop2", Integer.valueOf(1),
"prop3", String.valueOf("b")
)
,1.0);

print(configTree, Map.of(
"prop1", Double.valueOf(3),
"prop2", Integer.valueOf(1),
"prop3", String.valueOf("b")
),
25.0);

print(configTree, Map.of(
"prop1", Double.valueOf(0.5),
"prop2", Integer.valueOf(1),
"prop3", String.valueOf("a")
),
0.0);
}

private static void print(ConfigTree configTree, Map<String, Object> selector, Object expected) {
Object value = configTree.getConfig(selector);
System.out.print(value + "\tfor\t" + selector.toString());
Assertions.assertEquals(expected, value);
System.out.println("\tOK");
}
}

0 comments on commit 8147ae5

Please sign in to comment.