Skip to content

Commit 2bdf117

Browse files
committed
CAUSEWAY-3991: honor Introspection Policy w/ Java Records
1 parent a4360e8 commit 2bdf117

3 files changed

Lines changed: 42 additions & 10 deletions

File tree

api/applib/src/main/java/org/apache/causeway/applib/services/user/RoleMemento.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.causeway.applib.CausewayModuleApplib;
3232
import org.apache.causeway.applib.annotation.DomainObject;
3333
import org.apache.causeway.applib.annotation.DomainObjectLayout;
34+
import org.apache.causeway.applib.annotation.Introspection;
3435
import org.apache.causeway.applib.annotation.Nature;
3536
import org.apache.causeway.applib.annotation.PriorityPrecedence;
3637
import org.apache.causeway.applib.annotation.PropertyLayout;
@@ -44,7 +45,8 @@
4445
*/
4546
@Named(RoleMemento.LOGICAL_TYPE_NAME)
4647
@DomainObject(
47-
nature = Nature.VIEW_MODEL)
48+
nature = Nature.VIEW_MODEL,
49+
introspection = Introspection.ANNOTATION_OPTIONAL)
4850
@DomainObjectLayout(
4951
titleUiEvent = RoleMemento.TitleUiEvent.class
5052
)
@@ -91,7 +93,7 @@ public RoleMemento(
9193
// -- OBJECT CONTRACT
9294

9395
@Override
94-
public final boolean equals(Object obj) {
96+
public final boolean equals(final Object obj) {
9597
return (obj instanceof RoleMemento other)
9698
? name.equals(other.name)
9799
: false;

commons/src/main/java/org/apache/causeway/commons/internal/reflection/_ClassCache.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ public void close() throws Exception {
452452

453453
public static boolean methodExcludeFilter(final Method method) {
454454
return isByteCodeEnhanced(method)
455+
|| (method.isSynthetic() // exclude private synthetic
456+
&& Modifier.isPrivate(method.getModifiers()))
455457
|| method.isBridge()
456458
|| Modifier.isStatic(method.getModifiers())
457459
|| method.getDeclaringClass().equals(Object.class)

core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/FacetedMethodsBuilder.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.causeway.core.metamodel.spec.impl;
2020

21+
import java.lang.annotation.Annotation;
2122
import java.util.ArrayList;
2223
import java.util.Collections;
2324
import java.util.HashSet;
@@ -33,7 +34,9 @@
3334
import org.jspecify.annotations.Nullable;
3435

3536
import org.apache.causeway.applib.annotation.Action;
37+
import org.apache.causeway.applib.annotation.Collection;
3638
import org.apache.causeway.applib.annotation.Introspection.IntrospectionPolicy;
39+
import org.apache.causeway.applib.annotation.Property;
3740
import org.apache.causeway.applib.exceptions.unrecoverable.MetaModelException;
3841
import org.apache.causeway.commons.collections.Can;
3942
import org.apache.causeway.commons.internal.base._NullSafe;
@@ -216,6 +219,10 @@ private void createCollectionFacetedMethodsFromAccessors(
216219
var mmc = getMetaModelContext();
217220

218221
for (final ResolvedMethod accessorMethod : accessorMethods) {
222+
if(!satisfiesIntrospectionPolicy(accessorMethod, Collection.class)) {
223+
continue;
224+
}
225+
219226
if (log.isDebugEnabled()) {
220227
log.debug(" identified accessor method representing collection: {}", accessorMethod);
221228
}
@@ -240,7 +247,9 @@ private void createCollectionFacetedMethodsFromAccessors(
240247
.orElse(Object.class);
241248

242249
// skip if class substitutor says so
243-
if (classSubstitutorRegistry.getSubstitution(elementType).isNeverIntrospect()) continue;
250+
if (classSubstitutorRegistry.getSubstitution(elementType).isNeverIntrospect()) {
251+
continue;
252+
}
244253

245254
onNewFacetMethod.accept(facetedMethod.withElementType(elementType));
246255
}
@@ -251,12 +260,18 @@ private void createPropertyFacetedMethodsFromAccessors(
251260
final Consumer<FacetedMethod> onNewFacetedMethod) throws MetaModelException {
252261

253262
for (final ResolvedMethod accessorMethod : accessorMethods) {
263+
if(!satisfiesIntrospectionPolicy(accessorMethod, Property.class)) {
264+
continue;
265+
}
266+
254267
log.debug(" identified accessor method representing property: {}", accessorMethod);
255268

256269
final Class<?> returnType = accessorMethod.returnType();
257270

258271
// skip if class strategy says so.
259-
if (classSubstitutorRegistry.getSubstitution(returnType).isNeverIntrospect()) continue;
272+
if (classSubstitutorRegistry.getSubstitution(returnType).isNeverIntrospect()) {
273+
continue;
274+
}
260275

261276
// create a 1:1 association peer
262277
var facetedMethod = FacetedMethod
@@ -369,18 +384,32 @@ private boolean isAllParamTypesValid(final MethodFacade actionMethod) {
369384
return true;
370385
}
371386

387+
/**
388+
* At time of writing only filtering for Java Records,
389+
* because those have not type-hierarchy
390+
* (except potentially interfaces),
391+
* where the check for explicit annotations is simple.
392+
*/
393+
private <A extends Annotation> boolean satisfiesIntrospectionPolicy(
394+
final ResolvedMethod accessorMethod,
395+
final Class<A> requiredAnnotationType) {
396+
return inspectedTypeSpec.getCorrespondingClass().isRecord()
397+
&& introspectionPolicy().getMemberAnnotationPolicy().isMemberAnnotationsRequired()
398+
? _Annotations.isPresent(accessorMethod.method(), requiredAnnotationType)
399+
: true;
400+
}
401+
372402
private boolean representsAction(final ResolvedMethod actionMethod) {
373403

374404
//[CAUSEWAY-3556] if this throws, we have a framework bug (synthetic methods should no longer appear here)
375405
_Reflect.guardAgainstSynthetic(actionMethod.method());
376406

377407
// ensure we can load returned element type; otherwise ignore method
378408
var anyLoadedAsNull = TypeExtractor.streamMethodReturn(actionMethod)
379-
.map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY))
380-
.anyMatch(Objects::isNull);
381-
if (anyLoadedAsNull) {
409+
.map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY))
410+
.anyMatch(Objects::isNull);
411+
if (anyLoadedAsNull)
382412
return false;
383-
}
384413

385414
if(isMixinMain(actionMethod)) {
386415
// we are introspecting a mixin type and its main method,
@@ -431,10 +460,9 @@ private boolean isMixinMain(final ResolvedMethod method) {
431460
.orElse(null);
432461
if(mixinFacet==null) return false;
433462

434-
if(!inspectedTypeSpec.isFullyIntrospected()) {
463+
if(!inspectedTypeSpec.isFullyIntrospected())
435464
// members are not introspected yet, so make a guess
436465
return mixinFacet.isCandidateForMain(method);
437-
}
438466

439467
return inspectedTypeSpec
440468
.lookupMixedInAction(inspectedTypeSpec)

0 commit comments

Comments
 (0)