2525import dev .cel .common .values .CelValueConverter ;
2626import dev .cel .runtime .GlobalResolver ;
2727import java .util .NoSuchElementException ;
28+ import org .jspecify .annotations .Nullable ;
2829
2930@ Immutable
3031final class NamespacedAttribute implements Attribute {
@@ -34,6 +35,14 @@ final class NamespacedAttribute implements Attribute {
3435 private final CelValueConverter celValueConverter ;
3536 private final CelTypeProvider typeProvider ;
3637
38+ ImmutableList <Qualifier > qualifiers () {
39+ return qualifiers ;
40+ }
41+
42+ ImmutableSet <String > candidateVariableNames () {
43+ return namespacedNames ;
44+ }
45+
3746 @ Override
3847 public Object resolve (GlobalResolver ctx , ExecutionFrame frame ) {
3948 GlobalResolver inputVars = ctx ;
@@ -59,41 +68,63 @@ public Object resolve(GlobalResolver ctx, ExecutionFrame frame) {
5968 }
6069 }
6170
62- CelType type = typeProvider .findType (name ).orElse (null );
63- if (type != null ) {
64- if (qualifiers .isEmpty ()) {
65- // Resolution of a fully qualified type name: foo.bar.baz
66- return TypeType .create (type );
67- } else {
68- // This is potentially a fully qualified reference to an enum value
69- if (type instanceof EnumType && qualifiers .size () == 1 ) {
70- EnumType enumType = (EnumType ) type ;
71- String strQualifier = (String ) qualifiers .get (0 ).value ();
72- return enumType
73- .findNumberByName (strQualifier )
74- .orElseThrow (
75- () ->
76- new NoSuchElementException (
77- String .format (
78- "Field %s was not found on enum %s" ,
79- enumType .name (), strQualifier )));
80- }
81- }
82-
83- throw new IllegalStateException (
84- "Unexpected type resolution when there were remaining qualifiers: " + type .name ());
71+ // Attempt to resolve the qualify type name if the name is not a variable identifier
72+ value = findIdent (name );
73+ if (value != null ) {
74+ return value ;
8575 }
8676 }
8777
8878 return MissingAttribute .newMissingAttribute (namespacedNames );
8979 }
9080
91- ImmutableList <Qualifier > qualifiers () {
92- return qualifiers ;
81+ private @ Nullable Object findIdent (String name ) {
82+ CelType type = typeProvider .findType (name ).orElse (null );
83+ // If the name resolves directly, this is either a fully qualified type name
84+ // (ex: 'int' or 'google.protobuf.Timestamp')
85+ // or an enum with a string qualifier
86+ // (ex: my.enum_type, qualifier: .BAR)
87+ if (type != null ) {
88+ if (qualifiers .isEmpty ()) {
89+ // Resolution of a fully qualified type name: foo.bar.baz
90+ return TypeType .create (type );
91+ }
92+
93+ // If it's an EnumType, the first qualifier should be the enum field access.
94+ if (type instanceof EnumType && qualifiers .size () == 1 ) {
95+ return getEnumValue ((EnumType ) type , (String ) qualifiers .get (0 ).value ());
96+ }
97+
98+ throw new IllegalStateException (
99+ "Unexpected type resolution when there were remaining qualifiers: " + type .name ());
100+ }
101+
102+ // The name itself could be a fully qualified reference to an enum value
103+ // (e.g: my.enum_type.BAR)
104+ int lastDotIndex = name .lastIndexOf ('.' );
105+ if (lastDotIndex > 0 ) {
106+ String enumTypeName = name .substring (0 , lastDotIndex );
107+ String enumValueQualifier = name .substring (lastDotIndex + 1 );
108+
109+ return typeProvider
110+ .findType (enumTypeName )
111+ .filter (EnumType .class ::isInstance )
112+ .map (EnumType .class ::cast )
113+ .map (enumType -> getEnumValue (enumType , enumValueQualifier ))
114+ .orElse (null );
115+ }
116+
117+ return null ;
93118 }
94119
95- ImmutableSet <String > candidateVariableNames () {
96- return namespacedNames ;
120+ private static Long getEnumValue (EnumType enumType , String field ) {
121+ return enumType
122+ .findNumberByName (field )
123+ .map (Integer ::longValue )
124+ .orElseThrow (
125+ () ->
126+ new NoSuchElementException (
127+ String .format ("Field %s was not found on enum %s" , enumType .name (), field )));
97128 }
98129
99130 private GlobalResolver unwrapToNonLocal (GlobalResolver resolver ) {
0 commit comments