@@ -356,12 +356,12 @@ private static Optional<PropertyAccessor> buildPropertyAccessor(
356356 if (getter != null ) {
357357 propType = getter .getReturnType ();
358358 genericPropType = getter .getGenericReturnType ();
359- discoverCustomTypes ( genericPropType , queue );
359+ queue . addAll ( TypeReferenceCollector . collect ( genericPropType ) );
360360 compiledGetter = compileGetter (getter );
361361 } else if (field != null ) {
362362 propType = field .getType ();
363363 genericPropType = field .getGenericType ();
364- discoverCustomTypes ( genericPropType , queue );
364+ queue . addAll ( TypeReferenceCollector . collect ( genericPropType ) );
365365 compiledGetter = compileFieldGetter (field );
366366 }
367367
@@ -386,46 +386,53 @@ private static Optional<PropertyAccessor> buildPropertyAccessor(
386386
387387 /**
388388 * Recursively explores a {@link Type} and discovers any transitive, user-defined custom POJO
389- * classes nested inside multi-level generic collections, lists, maps, or optionals, pushing
390- * them into the scanning discovery queue .
389+ * classes nested inside multi-level generic collections, lists, maps, or optionals, collecting
390+ * them for subsequent properties discovery.
391391 *
392392 * <p>"Custom types" are any public non-primitive, non-built-in Java classes that require
393393 * explicit properties reflective scanning and mapping to a CEL StructType schema (as opposed to
394394 * standard built-in types like {@code String}, {@code List}, or {@code Map}).
395- *
396- * @param type The Java type token or parameterized collection type to recursively unpack.
397- * @param queue The central scanning queue where newly discovered custom classes are pushed for
398- * subsequent properties discovery.
399395 */
400- private static void discoverCustomTypes (Type type , Queue <Class <?>> queue ) {
401- Preconditions .checkNotNull (type , "Type to discover cannot be null." );
402- Preconditions .checkNotNull (queue , "Queue cannot be null." );
403- TypeToken <?> token = TypeToken .of (type );
404- Class <?> rawType = token .getRawType ();
405-
406- if (List .class .isAssignableFrom (rawType )) {
407- Type elementType = ReflectionUtil .resolveGenericParameter (token , List .class , 0 );
408- discoverCustomTypes (elementType , queue );
409- return ;
410- }
396+ private static final class TypeReferenceCollector {
397+ private final Set <Class <?>> collectedTypes = new HashSet <>();
398+
399+ /**
400+ * Traverses the given type and returns an immutable set of all custom POJO classes found.
401+ *
402+ * @param type The Java type token or parameterized collection type to recursively unpack.
403+ */
404+ private static ImmutableSet <Class <?>> collect (Type type ) {
405+ TypeReferenceCollector collector = new TypeReferenceCollector ();
406+ collector .discover (type );
407+ return ImmutableSet .copyOf (collector .collectedTypes );
408+ }
409+
410+ private void discover (Type type ) {
411+ Preconditions .checkNotNull (type , "Type to discover cannot be null." );
412+ TypeToken <?> token = TypeToken .of (type );
413+ Class <?> rawType = token .getRawType ();
414+
415+ if (List .class .isAssignableFrom (rawType )) {
416+ discover (ReflectionUtil .resolveGenericParameter (token , List .class , 0 ));
417+ return ;
418+ }
411419
412- if (Map .class .isAssignableFrom (rawType )) {
413- Type keyType = ReflectionUtil .resolveGenericParameter (token , Map .class , 0 );
414- Type valueType = ReflectionUtil .resolveGenericParameter (token , Map .class , 1 );
415- discoverCustomTypes (keyType , queue );
416- discoverCustomTypes (valueType , queue );
417- return ;
418- }
420+ if (Map .class .isAssignableFrom (rawType )) {
421+ discover (ReflectionUtil .resolveGenericParameter (token , Map .class , 0 ));
422+ discover (ReflectionUtil .resolveGenericParameter (token , Map .class , 1 ));
423+ return ;
424+ }
419425
420- if (rawType == Optional .class ) {
421- Type optionalType = ReflectionUtil .resolveGenericParameter (token , Optional .class , 0 );
422- discoverCustomTypes (optionalType , queue );
423- return ;
424- }
426+ if (rawType == Optional .class ) {
427+ discover (ReflectionUtil .resolveGenericParameter (token , Optional .class , 0 ));
428+ return ;
429+ }
425430
426- if (!JAVA_TO_DEFAULT_VALUE_MAP .containsKey (rawType )
427- && Modifier .isPublic (rawType .getModifiers ())) {
428- queue .add (rawType );
431+ // Custom types are non-builtin, public classes
432+ if (!JAVA_TO_DEFAULT_VALUE_MAP .containsKey (rawType )
433+ && Modifier .isPublic (rawType .getModifiers ())) {
434+ collectedTypes .add (rawType );
435+ }
429436 }
430437 }
431438
0 commit comments