You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2025/06/27 09:07:20 UTC
(causeway) 01/02: CAUSEWAY-2297: immutable InteractionResult (refactor)
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
commit b741654e834aad29c038bb152c48066410fc9bfc
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Jun 27 08:05:45 2025 +0200
CAUSEWAY-2297: immutable InteractionResult (refactor)
---
.../applib/events/InteractionEventTest.java | 22 +--
core/metamodel/src/main/java/module-info.java | 1 +
.../core/metamodel/consent/ConsentAbstract.java | 2 +-
.../core/metamodel/consent/InteractionResult.java | 149 ++++++---------------
.../metamodel/consent/InteractionResultSet.java | 13 +-
.../metamodel/interactions/InteractionUtils.java | 82 ++++++------
.../interactions/managed/ManagedFeature.java | 3 +-
.../interactions/managed/ManagedMember.java | 8 +-
.../interactions/managed/ManagedParameter.java | 11 +-
.../managed/ParameterNegotiationModel.java | 7 +-
.../core/metamodel/object/MmVisibilityUtils.java | 2 +-
.../metamodel/consent/InteractionResultTest.java | 59 ++++----
.../handlers/DomainObjectInvocationHandler.java | 56 +++-----
13 files changed, 164 insertions(+), 251 deletions(-)
diff --git a/api/applib/src/test/java/org/apache/causeway/applib/events/InteractionEventTest.java b/api/applib/src/test/java/org/apache/causeway/applib/events/InteractionEventTest.java
index cca86b18d5b..c7d2dcf37b9 100644
--- a/api/applib/src/test/java/org/apache/causeway/applib/events/InteractionEventTest.java
+++ b/api/applib/src/test/java/org/apache/causeway/applib/events/InteractionEventTest.java
@@ -42,7 +42,7 @@ class InteractionEventTest {
private static class CustomerOrder {}
@BeforeEach
- public void setUp() {
+ void setUp() {
source = new Object();
identifier = Identifier.actionIdentifier(
LogicalType.fqcn(CustomerOrder.class),
@@ -51,7 +51,7 @@ public void setUp() {
}
@Test
- public void getIdentifier() {
+ void getIdentifier() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -59,7 +59,7 @@ public void getIdentifier() {
}
@Test
- public void getSource() {
+ void getSource() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -67,7 +67,7 @@ public void getSource() {
}
@Test
- public void getClassName() {
+ void getClassName() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -75,7 +75,7 @@ public void getClassName() {
}
@Test
- public void getClassNaturalName() {
+ void getClassNaturalName() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -83,7 +83,7 @@ public void getClassNaturalName() {
}
@Test
- public void getMember() {
+ void getMember() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -91,7 +91,7 @@ public void getMember() {
}
@Test
- public void getMemberNaturalName() {
+ void getMemberNaturalName() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -99,7 +99,7 @@ public void getMemberNaturalName() {
}
@Test
- public void shouldInitiallyNotVeto() {
+ void shouldInitiallyNotVeto() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -107,7 +107,7 @@ public void shouldInitiallyNotVeto() {
}
@Test
- public void afterAdvisedShouldVeto() {
+ void afterAdvisedShouldVeto() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -116,7 +116,7 @@ public void afterAdvisedShouldVeto() {
}
@Test
- public void afterAdvisedShouldReturnReason() {
+ void afterAdvisedShouldReturnReason() {
interactionEvent = new InteractionEvent(source, identifier) {
};
@@ -125,7 +125,7 @@ public void afterAdvisedShouldReturnReason() {
}
@Test
- public void afterAdvisedShouldReturnAdvisorClass() {
+ void afterAdvisedShouldReturnAdvisorClass() {
interactionEvent = new InteractionEvent(source, identifier) {
};
diff --git a/core/metamodel/src/main/java/module-info.java b/core/metamodel/src/main/java/module-info.java
index aaae8756b6f..50a01928912 100644
--- a/core/metamodel/src/main/java/module-info.java
+++ b/core/metamodel/src/main/java/module-info.java
@@ -171,6 +171,7 @@
requires spring.context;
requires spring.core;
requires spring.boot.autoconfigure;
+ requires org.jspecify;
//JUnit testing stuff, not required as long this module is an 'open' one
// opens org.apache.causeway.core.metamodel.services to spring.core;
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/ConsentAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/ConsentAbstract.java
index 733db18abbb..fa4abf02d24 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/ConsentAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/ConsentAbstract.java
@@ -53,7 +53,7 @@ public static Consent allowIf(final boolean allowed) {
private static VetoReason determineReason(final InteractionResult interactionResult) {
return interactionResult == null
? null
- : interactionResult.getReason().orElse(null);
+ : interactionResult.vetoReason().orElse(null);
}
/**
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResult.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResult.java
index caa453b377f..6f2a155842b 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResult.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResult.java
@@ -19,131 +19,68 @@
package org.apache.causeway.core.metamodel.consent;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import org.apache.causeway.applib.services.wrapper.events.InteractionEvent;
+import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.core.metamodel.consent.Consent.VetoReason;
-public class InteractionResult {
-
- /**
- * Initially {@link #ADVISING}; when call
- * {@link InteractionResult#getInteractionEvent()}, flips over into
- * {@link #ADVISED}.
- *
- * <p>
- * Subsequent attempts to
- * {@link InteractionResult#advise(String, InteractionAdvisor)} will then be
- * disallowed.
- */
- enum State {
- ADVISING, ADVISED
- }
-
- private final InteractionEvent interactionEvent;
- private final List<Consent.VetoReason> reasonBuf = new ArrayList<>();
- private final List<InteractionAdvisor> advisors = new ArrayList<InteractionAdvisor>();
-
- private State state = State.ADVISING;
-
- public InteractionResult(final InteractionEvent interactionEvent) {
- this.interactionEvent = interactionEvent;
- }
-
- /**
- * Returns the contained {@link InteractionEvent}, if necessary updated with
- * the {@link #advise(org.apache.causeway.core.metamodel.consent.Consent.VetoReason, InteractionAdvisor) advice} of the
- * interactions.
- *
- * <p>
- * That is, if still {@link State#ADVISING advising}, then copies over the
- * details from this result into the contained {@link InteractionEvent}, and
- * flips into {@link State#ADVISED advised (done)}.
- */
- public InteractionEvent getInteractionEvent() {
- if (state == State.ADVISING) {
- final String nullableReasonString = getReason().map(VetoReason::string).orElse(null);
- interactionEvent.advised(nullableReasonString, getAdvisorClass());
- state = State.ADVISED;
+public record InteractionResult(
+ InteractionEvent interactionEvent,
+ Optional<Consent.VetoReason> vetoReason,
+ /**
+ * Any {@link InteractionAdvisor} advisors that have appended veto reasons.
+ */
+ Can<InteractionAdvisor> advisors) {
+
+ public static Builder builder(final InteractionEvent interactionEvent) { return new Builder(interactionEvent); }
+ public record Builder(
+ InteractionEvent interactionEvent,
+ List<Consent.VetoReason> reasonBuf,
+ List<InteractionAdvisor> advisors) {
+ public Builder(InteractionEvent interactionEvent) {
+ this(interactionEvent, new ArrayList<>(), new ArrayList<>());
}
- return interactionEvent;
- }
-
- private Class<?> getAdvisorClass() {
- final InteractionAdvisor advisor = getAdvisor();
- return advisor != null ? advisor.getClass() : null;
- }
-
- public void advise(final Consent.VetoReason reason, final InteractionAdvisor facet) {
- if (state == State.ADVISED) {
- throw new IllegalStateException("Cannot append since have called getInteractionEvent");
+ public void addAdvise(final VetoReason reason, final InteractionAdvisor facet) {
+ reasonBuf.add(Objects.requireNonNull(reason));
+ advisors.add(Objects.requireNonNull(facet));
}
- if (reason == null) {
- return;
+ public InteractionResult build() {
+ Optional<Consent.VetoReason> reason = reasonBuf.stream().reduce(Consent.VetoReason::reduce);
+ return new InteractionResult(interactionEvent, reason, Can.ofCollection(advisors));
}
- advisors.add(facet);
- reasonBuf.add(reason);
}
- public boolean isVetoing() {
- return !isNotVetoing();
+ // canonical constructor
+ public InteractionResult(
+ InteractionEvent interactionEvent,
+ Optional<Consent.VetoReason> vetoReason,
+ Can<InteractionAdvisor> advisors) {
+ this.interactionEvent = Objects.requireNonNull(interactionEvent);
+ this.vetoReason = Objects.requireNonNull(vetoReason);
+ this.advisors = Objects.requireNonNull(advisors);
+
+ vetoReason.ifPresent(reason->{
+ InteractionAdvisor advisor = advisors.stream().findFirst().orElseThrow();
+ interactionEvent.advised(reason.string(), advisor.getClass());
+ });
}
- public boolean isNotVetoing() {
- return reasonBuf.size() == 0;
- }
-
- /**
- * Returns the first of the {@link #getAdvisorFacets()} that has been
- * {@link #advise(org.apache.causeway.core.metamodel.consent.Consent.VetoReason, InteractionAdvisor) advised} , or <tt>null</tt> if
- * none yet.
- *
- * @see #getAdvisorFacets()
- */
- public InteractionAdvisor getAdvisor() {
- return advisors.size() >= 1 ? advisors.get(0) : null;
- }
-
- /**
- * Returns all {@link InteractionAdvisor advisor} (facet)s that have
- * {@link #advise(org.apache.causeway.core.metamodel.consent.Consent.VetoReason, InteractionAdvisor) append}ed reasons to the
- * buffer.
- *
- * @see #getAdvisor()
- */
- public List<InteractionAdvisor> getAdvisorFacets() {
- return Collections.unmodifiableList(advisors);
- }
+ public boolean isAllowing() { return vetoReason.isEmpty(); }
+ public boolean isVetoing() { return vetoReason.isPresent(); }
public Consent createConsent() {
- if (isNotVetoing()) {
- return new Allow(this);
- } else {
- return new Veto(this);
- }
- }
-
- /**
- * Gets the reason as currently known, but does not change the state.
- * <p>
- * If {@link #isNotVetoing()}, then returns <tt>Optional.empty()</tt>.
- */
- public Optional<Consent.VetoReason> getReason() {
- return reasonBuf.stream().reduce(Consent.VetoReason::reduce);
+ return isAllowing()
+ ? new Allow(this)
+ : new Veto(this);
}
@Override
public String toString() {
- return String.format("%s: %s: %s (%d facets advised)",
- interactionEvent, state, toStringInterpret(), advisors.size());
- }
-
- private String toStringInterpret() {
- return isNotVetoing()
- ? "allowed"
- : "vetoed";
+ return String.format("%s: %s (%d facets advised)",
+ interactionEvent, isAllowing() ? "allowed" : "vetoed", advisors.size());
}
}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResultSet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResultSet.java
index c8716d4029f..60abb129980 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResultSet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/consent/InteractionResultSet.java
@@ -39,9 +39,8 @@ public InteractionResultSet add(final InteractionResult result) {
}
/**
- * Empty only if all the {@link #add(InteractionResult) contained}
- * {@link InteractionResult}s are also
- * {@link InteractionResult#isNotVetoing() empty}.
+ * Allowing only if all the {@link #add(InteractionResult) contained}
+ * {@link InteractionResult}s are also {@link InteractionResult#isAllowing()}.
*/
public boolean isAllowed() {
return !isVetoed();
@@ -54,9 +53,7 @@ public boolean isAllowed() {
*/
public boolean isVetoed() {
for (final InteractionResult result : results) {
- if (result.isVetoing()) {
- return true;
- }
+ if (result.isVetoing()) return true;
}
return false;
}
@@ -84,9 +81,7 @@ public Consent createConsent() {
*/
public InteractionResult getInteractionResult() {
for (final InteractionResult result : results) {
- if (!result.isNotVetoing()) {
- return result;
- }
+ if (!result.isAllowing()) return result;
}
return firstResult != null ? firstResult : null;
}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java
index 68d144dcab6..4c95dd4f0a3 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/InteractionUtils.java
@@ -24,6 +24,7 @@
import org.jspecify.annotations.Nullable;
import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.commons.internal.base._Strings;
import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.core.config.environment.DeploymentType;
import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants;
@@ -49,7 +50,7 @@ public final class InteractionUtils {
public InteractionResult isVisibleResult(final FacetHolder facetHolder, final VisibilityContext context) {
- var iaResult = new InteractionResult(context.createInteractionEvent());
+ var builder = InteractionResult.builder(context.createInteractionEvent());
// depending on the ifHiddenPolicy, we may do no vetoing here (instead, it moves into the usability check).
var ifHiddenPolicy = context.renderPolicy().ifHiddenPolicy();
@@ -58,12 +59,9 @@ public InteractionResult isVisibleResult(final FacetHolder facetHolder, final Vi
facetHolder.streamFacets(HidingInteractionAdvisor.class)
.filter(advisor->compatible(advisor, context))
.forEach(advisor->{
- var hidingReasonString = advisor.hides(context);
- var hidingReason = Optional.ofNullable(hidingReasonString)
- .map(Consent.VetoReason::explicit)
- .orElse(null);
-
- iaResult.advise(hidingReason, advisor);
+ _Strings.nonEmpty(advisor.hides(context))
+ .map(Consent.VetoReason::explicit)
+ .ifPresent(hidingReason->builder.addAdvise(hidingReason, advisor));
});
break;
case SHOW_AS_DISABLED:
@@ -72,12 +70,12 @@ public InteractionResult isVisibleResult(final FacetHolder facetHolder, final Vi
break;
}
- return iaResult;
+ return builder.build();
}
public InteractionResult isUsableResult(final FacetHolder facetHolder, final UsabilityContext context) {
- var isResult = new InteractionResult(context.createInteractionEvent());
+ var builder = InteractionResult.builder(context.createInteractionEvent());
// depending on the ifHiddenPolicy, we additionally may disable using a hidden advisor
var ifHiddenPolicy = context.renderPolicy().ifHiddenPolicy();
@@ -88,53 +86,49 @@ public InteractionResult isUsableResult(final FacetHolder facetHolder, final Usa
case SHOW_AS_DISABLED_WITH_DIAGNOSTICS:
var visibilityContext = context.asVisibilityContext();
facetHolder.streamFacets(HidingInteractionAdvisor.class)
- .filter(advisor->compatible(advisor, context))
- .forEach(advisor->{
- String hidingReasonString = advisor.hides(visibilityContext);
- Consent.VetoReason hidingReason = Optional.ofNullable(hidingReasonString)
- .map(Consent.VetoReason::explicit)
- .orElse(null);
- if(hidingReason != null
- && ifHiddenPolicy.isShowAsDisabledWithDiagnostics()) {
- hidingReason = VetoUtil.withAdvisorAsDiagnostic(hidingReason, advisor);
- }
- isResult.advise(hidingReason, advisor);
- });
+ .filter(advisor->compatible(advisor, context))
+ .forEach(advisor->{
+ _Strings.nonEmpty(advisor.hides(visibilityContext))
+ .map(Consent.VetoReason::explicit)
+ .ifPresent(hidingReason->{
+ if(ifHiddenPolicy.isShowAsDisabledWithDiagnostics()) {
+ hidingReason = VetoUtil.withAdvisorAsDiagnostic(hidingReason, advisor);
+ }
+ builder.addAdvise(hidingReason, advisor);
+ });
+ });
break;
}
var ifDisabledPolicy = context.renderPolicy().ifDisabledPolicy();
facetHolder.streamFacets(DisablingInteractionAdvisor.class)
- .filter(advisor->compatible(advisor, context))
- .forEach(advisor->{
- Consent.VetoReason disablingReason = advisor.disables(context).orElse(null);
- if(disablingReason != null
- && ifDisabledPolicy.isShowAsDisabledWithDiagnostics()) {
- disablingReason = VetoUtil.withAdvisorAsDiagnostic(disablingReason, advisor);
- }
- isResult.advise(disablingReason, advisor);
- });
-
- return isResult;
+ .filter(advisor->compatible(advisor, context))
+ .forEach(advisor->{
+ advisor.disables(context)
+ .ifPresent(disablingReason->{
+ if(ifDisabledPolicy.isShowAsDisabledWithDiagnostics()) {
+ disablingReason = VetoUtil.withAdvisorAsDiagnostic(disablingReason, advisor);
+ }
+ builder.addAdvise(disablingReason, advisor);
+ });
+ });
+
+ return builder.build();
}
public InteractionResult isValidResult(final FacetHolder facetHolder, final ValidityContext context) {
- var iaResult = new InteractionResult(context.createInteractionEvent());
+ var builder = InteractionResult.builder(context.createInteractionEvent());
facetHolder.streamFacets(ValidatingInteractionAdvisor.class)
.filter(advisor->compatible(advisor, context))
.forEach(advisor->{
- var invalidatingReasonString =
- guardAgainstEmptyReasonString(advisor.invalidates(context), context.identifier());
-
- var invalidatingReason = Optional.ofNullable(invalidatingReasonString)
- .map(Consent.VetoReason::explicit)
- .orElse(null);
- iaResult.advise(invalidatingReason, advisor);
+ guardAgainstEmptyReasonString(advisor.invalidates(context), context.identifier())
+ .map(Consent.VetoReason::explicit)
+ .ifPresent(invalidatingReason->builder.addAdvise(invalidatingReason, advisor));
});
- return iaResult;
+ return builder.build();
}
public InteractionResultSet isValidResultSet(
@@ -158,7 +152,7 @@ public RenderPolicy renderPolicy(final ManagedObject ownerAdapter) {
* we should generate a message,
* explaining what was going wrong and hinting developers at a possible resolution
*/
- private String guardAgainstEmptyReasonString(
+ private Optional<String> guardAgainstEmptyReasonString(
final @Nullable String reason, final @NonNull Identifier identifier) {
if("".equals(reason)) {
var msg = ProgrammingModelConstants.MessageTemplate.INVALID_USE_OF_VALIDATION_SUPPORT_METHOD.builder()
@@ -166,9 +160,9 @@ private String guardAgainstEmptyReasonString(
.addVariable("memberName", identifier.memberLogicalName())
.buildMessage();
log.error(msg);
- return msg;
+ return Optional.of(msg);
}
- return reason;
+ return Optional.ofNullable(reason);
}
private static boolean compatible(final InteractionAdvisor advisor, final InteractionContext ic) {
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java
index 3f6cb957341..883af795091 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedFeature.java
@@ -28,7 +28,8 @@
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
-public interface ManagedFeature {
+sealed public interface ManagedFeature
+permits ManagedMember, ManagedParameter {
Identifier getIdentifier();
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java
index 9c6d29da56b..b782d649867 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedMember.java
@@ -20,6 +20,8 @@
import java.util.Optional;
+import org.jspecify.annotations.NonNull;
+
import org.apache.causeway.applib.Identifier;
import org.apache.causeway.applib.annotation.Where;
import org.apache.causeway.commons.internal.base._Casts;
@@ -30,15 +32,15 @@
import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
import lombok.Getter;
-import org.jspecify.annotations.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
@Log4j2
@RequiredArgsConstructor
-public abstract class ManagedMember
-implements ManagedFeature {
+public sealed abstract class ManagedMember
+implements ManagedFeature
+permits ManagedAction, ManagedCollection, ManagedProperty {
/**
* Some representations may vary according to whether the member is to be represented for read
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java
index 8e359d6e960..f3478560709 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ManagedParameter.java
@@ -26,17 +26,18 @@
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.spec.feature.ObjectActionParameter;
-public interface ManagedParameter
+public sealed interface ManagedParameter
extends
ManagedValue,
- ManagedFeature {
-
+ ManagedFeature
+permits ParameterNegotiationModel.ParameterModel {
+
ObjectActionParameter metaModel();
@Override default ObjectActionParameter getMetaModel() { return metaModel(); }
-
+
int paramIndex();
ParameterNegotiationModel negotiationModel();
-
+
/**
* @return non-empty if not usable/editable (meaning if read-only)
*/
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java
index ed4de33f9d3..6dd112dc317 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/interactions/managed/ParameterNegotiationModel.java
@@ -97,7 +97,7 @@ private ParameterNegotiationModel(
}
// -- ACTION SPECIFIC
-
+
public ObjectAction act() {
return managedAction.getAction();
}
@@ -120,7 +120,7 @@ public Can<ManagedObject> getParamValues() {
InteractionHead interactionHead() {
return managedAction.interactionHead();
}
-
+
public ActionInteractionHead actionInteractionHead() {
return managedAction.actionInteractionHead();
}
@@ -344,8 +344,7 @@ public ParameterNegotiationModel withParamValue(final int parameterIndex, @NonNu
// -- INTERNAL HOLDER OF PARAMETER BINDABLES
- @Log4j2
- private record ParameterModel(
+ @Log4j2 record ParameterModel(
int paramIndex,
@NonNull ObjectActionParameter metaModel,
@NonNull ParameterNegotiationModel negotiationModel,
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmVisibilityUtils.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmVisibilityUtils.java
index 8b01347329d..4a27cffc8c7 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmVisibilityUtils.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmVisibilityUtils.java
@@ -109,7 +109,7 @@ public static boolean isVisible(
Where.OBJECT_FORMS);
return InteractionUtils.isVisibleResult(spec, visibilityContext)
- .isNotVetoing();
+ .isAllowing();
}
private static VisibilityContext createVisibleInteractionContext(
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/consent/InteractionResultTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/consent/InteractionResultTest.java
index 23217ecdada..007b0290755 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/consent/InteractionResultTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/consent/InteractionResultTest.java
@@ -18,77 +18,76 @@
*/
package org.apache.causeway.core.metamodel.consent;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.causeway.applib.services.wrapper.events.InteractionEvent;
import org.apache.causeway.core.metamodel.consent.Consent.VetoReason;
class InteractionResultTest {
- private InteractionResult result;
-
- @BeforeEach
- public void setUp() throws Exception {
- result = new InteractionResult(null);
- }
-
- @AfterEach
- public void tearDown() throws Exception {
- result = null;
- }
+ private InteractionResult.Builder builder = InteractionResult.builder(Mockito.mock(InteractionEvent.class));
@Test
- public void shouldHaveNullReasonWhenJustInstantiated() {
+ void shouldHaveNullReasonWhenJustInstantiated() {
+ var result = builder.build();
assertEquals(null, extractReason(result));
}
@Test
- public void shouldBeEmptyWhenJustInstantiated() {
+ void shouldBeEmptyWhenJustInstantiated() {
+ var result = builder.build();
assertFalse(result.isVetoing());
- assertTrue(result.isNotVetoing());
+ assertTrue(result.isAllowing());
}
@Test
- public void shouldHaveNonNullReasonWhenAdvisedWithNonNull() {
- result.advise(vetoReason("foo"), InteractionAdvisor.forTesting());
+ void shouldHaveNonNullReasonWhenAdvisedWithNonNull() {
+ advise(vetoReason("foo"), InteractionAdvisor.forTesting());
+ var result = builder.build();
assertEquals("foo", extractReason(result));
}
@Test
- public void shouldConcatenateAdviseWhenAdvisedWithNonNull() {
- result.advise(vetoReason("foo"), InteractionAdvisor.forTesting());
- result.advise(vetoReason("bar"), InteractionAdvisor.forTesting());
+ void shouldConcatenateAdviseWhenAdvisedWithNonNull() {
+ advise(vetoReason("foo"), InteractionAdvisor.forTesting());
+ advise(vetoReason("bar"), InteractionAdvisor.forTesting());
+ var result = builder.build();
assertEquals("foo; bar", extractReason(result));
}
@Test
- public void shouldNotBeEmptyWhenAdvisedWithNonNull() {
- result.advise(vetoReason("foo"), InteractionAdvisor.forTesting());
+ void shouldNotBeEmptyWhenAdvisedWithNonNull() {
+ advise(vetoReason("foo"), InteractionAdvisor.forTesting());
+ var result = builder.build();
assertTrue(result.isVetoing());
- assertFalse(result.isNotVetoing());
+ assertFalse(result.isAllowing());
}
@Test
- public void shouldBeEmptyWhenAdvisedWithNull() {
- result.advise(null, InteractionAdvisor.forTesting());
- assertTrue(result.isNotVetoing());
- assertFalse(result.isVetoing());
- assertEquals(null, extractReason(result));
+ void shouldThrowWhenAdvisedWithNull() {
+ assertThrowsExactly(NullPointerException.class, ()->advise(null, InteractionAdvisor.forTesting()));
+ assertThrowsExactly(NullPointerException.class, ()->advise(vetoReason("foo"), null));
}
// -- HELPER
+ private void advise(VetoReason vetoReason, InteractionAdvisor forTesting) {
+ builder.addAdvise(vetoReason, forTesting);
+ }
+
+
static Consent.VetoReason vetoReason(final String reasonString) {
return Consent.VetoReason.explicit(reasonString);
}
static String extractReason(final InteractionResult result) {
- return result.getReason().map(VetoReason::string).orElse(null);
+ return result.vetoReason().map(VetoReason::string).orElse(null);
}
}
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
index 29af1c124fd..2b1f34deea9 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
@@ -100,7 +100,6 @@ public Object invoke(WrapperInvocation wrapperInvocation) throws Throwable {
final Object target = wrapperInvocation.origin().pojo();
final Method method = wrapperInvocation.method();
- final ManagedObject managedMixee = wrapperInvocation.origin().managedMixee();
if (classMetaData().isObjectMethod(method)
|| isEnhancedEntityMethod(method)) {
@@ -133,52 +132,48 @@ public Object invoke(WrapperInvocation wrapperInvocation) throws Throwable {
var objectMember = targetAdapter.objSpec().getMemberElseFail(resolvedMethod);
var intent = ImperativeFacet.getIntent(objectMember, resolvedMethod);
if(intent == Intent.CHECK_IF_HIDDEN || intent == Intent.CHECK_IF_DISABLED) {
- throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'", objectMember.getId()));
+ throw _Exceptions.unsupportedOperation("Cannot invoke supporting method '%s'", objectMember.getId());
}
if (intent == Intent.DEFAULTS || intent == Intent.CHOICES_OR_AUTOCOMPLETE) {
return method.invoke(target, wrapperInvocation.args());
}
- if (objectMember.isOneToOneAssociation()) {
+ if (objectMember instanceof OneToOneAssociation prop) {
if (intent == Intent.CHECK_IF_VALID || intent == Intent.MODIFY_PROPERTY_SUPPORTING) {
- throw new UnsupportedOperationException(String.format("Cannot invoke supporting method for '%s'; use only property accessor/mutator", objectMember.getId()));
+ throw _Exceptions.unsupportedOperation("Cannot invoke supporting method for '%s'; use only property accessor/mutator", objectMember.getId());
}
- final OneToOneAssociation otoa = (OneToOneAssociation) objectMember;
-
if (intent == Intent.ACCESSOR) {
- return handleGetterMethodOnProperty(wrapperInvocation, targetAdapter, otoa);
+ return handleGetterMethodOnProperty(wrapperInvocation, targetAdapter, prop);
}
if (intent == Intent.MODIFY_PROPERTY || intent == Intent.INITIALIZATION) {
- return handleSetterMethodOnProperty(wrapperInvocation, targetAdapter, otoa);
+ return handleSetterMethodOnProperty(wrapperInvocation, targetAdapter, prop);
}
}
- if (objectMember.isOneToManyAssociation()) {
+ if (objectMember instanceof OneToManyAssociation coll) {
if (intent == Intent.CHECK_IF_VALID) {
- throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only collection accessor/mutator", objectMember.getId()));
+ throw _Exceptions.unsupportedOperation("Cannot invoke supporting method '%s'; use only collection accessor/mutator", objectMember.getId());
}
- final OneToManyAssociation otma = (OneToManyAssociation) objectMember;
if (intent == Intent.ACCESSOR) {
- return handleGetterMethodOnCollection(wrapperInvocation, targetAdapter, otma, objectMember.getId());
+ return handleGetterMethodOnCollection(wrapperInvocation, targetAdapter, coll, objectMember.getId());
}
}
if (objectMember instanceof ObjectAction objectAction) {
if (intent == Intent.CHECK_IF_VALID) {
- throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only the 'invoke' method", objectMember.getId()));
+ throw _Exceptions.unsupportedOperation("Cannot invoke supporting method '%s'; use only the 'invoke' method", objectMember.getId());
}
if(targetAdapter.objSpec().isMixin()) {
+ final ManagedObject managedMixee = wrapperInvocation.origin().managedMixee();
if (managedMixee == null) {
- throw _Exceptions.illegalState(
- "Missing the required managedMixee for action '%s'",
- objectAction.getId());
+ throw _Exceptions.illegalState("Missing the required managedMixee for action '%s'", objectAction.getId());
}
MmAssertionUtils.assertIsBookmarkSupported(managedMixee);
@@ -197,8 +192,7 @@ public Object invoke(WrapperInvocation wrapperInvocation) throws Throwable {
return handleGetterMethodOnCollection(wrapperInvocation, managedMixee, (OneToManyAssociation)mixinMember, objectMember.getId());
}
} else {
- throw _Exceptions.illegalState(String.format(
- "Could not locate mixin member for action '%s' on spec '%s'", objectAction.getId(), targetAdapter.objSpec()));
+ throw _Exceptions.illegalState("Could not locate mixin member for action '%s' on spec '%s'", objectAction.getId(), targetAdapter.objSpec());
}
}
@@ -206,16 +200,15 @@ public Object invoke(WrapperInvocation wrapperInvocation) throws Throwable {
return handleActionMethod(wrapperInvocation, targetAdapter, objectAction);
}
- throw new UnsupportedOperationException(String.format("Unknown member type '%s'", objectMember));
+ throw _Exceptions.unsupportedOperation("Unknown member type '%s'", objectMember);
}
private static ObjectMember determineMixinMember(
final ManagedObject domainObjectAdapter,
final ObjectAction objectAction) {
- if(domainObjectAdapter == null) {
- return null;
- }
+ if(domainObjectAdapter == null) return null;
+
var specification = domainObjectAdapter.objSpec();
var objectActions = specification.streamAnyActions(MixedIn.INCLUDED);
var objectAssociations = specification.streamAssociations(MixedIn.INCLUDED);
@@ -368,8 +361,7 @@ private Object handleGetterMethodOnCollection(
return mapViewObject;
}
- var msg = String.format("Collection type '%s' not supported by framework", currentReferencedObj.getClass().getName());
- throw new IllegalArgumentException(msg);
+ throw _Exceptions.illegalArgument("Collection type '%s' not supported by framework", currentReferencedObj.getClass().getName());
}, ()->new ExceptionLogger("getter " + collection.getId(), targetAdapter));
}
@@ -481,7 +473,7 @@ private void checkUsability(
// -- NOTIFY LISTENERS
private void notifyListenersAndVetoIfRequired(final InteractionResult interactionResult) {
- var interactionEvent = interactionResult.getInteractionEvent();
+ var interactionEvent = interactionResult.interactionEvent();
mmc().getWrapperFactory().notifyListeners(interactionEvent);
if (interactionEvent.isVeto()) {
@@ -578,8 +570,7 @@ private Object handleException(
private Object singleArgUnderlyingElseNull(final Object[] args, final String name) {
if (args.length != 1) {
- throw new IllegalArgumentException(String.format(
- "Invoking '%s' should only have a single argument", name));
+ throw _Exceptions.illegalArgument("Invoking '%s' should only have a single argument", name);
}
var argumentObj = underlying(args[0]);
return argumentObj;
@@ -587,8 +578,7 @@ private Object singleArgUnderlyingElseNull(final Object[] args, final String nam
private void zeroArgsElseThrow(final Object[] args, final String name) {
if (!_NullSafe.isEmpty(args)) {
- throw new IllegalArgumentException(String.format(
- "Invoking '%s' should have no arguments", name));
+ throw _Exceptions.illegalArgument("Invoking '%s' should have no arguments", name);
}
}
@@ -598,13 +588,7 @@ String msg(Exception ex) {
String id = mo.isBookmarkMemoized()
? mo.getBookmarkElseFail().identifier()
: "<bookmark not memoized>";
- var buf = new StringBuilder("Failed to execute ").append(" ").append(what).append(" ");
- buf.append(" on '")
- .append(logicalType.logicalName())
- .append(":")
- .append(id)
- .append("'");
- return buf.toString();
+ return "Failed to execute %s on '%s:%s'".formatted(what, logicalType.logicalName(), id);
}
}