Skip to content

Commit 8147ae5

Browse files
committed
Added implemation of the ConfigTree.
1 parent 68bb938 commit 8147ae5

File tree

9 files changed

+240
-1
lines changed

9 files changed

+240
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package net.finmath.util.config;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.stream.Collectors;
6+
7+
import net.finmath.util.config.nodes.ConfigNode;
8+
import net.finmath.util.config.nodes.Node;
9+
import net.finmath.util.config.nodes.SpecialNodes;
10+
import net.finmath.util.config.nodes.ValueNode;
11+
12+
/**
13+
* Config Tree: A tree based representation of configurations that can be selected with a key-value map describing the selector.
14+
*
15+
* A configuration is a value (Object) assigned to a selector (Map<String, Object>).
16+
* A selector is a key-value map where certain properties (String) have certain values (Object).
17+
* Properties of the selector are checked in a fixed order,
18+
* such that a tree is formed, where each property corresponds to a level (depth) in the tree.
19+
* Each level has a special branch for DEFAULT VALUES, if the selector value does not match any value of other nodes.
20+
*
21+
* @author Christian Fries
22+
*/
23+
public class ConfigTree {
24+
25+
private final Node root;
26+
27+
/**
28+
* Construct the tree.
29+
*
30+
* @param keyOrder Order in which the string keys of a selector define the levels of the tree.
31+
* @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.
32+
*/
33+
public ConfigTree(List<String> keyOrder, List<Map<String, Object>> configs) {
34+
this.root = group(keyOrder, configs);
35+
}
36+
37+
/**
38+
* Get the configuration for a given specification of the properties (selector).
39+
*
40+
* @param selector Maps the name (String) of a property to its value (Object).
41+
* @return The configuration value for the given selector.
42+
*/
43+
public Object getConfig(Map<String, Object> selector) {
44+
45+
Node node = this.root;
46+
47+
while(node instanceof ConfigNode) {
48+
ConfigNode configNode = (ConfigNode)node;
49+
if(selector.containsKey(configNode.getKey()) && configNode.getValueToConfig().keySet().contains(selector.get(configNode.getKey()))) {
50+
node = configNode.getValueToConfig().get(selector.get(configNode.getKey()));
51+
}
52+
else {
53+
node = configNode.getValueToConfig().get(SpecialNodes.DEFAULT_VALUE);
54+
}
55+
}
56+
57+
if(node instanceof ValueNode) {
58+
ValueNode valueNode = (ValueNode)node;
59+
return valueNode.getValue();
60+
}
61+
else {
62+
throw new IllegalArgumentException("Unable to resolve configuration from the given properties.");
63+
}
64+
}
65+
66+
/**
67+
* Helper for the constructor. Recursive contruction of the tree.
68+
*
69+
* @param keyOrder Given key order.
70+
* @param configs List of configs.
71+
* @return Node of the (sub-)tree for the given config key.
72+
*/
73+
private Node group(List<String> keyOrder, List<Map<String, Object>> configs) {
74+
if(keyOrder.size() == 0) {
75+
if(configs.size() == 1) {
76+
Map<String, Object> config = configs.get(0);
77+
Object value = config.get("value");
78+
return new ValueNode( value);
79+
}
80+
else {
81+
throw new IllegalArgumentException("Multiple configs for same values.");
82+
}
83+
}
84+
85+
// Group all elements by the first key....
86+
String key = keyOrder.get(0);
87+
Map<Object, List<Map<String, Object>>> grouped = configs.stream().collect(Collectors.groupingBy(map -> map.get(key)));
88+
89+
// ...call group (recursive) for all values below this key...
90+
List<String> keyOrderRemain = keyOrder.subList(1, keyOrder.size());
91+
Map<Object, Node> valueToConfig = grouped.entrySet().stream().collect(Collectors.toMap(
92+
Map.Entry::getKey, entry -> group(keyOrderRemain, entry.getValue())));
93+
94+
// ...create a ConfigNode for this key.
95+
return new ConfigNode(key, valueToConfig);
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.finmath.util.config.nodes;
2+
3+
import java.util.Map;
4+
5+
public class ConfigNode implements Node {
6+
7+
private final String key;
8+
private final Map<Object, Node> valueToConfig;
9+
10+
public ConfigNode(String key, Map<Object, Node> valueToConfig) {
11+
super();
12+
this.key = key;
13+
this.valueToConfig = valueToConfig;
14+
}
15+
16+
public String getKey() {
17+
return key;
18+
}
19+
20+
public Map<Object, Node> getValueToConfig() {
21+
return valueToConfig;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package net.finmath.util.config.nodes;
2+
3+
public interface Node {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package net.finmath.util.config.nodes;
2+
3+
public enum SpecialNodes {
4+
DEFAULT_VALUE;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package net.finmath.util.config.nodes;
2+
3+
public class ValueNode implements Node {
4+
5+
private final Object value;
6+
7+
public ValueNode(Object value) {
8+
super();
9+
this.value = value;
10+
}
11+
12+
public Object getValue() {
13+
return value;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Utilities to manage configurations.
3+
*
4+
* @author Christian Fries
5+
*/
6+
package net.finmath.util.config.nodes;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Node implementation for the {@link net.finmath.util.config.ConfigTree}.
3+
*
4+
* @author Christian Fries
5+
*/
6+
package net.finmath.util.config;

src/main/java/net/finmath/util/package-info.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Provides utility classes.
2+
* Some utilities.
33
*
44
* @author Christian Fries
55
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package net.finmath.util.config;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import org.junit.jupiter.api.Assertions;
10+
import org.junit.jupiter.api.Test;
11+
12+
import net.finmath.util.config.nodes.SpecialNodes;
13+
14+
/**
15+
* Configuration Test
16+
*
17+
* @author Christian Fries
18+
*/
19+
class ConfigTreeTest {
20+
21+
@Test
22+
void test() {
23+
24+
/**
25+
* Create a list of map of configs (this is the config file - each map is a row, the keys are the columns)
26+
*/
27+
List<Object> prop1Values = List.of(0.5, 1.0, SpecialNodes.DEFAULT_VALUE);
28+
List<Object> prop2Values = List.of(1, 2, SpecialNodes.DEFAULT_VALUE);
29+
List<Object> prop3Values = List.of("a", "b", "c", SpecialNodes.DEFAULT_VALUE);
30+
31+
Double valueForConfig = 0.0;
32+
List<Map<String, Object>> configs = new ArrayList<>();
33+
for(Object prop1Value : prop1Values) {
34+
for(Object prop2Value : prop2Values) {
35+
for(Object prop3Value : prop3Values) {
36+
configs.add(Map.of(
37+
"prop1", prop1Value,
38+
"prop2", prop2Value,
39+
"prop3", prop3Value,
40+
"value", valueForConfig
41+
));
42+
valueForConfig += 1.0;
43+
}
44+
}
45+
}
46+
47+
System.out.println("Input Configurations");
48+
configs.forEach(System.out::println);
49+
System.out.println("_".repeat(79));
50+
51+
// Build configTree
52+
ConfigTree configTree = new ConfigTree(List.of("prop1", "prop2", "prop3"), configs);
53+
54+
// Fetch some stuff
55+
56+
print(configTree, Map.of(
57+
"prop1", Double.valueOf(0.5),
58+
"prop2", Integer.valueOf(1),
59+
"prop3", String.valueOf("b")
60+
)
61+
,1.0);
62+
63+
print(configTree, Map.of(
64+
"prop1", Double.valueOf(3),
65+
"prop2", Integer.valueOf(1),
66+
"prop3", String.valueOf("b")
67+
),
68+
25.0);
69+
70+
print(configTree, Map.of(
71+
"prop1", Double.valueOf(0.5),
72+
"prop2", Integer.valueOf(1),
73+
"prop3", String.valueOf("a")
74+
),
75+
0.0);
76+
}
77+
78+
private static void print(ConfigTree configTree, Map<String, Object> selector, Object expected) {
79+
Object value = configTree.getConfig(selector);
80+
System.out.print(value + "\tfor\t" + selector.toString());
81+
Assertions.assertEquals(expected, value);
82+
System.out.println("\tOK");
83+
}
84+
}

0 commit comments

Comments
 (0)