Skip to content

Commit 55b55e6

Browse files
committed
create and use ImmutableStructure for immutableContext.
Signed-off-by: thiyagu06 <[email protected]>
1 parent c428aed commit 55b55e6

File tree

6 files changed

+282
-111
lines changed

6 files changed

+282
-111
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,32 @@
11
package dev.openfeature.sdk;
22

33
import lombok.Getter;
4-
import lombok.Setter;
54
import lombok.ToString;
65
import lombok.experimental.Delegate;
76

8-
import java.time.Instant;
97
import java.util.HashMap;
10-
import java.util.List;
118
import java.util.Map;
129

1310
/**
1411
* The EvaluationContext is a container for arbitrary contextual data
1512
* that can be used as a basis for dynamic evaluation.
16-
* The MutableContext is an EvaluationContext implementation which is not threadsafe, and whose attributes can
17-
* be modified after instantiation.
13+
* The ImmutableContext is an EvaluationContext implementation which is threadsafe, and whose attributes can
14+
* not be modified after instantiation.
1815
*/
1916
@ToString
2017
@SuppressWarnings("PMD.BeanMembersShouldSerialize")
21-
public class ImmutableContext implements EvaluationContext {
18+
public final class ImmutableContext implements EvaluationContext {
2219

2320
@Getter
24-
private String targetingKey;
25-
@Delegate(excludes = HideDelegateAddMethods.class)
26-
private final MutableStructure structure;
21+
private final String targetingKey;
22+
@Delegate
23+
private final Structure structure;
2724

25+
/**
26+
* create an immutable context with an empty targeting_key and attributes provided.
27+
*/
2828
public ImmutableContext() {
29-
this.structure = new MutableStructure();
30-
this.targetingKey = "";
29+
this("", new HashMap<>());
3130
}
3231

3332
/**
@@ -36,19 +35,17 @@ public ImmutableContext() {
3635
* @param attributes evaluation context attributes
3736
*/
3837
public ImmutableContext(Map<String, Value> attributes) {
39-
HashMap<String, Value> copy = new HashMap<>(attributes);
40-
this.structure = new MutableStructure(copy);
41-
this.targetingKey = "";
38+
this("", attributes);
4239
}
4340

4441
/**
4542
* create an immutable context with given targetingKey and attributes provided.
4643
*
4744
* @param targetingKey targeting key
48-
* @param attributes evaluation context attributes
45+
* @param attributes evaluation context attributes
4946
*/
5047
public ImmutableContext(String targetingKey, Map<String, Value> attributes) {
51-
this(attributes);
48+
this.structure = new ImmutableStructure(attributes);
5249
this.targetingKey = targetingKey;
5350
}
5451

@@ -83,41 +80,4 @@ public EvaluationContext merge(EvaluationContext overridingContext) {
8380
merged.putAll(overridingContext.asMap());
8481
return new ImmutableContext(targetingKey, merged);
8582
}
86-
87-
/**
88-
* Hidden class to tell Lombok not to copy these methods over via delegation.
89-
*/
90-
private static class HideDelegateAddMethods {
91-
public MutableStructure add(String ignoredKey, Boolean ignoredValue) {
92-
return null;
93-
}
94-
95-
public MutableStructure add(String ignoredKey, Double ignoredValue) {
96-
return null;
97-
}
98-
99-
public MutableStructure add(String ignoredKey, String ignoredValue) {
100-
return null;
101-
}
102-
103-
public MutableStructure add(String ignoredKey, Value ignoredValue) {
104-
return null;
105-
}
106-
107-
public MutableStructure add(String ignoredKey, Integer ignoredValue) {
108-
return null;
109-
}
110-
111-
public MutableStructure add(String ignoredKey, List<Value> ignoredValue) {
112-
return null;
113-
}
114-
115-
public MutableStructure add(String ignoredKey, MutableStructure ignoredValue) {
116-
return null;
117-
}
118-
119-
public MutableStructure add(String ignoredKey, Instant ignoredValue) {
120-
return null;
121-
}
122-
}
12383
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package dev.openfeature.sdk;
2+
3+
import lombok.EqualsAndHashCode;
4+
import lombok.SneakyThrows;
5+
import lombok.ToString;
6+
7+
import java.time.Instant;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Set;
12+
import java.util.stream.Collectors;
13+
14+
/**
15+
* {@link ImmutableStructure} represents a potentially nested object type which is used to represent
16+
* structured data.
17+
* The ImmutableStructure is a Structure implementation which is threadsafe, and whose attributes can
18+
* not be modified after instantiation.
19+
*/
20+
@ToString
21+
@EqualsAndHashCode
22+
@SuppressWarnings({"PMD.BeanMembersShouldSerialize", "checkstyle:MissingJavadocType"})
23+
public final class ImmutableStructure implements Structure {
24+
25+
private final Map<String, Value> attributes;
26+
27+
/**
28+
* create an immutable structure with the empty attributes.
29+
*/
30+
public ImmutableStructure() {
31+
this(new HashMap<>());
32+
}
33+
34+
/**
35+
* create immutable structure with the given attributes.
36+
*
37+
* @param attributes attributes.
38+
*/
39+
public ImmutableStructure(Map<String, Value> attributes) {
40+
Map<String, Value> copy = attributes.entrySet()
41+
.stream()
42+
.collect(Collectors.toMap(Map.Entry::getKey, e -> copy(e.getValue())));
43+
this.attributes = new HashMap<>(copy);
44+
}
45+
46+
@Override
47+
public Set<String> keySet() {
48+
return this.attributes.keySet();
49+
}
50+
51+
// getters
52+
@Override
53+
public Value getValue(String key) {
54+
Value value = this.attributes.get(key);
55+
return copy(value);
56+
}
57+
58+
/**
59+
* Get all values.
60+
*
61+
* @return all attributes on the structure
62+
*/
63+
@Override
64+
public Map<String, Value> asMap() {
65+
return attributes
66+
.entrySet()
67+
.stream()
68+
.collect(Collectors.toMap(
69+
Map.Entry::getKey,
70+
e -> getValue(e.getKey())
71+
));
72+
}
73+
74+
/**
75+
* Get all values, with primitives types.
76+
*
77+
* @return all attributes on the structure into a Map
78+
*/
79+
@Override
80+
public Map<String, Object> asObjectMap() {
81+
return attributes
82+
.entrySet()
83+
.stream()
84+
.collect(Collectors.toMap(
85+
Map.Entry::getKey,
86+
e -> convertValue(getValue(e.getKey()))
87+
));
88+
}
89+
90+
/**
91+
* Perform deep copy of value object.
92+
*
93+
* @param value value object
94+
* @return new copy of the given value object
95+
*/
96+
@SneakyThrows
97+
private Value copy(Value value) {
98+
if (value.isList()) {
99+
List<Value> copy = value.asList().stream().map(Value::new).collect(Collectors.toList());
100+
return new Value(copy);
101+
}
102+
if (value.isStructure()) {
103+
Map<String, Value> copy = value.asStructure().asMap().entrySet().stream().collect(Collectors.toMap(
104+
Map.Entry::getKey,
105+
e -> copy(e.getValue())
106+
));
107+
return new Value(new ImmutableStructure(copy));
108+
}
109+
if (value.isInstant()) {
110+
Instant copy = Instant.ofEpochMilli(value.asInstant().toEpochMilli());
111+
return new Value(copy);
112+
}
113+
return new Value(value.asObject());
114+
}
115+
}

src/main/java/dev/openfeature/sdk/MutableStructure.java

+3-53
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package dev.openfeature.sdk;
22

3+
import lombok.EqualsAndHashCode;
4+
import lombok.ToString;
5+
36
import java.time.Instant;
47
import java.util.HashMap;
58
import java.util.List;
69
import java.util.Map;
710
import java.util.Set;
811
import java.util.stream.Collectors;
912

10-
import dev.openfeature.sdk.exceptions.ValueNotConvertableError;
11-
import lombok.EqualsAndHashCode;
12-
import lombok.ToString;
13-
1413
/**
1514
* {@link MutableStructure} represents a potentially nested object type which is used to represent
1615
* structured data.
@@ -109,53 +108,4 @@ public Map<String, Object> asObjectMap() {
109108
e -> convertValue(getValue(e.getKey()))
110109
));
111110
}
112-
113-
/**
114-
* convertValue is converting the object type Value in a primitive type.
115-
*
116-
* @param value - Value object to convert
117-
* @return an Object containing the primitive type.
118-
*/
119-
private Object convertValue(Value value) {
120-
if (value.isBoolean()) {
121-
return value.asBoolean();
122-
}
123-
124-
if (value.isNumber()) {
125-
Double valueAsDouble = value.asDouble();
126-
if (valueAsDouble == Math.floor(valueAsDouble) && !Double.isInfinite(valueAsDouble)) {
127-
return value.asInteger();
128-
}
129-
return valueAsDouble;
130-
}
131-
132-
if (value.isString()) {
133-
return value.asString();
134-
}
135-
136-
if (value.isInstant()) {
137-
return value.asInstant();
138-
}
139-
140-
if (value.isList()) {
141-
return value.asList()
142-
.stream()
143-
.map(this::convertValue)
144-
.collect(Collectors.toList());
145-
}
146-
147-
if (value.isStructure()) {
148-
Structure s = value.asStructure();
149-
return s.asMap()
150-
.keySet()
151-
.stream()
152-
.collect(
153-
Collectors.toMap(
154-
key -> key,
155-
key -> convertValue(s.getValue(key))
156-
)
157-
);
158-
}
159-
throw new ValueNotConvertableError();
160-
}
161111
}

src/main/java/dev/openfeature/sdk/Structure.java

+52
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package dev.openfeature.sdk;
22

3+
import dev.openfeature.sdk.exceptions.ValueNotConvertableError;
4+
35
import java.util.Map;
46
import java.util.Set;
7+
import java.util.stream.Collectors;
58

69
/**
710
* {@link Structure} represents a potentially nested object type which is used to represent
@@ -38,4 +41,53 @@ public interface Structure {
3841
* @return all attributes on the structure into a Map
3942
*/
4043
Map<String, Object> asObjectMap();
44+
45+
/**
46+
* convertValue is converting the object type Value in a primitive type.
47+
*
48+
* @param value - Value object to convert
49+
* @return an Object containing the primitive type.
50+
*/
51+
default Object convertValue(Value value) {
52+
if (value.isBoolean()) {
53+
return value.asBoolean();
54+
}
55+
56+
if (value.isNumber()) {
57+
Double valueAsDouble = value.asDouble();
58+
if (valueAsDouble == Math.floor(valueAsDouble) && !Double.isInfinite(valueAsDouble)) {
59+
return value.asInteger();
60+
}
61+
return valueAsDouble;
62+
}
63+
64+
if (value.isString()) {
65+
return value.asString();
66+
}
67+
68+
if (value.isInstant()) {
69+
return value.asInstant();
70+
}
71+
72+
if (value.isList()) {
73+
return value.asList()
74+
.stream()
75+
.map(this::convertValue)
76+
.collect(Collectors.toList());
77+
}
78+
79+
if (value.isStructure()) {
80+
Structure s = value.asStructure();
81+
return s.asMap()
82+
.keySet()
83+
.stream()
84+
.collect(
85+
Collectors.toMap(
86+
key -> key,
87+
key -> convertValue(s.getValue(key))
88+
)
89+
);
90+
}
91+
throw new ValueNotConvertableError();
92+
}
4193
}

0 commit comments

Comments
 (0)