diff --git a/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java b/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java index 1a04ef51..09f10c33 100644 --- a/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java +++ b/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java @@ -88,6 +88,11 @@ public CelMutableCreateStruct createStruct() { return (CelMutableCreateStruct) exprValue; } + public CelMutableCreateMap createMap() { + checkExprKind(Kind.CREATE_MAP); + return (CelMutableCreateMap) exprValue; + } + public void setConstant(CelConstant constant) { this.exprKind = ExprKind.Kind.CONSTANT; this.exprValue = checkNotNull(constant); @@ -118,6 +123,11 @@ public void setCreateStruct(CelMutableCreateStruct createStruct) { this.exprValue = checkNotNull(createStruct); } + public void setCreateMap(CelMutableCreateMap createMap) { + this.exprKind = ExprKind.Kind.CREATE_MAP; + this.exprValue = checkNotNull(createMap); + } + /** A mutable identifier expression. */ public static final class CelMutableIdent { private String name = ""; @@ -549,6 +559,145 @@ private CelMutableCreateStruct(String messageName, ListMaps are constructed as `{'key_name': 'value'}`. + */ + public static final class CelMutableCreateMap { + private List entries; + + public List entries() { + return entries; + } + + public void setEntries(List entries) { + this.entries = checkNotNull(entries); + } + + public void setEntry(int index, CelMutableCreateMap.Entry entry) { + checkArgument(index >= 0 && index < entries().size()); + entries.set(index, checkNotNull(entry)); + } + + /** Represents an entry of the map */ + public static final class Entry { + private long id; + private CelMutableExpr key; + private CelMutableExpr value; + private boolean optionalEntry; + + public long id() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public CelMutableExpr key() { + return key; + } + + public void setKey(CelMutableExpr key) { + this.key = checkNotNull(key); + } + + public CelMutableExpr value() { + return value; + } + + public void setValue(CelMutableExpr value) { + this.value = checkNotNull(value); + } + + public boolean optionalEntry() { + return optionalEntry; + } + + public void setOptionalEntry(boolean optionalEntry) { + this.optionalEntry = optionalEntry; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Entry) { + Entry that = (Entry) obj; + return this.id == that.id() + && this.key.equals(that.key()) + && this.value.equals(that.value()) + && this.optionalEntry == that.optionalEntry(); + } + return false; + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= (int) ((id >>> 32) ^ id); + h *= 1000003; + h ^= key.hashCode(); + h *= 1000003; + h ^= value.hashCode(); + h *= 1000003; + h ^= optionalEntry ? 1231 : 1237; + return h; + } + + public static Entry create(CelMutableExpr key, CelMutableExpr value) { + return create(0, key, value, false); + } + + public static Entry create(long id, CelMutableExpr key, CelMutableExpr value) { + return create(id, key, value, false); + } + + public static Entry create( + long id, CelMutableExpr key, CelMutableExpr value, boolean optionalEntry) { + return new Entry(id, key, value, optionalEntry); + } + + private Entry(long id, CelMutableExpr key, CelMutableExpr value, boolean optionalEntry) { + this.id = id; + this.key = checkNotNull(key); + this.value = checkNotNull(value); + this.optionalEntry = optionalEntry; + } + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof CelMutableCreateMap) { + CelMutableCreateMap that = (CelMutableCreateMap) obj; + return this.entries.equals(that.entries()); + } + return false; + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= entries.hashCode(); + return h; + } + + public static CelMutableCreateMap create(List entries) { + return new CelMutableCreateMap(new ArrayList<>(entries)); + } + + private CelMutableCreateMap(List entries) { + this.entries = checkNotNull(entries); + } + } + public static CelMutableExpr ofNotSet() { return ofNotSet(0L); } @@ -605,6 +754,14 @@ public static CelMutableExpr ofCreateStruct(long id, CelMutableCreateStruct muta return new CelMutableExpr(id, mutableCreateStruct); } + public static CelMutableExpr ofCreateMap(CelMutableCreateMap mutableCreateMap) { + return ofCreateMap(0, mutableCreateMap); + } + + public static CelMutableExpr ofCreateMap(long id, CelMutableCreateMap mutableCreateMap) { + return new CelMutableExpr(id, mutableCreateMap); + } + private CelMutableExpr(long id, CelConstant mutableConstant) { this.id = id; setConstant(mutableConstant); @@ -635,6 +792,11 @@ private CelMutableExpr(long id, CelMutableCreateStruct mutableCreateStruct) { setCreateStruct(mutableCreateStruct); } + private CelMutableExpr(long id, CelMutableCreateMap mutableCreateMap) { + this.id = id; + setCreateMap(mutableCreateMap); + } + private CelMutableExpr(long id) { this(); this.id = id; @@ -662,6 +824,7 @@ private Object exprValue() { case CREATE_STRUCT: return createStruct(); case CREATE_MAP: + return createMap(); case COMPREHENSION: // fall-through (not implemented yet) } diff --git a/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java b/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java index 7f34c397..612247f1 100644 --- a/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java +++ b/common/src/test/java/dev/cel/common/ast/CelMutableExprTest.java @@ -24,6 +24,7 @@ import dev.cel.common.ast.CelExpr.ExprKind.Kind; import dev.cel.common.ast.CelMutableExpr.CelMutableCall; import dev.cel.common.ast.CelMutableExpr.CelMutableCreateList; +import dev.cel.common.ast.CelMutableExpr.CelMutableCreateMap; import dev.cel.common.ast.CelMutableExpr.CelMutableCreateStruct; import dev.cel.common.ast.CelMutableExpr.CelMutableIdent; import dev.cel.common.ast.CelMutableExpr.CelMutableSelect; @@ -373,6 +374,81 @@ public void mutableCreateStructEntry_setters() { 2L, "field2", CelMutableExpr.ofConstant(CelConstant.ofValue("value2")), true)); } + @Test + public void ofCreateMap() { + CelMutableExpr mutableExpr = + CelMutableExpr.ofCreateMap(CelMutableCreateMap.create(ImmutableList.of())); + + assertThat(mutableExpr.id()).isEqualTo(0L); + assertThat(mutableExpr.createMap().entries()).isEmpty(); + } + + @Test + public void ofCreateMap_withId() { + CelMutableExpr mutableExpr = + CelMutableExpr.ofCreateMap( + 9L, + CelMutableCreateMap.create( + ImmutableList.of( + CelMutableCreateMap.Entry.create( + 10L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value")), + /* optionalEntry= */ true)))); + + assertThat(mutableExpr.id()).isEqualTo(9L); + assertThat(mutableExpr.createMap().entries()) + .containsExactly( + CelMutableCreateMap.Entry.create( + 10L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value")), + /* optionalEntry= */ true)); + } + + @Test + public void mutableCreateMap_setEntryAtIndex() { + CelMutableCreateMap createMap = + CelMutableCreateMap.create( + ImmutableList.of( + CelMutableCreateMap.Entry.create( + 10L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value"))))); + CelMutableCreateMap.Entry newEntry = + CelMutableCreateMap.Entry.create( + 2L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key2")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value2")), + /* optionalEntry= */ true); + + createMap.setEntry(0, newEntry); + + assertThat(createMap.entries()).containsExactly(newEntry); + } + + @Test + public void mutableCreateMapEntry_setters() { + CelMutableCreateMap.Entry createMapEntry = + CelMutableCreateMap.Entry.create( + 1L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value"))); + + createMapEntry.setId(2L); + createMapEntry.setKey(CelMutableExpr.ofConstant(CelConstant.ofValue("key2"))); + createMapEntry.setValue(CelMutableExpr.ofConstant(CelConstant.ofValue("value2"))); + createMapEntry.setOptionalEntry(true); + + assertThat(createMapEntry) + .isEqualTo( + CelMutableCreateMap.Entry.create( + 2L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key2")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value2")), + true)); + } + @Test public void equalityTest() { new EqualsTester() @@ -441,6 +517,23 @@ public void equalityTest() { "field", CelMutableExpr.ofConstant(CelConstant.ofValue("value")), /* optionalEntry= */ true))))) + .addEqualityGroup( + CelMutableExpr.ofCreateMap(CelMutableCreateMap.create(ImmutableList.of()))) + .addEqualityGroup( + CelMutableCreateMap.create( + ImmutableList.of( + CelMutableCreateMap.Entry.create( + 9L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value")), + /* optionalEntry= */ true))), + CelMutableCreateMap.create( + ImmutableList.of( + CelMutableCreateMap.Entry.create( + 9L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value")), + /* optionalEntry= */ true)))) .testEquals(); } @@ -454,7 +547,9 @@ private enum MutableExprKindTestCase { CREATE_LIST(CelMutableExpr.ofCreateList(CelMutableCreateList.create())), CREATE_STRUCT( CelMutableExpr.ofCreateStruct( - CelMutableCreateStruct.create("message", ImmutableList.of()))); + CelMutableCreateStruct.create("message", ImmutableList.of()))), + CREATE_MAP(CelMutableExpr.ofCreateMap(CelMutableCreateMap.create(ImmutableList.of()))), + ; private final CelMutableExpr mutableExpr; @@ -487,6 +582,9 @@ public void getExprValue_invalidKind_throws(@TestParameter MutableExprKindTestCa if (!testCaseKind.equals(Kind.CREATE_STRUCT)) { assertThrows(IllegalArgumentException.class, testCase.mutableExpr::createStruct); } + if (!testCaseKind.equals(Kind.CREATE_MAP)) { + assertThrows(IllegalArgumentException.class, testCase.mutableExpr::createMap); + } } @SuppressWarnings("Immutable") // Mutable by design @@ -526,6 +624,17 @@ private enum HashCodeTestCase { CelMutableExpr.ofConstant(CelConstant.ofValue("value")), /* optionalEntry= */ true)))), 2064611987), + CREATE_MAP( + CelMutableExpr.ofCreateMap( + 8L, + CelMutableCreateMap.create( + ImmutableList.of( + CelMutableCreateMap.Entry.create( + 9L, + CelMutableExpr.ofConstant(CelConstant.ofValue("key")), + CelMutableExpr.ofConstant(CelConstant.ofValue("value")), + /* optionalEntry= */ true)))), + 1260717292), ; private final CelMutableExpr mutableExpr;