forked from open-feature/java-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStructure.java
126 lines (111 loc) · 3.89 KB
/
Structure.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package dev.openfeature.sdk;
import dev.openfeature.sdk.exceptions.ValueNotConvertableError;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* {@link Structure} represents a potentially nested object type which is used to represent
* structured data.
*/
@SuppressWarnings("PMD.BeanMembersShouldSerialize")
public interface Structure {
/**
* Get all keys.
*
* @return the set of keys
*/
Set<String> keySet();
/**
* Get the value indexed by key.
*
* @param key String the key.
* @return the Value
*/
Value getValue(String key);
/**
* Get all values, as a map of Values.
*
* @return all attributes on the structure into a Map
*/
Map<String, Value> asMap();
/**
* Get all values, with as a map of Object.
*
* @return all attributes on the structure into a Map
*/
Map<String, Object> asObjectMap();
/**
* convertValue is converting the object type Value in a primitive type.
*
* @param value - Value object to convert
* @return an Object containing the primitive type.
*/
default Object convertValue(Value value) {
if (value.isBoolean()) {
return value.asBoolean();
}
if (value.isNumber() && !value.isNull()) {
Number numberValue = (Number) value.asObject();
if (numberValue instanceof Double) {
return numberValue.doubleValue();
} else if (numberValue instanceof Integer) {
return numberValue.intValue();
}
}
if (value.isString()) {
return value.asString();
}
if (value.isInstant()) {
return value.asInstant();
}
if (value.isList()) {
return value.asList()
.stream()
.map(this::convertValue)
.collect(Collectors.toList());
}
if (value.isStructure()) {
Structure s = value.asStructure();
return s.asMap()
.keySet()
.stream()
.collect(
Collectors.toMap(
key -> key,
key -> convertValue(s.getValue(key))
)
);
}
throw new ValueNotConvertableError();
}
/**
* Recursively merges the base map with the overriding map.
*
* @param <T> Structure type
* @param newStructure function to create the right structure
* @param base base map to merge
* @param overriding overriding map to merge
* @return resulting merged map
*/
default <T extends Structure> Map<String, Value> merge(Function<Map<String, Value>, Structure> newStructure,
Map<String, Value> base,
Map<String, Value> overriding) {
Map<String, Value> merged = new HashMap<>();
merged.putAll(base);
for (Entry<String, Value> overridingEntry : overriding.entrySet()) {
String key = overridingEntry.getKey();
if (overridingEntry.getValue().isStructure() && merged.containsKey(key) && merged.get(key).isStructure()) {
Structure mergedValue = merged.get(key).asStructure();
Structure overridingValue = overridingEntry.getValue().asStructure();
Map<String, Value> newMap = this.merge(newStructure, mergedValue.asMap(), overridingValue.asMap());
merged.put(key, new Value(newStructure.apply(newMap)));
} else {
merged.put(key, overridingEntry.getValue());
}
}
return merged;
}
}