1616
1717package org .springframework .ai .model .function ;
1818
19+ import java .lang .reflect .GenericArrayType ;
20+ import java .lang .reflect .ParameterizedType ;
1921import java .lang .reflect .Type ;
2022import java .util .function .BiFunction ;
2123import java .util .function .Function ;
2224
23- import kotlin .jvm .functions .Function1 ;
24- import kotlin .jvm .functions .Function2 ;
25+ import net .jodah .typetools .TypeResolver ;
2526
26- import org .springframework .core .KotlinDetector ;
27- import org .springframework .core .ResolvableType ;
27+ import org .springframework .cloud .function .context .catalog .FunctionTypeUtils ;
2828
2929/**
3030 * A utility class that provides methods for resolving types and classes related to
3131 * functions.
3232 *
3333 * @author Christian Tzolov
34- * @author Sebastien Dekeuze
3534 */
3635public abstract class TypeResolverHelper {
3736
@@ -69,9 +68,12 @@ public static Class<?> getFunctionOutputClass(Class<? extends Function<?, ?>> fu
6968 * @return The class of the specified function argument.
7069 */
7170 public static Class <?> getFunctionArgumentClass (Class <? extends Function <?, ?>> functionClass , int argumentIndex ) {
72- ResolvableType resolvableType = ResolvableType .forClass (functionClass ).as (Function .class );
73- return (resolvableType == ResolvableType .NONE ? Object .class
74- : resolvableType .getGeneric (argumentIndex ).toClass ());
71+ Type type = TypeResolver .reify (Function .class , functionClass );
72+
73+ var argumentType = type instanceof ParameterizedType
74+ ? ((ParameterizedType ) type ).getActualTypeArguments ()[argumentIndex ] : Object .class ;
75+
76+ return toRawClass (argumentType );
7577 }
7678
7779 /**
@@ -82,65 +84,80 @@ public static Class<?> getFunctionArgumentClass(Class<? extends Function<?, ?>>
8284 */
8385 public static Class <?> getBiFunctionArgumentClass (Class <? extends BiFunction <?, ?, ?>> biFunctionClass ,
8486 int argumentIndex ) {
85- ResolvableType resolvableType = ResolvableType .forClass (biFunctionClass ).as (BiFunction .class );
86- return (resolvableType == ResolvableType .NONE ? Object .class
87- : resolvableType .getGeneric (argumentIndex ).toClass ());
87+ Type type = TypeResolver .reify (BiFunction .class , biFunctionClass );
88+
89+ Type argumentType = type instanceof ParameterizedType
90+ ? ((ParameterizedType ) type ).getActualTypeArguments ()[argumentIndex ] : Object .class ;
91+
92+ return toRawClass (argumentType );
93+ }
94+
95+ /**
96+ * Returns the input type of a given function class.
97+ * @param functionClass The class of the function.
98+ * @return The input type of the function.
99+ */
100+ public static Type getFunctionInputType (Class <? extends Function <?, ?>> functionClass ) {
101+ return getFunctionArgumentType (functionClass , 0 );
102+ }
103+
104+ /**
105+ * Retrieves the output type of a given function class.
106+ * @param functionClass The function class.
107+ * @return The output type of the function.
108+ */
109+ public static Type getFunctionOutputType (Class <? extends Function <?, ?>> functionClass ) {
110+ return getFunctionArgumentType (functionClass , 1 );
88111 }
89112
90113 /**
91114 * Retrieves the type of a specific argument in a given function class.
92- * @param functionType The function type .
115+ * @param functionClass The function class .
93116 * @param argumentIndex The index of the argument whose type should be retrieved.
94117 * @return The type of the specified function argument.
95- * @throws IllegalArgumentException if functionType is not a supported type
96118 */
97- public static ResolvableType getFunctionArgumentType (Type functionType , int argumentIndex ) {
98-
99- ResolvableType resolvableType = ResolvableType .forType (functionType );
100- Class <?> resolvableClass = resolvableType .toClass ();
101- ResolvableType functionArgumentResolvableType = ResolvableType .NONE ;
119+ public static Type getFunctionArgumentType (Class <? extends Function <?, ?>> functionClass , int argumentIndex ) {
120+ Type functionType = TypeResolver .reify (Function .class , functionClass );
121+ return getFunctionArgumentType (functionType , argumentIndex );
122+ }
102123
103- if (Function .class .isAssignableFrom (resolvableClass )) {
104- functionArgumentResolvableType = resolvableType .as (Function .class );
105- }
106- else if (BiFunction .class .isAssignableFrom (resolvableClass )) {
107- functionArgumentResolvableType = resolvableType .as (BiFunction .class );
108- }
109- else if (KotlinDetector .isKotlinPresent ()) {
110- if (KotlinDelegate .isKotlinFunction (resolvableClass )) {
111- functionArgumentResolvableType = KotlinDelegate .adaptToKotlinFunctionType (resolvableType );
124+ /**
125+ * Retrieves the type of a specific argument in a given function type.
126+ * @param functionType The function type.
127+ * @param argumentIndex The index of the argument whose type should be retrieved.
128+ * @return The type of the specified function argument.
129+ */
130+ public static Type getFunctionArgumentType (Type functionType , int argumentIndex ) {
131+
132+ // Resolves: https://github.com/spring-projects/spring-ai/issues/726
133+ if (!(functionType instanceof ParameterizedType )) {
134+ Class <?> functionalClass = FunctionTypeUtils .getRawType (functionType );
135+ // Resolves: https://github.com/spring-projects/spring-ai/issues/1576
136+ if (BiFunction .class .isAssignableFrom (functionalClass )) {
137+ functionType = TypeResolver .reify (BiFunction .class , (Class <BiFunction <?, ?, ?>>) functionalClass );
112138 }
113- else if ( KotlinDelegate . isKotlinBiFunction ( resolvableClass )) {
114- functionArgumentResolvableType = KotlinDelegate . adaptToKotlinBiFunctionType ( resolvableType );
139+ else {
140+ functionType = FunctionTypeUtils . discoverFunctionTypeFromClass ( functionalClass );
115141 }
116142 }
117143
118- if (functionArgumentResolvableType == ResolvableType .NONE ) {
119- throw new IllegalArgumentException (
120- "Type must be a Function, BiFunction, Function1 or Function2. Found: " + resolvableType );
121- }
144+ var argumentType = functionType instanceof ParameterizedType
145+ ? ((ParameterizedType ) functionType ).getActualTypeArguments ()[argumentIndex ] : Object .class ;
122146
123- return functionArgumentResolvableType . getGeneric ( argumentIndex ) ;
147+ return argumentType ;
124148 }
125149
126- private static class KotlinDelegate {
127-
128- public static boolean isKotlinFunction (Class <?> clazz ) {
129- return Function1 .class .isAssignableFrom (clazz );
130- }
131-
132- public static ResolvableType adaptToKotlinFunctionType (ResolvableType resolvableType ) {
133- return resolvableType .as (Function1 .class );
134- }
135-
136- public static boolean isKotlinBiFunction (Class <?> clazz ) {
137- return Function2 .class .isAssignableFrom (clazz );
138- }
139-
140- public static ResolvableType adaptToKotlinBiFunctionType (ResolvableType resolvableType ) {
141- return resolvableType .as (Function2 .class );
142- }
143-
150+ /**
151+ * Effectively converts {@link Type} which could be {@link ParameterizedType} to raw
152+ * Class (no generics).
153+ * @param type actual {@link Type} instance
154+ * @return instance of {@link Class} as raw representation of the provided
155+ * {@link Type}
156+ */
157+ public static Class <?> toRawClass (Type type ) {
158+ return type != null
159+ ? TypeResolver .resolveRawClass (type instanceof GenericArrayType ? type : TypeResolver .reify (type ), null )
160+ : null ;
144161 }
145162
146163}
0 commit comments