in core/metamodel/src/main/java/org/apache/causeway/core/metamodel/methods/MethodFinder.java [44:268]
public record MethodFinder(
@NonNull Class<?> correspondingClass,
@NonNull Can<String> methodNameCandidates,
@NonNull EncapsulationPolicy encapsulationPolicy,
@NonNull Predicate<ResolvedMethod> mustSatisfy) {
public static final Can<String> ANY_NAME = Can.of(""); // arbitrary marker
public static final Class<?>[] NO_ARG = new Class<?>[0];
public static MethodFinder of(
final @NonNull Class<?> correspondingClass,
final @NonNull Can<String> methodNameCandidatesPossiblyDuplicated,
final @NonNull EncapsulationPolicy encapsulationPolicy,
final @NonNull Predicate<ResolvedMethod> mustSatisfy) {
final Predicate<ResolvedMethod> isNotStatic = MethodUtil::isNotStatic;
var methodNameCandidates = methodNameCandidatesPossiblyDuplicated.distinct();
return new MethodFinder(
correspondingClass,
methodNameCandidates,
encapsulationPolicy,
methodNameCandidates.equals(ANY_NAME)
? isNotStatic.and(mustSatisfy)
: isNotStatic
.and(method->methodNameCandidates.contains(method.name()))
.and(mustSatisfy));
}
public static MethodFinder notNecessarilyPublic(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates) {
return of(
correspondingClass,
methodNameCandidates,
EncapsulationPolicy.ENCAPSULATED_MEMBERS_SUPPORTED,
_Predicates.alwaysTrue());
}
public static MethodFinder publicOnly(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates) {
return of(
correspondingClass,
methodNameCandidates,
EncapsulationPolicy.ONLY_PUBLIC_MEMBERS_SUPPORTED,
_Predicates.alwaysTrue());
}
public static MethodFinder accessor(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates,
final IntrospectionPolicy memberIntrospectionPolicy) {
return havingAnyOrNoAnnotation(
correspondingClass,
methodNameCandidates,
memberIntrospectionPolicy);
}
public static MethodFinder objectSupport(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates,
final IntrospectionPolicy memberIntrospectionPolicy) {
return supportMethod(
correspondingClass,
methodNameCandidates,
memberIntrospectionPolicy,
Domain.Include.class,
ProgrammingModelConstants.ConflictingAnnotations.OBJECT_SUPPORT);
}
public static MethodFinder livecycleCallback(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates,
final IntrospectionPolicy memberIntrospectionPolicy) {
return supportMethod(
correspondingClass,
methodNameCandidates,
memberIntrospectionPolicy,
Domain.Include.class,
ProgrammingModelConstants.ConflictingAnnotations.OBJECT_LIFECYCLE);
}
public static MethodFinder memberSupport(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates,
final IntrospectionPolicy memberIntrospectionPolicy) {
return supportMethod(
correspondingClass,
methodNameCandidates,
memberIntrospectionPolicy,
Domain.Include.class,
ProgrammingModelConstants.ConflictingAnnotations.MEMBER_SUPPORT);
}
public Stream<ResolvedMethod> streamMethodsMatchingSignature(
final @Nullable Class<?>[] paramTypes) {
if(paramTypes==null) return streamMethodsIgnoringSignature();
var type = correspondingClass();
var classCache = _ClassCache.getInstance();
var isEncapsulationSupported = encapsulationPolicy().isEncapsulatedMembersSupported();
if(methodNameCandidates.equals(ANY_NAME)) {
//stream all
return (isEncapsulationSupported
? classCache.streamResolvedMethods(type)
: classCache.streamPublicMethods(type))
.filter(method->Arrays.equals(paramTypes, method.paramTypes()))
.filter(mustSatisfy);
}
return methodNameCandidates.stream()
.map(name->isEncapsulationSupported
? classCache.lookupResolvedMethod(type, name, paramTypes)
: classCache.lookupPublicMethod(type, name, paramTypes))
.filter(Optional::isPresent)
.map(Optional::get)
.filter(mustSatisfy);
}
public Stream<ResolvedMethod> streamMethodsIgnoringSignature() {
var type = correspondingClass();
var classCache = _ClassCache.getInstance();
var isEncapsulationSupported = encapsulationPolicy().isEncapsulatedMembersSupported();
return (isEncapsulationSupported
? classCache.streamResolvedMethods(type)
: classCache.streamPublicMethods(type))
.filter(mustSatisfy);
}
// -- WITHERS
public MethodFinder withRequiredReturnType(final @NonNull Class<?> requiredReturnType) {
return new MethodFinder(
correspondingClass,
methodNameCandidates,
encapsulationPolicy,
mustSatisfy.and(resolvedMethod->resolvedMethod.isReturnTypeATypeOf(requiredReturnType)));
}
public MethodFinder withReturnTypeAnyOf(final @NonNull Can<Class<?>> anyOfReturnTypes) {
return new MethodFinder(
correspondingClass,
methodNameCandidates,
encapsulationPolicy,
mustSatisfy.and(resolvedMethod->resolvedMethod.isReturnTypeAnyTypeOf(anyOfReturnTypes)));
}
// -- HELPER
private static MethodFinder havingAnyOrNoAnnotation(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates,
final IntrospectionPolicy memberIntrospectionPolicy) {
return of(
correspondingClass,
methodNameCandidates,
memberIntrospectionPolicy.getEncapsulationPolicy(),
_Predicates.alwaysTrue());
}
private static MethodFinder supportMethod(
final Class<?> correspondingClass,
final Can<String> methodNameCandidates,
final IntrospectionPolicy memberIntrospectionPolicy,
final Class<? extends Annotation> annotationType,
final ConflictingAnnotations conflictingAnnotations) {
var finder = of(
correspondingClass,
methodNameCandidates,
// support methods are always allowed private
EncapsulationPolicy.ENCAPSULATED_MEMBERS_SUPPORTED,
havingAnnotationIfEnforcedByPolicyOrAccessibility(
memberIntrospectionPolicy.getSupportMethodAnnotationPolicy().isSupportMethodAnnotationsRequired(),
annotationType,
conflictingAnnotations.getProhibits()));
return finder;
}
private static Predicate<ResolvedMethod> havingAnnotationIfEnforcedByPolicyOrAccessibility(
final boolean annotationRequired,
final Class<? extends Annotation> annotationType,
final Can<Class<? extends Annotation>> conflictingAnnotations) {
return annotationRequired
? method->havingAnnotation(method, annotationType, conflictingAnnotations)
: method->havingAnnotationOrPublic(method, annotationType, conflictingAnnotations);
}
private static boolean havingAnnotationOrPublic(
final ResolvedMethod method,
final Class<? extends Annotation> annotationType,
final Can<Class<? extends Annotation>> conflictingAnnotations) {
return _Reflect.isPublicNonSynthetic(method.method())
? true
: havingAnnotation(method, annotationType, conflictingAnnotations);
}
//FIXME[CAUSEWAY-2774] if annotation appears on an abstract method that was inherited with given method,
// its not detected here
private static boolean havingAnnotation(
final ResolvedMethod method,
final Class<? extends Annotation> annotationType,
final Can<Class<? extends Annotation>> conflictingAnnotations) {
var isMarkerAnnotationPresent = _Annotations.synthesize(method.method(), annotationType).isPresent();
if(isMarkerAnnotationPresent) {
var isConflictingAnnotationPresent = conflictingAnnotations
.stream()
.anyMatch(conflictingAnnotationType->
_Annotations.synthesize(method.method(), conflictingAnnotationType).isPresent());
// do not pickup this method if conflicting - so meta-model validation will fail later on
return !isConflictingAnnotationPresent;
}
return isMarkerAnnotationPresent;
}
}