1616package org .springframework .data .core ;
1717
1818import kotlin .reflect .KProperty ;
19+ import kotlin .reflect .KProperty1 ;
20+ import kotlin .reflect .jvm .internal .KProperty1Impl ;
21+ import kotlin .reflect .jvm .internal .KPropertyImpl ;
1922
2023import java .beans .Introspector ;
2124import java .beans .PropertyDescriptor ;
3033import java .util .stream .Stream ;
3134
3235import org .jspecify .annotations .Nullable ;
36+
3337import org .springframework .beans .BeanUtils ;
3438import org .springframework .core .KotlinDetector ;
3539import org .springframework .core .ResolvableType ;
@@ -95,10 +99,69 @@ public static <T, M, P> TypedPropertyPath<T, P> compose(TypedPropertyPath<T, M>
9599 }
96100
97101 /**
98- * Create a {@link TypedPropertyPath} from a {@link PropertyReference} .
102+ * Introspect {@link PropertyReference} and return an introspected {@link ResolvedTypedPropertyPath} variant .
99103 */
104+ @ SuppressWarnings ({ "unchecked" , "rawtypes" })
100105 public static <P , T > TypedPropertyPath <T , P > of (PropertyReference <T , P > lambda ) {
101- return new PropertyReferenceWrapper <>(PropertyReferences .of (lambda ));
106+
107+ if (lambda instanceof Resolved ) {
108+ return (TypedPropertyPath ) lambda ;
109+ }
110+
111+ Map <PropertyReference <?, ?>, TypedPropertyPath <?, ?>> cache ;
112+ synchronized (resolved ) {
113+ cache = (Map ) resolved .computeIfAbsent (lambda .getClass ().getClassLoader (),
114+ k -> new ConcurrentReferenceHashMap <>());
115+ }
116+
117+ return (TypedPropertyPath ) cache .computeIfAbsent (lambda , o -> doResolvePropertyReference (lambda ));
118+ }
119+
120+ @ SuppressWarnings ({ "rawtypes" , "unchecked" })
121+ private static <T , P > TypedPropertyPath <?, ?> doResolvePropertyReference (PropertyReference <T , P > lambda ) {
122+
123+ if (lambda instanceof PropertyReferences .ResolvedPropertyReferenceSupport resolved ) {
124+ return new PropertyReferenceWrapper <>(resolved );
125+ }
126+
127+ PropertyPathMetadata metadata = getMetadata (lambda );
128+
129+ if (KotlinDetector .isKotlinReflectPresent () && metadata instanceof KPropertyPathMetadata kMetadata
130+ && kMetadata .getProperty () instanceof KPropertyReferenceImpl <?, ?> ref ) {
131+ return KotlinDelegate .of (ref );
132+ }
133+
134+ return new ResolvedPropertyReference <>(lambda , metadata );
135+ }
136+
137+ static class KotlinDelegate {
138+
139+ @ SuppressWarnings ({ "rawtypes" , "unchecked" })
140+ public static <T , P > TypedPropertyPath <T , P > of (KProperty1 <T , P > property ) {
141+
142+ if (property instanceof KPropertyReferenceImpl paths ) {
143+
144+ TypedPropertyPath parent = of (paths .getProperty ());
145+ TypedPropertyPath child = of (paths .getLeaf ());
146+
147+ return TypedPropertyPaths .compose (parent , child );
148+ }
149+
150+ if (property instanceof KPropertyImpl impl ) {
151+
152+ Class <?> owner = impl .getJavaField () != null ? impl .getJavaField ().getDeclaringClass ()
153+ : impl .getGetter ().getCaller ().getMember ().getDeclaringClass ();
154+ KPropertyPathMetadata metadata = TypedPropertyPaths .KPropertyPathMetadata
155+ .of (MemberDescriptor .KPropertyReferenceDescriptor .create (owner , property ));
156+ return new TypedPropertyPaths .ResolvedKPropertyPath (metadata );
157+ }
158+
159+ if (property .getGetter ().getProperty () instanceof KProperty1Impl impl ) {
160+ return of (impl );
161+ }
162+
163+ throw new IllegalArgumentException ("Property " + property .getName () + " is not a KProperty" );
164+ }
102165 }
103166
104167 /**
@@ -117,8 +180,8 @@ public static <P, T> TypedPropertyPath<T, P> of(TypedPropertyPath<T, P> lambda)
117180 k -> new ConcurrentReferenceHashMap <>());
118181 }
119182
120- return (TypedPropertyPath < T , P > ) cache .computeIfAbsent (lambda ,
121- o -> new ResolvedTypedPropertyPath (o , getMetadata (lambda )));
183+ return (TypedPropertyPath ) cache .computeIfAbsent (lambda ,
184+ o -> new ResolvedTypedPropertyPath (o , doGetMetadata (lambda )));
122185 }
123186
124187 /**
@@ -134,26 +197,44 @@ public static <T, P> TypedPropertyPath<T, P> of(TypedPropertyPath<T, P> delegate
134197 return new ResolvedTypedPropertyPath <>(delegate , metadata );
135198 }
136199
200+ /**
201+ * Retrieve {@link PropertyPathMetadata} for a given {@link PropertyReference}.
202+ */
203+ public static PropertyPathMetadata getMetadata (PropertyReference <?, ?> lambda ) {
204+ return doGetMetadata (lambda );
205+ }
206+
137207 /**
138208 * Retrieve {@link PropertyPathMetadata} for a given {@link TypedPropertyPath}.
139209 */
140210 public static PropertyPathMetadata getMetadata (TypedPropertyPath <?, ?> lambda ) {
211+ return doGetMetadata (lambda );
212+ }
213+
214+ private static PropertyPathMetadata doGetMetadata (Object lambda ) {
141215
142216 Map <Object , PropertyPathMetadata > cache ;
217+
143218 synchronized (lambdas ) {
144219 cache = lambdas .computeIfAbsent (lambda .getClass ().getClassLoader (), k -> new ConcurrentReferenceHashMap <>());
145220 }
146- Map <Object , PropertyPathMetadata > lambdaMap = cache ;
147221
148- return lambdaMap .computeIfAbsent (lambda , o -> read (lambda ));
222+ return cache .computeIfAbsent (lambda , o -> read (lambda ));
149223 }
150224
151- private static PropertyPathMetadata read (TypedPropertyPath <?, ?> lambda ) {
225+ private static PropertyPathMetadata read (Object lambda ) {
152226
153227 MemberDescriptor reference = reader .read (lambda );
154228
155- if (KotlinDetector .isKotlinReflectPresent () && reference instanceof KPropertyReferenceDescriptor kProperty ) {
156- return KPropertyPathMetadata .of (kProperty );
229+ if (KotlinDetector .isKotlinReflectPresent ()) {
230+
231+ if (reference instanceof KPropertyReferenceDescriptor kProperty ) {
232+ return KPropertyPathMetadata .of (kProperty );
233+ }
234+
235+ if (reference instanceof MemberDescriptor .KPropertyPathDescriptor kProperty ) {
236+ return KPropertyPathMetadata .of (kProperty );
237+ }
157238 }
158239
159240 if (reference instanceof MethodDescriptor method ) {
@@ -258,6 +339,13 @@ public static KPropertyPathMetadata of(KPropertyReferenceDescriptor descriptor)
258339 return new KPropertyPathMetadata (descriptor .getOwner (), descriptor .property (), descriptor .getType ());
259340 }
260341
342+ /**
343+ * Create a new {@code KPropertyPathMetadata}.
344+ */
345+ public static KPropertyPathMetadata of (MemberDescriptor .KPropertyPathDescriptor descriptor ) {
346+ return new KPropertyPathMetadata (descriptor .getOwner (), descriptor .property (), descriptor .getType ());
347+ }
348+
261349 public KProperty <?> getProperty () {
262350 return property ;
263351 }
0 commit comments