2020import com .google .errorprone .annotations .Immutable ;
2121import dev .cel .common .annotations .Internal ;
2222import java .util .Collection ;
23+ import java .util .Iterator ;
24+ import java .util .List ;
2325import java .util .Map ;
26+ import java .util .Map .Entry ;
2427import java .util .Optional ;
28+ import java .util .RandomAccess ;
2529import java .util .function .Function ;
2630
2731/**
@@ -57,19 +61,37 @@ public Object maybeUnwrap(Object value) {
5761 return unwrap ((CelValue ) value );
5862 }
5963
60- Object mapped = mapContainer (value , maybeUnwrapFunction );
61- if (mapped != value ) {
62- return mapped ;
63- }
64-
65- return value ;
64+ return mapContainer (value , maybeUnwrapFunction );
6665 }
6766
6867 /**
6968 * Maps a container (Collection or Map) by applying the provided mapper function to its elements.
7069 * Returns the original value if it's not a supported container.
7170 */
7271 protected Object mapContainer (Object value , Function <Object , Object > mapper ) {
72+ if (value instanceof List && value instanceof RandomAccess ) {
73+ List <Object > list = (List <Object >) value ;
74+ for (int i = 0 ; i < list .size (); i ++) {
75+ Object element = list .get (i );
76+ Object mapped = mapper .apply (element );
77+
78+ if (mapped != element ) {
79+ ImmutableList .Builder <Object > builder =
80+ ImmutableList .builderWithExpectedSize (list .size ());
81+ for (int j = 0 ; j < i ; j ++) {
82+ builder .add (list .get (j ));
83+ }
84+ builder .add (mapped );
85+ for (int j = i + 1 ; j < list .size (); j ++) {
86+ builder .add (mapper .apply (list .get (j )));
87+ }
88+ return builder .build ();
89+ }
90+ }
91+ return value ;
92+ }
93+
94+ // Fallback for cases where the collection is unordered, or random access is impossible.
7395 if (value instanceof Collection ) {
7496 Collection <Object > collection = (Collection <Object >) value ;
7597 ImmutableList .Builder <Object > builder =
@@ -82,12 +104,34 @@ protected Object mapContainer(Object value, Function<Object, Object> mapper) {
82104
83105 if (value instanceof Map ) {
84106 Map <Object , Object > map = (Map <Object , Object >) value ;
85- ImmutableMap .Builder <Object , Object > builder =
86- ImmutableMap .builderWithExpectedSize (map .size ());
87- for (Map .Entry <Object , Object > entry : map .entrySet ()) {
88- builder .put (mapper .apply (entry .getKey ()), mapper .apply (entry .getValue ()));
107+ Iterator <Entry <Object , Object >> iterator = map .entrySet ().iterator ();
108+
109+ while (iterator .hasNext ()) {
110+ Map .Entry <Object , Object > entry = iterator .next ();
111+ Object mappedKey = mapper .apply (entry .getKey ());
112+ Object mappedValue = mapper .apply (entry .getValue ());
113+
114+ if (mappedKey != entry .getKey () || mappedValue != entry .getValue ()) {
115+ ImmutableMap .Builder <Object , Object > builder =
116+ ImmutableMap .builderWithExpectedSize (map .size ());
117+
118+ for (Map .Entry <Object , Object > prevEntry : map .entrySet ()) {
119+ if (prevEntry .getKey () == entry .getKey ()) {
120+ break ;
121+ }
122+ builder .put (mapper .apply (prevEntry .getKey ()), mapper .apply (prevEntry .getValue ()));
123+ }
124+
125+ builder .put (mappedKey , mappedValue );
126+
127+ while (iterator .hasNext ()) {
128+ Map .Entry <Object , Object > nextEntry = iterator .next ();
129+ builder .put (mapper .apply (nextEntry .getKey ()), mapper .apply (nextEntry .getValue ()));
130+ }
131+ return builder .buildOrThrow ();
132+ }
89133 }
90- return builder . buildOrThrow () ;
134+ return value ;
91135 }
92136
93137 return value ;
0 commit comments