in core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/FacetedMethod.java [46:253]
public record FacetedMethod(
FacetHolder facetHolder,
FeatureType featureType,
/**
* resolved method return type
*/
ResolvedType resolvedType,
/**
* The {@link Class} that owns this {@link Method} (as per
* {@link Class#getMethods()}, returning the {@link Method}s both of this
* class and of its super-classes).
* <p>
* Note: we don't call this the 'declaring type' because
* {@link Class#getDeclaredMethods()} does not return methods from
* super-classes.
*/
Class<?> owningType,
/**
* A {@link Method} obtained from the {@link #owningType()}
* using {@link Class#getMethods()}.
*/
MethodFacade methodFacade,
Can<FacetedMethodParameter> parameters,
/**
* @apiNote lazily memoized only once the metamodel was populated,
* otherwise during metamodel introspection would trigger cascading object introspection as a side-effect
*/
_Lazy<ObjectSpecification> elementSpecificationLazy
) implements TypedFacetHolder {
// -- FACTORIES
public static FacetedMethod createForProperty(
final MetaModelContext mmc,
final Class<?> declaringType,
final ResolvedMethod getterMethod) {
var methodFacade = _MethodFacades.regular(getterMethod);
return new FacetedMethod(mmc, FeatureType.PROPERTY,
declaringType, methodFacade, _GenericResolver.forMethodReturn(getterMethod), Can.empty());
}
public static FacetedMethod createForCollection(
final MetaModelContext mmc,
final Class<?> declaringType,
final ResolvedMethod getterMethod) {
var methodFacade = _MethodFacades.regular(getterMethod);
return new FacetedMethod(mmc, FeatureType.COLLECTION,
declaringType, methodFacade, _GenericResolver.forMethodReturn(getterMethod), Can.empty());
}
public static FacetedMethod createForAction(
final MetaModelContext mmc,
final Class<?> declaringType,
final MethodFacade methodFacade) {
return new FacetedMethod(mmc, FeatureType.ACTION,
declaringType, methodFacade, methodFacade.resolveMethodReturn(),
getParameters(mmc, declaringType, methodFacade));
}
private static Can<FacetedMethodParameter> getParameters(
final MetaModelContext mmc,
final Class<?> declaringType,
final MethodFacade actionMethod) {
final List<FacetedMethodParameter> actionParams =
_Lists.newArrayList(actionMethod.getParameterCount());
int paramIndex = -1;
for(var parameterType : actionMethod.getParameterTypes()) {
paramIndex++;
final FeatureType featureType =
CollectionSemantics.valueOf(parameterType).isPresent()
? FeatureType.ACTION_PARAMETER_PLURAL
: FeatureType.ACTION_PARAMETER_SINGULAR;
var facetedMethodParam =
new FacetedMethodParameter(mmc, featureType, declaringType, actionMethod, paramIndex);
if(featureType != FeatureType.ACTION_PARAMETER_PLURAL) {
actionParams.add(facetedMethodParam);
continue;
}
// this is based on similar logic to ActionAnnotationFacetFactory#processTypeOf
var facetedMethodParamToUse = TypeOfFacet
.inferFromMethodParameter(actionMethod, paramIndex, facetedMethodParam)
.map(typeOfFacet->{
// (corresponds to similar code for OneToManyAssociation in FacetMethodsBuilder).
FacetUtil.addFacet(typeOfFacet);
return facetedMethodParam.withResolvedType(typeOfFacet.value());
})
.orElse(facetedMethodParam);
actionParams.add(facetedMethodParamToUse);
}
return Can.ofCollection(actionParams);
}
// -- FACTORIES (JUNIT)
/**
* Principally for testing purposes.
*/
public static class testing {
public static FacetedMethod createSetterForProperty(
final MetaModelContext mmc,
final Class<?> declaringType,
final String propertyName) {
var method = _GenericResolver.testing
.resolveMethod(declaringType, "set" + _Strings.asPascalCase.apply(propertyName), String.class);
return FacetedMethod.createForProperty(mmc, declaringType, method);
}
public static FacetedMethod createGetterForProperty(
final MetaModelContext mmc,
final Class<?> declaringType,
final String propertyName) {
var method = _GenericResolver.testing
.resolveMethod(declaringType, "get" + _Strings.asPascalCase.apply(propertyName));
return FacetedMethod.createForProperty(mmc, declaringType, method);
}
public static FacetedMethod createForCollection(
final MetaModelContext mmc,
final Class<?> declaringType,
final String collectionName) {
var method = _GenericResolver.testing
.resolveMethod(declaringType, "get" + _Strings.asPascalCase.apply(collectionName));
return FacetedMethod.createForCollection(mmc, declaringType, method);
}
public static FacetedMethod createForAction(
final MetaModelContext mmc,
final Class<?> declaringType,
final String actionName,
final Class<?>... parameterTypes) {
var methodFacade = _MethodFacades.regular(
_GenericResolver.testing.resolveMethod(declaringType, actionName, parameterTypes));
return FacetedMethod.createForAction(mmc, declaringType, methodFacade);
}
}
// -- CONSTRUCTOR
private FacetedMethod(
final MetaModelContext mmc,
final FeatureType featureType,
final Class<?> declaringType,
final MethodFacade method,
final ResolvedType resolvedType,
final Can<FacetedMethodParameter> parameters) {
this(
FacetHolder
.simple(mmc, methodIdentifier(mmc.getSpecificationLoader(), featureType, declaringType, method)),
featureType,
resolvedType,
declaringType,
method,
parameters,
elementSpecificationLazy(mmc.getSpecificationLoader(), resolvedType));
}
public FacetedMethodParameter parameter(final int paramIndex) {
return parameters.getElseFail(paramIndex);
}
@Override
public String toString() {
return featureType().name() + " Peer [identifier=\"" + getFeatureIdentifier()
+ "\",type=" + resolvedType() + " ]";
}
public ObjectSpecification elementSpecification() {
return elementSpecificationLazy.get();
}
/**
* Returns an instance with {@link #resolvedType} replaced by a new {@link ResolvedType} that has given {@code elementType}.
* @param elementType
*/
public FacetedMethod withElementType(final Class<?> elementType) {
var newResolvedType = resolvedType.withElementType(elementType);
return new FacetedMethod(facetHolder, featureType, newResolvedType, owningType, methodFacade, parameters,
elementSpecificationLazy(facetHolder.getSpecificationLoader(), newResolvedType));
}
// -- HELPER
private static Identifier methodIdentifier(
final SpecificationLoader specificationLoader,
final FeatureType featureType,
final Class<?> declaringType,
final MethodFacade method) {
return featureType.identifierFor(LogicalType.infer(declaringType), method);
}
private static _Lazy<ObjectSpecification> elementSpecificationLazy(
final SpecificationLoader specificationLoader,
final ResolvedType resolvedType) {
return _Lazy.threadSafe(()->specificationLoader.specForTypeElseFail(resolvedType.elementType()));
}
}