Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.causeway.core.metamodel.spec;

import java.util.Optional;
import java.util.function.Function;

import org.jspecify.annotations.NonNull;

import org.apache.causeway.applib.id.LogicalType;
import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
import org.apache.causeway.core.config.beans.CausewayBeanTypeRegistry;

/**
* Some times types end up in the meta-model unexpected.
* The {@link IntrospectionTrigger} helps find the cause for why a specific type got introspected.
*
* @since 4.0
*/
public record IntrospectionTrigger(
IntrospectionCause introspectionCause,
Optional<Class<?>> triggerClass) {

public enum IntrospectionCause {
/**
* originating directly from {@link CausewayBeanTypeRegistry}
*/
BEAN_CANDIDATE,
/**
* lookup of class directly or via {@link LogicalType}
*/
CLASS_LOOKUP,
/**
* action result (introspection of the action method return)
*/
ACTION_RETURN,
/**
* association (introspection of the getter method return)
*/
GETTER,

MIXIN,
/**
* class directly reloaded (includes purging of caches)
*/
RELOAD,
/**
* used during reloading, to purge the class-hierarchy from any caches
*/
TEMPORARY,

TYPE_HIERARCHY,
/**
* originating from {@link CausewayBeanTypeRegistry} categorized as domain service
*/
SERVICE,
/**
* originating from {@link CausewayBeanTypeRegistry} categorized as domain entity
*/
ENTITY,
/**
* originating from {@link CausewayBeanTypeRegistry} categorized as view-model
*/
VIEWMODEL,
/**
* originating from registered {@link ValueSemanticsProvider}
*/
VALUE_TYPE,

@Deprecated // not used yet - why?
ACTION_PARAM
}

private final static IntrospectionTrigger BEAN_CANDIDATE = new IntrospectionTrigger(
IntrospectionCause.BEAN_CANDIDATE, Optional.empty());
private final static IntrospectionTrigger TEMPORARY = new IntrospectionTrigger(
IntrospectionCause.TEMPORARY, Optional.empty());

// -- FACTORIES

public static IntrospectionTrigger beanCandidate() {
return BEAN_CANDIDATE;
}

public static IntrospectionTrigger typeHierarchy(@NonNull Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.TYPE_HIERARCHY, Optional.of(cls));
}

public static IntrospectionTrigger valueType(@NonNull Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.VALUE_TYPE, Optional.of(cls));
}

public static IntrospectionTrigger lookup(@NonNull Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.CLASS_LOOKUP, Optional.of(cls));
}
public static IntrospectionTrigger lookup(@NonNull String logicalTypeNameNotUsed) {
return new IntrospectionTrigger(IntrospectionCause.CLASS_LOOKUP, Optional.empty());
}

public static IntrospectionTrigger actionReturn(Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.ACTION_RETURN, Optional.of(cls));
}

public static IntrospectionTrigger getter(Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.GETTER, Optional.of(cls));
}

public static IntrospectionTrigger mixin(Class<?> mixinType) {
return new IntrospectionTrigger(IntrospectionCause.MIXIN, Optional.of(mixinType));
}

public static IntrospectionTrigger service(Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.SERVICE, Optional.of(cls));
}

public static IntrospectionTrigger entity(Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.SERVICE, Optional.of(cls));
}

public static IntrospectionTrigger viewmodel(Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.SERVICE, Optional.of(cls));
}

public static IntrospectionTrigger temporary(Class<?> domainTypeNotUsed) {
return TEMPORARY;
}

public static IntrospectionTrigger reload(Class<?> cls) {
return new IntrospectionTrigger(IntrospectionCause.RELOAD, Optional.of(cls));
}

/**
* @deprecated refactoring helper
*/
@Deprecated
public static Function<Class<?>, IntrospectionTrigger> dummy() {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ class Comparators{
}

IntrospectionPolicy getIntrospectionPolicy();

/**
* Introduced for debugging.
* @see IntrospectionTrigger
* @since 4.0
*/
IntrospectionTrigger introspectionTrigger();

/**
* Natural order, that is, by {@link BeanSort} then by {@link LogicalType}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.apache.causeway.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet;
import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger;
import org.apache.causeway.core.metamodel.spec.impl.ObjectSpecificationMutable.IntrospectionRequest;
import org.apache.causeway.core.metamodel.specloader.typeextract.TypeExtractor;

Expand Down Expand Up @@ -140,17 +141,13 @@ public FacetedMethodsBuilder(
this.methodRemover = new ConcurrentMethodRemover(introspectedClass, methodsRemaining);
}

// ////////////////////////////////////////////////////////////////////////////
// Class and stuff immediately derived from class
// ////////////////////////////////////////////////////////////////////////////
// -- SHORTCUT

private String getClassName() {
return introspectedClass.getName();
}

// ////////////////////////////////////////////////////////////////////////////
// introspect class
// ////////////////////////////////////////////////////////////////////////////
// -- INTROSPECT CLASS

public void introspectClass() {
if (log.isDebugEnabled()) {
Expand All @@ -163,9 +160,7 @@ public void introspectClass() {
.process(introspectedClass, introspectionPolicy(), methodRemover, inspectedTypeSpec);
}

// ////////////////////////////////////////////////////////////////////////////
// introspect associations
// ////////////////////////////////////////////////////////////////////////////
// -- INTROSPECT ASSOCIATIONS

/**
* Returns a {@link List} of {@link FacetedMethod}s representing object
Expand Down Expand Up @@ -195,7 +190,8 @@ private List<FacetedMethod> createAssociationFacetedMethods() {
// Ensure all return types are known
TypeExtractor.streamMethodReturn(associationCandidateMethods)
.filter(typeToLoad->typeToLoad!=introspectedClass)
.forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY));
.forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY,
IntrospectionTrigger.getter(typeToLoad)));

// now create FacetedMethods for collections and for properties
var associationFacetedMethods = new ArrayList<FacetedMethod>();
Expand Down Expand Up @@ -376,8 +372,9 @@ private boolean representsAction(final ResolvedMethod actionMethod) {

// ensure we can load returned element type; otherwise ignore method
var anyLoadedAsNull = TypeExtractor.streamMethodReturn(actionMethod)
.map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY))
.anyMatch(Objects::isNull);
.map(typeToLoad->specLoaderInternal().loadSpecification(typeToLoad, IntrospectionRequest.TYPE_ONLY,
IntrospectionTrigger.actionReturn(typeToLoad)))
.anyMatch(Objects::isNull);
if (anyLoadedAsNull) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.services.classsubstitutor.ClassSubstitutorRegistry;
import org.apache.causeway.core.metamodel.spec.ActionScope;
import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
Expand All @@ -115,6 +116,7 @@
import static org.apache.causeway.commons.internal.base._NullSafe.stream;

import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

@Slf4j
Expand All @@ -133,19 +135,24 @@ final class ObjectSpecificationDefault

@Getter(onMethod_={@Override})
private final IntrospectionPolicy introspectionPolicy;

@Getter(onMethod_={@Override}) @Accessors(fluent = true)
private final IntrospectionTrigger introspectionTrigger;

public ObjectSpecificationDefault(
final @NonNull CausewayBeanMetaData typeMeta,
final @NonNull MetaModelContext mmc,
final @NonNull FacetProcessor facetProcessor,
final @NonNull PostProcessor postProcessor,
final @NonNull ClassSubstitutorRegistry classSubstitutorRegistry) {
final @NonNull ClassSubstitutorRegistry classSubstitutorRegistry,
final @NonNull IntrospectionTrigger introspectionTrigger) {

this.correspondingClass = typeMeta.getCorrespondingClass();
this.logicalType = typeMeta.logicalType();
this.fullName = correspondingClass.getName();
this.shortName = typeMeta.logicalType().logicalSimpleName();
this.beanSort = typeMeta.beanSort();
this.introspectionTrigger = introspectionTrigger;

this.facetHolder = FacetHolder.simple(
facetProcessor.getMetaModelContext(),
Expand Down Expand Up @@ -555,7 +562,7 @@ public final String getFullIdentifier() {
}

@Override
public void introspect(IntrospectionRequest request) {
public void introspect(IntrospectionRequest request, IntrospectionTrigger introspectionTrigger) {
switch (request) {
case REGISTER -> introspectUpTo(IntrospectionState.NOT_INTROSPECTED,
()->"introspect(%s)".formatted(request));
Expand Down Expand Up @@ -1070,7 +1077,7 @@ private Stream<ObjectAssociation> createMixedInAssociations() {

private Stream<ObjectAssociation> createMixedInAssociation(final Class<?> mixinType) {
var mixinSpec = specLoaderInternal().loadSpecification(mixinType,
IntrospectionRequest.FULL);
IntrospectionRequest.FULL, IntrospectionTrigger.mixin(mixinType));
if (mixinSpec == null
|| mixinSpec == this) {
return Stream.empty();
Expand Down Expand Up @@ -1104,7 +1111,7 @@ private Stream<ObjectActionMixedIn> createMixedInActions() {
private Stream<ObjectActionMixedIn> createMixedInAction(final Class<?> mixinType) {

var mixinSpec = specLoaderInternal().loadSpecification(mixinType,
IntrospectionRequest.FULL);
IntrospectionRequest.FULL, IntrospectionTrigger.mixin(mixinType));
if (mixinSpec == null
|| mixinSpec == this) {
return Stream.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.causeway.core.metamodel.spec.impl;

import org.apache.causeway.core.metamodel.spec.IntrospectionTrigger;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;

interface ObjectSpecificationMutable extends ObjectSpecification {
Expand All @@ -37,6 +38,6 @@ enum IntrospectionRequest {
FULL
}

void introspect(IntrospectionRequest request);
void introspect(IntrospectionRequest request, IntrospectionTrigger introspectionTrigger);

}
Loading