This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/main by this push:
new cf05433a7e5 CAUSEWAY-3859: regression fix for composite value support
cf05433a7e5 is described below
commit cf05433a7e546ccb343af0a53fd980e2b9375208
Author: Andi Huber <[email protected]>
AuthorDate: Fri Feb 28 10:02:18 2025 +0100
CAUSEWAY-3859: regression fix for composite value support
---
.../commons/internal/delegate/_Delegate.java | 11 +-
.../facets/object/value/CompositeValueUpdater.java | 29 +++-
.../facets/object/value/ValueFacetAbstract.java | 10 +-
.../interactions/managed/ActionInteraction.java | 8 +-
.../metamodel/spec/feature/HasObjectAction.java | 180 +++++++++++++++++++++
.../metamodel/spec/impl/ObjectActionDefault.java | 2 +-
.../interaction/act/ActionInteractionWkt.java | 25 ++-
7 files changed, 235 insertions(+), 30 deletions(-)
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/delegate/_Delegate.java
b/commons/src/main/java/org/apache/causeway/commons/internal/delegate/_Delegate.java
index d6f017c4989..d31483d1c5b 100644
---
a/commons/src/main/java/org/apache/causeway/commons/internal/delegate/_Delegate.java
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/delegate/_Delegate.java
@@ -29,7 +29,6 @@
import org.apache.causeway.commons.internal.reflection._ClassCache;
-import lombok.RequiredArgsConstructor;
import lombok.experimental.UtilityClass;
/**
@@ -66,11 +65,9 @@ public <T> T createProxy(final Class<T> bluePrint, final
Object delegate) {
new DelegatingInvocationHandler(delegate));
}
- @RequiredArgsConstructor
- static class DelegatingInvocationHandler implements InvocationHandler {
-
- final Object delegate;
- final _ClassCache classCache = _ClassCache.getInstance();
+ record DelegatingInvocationHandler(
+ Object delegate)
+ implements InvocationHandler {
@Override
public Object invoke(final Object proxy, final Method method, final
Object[] args)
@@ -90,7 +87,7 @@ public Object invoke(final Object proxy, final Method method,
final Object[] arg
// Invoke method with same signature on delegate ()
try {
var delegateMethod =
-
classCache.lookupResolvedMethodElseFail(delegate.getClass(),
+
_ClassCache.getInstance().lookupResolvedMethodElseFail(delegate.getClass(),
method.getName(), method.getParameterTypes());
return delegateMethod.method().invoke(delegate, args);
} catch (InvocationTargetException ex) {
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/CompositeValueUpdater.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/CompositeValueUpdater.java
index 455bb347aee..4a44ea9096b 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/CompositeValueUpdater.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/CompositeValueUpdater.java
@@ -17,10 +17,15 @@
* under the License.
*/
package org.apache.causeway.core.metamodel.facets.object.value;
+
+import org.apache.causeway.applib.annotation.PromptStyle;
+import org.apache.causeway.applib.annotation.Where;
import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.commons.internal.delegate._Delegate;
import org.apache.causeway.core.metamodel.commons.CanonicalInvoker;
import org.apache.causeway.core.metamodel.commons.ParameterConverters;
+import org.apache.causeway.core.metamodel.consent.Allow;
+import org.apache.causeway.core.metamodel.consent.Consent;
import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
import org.apache.causeway.core.metamodel.facets.HasFacetedMethod;
import
org.apache.causeway.core.metamodel.facets.object.value.CompositeValueUpdater.CompositeValueUpdaterForParameter;
@@ -31,17 +36,35 @@
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.MmUnwrapUtils;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.HasObjectAction;
import org.apache.causeway.core.metamodel.spec.feature.MixedInAction;
import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
-sealed interface CompositeValueUpdater
+/**
+ * Implementations are proxied in support of composite value types.
+ * <p>
+ * @implSpec The proxy mimics an {@link ObjectAction},
+ * hence extending {@link HasObjectAction} for delegation to {@link
#mixedInAction()}.
+ */
+public sealed interface CompositeValueUpdater extends HasObjectAction
permits CompositeValueUpdaterForProperty, CompositeValueUpdaterForParameter {
MixedInAction mixedInAction();
ObjectSpecification returnType();
ManagedObject map(final ManagedObject valueType);
- default ManagedObject execute(
+ // HasObjectAction
+ @Override default ObjectAction getObjectAction() { return mixedInAction();
}
+
+
+ // -- OBJECT ACTION MOCKUP
+
+ @Override default String getId() { return "proxyCompositeValueUpdater"; }
+ @Override default Consent isVisible(final ManagedObject a, final
InteractionInitiatedBy b, final Where c) { return Allow.DEFAULT; }
+ @Override default Consent isUsable(final ManagedObject a, final
InteractionInitiatedBy b, final Where c) { return Allow.DEFAULT; }
+ @Override default PromptStyle getPromptStyle() { return
PromptStyle.INLINE_AS_IF_EDIT; }
+
+ @Override default ManagedObject execute(
final InteractionHead head, final Can<ManagedObject> parameters,
final InteractionInitiatedBy interactionInitiatedBy) {
return map(simpleExecute(head, parameters));
@@ -64,7 +87,6 @@ public ManagedObject map(final ManagedObject newParamValue) {
parameterNegotiationModel.setParamValue(paramIndex, newParamValue);
return newParamValue;
}
-
}
record CompositeValueUpdaterForProperty(
@@ -84,7 +106,6 @@ public ManagedObject map(final ManagedObject valueType) {
propNeg.submit();
return managedProperty.getOwner();
}
-
}
// -- FACTORIES
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
index c56f432454a..779e1eb0af7 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/value/ValueFacetAbstract.java
@@ -349,11 +349,11 @@ private Predicate<ValueSemanticsProvider<T>>
isMatchingAnyOf(final Can<String> q
*/
protected Optional<ObjectAction>
resolveCompositeValueMixinForFeature(final ObjectFeature feature) {
return qualifiersAccepted(feature).add("default")
- .stream()
- .map(qualifier->feature.getElementType().getAction(qualifier,
MixedIn.ONLY))
- .filter(Optional::isPresent)
- .map(Optional::get)
- .findFirst();
+ .stream()
+ .map(qualifier->feature.getElementType().getAction(qualifier,
MixedIn.ONLY))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst();
}
@RequiredArgsConstructor
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
index a4938aa28b9..ca83cca982f 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ActionInteraction.java
@@ -63,14 +63,14 @@ public static interface ParameterInvalidCallback {
// -- FACTORIES
- public static final ActionInteraction start(
+ public static ActionInteraction start(
final @NonNull ManagedObject owner,
final @NonNull String memberId,
final @NonNull Where where) {
return startWithMultiselect(owner, memberId, where, Can::empty);
}
- public static final ActionInteraction startWithMultiselect(
+ public static ActionInteraction startWithMultiselect(
final @NonNull ManagedObject owner,
final @NonNull String actionId,
final @NonNull Where where,
@@ -160,10 +160,6 @@ public static ActionInteraction startAsBoundToParameter(
}
}
- //XXX[CAUSEWAY-3080] prior to this fix we returned... (which I'm not
sure why - makes no sense to me)
- //var paramValue = parameterNegotiationModel.getParamValue(paramIndex);
- //return ActionInteraction.start(paramValue, memberId, where);
-
// else if not a composite value
return ActionInteraction.start(actionOwner, memberId, where);
}
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectAction.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectAction.java
new file mode 100644
index 00000000000..79636a8e75c
--- /dev/null
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/feature/HasObjectAction.java
@@ -0,0 +1,180 @@
+/*
+ * 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.feature;
+
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import org.jspecify.annotations.NonNull;
+
+import org.apache.causeway.applib.annotation.SemanticsOf;
+import org.apache.causeway.applib.annotation.Where;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.collections.CanVector;
+import org.apache.causeway.core.metamodel.consent.Consent;
+import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.causeway.core.metamodel.consent.InteractionResultSet;
+import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
+import org.apache.causeway.core.metamodel.facetapi.FeatureType;
+import org.apache.causeway.core.metamodel.interactions.InteractionHead;
+import
org.apache.causeway.core.metamodel.interactions.managed.ActionInteractionHead;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.spec.ActionScope;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+
+/**
+ * Introduced to allow for proxies of {@link ObjectAction}.
+ */
+@FunctionalInterface
+public interface HasObjectAction extends ObjectAction {
+
+ ObjectAction getObjectAction();
+
+ @Override default ObjectSpecification getDeclaringType() {
+ return getObjectAction().getDeclaringType();
+ }
+ @Override default String getHelp() {
+ return getObjectAction().getHelp();
+ }
+ @Override default boolean isAlwaysHidden() {
+ return getObjectAction().isAlwaysHidden();
+ }
+ @Override default Consent isVisible(final ManagedObject target, final
InteractionInitiatedBy interactionInitiatedBy, final Where where) {
+ return getObjectAction().isVisible(target, interactionInitiatedBy,
where);
+ }
+ @Override default Consent isUsable(final ManagedObject target, final
InteractionInitiatedBy interactionInitiatedBy, final Where where) {
+ return getObjectAction().isUsable(target, interactionInitiatedBy,
where);
+ }
+ @Override default boolean isPropertyOrCollection() {
+ return getObjectAction().isPropertyOrCollection();
+ }
+ @Override default boolean isOneToManyAssociation() {
+ return getObjectAction().isOneToManyAssociation();
+ }
+ @Override default boolean isOneToOneAssociation() {
+ return getObjectAction().isOneToOneAssociation();
+ }
+ @Override default boolean isAction() {
+ return getObjectAction().isAction();
+ }
+ @Override default boolean isExplicitlyAnnotated() {
+ return getObjectAction().isExplicitlyAnnotated();
+ }
+ @Override default String getId() {
+ return getObjectAction().getId();
+ }
+ @Override default String getFriendlyName(final Supplier<ManagedObject>
domainObjectProvider) {
+ return getObjectAction().getFriendlyName(domainObjectProvider);
+ }
+ @Override default Optional<String> getStaticFriendlyName() {
+ return getObjectAction().getStaticFriendlyName();
+ }
+ @Override default String getCanonicalFriendlyName() {
+ return getObjectAction().getCanonicalFriendlyName();
+ }
+ @Override default Optional<String> getDescription(final
Supplier<ManagedObject> domainObjectProvider) {
+ return getObjectAction().getDescription(domainObjectProvider);
+ }
+ @Override default Optional<String> getStaticDescription() {
+ return getObjectAction().getStaticDescription();
+ }
+ @Override default Optional<String> getCanonicalDescription() {
+ return getObjectAction().getCanonicalDescription();
+ }
+ @Override default ObjectSpecification getElementType() {
+ return getObjectAction().getElementType();
+ }
+ @Override default String asciiId() {
+ return getObjectAction().asciiId();
+ }
+ @Override default FeatureType getFeatureType() {
+ return getObjectAction().getFeatureType();
+ }
+ @Override
+ default FacetHolder getFacetHolder() {
+ return getObjectAction().getFacetHolder();
+ }
+ @Override default SemanticsOf getSemantics() {
+ return getObjectAction().getSemantics();
+ }
+ @Override default ActionScope getScope() {
+ return getObjectAction().getScope();
+ }
+ @Override default boolean isPrototype() {
+ return getObjectAction().isPrototype();
+ }
+ @Override default boolean isDeclaredOnMixin() {
+ return getObjectAction().isDeclaredOnMixin();
+ }
+ @Override default ObjectSpecification getReturnType() {
+ return getObjectAction().getReturnType();
+ }
+ @Override default boolean hasReturn() {
+ return getObjectAction().hasReturn();
+ }
+ @Override default ManagedObject executeWithRuleChecking(final
InteractionHead head, final Can<ManagedObject> parameters,
+ final InteractionInitiatedBy interactionInitiatedBy, final Where
where) throws AuthorizationException {
+ return getObjectAction().executeWithRuleChecking(head, parameters,
interactionInitiatedBy, where);
+ }
+ @Override default ManagedObject execute(final InteractionHead head, final
Can<ManagedObject> parameters,
+ final InteractionInitiatedBy interactionInitiatedBy) {
+ return getObjectAction().execute(head, parameters,
interactionInitiatedBy);
+ }
+ @Override default Consent isArgumentSetValid(final InteractionHead head,
final Can<ManagedObject> proposedArguments,
+ final InteractionInitiatedBy interactionInitiatedBy) {
+ return getObjectAction().isArgumentSetValid(head, proposedArguments,
interactionInitiatedBy);
+ }
+ @Override default InteractionResultSet
isArgumentSetValidForParameters(final InteractionHead head,
+ final Can<ManagedObject> proposedArguments, final
InteractionInitiatedBy interactionInitiatedBy) {
+ return getObjectAction().isArgumentSetValidForParameters(head,
proposedArguments, interactionInitiatedBy);
+ }
+ @Override default Consent isArgumentSetValidForAction(final
InteractionHead head, final Can<ManagedObject> proposedArguments,
+ final InteractionInitiatedBy interactionInitiatedBy) {
+ return getObjectAction().isArgumentSetValidForAction(head,
proposedArguments, interactionInitiatedBy);
+ }
+ @Override default ActionInteractionHead interactionHead(@NonNull final
ManagedObject actionOwner) {
+ return getObjectAction().interactionHead(actionOwner);
+ }
+ @Override default int getParameterCount() {
+ return getObjectAction().getParameterCount();
+ }
+ @Override default Can<ObjectActionParameter> getParameters() {
+ return getObjectAction().getParameters();
+ }
+ @Override default Can<ObjectSpecification> getParameterTypes() {
+ return getObjectAction().getParameterTypes();
+ }
+ @Override default Can<ObjectActionParameter> getParameters(final
Predicate<ObjectActionParameter> predicate) {
+ return getObjectAction().getParameters();
+ }
+ @Override default ObjectActionParameter getParameterById(final String
paramId) {
+ return getObjectAction().getParameterById(paramId);
+ }
+ @Override default ObjectActionParameter getParameterByName(final String
paramName) {
+ return getObjectAction().getParameterByName(paramName);
+ }
+ @Override default ManagedObject realTargetAdapter(final ManagedObject
targetAdapter) {
+ return getObjectAction().realTargetAdapter(targetAdapter);
+ }
+ @Override default CanVector<ManagedObject> getChoices(final ManagedObject
target, final InteractionInitiatedBy interactionInitiatedBy) {
+ return getObjectAction().getChoices(target, interactionInitiatedBy);
+ }
+
+}
\ No newline at end of file
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectActionDefault.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectActionDefault.java
index 375b6235a78..02f7681c796 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectActionDefault.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/impl/ObjectActionDefault.java
@@ -70,7 +70,7 @@
import lombok.extern.log4j.Log4j2;
@Log4j2
- class ObjectActionDefault
+class ObjectActionDefault
extends ObjectMemberAbstract
implements ObjectAction, HasSpecificationLoaderInternal {
private static final long serialVersionUID = 1L;
diff --git
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
index 03581670d8c..e4b06783fbc 100644
---
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
+++
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
@@ -18,6 +18,7 @@
*/
package org.apache.causeway.viewer.wicket.model.models.interaction.act;
+import java.lang.reflect.Proxy;
import java.util.Optional;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@@ -77,8 +78,11 @@ public class ActionInteractionWkt
* we don't have to re-attach the entire model (ActionInteraction)
* <p>
* nullable in support of lazy evaluation
+ * <p>
+ * make sure we don't memoize non-serializable ObjectAction proxies (as
introduced via composite value type support)
*/
private @Nullable ObjectAction actionMemento;
+ private transient @Nullable ObjectAction objectAction; // might be a proxy
(non-serializbale)
private Can<UiParameterWkt> childModels;
private @Nullable PropertyModel associatedWithPropertyIfAny;
@@ -125,13 +129,20 @@ private ActionInteractionWkt(
super(bookmarkedObject);
this.memberId = memberId;
this.where = where;
- this.actionMemento = objectAction
- .orElse(null);
+ setObjectAction(objectAction.orElse(null));
this.associatedWithPropertyIfAny = associatedWithPropertyIfAny;
this.associatedWithParameterIfAny = associatedWithParameterIfAny;
this.associatedWithCollectionIfAny = associatedWithCollectionIfAny;
}
+ private void setObjectAction(final ObjectAction objectAction) {
+ this.objectAction = objectAction;
+ this.actionMemento = objectAction!=null
+ && !Proxy.isProxyClass(objectAction.getClass())
+ ? objectAction
+ : null;
+ }
+
@Override
protected ActionInteraction load() {
@@ -162,13 +173,13 @@ public final ActionInteraction actionInteraction() {
public final ObjectAction getMetaModel() {
//[CAUSEWAY-3648] In support of the composite value type's
'Xxx_default' mixin.
- if(actionMemento==null) {
- this.actionMemento = actionInteraction()
- .getObjectActionElseFail();
- }
+ if(objectAction!=null) return objectAction;
+ if(actionMemento!=null) return actionMemento;
+ setObjectAction(actionInteraction()
+ .getObjectActionElseFail());
// re-attachment fails, if the owner is not found (eg. deleted entity),
// hence we return the directly memoized meta-model of the underlying
action
- return actionMemento;
+ return objectAction;
}
public Optional<PropertyModel> associatedWithProperty() {