forked from open-feature/java-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStructure.java
145 lines (125 loc) · 4.5 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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;
import static dev.openfeature.sdk.Value.objectToValue;
/**
* {@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();
/**
* Converts the Value into its equivalent primitive type.
*
* @param value - Value object to convert
* @return an Object containing the primitive type, or null.
*/
default Object convertValue(Value value) {
if (value == null || value.isNull()) {
return null;
}
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()
.entrySet()
.stream()
.collect(HashMap::new,
(accumulated, entry) -> accumulated.put(entry.getKey(),
convertValue(entry.getValue())),
HashMap::putAll);
}
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) {
final Map<String, Value> merged = new HashMap<>(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;
}
/**
* Transform an object map to a {@link Structure} type.
*
* @param map map of objects
* @return a Structure object in the SDK format
*/
static Structure mapToStructure(Map<String, Object> map) {
return new MutableStructure(map.entrySet().stream()
.collect(HashMap::new,
(accumulated, entry) -> accumulated.put(entry.getKey(),
objectToValue(entry.getValue())),
HashMap::putAll));
}
}