4646import java .util .Map ;
4747import java .util .Optional ;
4848import java .util .TreeMap ;
49+ import org .jspecify .annotations .Nullable ;
4950
5051/**
5152 * {@code ProtoLiteCelValueConverter} handles bidirectional conversion between native Java and
6061@ Immutable
6162@ Internal
6263public final class ProtoLiteCelValueConverter extends BaseProtoCelValueConverter {
63- static final String MAP_KEY_FIELD_NAME = "key" ;
64- static final String MAP_VALUE_FIELD_NAME = "value" ;
64+ private static final String MAP_KEY_FIELD_NAME = "key" ;
65+ private static final String MAP_VALUE_FIELD_NAME = "value" ;
6566
6667 private final CelLiteDescriptorPool descriptorPool ;
6768
@@ -134,29 +135,58 @@ private static Object readFixed64BitField(
134135 }
135136
136137 private Object readLengthDelimitedField (
137- CodedInputStream inputStream , FieldLiteDescriptor fieldDescriptor ) throws IOException {
138+ CodedInputStream inputStream ,
139+ FieldLiteDescriptor fieldDescriptor ,
140+ @ Nullable Object existingValue )
141+ throws IOException {
138142 FieldLiteDescriptor .Type fieldType = fieldDescriptor .getProtoFieldType ();
139143
140144 switch (fieldType ) {
141145 case BYTES :
142146 return inputStream .readBytes ();
143147 case MESSAGE :
144- String fieldProtoTypeName = fieldDescriptor .getFieldProtoTypeName ();
145- MessageLiteDescriptor descriptor =
146- descriptorPool .findDescriptor (fieldProtoTypeName ).orElse (null );
147- if (descriptor == null ) {
148- return RawProtoMessageLiteValue .create (inputStream .readBytes (), fieldProtoTypeName , this );
149- }
150- MessageLite .Builder builder = descriptor .newMessageBuilder ();
151- inputStream .readMessage (builder , ExtensionRegistryLite .getEmptyRegistry ());
152- return builder .build ();
148+ return mergeOrReadMessageField (
149+ inputStream .readBytes (), fieldDescriptor .getFieldProtoTypeName (), existingValue );
153150 case STRING :
154151 return inputStream .readStringRequireUtf8 ();
155152 default :
156153 throw new IllegalStateException ("Unexpected field type: " + fieldType );
157154 }
158155 }
159156
157+ private Object readMessageField (ByteString bytes , String fieldProtoTypeName ) {
158+ return mergeOrReadMessageField (bytes , fieldProtoTypeName , /* existingValue= */ null );
159+ }
160+
161+ private Object mergeOrReadMessageField (
162+ ByteString bytes , String fieldProtoTypeName , @ Nullable Object existingValue ) {
163+ MessageLiteDescriptor descriptor =
164+ descriptorPool .findDescriptor (fieldProtoTypeName ).orElse (null );
165+ if (descriptor == null ) {
166+ if (existingValue instanceof RawProtoMessageLiteValue ) {
167+ bytes = ((RawProtoMessageLiteValue ) existingValue ).toByteString ().concat (bytes );
168+ }
169+ return RawProtoMessageLiteValue .create (bytes , fieldProtoTypeName , this );
170+ }
171+ WellKnownProto wellKnownProto = WellKnownProto .getByTypeName (fieldProtoTypeName ).orElse (null );
172+ if (isStructLike (wellKnownProto )) {
173+ if (existingValue instanceof ProtoMessageLiteValue ) {
174+ bytes = ((ProtoMessageLiteValue ) existingValue ).toByteString ().concat (bytes );
175+ }
176+ return ProtoMessageLiteValue .create (bytes , fieldProtoTypeName , this );
177+ }
178+ if (existingValue instanceof MessageLite ) {
179+ return mergeMessageLite (((MessageLite ) existingValue ).toBuilder (), bytes , fieldProtoTypeName );
180+ }
181+ return parseMessageLite (bytes , descriptor );
182+ }
183+
184+ // Unlike other WellKnownProtos (which unbox to CEL scalars/containers), FieldMask is
185+ // represented as a standard struct message so field selection (e.g., mask.paths) works.
186+ private static boolean isStructLike (@ Nullable WellKnownProto wellKnownProto ) {
187+ return wellKnownProto == null || wellKnownProto == WellKnownProto .FIELD_MASK ;
188+ }
189+
160190 Object getDefaultCelValue (String protoTypeName , String fieldName ) {
161191 MessageLiteDescriptor messageDescriptor = descriptorPool .getDescriptorOrThrow (protoTypeName );
162192 return getDefaultCelValue (messageDescriptor .getByFieldNameOrThrow (fieldName ));
@@ -172,6 +202,28 @@ Optional<FieldLiteDescriptor> findFieldDescriptor(String protoTypeName, int fiel
172202 .flatMap (desc -> desc .findByFieldNumber (fieldNumber ));
173203 }
174204
205+ MessageLite parseMessageLite (ByteString bytes , String protoTypeName ) {
206+ MessageLiteDescriptor descriptor = descriptorPool .getDescriptorOrThrow (protoTypeName );
207+ return parseMessageLite (bytes , descriptor );
208+ }
209+
210+ private static MessageLite parseMessageLite (ByteString bytes , MessageLiteDescriptor descriptor ) {
211+ if (bytes .isEmpty ()) {
212+ return descriptor .newMessageBuilder ().getDefaultInstanceForType ();
213+ }
214+ return mergeMessageLite (descriptor .newMessageBuilder (), bytes , descriptor .getProtoTypeName ());
215+ }
216+
217+ private static MessageLite mergeMessageLite (
218+ MessageLite .Builder builder , ByteString bytes , String protoTypeName ) {
219+ try {
220+ return builder .mergeFrom (bytes , ExtensionRegistryLite .getEmptyRegistry ()).build ();
221+ } catch (IOException e ) {
222+ throw new IllegalArgumentException (
223+ "Failed to decode proto message of type: " + protoTypeName , e );
224+ }
225+ }
226+
175227 Optional <Object > tryDecodeProtoMessage (ByteString bytes , String protoTypeName ) {
176228 return descriptorPool
177229 .findDescriptor (protoTypeName )
@@ -180,14 +232,11 @@ Optional<Object> tryDecodeProtoMessage(ByteString bytes, String protoTypeName) {
180232
181233 private Object decodeProtoMessage (
182234 ByteString bytes , String protoTypeName , MessageLiteDescriptor descriptor ) {
183- try {
184- MessageLite .Builder builder =
185- descriptor .newMessageBuilder ().mergeFrom (bytes , ExtensionRegistryLite .getEmptyRegistry ());
186- return toRuntimeValue (builder .build (), descriptor );
187- } catch (IOException e ) {
188- throw new IllegalArgumentException (
189- "Failed to decode proto message of type: " + protoTypeName , e );
235+ WellKnownProto wellKnownProto = WellKnownProto .getByTypeName (protoTypeName ).orElse (null );
236+ if (isStructLike (wellKnownProto )) {
237+ return ProtoMessageLiteValue .create (bytes , protoTypeName , this );
190238 }
239+ return fromWellKnownProto (parseMessageLite (bytes , descriptor ), checkNotNull (wellKnownProto ));
191240 }
192241
193242 @ Override
@@ -209,12 +258,11 @@ public Object toRuntimeValue(Object value) {
209258 private Object toRuntimeValue (MessageLite msg , MessageLiteDescriptor descriptor ) {
210259 WellKnownProto wellKnownProto =
211260 WellKnownProto .getByTypeName (descriptor .getProtoTypeName ()).orElse (null );
212-
213- if (wellKnownProto == null || wellKnownProto == WellKnownProto .FIELD_MASK ) {
261+ if (isStructLike (wellKnownProto )) {
214262 return ProtoMessageLiteValue .create (msg , descriptor .getProtoTypeName (), this );
215263 }
216264
217- return fromWellKnownProto (msg , wellKnownProto );
265+ return fromWellKnownProto (msg , checkNotNull ( wellKnownProto ) );
218266 }
219267
220268 private Object getDefaultValue (FieldLiteDescriptor fieldDescriptor ) {
@@ -260,12 +308,7 @@ private Object getScalarDefaultValue(FieldLiteDescriptor fieldDescriptor) {
260308 if (WellKnownProto .isWrapperType (fieldProtoTypeName )) {
261309 return NullValue .NULL_VALUE ;
262310 }
263- MessageLiteDescriptor descriptor =
264- descriptorPool .findDescriptor (fieldProtoTypeName ).orElse (null );
265- if (descriptor == null ) {
266- return RawProtoMessageLiteValue .create (ByteString .EMPTY , fieldProtoTypeName , this );
267- }
268- return descriptor .newMessageBuilder ().build ();
311+ return readMessageField (ByteString .EMPTY , fieldProtoTypeName );
269312 }
270313 throw new IllegalStateException ("Unexpected java type: " + type );
271314 }
@@ -286,7 +329,7 @@ private Map.Entry<Object, Object> readSingleMapEntry(
286329 CodedInputStream inputStream , FieldLiteDescriptor fieldDescriptor ) throws IOException {
287330 String entryTypeName = fieldDescriptor .getFieldProtoTypeName ();
288331 ImmutableMap <String , Object > singleMapEntry =
289- readAllFields (inputStream .readByteArray (), entryTypeName ).values ();
332+ readAllFields (inputStream .readBytes (), entryTypeName ).values ();
290333 Object key = singleMapEntry .get (MAP_KEY_FIELD_NAME );
291334 if (key == null ) {
292335 key = getDefaultCelValue (entryTypeName , MAP_KEY_FIELD_NAME );
@@ -299,25 +342,32 @@ private Map.Entry<Object, Object> readSingleMapEntry(
299342 return new AbstractMap .SimpleEntry <>(key , value );
300343 }
301344
302- MessageFields readAllFields (byte [] bytes , String protoTypeName ) throws IOException {
345+ MessageFields readAllFields (ByteString bytes , String protoTypeName ) throws IOException {
303346 MessageLiteDescriptor messageDescriptor = descriptorPool .getDescriptorOrThrow (protoTypeName );
304- CodedInputStream inputStream = CodedInputStream .newInstance (bytes );
347+ if (bytes .isEmpty ()) {
348+ return MessageFields .EMPTY ;
349+ }
350+ return readAllFields (bytes .newCodedInput (), messageDescriptor );
351+ }
305352
306- Multimap <Integer , Object > unknownFields =
307- Multimaps .newMultimap (new TreeMap <>(), ArrayList ::new );
308- ImmutableMap .Builder <String , Object > fieldValues = ImmutableMap .builder ();
309- Map <Integer , List <Object >> repeatedFieldValues = new LinkedHashMap <>();
310- Map <Integer , Map <Object , Object >> mapFieldValues = new LinkedHashMap <>();
353+ private MessageFields readAllFields (
354+ CodedInputStream inputStream , MessageLiteDescriptor messageDescriptor ) throws IOException {
355+ Multimap <Integer , Object > unknownFields = null ;
356+ Map <String , Object > fieldValues = new LinkedHashMap <>();
311357 for (int tag = inputStream .readTag (); tag != 0 ; tag = inputStream .readTag ()) {
312358 int tagWireType = WireFormat .getTagWireType (tag );
313359 int fieldNumber = WireFormat .getTagFieldNumber (tag );
314360 FieldLiteDescriptor fieldDescriptor =
315361 messageDescriptor .findByFieldNumber (fieldNumber ).orElse (null );
316362 if (fieldDescriptor == null ) {
363+ if (unknownFields == null ) {
364+ unknownFields = Multimaps .newMultimap (new TreeMap <>(), ArrayList ::new );
365+ }
317366 unknownFields .put (fieldNumber , readUnknownField (tagWireType , inputStream ));
318367 continue ;
319368 }
320369
370+ String fieldName = fieldDescriptor .getFieldName ();
321371 Object payload ;
322372 switch (tagWireType ) {
323373 case WireFormat .WIRETYPE_VARINT :
@@ -347,18 +397,24 @@ MessageFields readAllFields(byte[] bytes, String protoTypeName) throws IOExcepti
347397 + protoFieldType );
348398 }
349399
350- payload = readLengthDelimitedField (inputStream , fieldDescriptor );
400+ payload =
401+ readLengthDelimitedField (
402+ inputStream , fieldDescriptor , /* existingValue= */ null );
351403 }
352404 break ;
353405 case MAP :
406+ // Safe because MAP fields only ever store a LinkedHashMap in fieldValues.
407+ @ SuppressWarnings ("unchecked" )
354408 Map <Object , Object > fieldMap =
355- mapFieldValues .computeIfAbsent (fieldNumber , (unused ) -> new LinkedHashMap <>());
409+ (Map <Object , Object >)
410+ fieldValues .computeIfAbsent (fieldName , (unused ) -> new LinkedHashMap <>());
356411 Map .Entry <Object , Object > mapEntry = readSingleMapEntry (inputStream , fieldDescriptor );
357412 fieldMap .put (mapEntry .getKey (), mapEntry .getValue ());
358- payload = fieldMap ;
359- break ;
413+ continue ;
360414 default :
361- payload = readLengthDelimitedField (inputStream , fieldDescriptor );
415+ payload =
416+ readLengthDelimitedField (
417+ inputStream , fieldDescriptor , fieldValues .get (fieldName ));
362418 break ;
363419 }
364420 break ;
@@ -371,30 +427,27 @@ MessageFields readAllFields(byte[] bytes, String protoTypeName) throws IOExcepti
371427 }
372428
373429 if (fieldDescriptor .getEncodingType ().equals (EncodingType .LIST )) {
374- String fieldName = fieldDescriptor .getFieldName ();
375- List <Object > repeatedValues =
376- repeatedFieldValues .computeIfAbsent (fieldNumber , (unused ) -> new ArrayList <>());
377-
378430 if (payload instanceof Collection ) {
379- repeatedValues .addAll ((Collection <?>) payload );
431+ Collection <?> elements = (Collection <?>) payload ;
432+ if (!elements .isEmpty ()) {
433+ getOrCreateRepeatedList (fieldValues , fieldName ).addAll (elements );
434+ }
380435 } else {
381- repeatedValues .add (payload );
382- }
383- if (!repeatedValues .isEmpty ()) {
384- fieldValues .put (fieldName , repeatedValues );
436+ getOrCreateRepeatedList (fieldValues , fieldName ).add (payload );
385437 }
386438 } else {
387- fieldValues .put (fieldDescriptor . getFieldName () , payload );
439+ fieldValues .put (fieldName , payload );
388440 }
389441 }
390442
391- // Protobuf encoding follows a "last one wins" semantics. This means for duplicated fields,
392- // we accept the last value encountered.
393- return MessageFields .create (fieldValues .buildKeepingLast (), unknownFields );
443+ return MessageFields .create (ImmutableMap .copyOf (fieldValues ), unknownFields );
394444 }
395445
396- MessageFields readMessageFields (MessageLite msg , String protoTypeName ) throws IOException {
397- return readAllFields (msg .toByteArray (), protoTypeName );
446+ // Safe because LIST fields only ever store an ArrayList in fieldValues.
447+ @ SuppressWarnings ("unchecked" )
448+ private static List <Object > getOrCreateRepeatedList (
449+ Map <String , Object > fieldValues , String fieldName ) {
450+ return (List <Object >) fieldValues .computeIfAbsent (fieldName , (unused ) -> new ArrayList <>());
398451 }
399452
400453 static Object readUnknownField (int tagWireType , CodedInputStream inputStream ) throws IOException {
@@ -421,19 +474,26 @@ static Object readUnknownField(int tagWireType, CodedInputStream inputStream) th
421474 @ Immutable
422475 @ SuppressWarnings ("Immutable" ) // Safe immutable fields
423476 abstract static class MessageFields {
477+ static final MessageFields EMPTY =
478+ new AutoValue_ProtoLiteCelValueConverter_MessageFields (
479+ ImmutableMap .of (), ImmutableListMultimap .of ());
424480
425481 abstract ImmutableMap <String , Object > values ();
426482
427483 abstract ImmutableListMultimap <Integer , Object > unknowns ();
428484
429- static MessageFields create (
430- ImmutableMap <String , Object > fieldValues , Multimap <Integer , Object > unknownFields ) {
485+ private static MessageFields create (
486+ ImmutableMap <String , Object > fieldValues ,
487+ @ Nullable Multimap <Integer , Object > unknownFields ) {
431488 return new AutoValue_ProtoLiteCelValueConverter_MessageFields (
432- fieldValues , ImmutableListMultimap .copyOf (unknownFields ));
489+ fieldValues ,
490+ unknownFields == null
491+ ? ImmutableListMultimap .of ()
492+ : ImmutableListMultimap .copyOf (unknownFields ));
433493 }
434494 }
435495
436496 private ProtoLiteCelValueConverter (CelLiteDescriptorPool celLiteDescriptorPool ) {
437- this .descriptorPool = celLiteDescriptorPool ;
497+ this .descriptorPool = checkNotNull ( celLiteDescriptorPool ) ;
438498 }
439499}
0 commit comments