Skip to content

Commit 02152f3

Browse files
l46kokcopybara-github
authored andcommitted
Add value type parameter in StructValue
PiperOrigin-RevId: 904602834
1 parent d699e28 commit 02152f3

6 files changed

Lines changed: 76 additions & 37 deletions

File tree

common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
*/
3636
@AutoValue
3737
@Immutable
38-
public abstract class ProtoMessageLiteValue extends StructValue<String> {
38+
public abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite> {
3939

4040
@Override
4141
public abstract MessageLite value();

common/src/main/java/dev/cel/common/values/ProtoMessageValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
/** ProtoMessageValue is a struct value with protobuf support. */
2929
@AutoValue
3030
@Immutable
31-
public abstract class ProtoMessageValue extends StructValue<String> {
31+
public abstract class ProtoMessageValue extends StructValue<String, Message> {
3232

3333
@Override
3434
public abstract Message value();

common/src/main/java/dev/cel/common/values/StructValue.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,21 @@
1919
/**
2020
* StructValue is a representation of a structured object with typed properties.
2121
*
22-
* <p>Users may extend from this class to provide a custom struct that CEL can understand (ex:
23-
* POJOs). Custom struct implementations must provide all functionalities denoted in the CEL
24-
* specification, such as field selection, presence testing and new object creation.
22+
* <p>Users may extend from this class to provide a custom struct that CEL can understand by
23+
* wrapping a native Java object (e.g., a POJO or a Map). Custom struct implementations must provide
24+
* all functionalities denoted in the CEL specification, such as field selection, presence testing
25+
* and new object creation.
2526
*
2627
* <p>For an expression `e` selecting a field `f`, `e.f` must throw an exception if `f` does not
2728
* exist in the struct (i.e: hasField returns false). If the field exists but is not set, the
2829
* implementation should return an appropriate default value based on the struct's semantics.
30+
*
31+
* @param <F> The type of the field identifier. Only {@code String} is supported for now, but we may
32+
* extend support to other types in the future.
33+
* @param <V> The type of the wrapped native object.
2934
*/
3035
@Immutable
31-
public abstract class StructValue<T> extends CelValue implements SelectableValue<T> {}
36+
public abstract class StructValue<F, V> extends CelValue implements SelectableValue<F> {
37+
@Override
38+
public abstract V value();
39+
}

common/src/test/java/dev/cel/common/values/OptionalValueTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public void celTypeTest() {
141141
}
142142

143143
@SuppressWarnings("Immutable") // Test only
144-
private static class CelCustomStruct extends StructValue<String> {
144+
private static class CelCustomStruct extends StructValue<String, Long> {
145145
private final long data;
146146

147147
@Override

common/src/test/java/dev/cel/common/values/StructValueTest.java

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,42 @@ public Optional<CelType> findType(String typeName) {
5959
};
6060

6161
private static final CelValueProvider CUSTOM_STRUCT_VALUE_PROVIDER =
62-
(structType, fields) -> {
63-
if (structType.equals(CUSTOM_STRUCT_TYPE.name())) {
64-
return Optional.of(new CelCustomStructValue(fields));
62+
new CelValueProvider() {
63+
@Override
64+
public Optional<Object> newValue(String structType, Map<String, Object> fields) {
65+
if (structType.equals(CUSTOM_STRUCT_TYPE.name())) {
66+
return Optional.of(new CelCustomStructValue(fields));
67+
}
68+
return Optional.empty();
69+
}
70+
71+
@Override
72+
public CelValueConverter celValueConverter() {
73+
return new CelValueConverter() {
74+
@Override
75+
public Object toRuntimeValue(Object value) {
76+
if (value instanceof CustomPojo) {
77+
return new CelCustomStructValue((CustomPojo) value);
78+
}
79+
return super.toRuntimeValue(value);
80+
}
81+
};
6582
}
66-
return Optional.empty();
6783
};
6884

6985
@Test
7086
public void emptyStruct() {
7187
CelCustomStructValue celCustomStruct = new CelCustomStructValue(0);
7288

73-
assertThat(celCustomStruct.value()).isEqualTo(celCustomStruct);
89+
assertThat(celCustomStruct.value().getData()).isEqualTo(0L);
7490
assertThat(celCustomStruct.isZeroValue()).isTrue();
7591
}
7692

7793
@Test
7894
public void constructStruct() {
7995
CelCustomStructValue celCustomStruct = new CelCustomStructValue(5);
8096

81-
assertThat(celCustomStruct.value()).isEqualTo(celCustomStruct);
97+
assertThat(celCustomStruct.value().getData()).isEqualTo(5L);
8298
assertThat(celCustomStruct.isZeroValue()).isFalse();
8399
}
84100

@@ -115,41 +131,41 @@ public void celTypeTest() {
115131
@Test
116132
public void evaluate_usingCustomClass_createNewStruct() throws Exception {
117133
Cel cel =
118-
CelFactory.standardCelBuilder()
119-
.setOptions(CelOptions.current().enableCelValue(true).build())
134+
CelFactory.plannerCelBuilder()
135+
.setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build())
120136
.setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER)
121137
.setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER)
122138
.build();
123139
CelAbstractSyntaxTree ast = cel.compile("custom_struct{data: 50}").getAst();
124140

125-
CelCustomStructValue result = (CelCustomStructValue) cel.createProgram(ast).eval();
141+
CustomPojo result = (CustomPojo) cel.createProgram(ast).eval();
126142

127-
assertThat(result.data).isEqualTo(50);
143+
assertThat(result.getData()).isEqualTo(50);
128144
}
129145

130146
@Test
131147
public void evaluate_usingCustomClass_asVariable() throws Exception {
132148
Cel cel =
133-
CelFactory.standardCelBuilder()
134-
.setOptions(CelOptions.current().enableCelValue(true).build())
149+
CelFactory.plannerCelBuilder()
150+
.setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build())
135151
.addVar("a", CUSTOM_STRUCT_TYPE)
136152
.setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER)
137153
.setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER)
138154
.build();
139155
CelAbstractSyntaxTree ast = cel.compile("a").getAst();
140156

141-
CelCustomStructValue result =
142-
(CelCustomStructValue)
157+
CustomPojo result =
158+
(CustomPojo)
143159
cel.createProgram(ast).eval(ImmutableMap.of("a", new CelCustomStructValue(10)));
144160

145-
assertThat(result.data).isEqualTo(10);
161+
assertThat(result.getData()).isEqualTo(10);
146162
}
147163

148164
@Test
149165
public void evaluate_usingCustomClass_asVariableSelectField() throws Exception {
150166
Cel cel =
151-
CelFactory.standardCelBuilder()
152-
.setOptions(CelOptions.current().enableCelValue(true).build())
167+
CelFactory.plannerCelBuilder()
168+
.setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build())
153169
.addVar("a", CUSTOM_STRUCT_TYPE)
154170
.setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER)
155171
.setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER)
@@ -163,8 +179,8 @@ public void evaluate_usingCustomClass_asVariableSelectField() throws Exception {
163179
@Test
164180
public void evaluate_usingCustomClass_selectField() throws Exception {
165181
Cel cel =
166-
CelFactory.standardCelBuilder()
167-
.setOptions(CelOptions.current().enableCelValue(true).build())
182+
CelFactory.plannerCelBuilder()
183+
.setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build())
168184
.setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER)
169185
.setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER)
170186
.build();
@@ -178,8 +194,8 @@ public void evaluate_usingCustomClass_selectField() throws Exception {
178194
@Test
179195
public void evaluate_usingMultipleProviders_selectFieldFromCustomClass() throws Exception {
180196
Cel cel =
181-
CelFactory.standardCelBuilder()
182-
.setOptions(CelOptions.current().enableCelValue(true).build())
197+
CelFactory.plannerCelBuilder()
198+
.setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build())
183199
.setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER)
184200
.setValueProvider(
185201
CombinedCelValueProvider.combine(
@@ -197,19 +213,31 @@ public void evaluate_usingMultipleProviders_selectFieldFromCustomClass() throws
197213
// TODO: Bring back evaluate_usingMultipleProviders_selectFieldFromProtobufMessage
198214
// once planner is exposed from factory
199215

216+
private static class CustomPojo {
217+
private final long data;
218+
219+
CustomPojo(long data) {
220+
this.data = data;
221+
}
222+
223+
long getData() {
224+
return data;
225+
}
226+
}
227+
200228
@SuppressWarnings("Immutable") // Test only
201-
private static class CelCustomStructValue extends StructValue<String> {
229+
private static class CelCustomStructValue extends StructValue<String, CustomPojo> {
202230

203-
private final long data;
231+
private final CustomPojo pojo;
204232

205233
@Override
206-
public CelCustomStructValue value() {
207-
return this;
234+
public CustomPojo value() {
235+
return pojo;
208236
}
209237

210238
@Override
211239
public boolean isZeroValue() {
212-
return data == 0;
240+
return pojo.getData() == 0;
213241
}
214242

215243
@Override
@@ -226,7 +254,7 @@ public Object select(String field) {
226254
@Override
227255
public Optional<Object> find(String field) {
228256
if (field.equals("data")) {
229-
return Optional.of(value().data);
257+
return Optional.of(pojo.getData());
230258
}
231259

232260
return Optional.empty();
@@ -237,7 +265,11 @@ private CelCustomStructValue(Map<String, Object> fields) {
237265
}
238266

239267
private CelCustomStructValue(long data) {
240-
this.data = data;
268+
this.pojo = new CustomPojo(data);
269+
}
270+
271+
private CelCustomStructValue(CustomPojo pojo) {
272+
this.pojo = pojo;
241273
}
242274
}
243275
}

runtime/src/main/java/dev/cel/runtime/planner/EvalCreateStruct.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,8 @@ Object evalInternal(GlobalResolver resolver, ExecutionFrame frame) {
8787
.newValue(structType.name(), Collections.unmodifiableMap(fieldValues))
8888
.orElseThrow(
8989
() -> new IllegalArgumentException("Type name not found: " + structType.name()));
90-
9190
if (value instanceof StructValue) {
92-
return ((StructValue<?>) value).value();
91+
return ((StructValue<?, ?>) value).value();
9392
}
9493

9594
return value;

0 commit comments

Comments
 (0)