Skip to content

Commit b1d6892

Browse files
committed
Documentation, polishing.
1 parent f383e78 commit b1d6892

File tree

8 files changed

+118
-76
lines changed

8 files changed

+118
-76
lines changed

src/main/antora/modules/ROOT/pages/property-paths.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ Java::
131131
+
132132
[source,java,role="primary"]
133133
----
134+
// Static import variant
135+
path(Person::getAddress)
136+
.then(Address::getCity);
137+
138+
// Composition factory method
139+
path(Person::getAddress, Address::getCity);
140+
141+
// Fluent composition
134142
TypedPropertyPath.of(Person::getAddress)
135143
.then(Address::getCity);
136144
----

src/main/java/org/springframework/data/core/PropertyPath.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,38 +45,39 @@
4545
* @author Johannes Englmeier
4646
* @see PropertyReference
4747
* @see TypedPropertyPath
48+
* @see java.beans.PropertyDescriptor
4849
*/
4950
public interface PropertyPath extends Streamable<PropertyPath> {
5051

5152
/**
52-
* Syntax sugar to create a {@link TypedPropertyPath} from a method reference or lambda.
53+
* Syntax sugar to create a {@link TypedPropertyPath} from a method reference to a Java beans property.
5354
* <p>
54-
* This method returns a resolved {@link TypedPropertyPath} by introspecting the given method reference or lambda.
55+
* This method returns a resolved {@link TypedPropertyPath} by introspecting the given method reference.
5556
*
56-
* @param propertyPath the method reference or lambda.
57+
* @param property the method reference referring to a Java beans property.
5758
* @param <T> owning type.
5859
* @param <P> property type.
5960
* @return the typed property path.
6061
* @since 4.1
6162
*/
62-
static <T, P> TypedPropertyPath<T, P> of(PropertyReference<T, P> propertyPath) {
63-
return TypedPropertyPaths.of(propertyPath);
63+
static <T, P> TypedPropertyPath<T, P> of(PropertyReference<T, P> property) {
64+
return TypedPropertyPaths.of(property);
6465
}
6566

6667
/**
67-
* Syntax sugar to create a {@link TypedPropertyPath} from a method reference or lambda for a collection property.
68+
* Syntax sugar to create a {@link TypedPropertyPath} from a method reference to a Java beans collection property.
6869
* <p>
69-
* This method returns a resolved {@link TypedPropertyPath} by introspecting the given method reference or lambda.
70+
* This method returns a resolved {@link TypedPropertyPath} by introspecting the given method reference.
7071
*
71-
* @param propertyPath the method reference or lambda.
72+
* @param property the method reference referring to a property.
7273
* @param <T> owning type.
7374
* @param <P> property type.
7475
* @return the typed property path.
7576
* @since 4.1
7677
*/
7778
@SuppressWarnings({ "unchecked", "rawtypes" })
78-
static <T, P> TypedPropertyPath<T, P> ofMany(PropertyReference<T, ? extends Iterable<P>> propertyPath) {
79-
return (TypedPropertyPath) TypedPropertyPaths.of(propertyPath);
79+
static <T, P> TypedPropertyPath<T, P> ofMany(PropertyReference<T, ? extends Iterable<P>> property) {
80+
return (TypedPropertyPath) TypedPropertyPaths.of(property);
8081
}
8182

8283
/**

src/main/java/org/springframework/data/core/PropertyReference.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* reference, for example:
3131
*
3232
* <pre class="code">
33-
* PropertyReference&lt;Person, String&gt; name = PropertyReference.of(Person::getName);
33+
* PropertyReference.property(Person::getName);
3434
* </pre>
3535
*
3636
* The resulting object can be used to obtain the {@link #getName() property name} and to interact with the target
@@ -56,18 +56,23 @@
5656
* @param <P> the property value type.
5757
* @author Mark Paluch
5858
* @since 4.1
59+
* @see #property(PropertyReference)
5960
* @see #then(PropertyReference)
61+
* @see #of(PropertyReference)
62+
* @see #ofMany(PropertyReference)
6063
* @see TypedPropertyPath
64+
* @see java.beans.PropertyDescriptor
6165
*/
6266
@FunctionalInterface
6367
public interface PropertyReference<T, P extends @Nullable Object> extends Serializable {
6468

6569
/**
66-
* Syntax sugar to create a {@link PropertyReference} from a method reference. Suitable for static imports.
70+
* Syntax sugar to create a {@link PropertyReference} from a method reference to a Java beans property. Suitable for
71+
* static imports.
6772
* <p>
6873
* This method returns a resolved {@link PropertyReference} by introspecting the given method reference.
6974
*
70-
* @param property the method reference.
75+
* @param property the method reference to a Java beans property.
7176
* @param <T> owning type.
7277
* @param <P> property type.
7378
* @return the typed property reference.
@@ -77,11 +82,11 @@ public interface PropertyReference<T, P extends @Nullable Object> extends Serial
7782
}
7883

7984
/**
80-
* Syntax sugar to create a {@link PropertyReference} from a method reference.
85+
* Syntax sugar to create a {@link PropertyReference} from a method reference to a Java beans property.
8186
* <p>
8287
* This method returns a resolved {@link PropertyReference} by introspecting the given method reference.
8388
*
84-
* @param property the method reference.
89+
* @param property the method reference to a Java beans property.
8590
* @param <T> owning type.
8691
* @param <P> property type.
8792
* @return the typed property reference.
@@ -91,13 +96,13 @@ public interface PropertyReference<T, P extends @Nullable Object> extends Serial
9196
}
9297

9398
/**
94-
* Syntax sugar to create a {@link PropertyReference} from a method reference or lambda for a collection property.
99+
* Syntax sugar to create a {@link PropertyReference} from a method reference to a Java beans property.
95100
* <p>
96101
* This method returns a resolved {@link PropertyReference} by introspecting the given method reference. Note that
97102
* {@link #get(Object)} becomes unusable for collection properties as the property type adapted from
98103
* {@code Iterable &lt;P&gt;} and a single {@code P} cannot represent a collection of items.
99104
*
100-
* @param property the method reference.
105+
* @param property the method reference to a Java beans property.
101106
* @param <T> owning type.
102107
* @param <P> property type.
103108
* @return the typed property reference.
@@ -170,8 +175,8 @@ default boolean isCollection() {
170175
* Extend the property to a property path by appending the {@code next} path segment and return a new property path
171176
* instance.
172177
*
173-
* @param next the next property path segment as method reference or lambda accepting the owner object {@code P} type
174-
* and returning {@code N} as result of accessing a property.
178+
* @param next the next property path segment as method reference accepting the owner object {@code P} type and
179+
* returning {@code N} as result of accessing the property.
175180
* @param <N> the new property value type.
176181
* @return a new composed {@code TypedPropertyPath}.
177182
*/
@@ -186,8 +191,8 @@ default boolean isCollection() {
186191
* Note that {@link #get(Object)} becomes unusable for collection properties as the property type adapted from
187192
* {@code Iterable &lt;P&gt;} and a single {@code P} cannot represent a collection of items.
188193
*
189-
* @param next the next property path segment as method reference or lambda accepting the owner object {@code P} type
190-
* and returning {@code N} as result of accessing a property.
194+
* @param next the next property path segment as method reference accepting the owner object {@code P} type and
195+
* returning {@code N} as result of accessing the property.
191196
* @param <N> the new property value type.
192197
* @return a new composed {@code TypedPropertyPath}.
193198
*/

src/main/java/org/springframework/data/core/PropertyReferences.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,12 @@ public TypeInformation<P> getTypeInformation() {
252252

253253
@Override
254254
public boolean equals(@Nullable Object obj) {
255-
return PropertyUtil.equals(this, obj);
255+
return PropertyPathUtil.equals(this, obj);
256256
}
257257

258258
@Override
259259
public int hashCode() {
260-
return PropertyUtil.hashCode(this);
260+
return PropertyPathUtil.hashCode(this);
261261
}
262262

263263
@Override

src/main/java/org/springframework/data/core/SerializableLambdaReader.java

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,7 @@ public MemberDescriptor read(Object lambdaObject) {
141141
SerializedLambda lambda = serialize(lambdaObject);
142142

143143
if (isKotlinPropertyReference(lambda)) {
144-
145-
Object captured = lambda.getCapturedArg(0);
146-
if (captured instanceof PropertyReference propRef //
147-
&& propRef.getOwner() instanceof KClass<?> owner //
148-
&& captured instanceof KProperty1<?, ?> kProperty) {
149-
return new KPropertyReferenceDescriptor(JvmClassMappingKt.getJavaClass(owner), kProperty);
150-
}
151-
152-
if (captured instanceof KPropertyPath<?, ?> propRef) {
153-
return KPropertyPathDescriptor.create(propRef);
154-
}
144+
return KotlinDelegate.read(lambda);
155145
}
156146

157147
assertNotConstructor(lambda);
@@ -178,11 +168,6 @@ public MemberDescriptor read(Object lambdaObject) {
178168
+ ". The given value is not a lambda or method reference.");
179169
}
180170

181-
public static boolean isKotlinPropertyReference(SerializedLambda lambda) {
182-
return KotlinDetector.isKotlinReflectPresent() && lambda.getCapturedArgCount() == 1
183-
&& lambda.getCapturedArg(0) != null && KotlinDetector.isKotlinType(lambda.getCapturedArg(0).getClass());
184-
}
185-
186171
private void assertNotConstructor(SerializedLambda lambda) {
187172

188173
if (lambda.getImplMethodKind() == MethodHandleInfo.REF_newInvokeSpecial
@@ -219,7 +204,7 @@ private MemberDescriptor getMemberDescriptor(Object lambdaObject, SerializedLamb
219204
}
220205
}
221206

222-
static SerializedLambda serialize(Object lambda) {
207+
private static SerializedLambda serialize(Object lambda) {
223208

224209
try {
225210
Method method = lambda.getClass().getDeclaredMethod("writeReplace");
@@ -231,6 +216,40 @@ static SerializedLambda serialize(Object lambda) {
231216
}
232217
}
233218

219+
private static boolean isKotlinPropertyReference(SerializedLambda lambda) {
220+
221+
return KotlinDetector.isKotlinReflectPresent() //
222+
&& lambda.getCapturedArgCount() == 1 //
223+
&& lambda.getCapturedArg(0) != null //
224+
&& KotlinDetector.isKotlinType(lambda.getCapturedArg(0).getClass());
225+
}
226+
227+
/**
228+
* Delegate to detect and read Kotlin property references.
229+
* <p>
230+
* Inner class delays loading of Kotlin classes.
231+
*/
232+
static class KotlinDelegate {
233+
234+
public static MemberDescriptor read(SerializedLambda lambda) {
235+
236+
Object captured = lambda.getCapturedArg(0);
237+
238+
if (captured instanceof PropertyReference propRef //
239+
&& propRef.getOwner() instanceof KClass<?> owner //
240+
&& captured instanceof KProperty1<?, ?> kProperty) {
241+
return new KPropertyReferenceDescriptor(JvmClassMappingKt.getJavaClass(owner), kProperty);
242+
}
243+
244+
if (captured instanceof KPropertyPath<?, ?> propRef) {
245+
return KPropertyPathDescriptor.create(propRef);
246+
}
247+
248+
throw new InvalidDataAccessApiUsageException("Cannot extract MemberDescriptor from: " + lambda);
249+
}
250+
251+
}
252+
234253
class LambdaReadingVisitor extends ClassVisitor {
235254

236255
private final String implMethodName;
@@ -306,14 +325,14 @@ public void visitInsn(int opcode) {
306325
@Override
307326
public void visitLdcInsn(Object value) {
308327
errors.add(new ReadingError(line,
309-
"Lambda expressions may only contain method calls to getters, record components, or field access", null));
328+
"Code loads a constant. Only method calls to getters, record components, or field access allowed.", null));
310329
}
311330

312331
@Override
313332
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
314333

315334
if (opcode == Opcodes.PUTSTATIC || opcode == Opcodes.PUTFIELD) {
316-
errors.add(new ReadingError(line, "Setting a field not allowed", null));
335+
errors.add(new ReadingError(line, String.format("Code attempts to set field '%s'", name), null));
317336
return;
318337
}
319338

@@ -334,7 +353,7 @@ public void visitFieldInsn(int opcode, String owner, String name, String descrip
334353
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
335354

336355
if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) {
337-
errors.add(new ReadingError(line, "Lambda must not invoke constructors", null));
356+
errors.add(new ReadingError(line, "Constructor calls not supported.", null));
338357
return;
339358
}
340359

@@ -356,8 +375,8 @@ public void visitMethodInsn(int opcode, String owner, String name, String descri
356375
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
357376
String signature = Arrays.stream(argumentTypes).map(Type::getClassName).collect(Collectors.joining(", "));
358377
errors.add(new ReadingError(line,
359-
"Cannot derive a method reference from method invocation '%s(%s)' on a different type than the owning one.%nExpected owning type: '%s', but was: '%s'"
360-
.formatted(name, signature, this.owningType.getClassName(), ownerType.getClassName())));
378+
"Cannot derive method reference from '%s#%s(%s)': Method calls allowed on owning type '%s' only."
379+
.formatted(ownerType.getClassName(), name, signature, this.owningType.getClassName())));
361380
return;
362381
}
363382

src/main/java/org/springframework/data/core/SimplePropertyPath.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,12 @@ public boolean hasNext() {
177177

178178
@Override
179179
public boolean equals(@Nullable Object o) {
180-
return PropertyUtil.equals(this, o);
180+
return PropertyPathUtil.equals(this, o);
181181
}
182182

183183
@Override
184184
public int hashCode() {
185-
return PropertyUtil.hashCode(this);
185+
return PropertyPathUtil.hashCode(this);
186186
}
187187

188188
/**

0 commit comments

Comments
 (0)