1717import static com .google .common .base .Preconditions .checkNotNull ;
1818
1919import com .google .auto .value .AutoValue ;
20+ import com .google .common .collect .ImmutableList ;
2021import com .google .errorprone .annotations .Immutable ;
2122import com .google .protobuf .Descriptors .Descriptor ;
2223import com .google .protobuf .Descriptors .FieldDescriptor ;
24+ import com .google .protobuf .DynamicMessage ;
2325import com .google .protobuf .Message ;
2426import dev .cel .common .internal .CelDescriptorPool ;
27+ import dev .cel .common .internal .WellKnownProto ;
2528import dev .cel .common .types .CelType ;
2629import dev .cel .common .types .StructTypeReference ;
2730import java .util .Optional ;
@@ -76,9 +79,7 @@ public Optional<Object> find(String field) {
7679
7780 @ Override
7881 public Object selectByFieldNumber (SelectField field ) {
79- FieldDescriptor fieldDescriptor = findFieldByNumber (value ().getDescriptorForType (), field );
80-
81- return protoCelValueConverter ().fromProtoMessageFieldToCelValue (value (), fieldDescriptor );
82+ return selectField (value (), field );
8283 }
8384
8485 @ Override
@@ -95,6 +96,42 @@ public Optional<Object> findByFieldNumber(SelectField field) {
9596 return findFieldValue (fieldDescriptor );
9697 }
9798
99+ @ Override
100+ public Object selectPath (ImmutableList <SelectField > fields , int from ) {
101+ Message message = value ();
102+ if (message instanceof DynamicMessage ) {
103+ // Each hop adapts a dynamic message into its generated class, if it has one.
104+ return OptimizedSelectTraversal .qualify (
105+ selectByFieldNumber (fields .get (from )), fields , from + 1 );
106+ }
107+ int i = from ;
108+ // Walks nested messages without wrapping them, up to the one holding the last field.
109+ while (i < fields .size () - 1 ) {
110+ FieldDescriptor fieldDescriptor =
111+ findFieldByNumber (message .getDescriptorForType (), fields .get (i ));
112+ if (!isWalkable (fieldDescriptor )) {
113+ break ;
114+ }
115+ message = (Message ) message .getField (fieldDescriptor );
116+ i ++;
117+ }
118+ return OptimizedSelectTraversal .qualify (selectField (message , fields .get (i )), fields , i + 1 );
119+ }
120+
121+ // A singular message field that isn't of a well-known type, which the converter would adapt.
122+ private static boolean isWalkable (FieldDescriptor fieldDescriptor ) {
123+ return fieldDescriptor .getJavaType () == FieldDescriptor .JavaType .MESSAGE
124+ && !fieldDescriptor .isRepeated ()
125+ && !WellKnownProto .getByTypeName (fieldDescriptor .getMessageType ().getFullName ())
126+ .isPresent ();
127+ }
128+
129+ private Object selectField (Message message , SelectField field ) {
130+ FieldDescriptor fieldDescriptor = findFieldByNumber (message .getDescriptorForType (), field );
131+
132+ return protoCelValueConverter ().fromProtoMessageFieldToCelValue (message , fieldDescriptor );
133+ }
134+
98135 public static ProtoMessageValue create (
99136 Message value ,
100137 CelDescriptorPool celDescriptorPool ,
0 commit comments