You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/06/06 08:21:27 UTC
(causeway) 35/39: CAUSEWAY-4012: allows selector string partial matching
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4012
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 32da54a23f2fe8b214629f34255fa6c2d27fc992
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Sat Jun 6 06:44:34 2026 +0100
CAUSEWAY-4012: allows selector string partial matching
---
.../ParentedCollectionSelectorMatchingUtil.java | 10 +++++-
.../ParentedCollectionSelectorActionUtilTest.java | 41 ++++++++++++++++++++++
.../allow-selector-string-partial-match/tasks.md | 16 ++++-----
3 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/ParentedCollectionSelectorMatchingUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/ParentedCollectionSelectorMatchingUtil.java
index 687f36b3569..ee380928df0 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/ParentedCollectionSelectorMatchingUtil.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/ParentedCollectionSelectorMatchingUtil.java
@@ -93,13 +93,21 @@ private boolean matches(
}
val property = scalarProperties.getElseFail(i);
val propertyValue = property.get(childAdapter, interactionInitiatedBy);
- if(!Objects.equals(MmUnwrapUtils.single(propertyValue), MmUnwrapUtils.single(argumentAdapter))) {
+ if(!matches(MmUnwrapUtils.single(propertyValue), MmUnwrapUtils.single(argumentAdapter))) {
return false;
}
}
return true;
}
+ private boolean matches(final Object propertyValue, final Object argumentValue) {
+ if(propertyValue instanceof String
+ && argumentValue instanceof String) {
+ return ((String) propertyValue).contains((String) argumentValue);
+ }
+ return Objects.equals(propertyValue, argumentValue);
+ }
+
@Value(staticConstructor = "of")
static class MatchResult {
@NonNull Status status;
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java
index 2ed85ad5c06..9febf4f174b 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java
@@ -324,6 +324,31 @@ void validates_and_invokes_selector_action_returning_single_matching_child() {
assertThat(result.getPojo(), is(matchingItem));
}
+ @Test
+ void partial_string_filter_validates_and_invokes_when_it_identifies_one_child() {
+ val lease = new Lease();
+ val matchingItem = new LeaseItem("first child", 1, null);
+ lease.getItems().add(matchingItem);
+ lease.getItems().add(new LeaseItem("second child", 2, null));
+
+ assertThat(validate(lease, "irs", null).isAllowed(), is(true));
+
+ val result = invoke(lease, "irs", null);
+
+ assertThat(result.getPojo(), is(matchingItem));
+ }
+
+ @Test
+ void non_string_scalar_filter_still_uses_exact_equality() {
+ val lease = new Lease();
+ lease.getItems().add(new LeaseItem("first", 12, null));
+
+ val consent = validate(lease, "first", 1);
+
+ assertThat(consent.isVetoed(), is(true));
+ assertThat(consent.getReasonAsString().orElseThrow(), containsString("0 items match. Use parameters to match just one item."));
+ }
+
@Test
void selector_action_validation_rejects_when_no_child_matches() {
val lease = new Lease();
@@ -355,6 +380,22 @@ void selector_action_validation_rejects_when_multiple_children_match() {
assertThat(ex.getMessage(), containsString("2 items match. Use parameters to match just one item."));
}
+ @Test
+ void partial_string_filter_is_ambiguous_when_it_matches_multiple_children() {
+ val lease = new Lease();
+ lease.getItems().add(new LeaseItem("first child", 1, null));
+ lease.getItems().add(new LeaseItem("second child", 2, null));
+
+ val consent = validate(lease, "child", null);
+
+ assertThat(consent.isVetoed(), is(true));
+ assertThat(consent.getReasonAsString().orElseThrow(), containsString("2 items match. Use parameters to match just one item."));
+
+ val ex = assertThrows(RecoverableException.class, () -> executeWithRuleChecking(lease, "child", null));
+
+ assertThat(ex.getMessage(), containsString("2 items match. Use parameters to match just one item."));
+ }
+
@Test
void direct_invocation_still_fails_when_no_child_matches() {
val lease = new Lease();
diff --git a/openspec/changes/allow-selector-string-partial-match/tasks.md b/openspec/changes/allow-selector-string-partial-match/tasks.md
index b158a19c52d..775b17392cb 100644
--- a/openspec/changes/allow-selector-string-partial-match/tasks.md
+++ b/openspec/changes/allow-selector-string-partial-match/tasks.md
@@ -1,16 +1,16 @@
## 1. Matching Implementation
-- [ ] 1.1 Update `ParentedCollectionSelectorMatchingUtil` so supplied string filter values match child string property values using containment rather than exact equality.
-- [ ] 1.2 Keep non-string scalar filter values using exact equality and keep null or absent optional filter values ignored.
-- [ ] 1.3 Verify validation and invocation both use the same updated matching behavior.
+- [x] 1.1 Update `ParentedCollectionSelectorMatchingUtil` so supplied string filter values match child string property values using containment rather than exact equality.
+- [x] 1.2 Keep non-string scalar filter values using exact equality and keep null or absent optional filter values ignored.
+- [x] 1.3 Verify validation and invocation both use the same updated matching behavior.
## 2. Test Coverage
-- [ ] 2.1 Add or update selector action tests for a partial string filter that uniquely selects one child.
-- [ ] 2.2 Add or update selector action tests for a partial string filter that matches multiple children and remains ambiguous.
-- [ ] 2.3 Add or update selector action tests confirming non-string scalar filters still require exact equality.
+- [x] 2.1 Add or update selector action tests for a partial string filter that uniquely selects one child.
+- [x] 2.2 Add or update selector action tests for a partial string filter that matches multiple children and remains ambiguous.
+- [x] 2.3 Add or update selector action tests confirming non-string scalar filters still require exact equality.
## 3. Validation
-- [ ] 3.1 Run the focused selector action test suite for `ParentedCollectionSelectorActionUtilTest`.
-- [ ] 3.2 Run `openspec validate allow-selector-string-partial-match --strict`.
+- [x] 3.1 Run the focused selector action test suite for `ParentedCollectionSelectorActionUtilTest`.
+- [x] 3.2 Run `openspec validate allow-selector-string-partial-match --strict`.