This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch CAUSEWAY-2485
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 8693cc8e853e7b16c338c6bb1e897bbe6858fff1
Author: danhaywood <[email protected]>
AuthorDate: Mon May 15 21:37:28 2023 +0100

    CAUSEWAY-2485: completes Action validate demo example
---
 .../autocomplete/ActionAutoCompleteMenu.java       |   2 +-
 .../ActionAutoCompletePage-description.adoc        |   2 +-
 .../choices/ActionChoicesPage-description.adoc     |   2 +-
 .../progmodel/defaults/ActionDefaultsMenu.java     |   2 +-
 .../defaults/ActionDefaultsPage-description.adoc   |   2 +-
 .../dom/domain/actions/progmodel/disable/notes.txt |   4 +-
 .../dom/domain/actions/progmodel/hide/notes.txt    |   4 +-
 .../ActionValidateMenu.java}                       |  12 +--
 ...nValidatePage-allArgsValidationDescription.adoc |   7 ++
 .../validate/ActionValidatePage-description.adoc   |  79 +++++++++++++++
 ...nValidatePage-parameterMatchingDescription.adoc |   7 ++
 ...tionValidatePage-singleValidateDescription.adoc |   6 ++
 .../progmodel/validate/ActionValidatePage.java     | 108 +++++++++++++++++++++
 .../validate/ActionValidatePage.layout.xml         |  81 ++++++++++++++++
 ...ActionValidatePage_selectTvCharacterByShow.java |  65 +++++++++++++
 .../ActionValidatePage_selectTvCharacters.java     |  76 +++++++++++++++
 ...ge_selectTvCharactersByShowAndSexUsingName.java |  90 +++++++++++++++++
 ..._selectTvCharactersByShowAndSexUsingRecord.java |  99 +++++++++++++++++++
 .../src/main/java/demoapp/dom/menubars.layout.xml  |   1 +
 19 files changed, 636 insertions(+), 13 deletions(-)

diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompleteMenu.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompleteMenu.java
index 94a6cffcb4..b346eb75be 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompleteMenu.java
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompleteMenu.java
@@ -40,7 +40,7 @@ public class ActionAutoCompleteMenu {
     final TvCharacterPopulator tvCharacterPopulator;
 
     @Action
-    @ActionLayout(cssClassFa="fa-bolt")
+    @ActionLayout(cssClassFa="fa-wand-magic-sparkles")
     public ActionAutoCompletePage autoComplete(){
         val page = factoryService.viewModel(new ActionAutoCompletePage());
         tvCharacterPopulator.populate(page.getTvCharacters());
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompletePage-description.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompletePage-description.adoc
index 889f07a886..e3b96783ee 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompletePage-description.adoc
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/autocomplete/ActionAutoCompletePage-description.adoc
@@ -14,7 +14,7 @@ There are several variations:
 * the parameter for which candidates are being supplied can be either a scalar 
or a collection
 * the supporting "autoComplete" method can be associated with the parameter by 
number, or by name
 * the list of candidates for a parameter can depend upon the value already 
selected for an earlier parameter.
-** The preceding N-1 parameters are supplied to the Nth "autoComplete" 
supporting method.
+** The preceding N-1 parameters are supplied to the N^th^ "autoComplete" 
supporting method.
 ** Or a single record-like structure can be defined and this can be passed 
through to all supporting methods instead.
 
 As noted above, a supporting method to return a list of candidate arguments is 
_always_ required for reference types.
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/choices/ActionChoicesPage-description.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/choices/ActionChoicesPage-description.adoc
index 9d4f3cea90..e0f9e1357e 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/choices/ActionChoicesPage-description.adoc
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/choices/ActionChoicesPage-description.adoc
@@ -14,7 +14,7 @@ There are several variations:
 * the parameter for which candidates are being supplied can be either a scalar 
or a collection
 * the supporting "choices" method can be associated with the parameter by 
number, or by name
 * the list of candidates for a parameter can depend upon the value already 
selected for an earlier parameter.
-** The preceding N-1 parameters are supplied to the Nth "choices" supporting 
method.
+** The preceding N-1 parameters are supplied to the N^th^ "choices" supporting 
method.
 ** Or a single record-like structure can be defined and this can be passed 
through to all supporting methods instead.
 
 As noted above, a supporting method to return a list of candidate arguments is 
_always_ required for reference types.
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
index 77d84ac622..41b11fb0e2 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
@@ -44,7 +44,7 @@ public class ActionDefaultsMenu {
     final TvCharacterPopulator tvCharacterPopulator;
 
     @Action
-    @ActionLayout(cssClassFa="fa-bolt")
+    @ActionLayout(cssClassFa="fa-heart")
     public ActionDefaultsPage defaults(){
         val page = factoryService.viewModel(new ActionDefaultsPage());
         tvCharacterPopulator.populate(page.getTvCharacters());
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsPage-description.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsPage-description.adoc
index 02c5d23426..7ef678a536 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsPage-description.adoc
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsPage-description.adoc
@@ -8,7 +8,7 @@ There are several variations:
 * the parameter argument being defaulted could be either a scalar or a 
collection
 * the supporting "default" method can be associated with the parameter by 
number, or by name
 * the default for a parameter can depend upon the value already selected for 
an earlier parameter.
-** The preceding N-1 parameters are supplied to the Nth "default" supporting 
method.
+** The preceding N-1 parameters are supplied to the N^th^ "default" supporting 
method.
 ** Or a single record-like structure can be defined and this can be passed 
through to all supporting methods instead.
 
 It's common for "default" supporting method to be paired with a "choices" or 
"autoComplete" supporting method, but this won't always be the case.
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/disable/notes.txt
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/disable/notes.txt
index e9b99e3953..953ec611dd 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/disable/notes.txt
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/disable/notes.txt
@@ -1 +1,3 @@
-example of disabling, with depargs
\ No newline at end of file
+example of disabling, with depargs
+
+circle-stop
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/hide/notes.txt
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/hide/notes.txt
index ebe67b80ff..ef8be9378e 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/hide/notes.txt
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/hide/notes.txt
@@ -1 +1,3 @@
-example of hiding, with depargs
\ No newline at end of file
+example of hiding, with depargs
+
+eye-slash
\ No newline at end of file
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidateMenu.java
similarity index 86%
copy from 
examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
copy to 
examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidateMenu.java
index 77d84ac622..3ad8375656 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/defaults/ActionDefaultsMenu.java
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidateMenu.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.domain.actions.progmodel.defaults;
+package demoapp.dom.domain.actions.progmodel.validate;
 
 import javax.annotation.Priority;
 import javax.inject.Inject;
@@ -34,19 +34,19 @@ import lombok.val;
 
 import demoapp.dom.domain.actions.progmodel.TvCharacterPopulator;
 
-@Named("demo.ActionDefaultsMenu")
+@Named("demo.ActionValidateMenu")
 @DomainService(nature=NatureOfService.VIEW)
 @Priority(PriorityPrecedence.EARLY)
 @RequiredArgsConstructor(onConstructor_ = { @Inject })
-public class ActionDefaultsMenu {
+public class ActionValidateMenu {
 
     final FactoryService factoryService;
     final TvCharacterPopulator tvCharacterPopulator;
 
     @Action
-    @ActionLayout(cssClassFa="fa-bolt")
-    public ActionDefaultsPage defaults(){
-        val page = factoryService.viewModel(new ActionDefaultsPage());
+    @ActionLayout(cssClassFa="fa-circle-question")
+    public ActionValidatePage validate(){
+        val page = factoryService.viewModel(new ActionValidatePage());
         tvCharacterPopulator.populate(page.getTvCharacters());
         return page;
     }
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-allArgsValidationDescription.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-allArgsValidationDescription.adoc
new file mode 100644
index 0000000000..75bd342be5
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-allArgsValidationDescription.adoc
@@ -0,0 +1,7 @@
+:Notice: 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 ag [...]
+
+The action above illustrates how it's possible to validate _all_ of the 
arguments of action together (as opposed to each, singly).
+
+The action provides two parameters, one listing male characters and the other 
female.
+The validation simply requires that the same number of male and female 
charactes have been selected.
+
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-description.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-description.adoc
new file mode 100644
index 0000000000..20645b0736
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-description.adoc
@@ -0,0 +1,79 @@
+:Notice: 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 ag [...]
+
+There are often business rules which determine which action arguments are 
valid and which are invalid.
+For example, it probably is invalid to specify a negative number of product 
items in an order, or to specify a date of birth in the future.
+
+Using the "choices" or "autoComplete" supporting methods, it is sometimes 
possible to restrict the candidates only to those that are valid.
+And the "default" supporting method can also provide a reasonable initial 
value (such as 1 item in an order).
+
+Even so, when the action is invoked, it's necessary to make sure that all of 
the arguments provided are valid, both individually and in combination.
+
+The purpose of the "validate" supporting method is to allow the action 
arguments to be checked before actually executing the action.
+If this returns a non-null `String` (or for i18n, a non-null 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/services/i18n/TranslatableString.html#section[TranslatableString]),
 then a warning is shown and the action may not be executed.
+
+There are several variations:
+
+* the parameter argument being checked could be either a scalar or a collection
+* the supporting "validate" method can be associated with the parameter by 
number, or by name
+* the validation of a parameter might depend upon the value already selected 
for an earlier parameter.
+** A single record-like structure can be defined and this can be passed 
through to all supporting methods instead.
+** Or, the preceding N-1 parameters are supplied to the N^th^ "validate" 
supporting method.
+
+Finally, there is also the "validate" supporting method for all of the 
arguments taken together.
+
+== How this demo works
+
+On the left hand side is a list of objects representing TV characters, also 
indicating the TV show that they appeared in.
+Below is a list of selected characters, initially empty.
+
+In the middle are a set of actions, each of which can be used to select a TV 
character or characters.
+In some cases a property of the main page can be used to pre-select a value, 
in which case it will be used as the default for the action's parameter(s).
+The last parameter of each action has a validation rule that must be 
satisified.
+
+In terms of code:
+
+* this action allows a single TV character to be selected.
+It matches the supporting "choices" method with the parameter by number.
++
+[source,java]
+.ActionValidatePage_selectTvCharacterByShow.java
+----
+include::ActionValidatePage_selectTvCharacterByShow.java[tags=class]
+----
+<.> 0^th^ parameter has a supporting validation method to ensure that the 
selected argument appears in the TV show
+
+* this action demonstrates dependent validation, using a "record" object to 
group together the preceding arguments.
++
+[source,java]
+.ActionValidatePage_selectTvCharactersByShowAndSexUsingRecord.java
+----
+include::ActionValidatePage_selectTvCharactersByShowAndSexUsingRecord.java[tags=class]
+----
+<.> the "tvCharacters" parameter argument is validated and so has a supporting 
"validate" method, associated by name.
+<.> the "Parameters" record object groups together the preceding arguments, 
passed into the "validate" method
+<.> the preceding parameter values are accessed and used for the validation.
+
+* alternatively, this action implements the same behaviour but _without_ a 
"record" object, instead just passing through the N-1 arguments.
++
+[source,java]
+.ActionValidatePage_selectTvCharactersByShowAndSexUsingName.java
+----
+include::ActionValidatePage_selectTvCharactersByShowAndSexUsingName.java[tags=class]
+----
+<.> the "tvCharacters" parameter argument is validated and so has a supporting 
"validate" method, associated by name.
+<.> the N-1 arguments are passed into the "validate" method and can be used 
for validation
+<.> the argument actually being validated.
+
++
+WARNING: this version currently fails; being tracked with 
link:https://issues.apache.org/jira/browse/CAUSEWAY-3462[CAUSEWAY-3462].
+
+* the final example demonstrates validating _all_ of the arguments or an 
action together.
++
+[source,java]
+.ActionValidatePage_selectTvCharacters.java
+----
+include::ActionValidatePage_selectTvCharacters.java[tags=class]
+----
+<.> the set of parameters for the action method and the "validate" method are 
the same.
+Note also that the "validate" method is named after the action, not after a 
parameter of the action.
+<.> the validation accesses all of the parameters
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-parameterMatchingDescription.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-parameterMatchingDescription.adoc
new file mode 100644
index 0000000000..853627127f
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-parameterMatchingDescription.adoc
@@ -0,0 +1,7 @@
+:Notice: 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 ag [...]
+
+The action in this section uses a "record" structure to represent the set of 
parameters accepted by the action.
+With this coding convention it's idiomatic to associate the supporting method 
by *name* rather than number.
+
+Use the properties below to preselect a TV show and character sex.
+The chosen TV characters must all match this show/sex.
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-singleValidateDescription.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-singleValidateDescription.adoc
new file mode 100644
index 0000000000..1482dc3a7f
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage-singleValidateDescription.adoc
@@ -0,0 +1,6 @@
+:Notice: 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 ag [...]
+
+The action above illustrates how to validate a scalar (single-valued) action 
parameter.
+The validation requires that the TV character selected must appear in the TV 
show specified in the property below.
+
+The action associates the supporting "validate" methods with the parameter by 
number (`defaultNAct`).
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage.java
new file mode 100644
index 0000000000..c80427d9e8
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage.java
@@ -0,0 +1,108 @@
+/*
+ *  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 demoapp.dom.domain.actions.progmodel.validate;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.causeway.applib.annotation.Collection;
+import org.apache.causeway.applib.annotation.CollectionLayout;
+import org.apache.causeway.applib.annotation.DomainObject;
+import org.apache.causeway.applib.annotation.Editing;
+import org.apache.causeway.applib.annotation.LabelPosition;
+import org.apache.causeway.applib.annotation.Nature;
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.Optionality;
+import org.apache.causeway.applib.annotation.Property;
+import org.apache.causeway.applib.annotation.PropertyLayout;
+import org.apache.causeway.valuetypes.asciidoc.applib.value.AsciiDoc;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.resources.AsciiDocReaderService;
+import demoapp.dom.domain.actions.progmodel.TvCharacter;
+import demoapp.dom.domain.actions.progmodel.TvShow;
+
+@XmlRootElement(name = "Demo")
+@XmlType
+@XmlAccessorType(XmlAccessType.FIELD)
+@Named("demo.ActionValidate")
+@DomainObject(nature=Nature.VIEW_MODEL, editing=Editing.ENABLED)
+public class ActionValidatePage implements HasAsciiDocDescription {
+
+    @ObjectSupport public String title() {
+        return "Action Validates";
+    }
+
+
+    @Property
+    @PropertyLayout(labelPosition=LabelPosition.NONE)
+    public AsciiDoc getSingleValidateDescription() {
+        return asciiDocReaderService.readFor(this, 
"singleValidateDescription");
+    }
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @Getter @Setter
+    private TvShow selectedTvShow;
+
+    @Property
+    @PropertyLayout(labelPosition=LabelPosition.NONE)
+    public AsciiDoc getParameterMatchingDescription() {
+        return asciiDocReaderService.readFor(this, 
"parameterMatchingDescription");
+    }
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @Getter @Setter
+    private TvShow preselectTvShow2;
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @Getter @Setter
+    private TvCharacter.Sex preselectCharacterSex2;
+
+    @Property
+    @PropertyLayout(labelPosition=LabelPosition.NONE)
+    public AsciiDoc getAllArgsValidationDescription() {
+        return asciiDocReaderService.readFor(this, 
"allArgsValidationDescription");
+    }
+
+
+    @Collection
+    @CollectionLayout
+    @Getter
+    private final Set<TvCharacter> tvCharacters = new LinkedHashSet<>();
+
+    @Collection
+    @CollectionLayout
+    @Getter
+    private final Set<TvCharacter> selectedTvCharacters = new 
LinkedHashSet<>();
+
+    @Inject @XmlTransient AsciiDocReaderService asciiDocReaderService;
+
+}
+
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage.layout.xml
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage.layout.xml
new file mode 100644
index 0000000000..59c4784113
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage.layout.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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. -->
+<bs3:grid
+               
xsi:schemaLocation="https://causeway.apache.org/applib/layout/component 
https://causeway.apache.org/applib/layout/component/component.xsd   
https://causeway.apache.org/applib/layout/grid/bootstrap3 
https://causeway.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd";
+               
xmlns:bs3="https://causeway.apache.org/applib/layout/grid/bootstrap3";
+               xmlns:cpt="https://causeway.apache.org/applib/layout/component";
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+    <bs3:row>
+        <bs3:col span="10" unreferencedActions="true">
+            <cpt:domainObject />
+        </bs3:col>
+        <bs3:col span="2">
+            <cpt:fieldSet name="" id="sources" />
+        </bs3:col>
+    </bs3:row>
+       <bs3:row>
+
+               <bs3:col span="6">
+            <bs3:row>
+                <bs3:col span="12">
+                    <bs3:row>
+                        <bs3:col span="6">
+                            <cpt:collection id="tvCharacters"/>
+                            <cpt:collection id="selectedTvCharacters"/>
+                        </bs3:col>
+                        <bs3:col span="6">
+                            <cpt:fieldSet name="Single Validate" 
id="single-choices">
+                                <cpt:action id="selectTvCharacterByShow"/>
+                                <cpt:property id="singleValidateDescription"/>
+                                <cpt:property id="selectedTvShow"/>
+                            </cpt:fieldSet>
+                            <cpt:fieldSet name="Parameter Matching" 
id="parameter-matching">
+                                <cpt:action 
id="selectTvCharactersByShowAndSexUsingRecord"/>
+                                <cpt:action 
id="selectTvCharactersByShowAndSexUsingName"/>
+                                <cpt:property 
id="parameterMatchingDescription"/>
+                                <cpt:property id="preselectCharacterSex2"/>
+                                <cpt:property id="preselectTvShow2"/>
+                            </cpt:fieldSet>
+                            <cpt:fieldSet name="All args validation" 
id="all-arg-validation">
+                                <cpt:action id="selectTvCharacters"/>
+                                <cpt:property 
id="allArgsValidationDescription"/>
+                            </cpt:fieldSet>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:col>
+            </bs3:row>
+                       <cpt:fieldSet name="Other" id="other" 
unreferencedProperties="true"/>
+               </bs3:col>
+
+               <bs3:col span="6">
+                       <cpt:fieldSet name="Description" id="description" >
+                               <cpt:action id="clearHints" position="PANEL" />
+                               <cpt:action id="rebuildMetamodel" 
position="PANEL"/>
+                               <cpt:action id="downloadLayout"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="inspectMetamodel"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="downloadMetamodelXml"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="downloadJdoMetamodel"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="recentCommands"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="recentExecutions"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="recentAuditTrailEntries"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="impersonateWithRoles"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="openRestApi" 
position="PANEL_DROPDOWN" />
+                               <cpt:property id="description"/>
+                       </cpt:fieldSet>
+               </bs3:col>
+
+       </bs3:row>
+       <bs3:row>
+               <bs3:col span="12" unreferencedCollections="true"/>
+       </bs3:row>
+
+</bs3:grid>
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharacterByShow.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharacterByShow.java
new file mode 100644
index 0000000000..52d54ec7f9
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharacterByShow.java
@@ -0,0 +1,65 @@
+/*
+ *  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 demoapp.dom.domain.actions.progmodel.validate;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import org.apache.causeway.applib.annotation.Action;
+import org.apache.causeway.applib.annotation.MemberSupport;
+import org.apache.causeway.applib.annotation.Optionality;
+import org.apache.causeway.applib.annotation.Parameter;
+import org.apache.causeway.applib.annotation.SemanticsOf;
+
+import lombok.RequiredArgsConstructor;
+
+import demoapp.dom.domain.actions.progmodel.TvCharacter;
+import demoapp.dom.domain.actions.progmodel.TvShow;
+
+//tag::class[]
+@Action(semantics = SemanticsOf.IDEMPOTENT)
+@RequiredArgsConstructor
+public class ActionValidatePage_selectTvCharacterByShow {
+
+    private final ActionValidatePage page;
+
+    @MemberSupport public ActionValidatePage act(
+        @Parameter(optionality = Optionality.MANDATORY)
+        final TvCharacter tvCharacter                       // <.>
+    ) {
+        page.getSelectedTvCharacters().clear();
+        page.getSelectedTvCharacters().add(tvCharacter);
+        return page;
+    }
+
+    @MemberSupport public Collection<TvCharacter> choices0Act() {
+        return page.getTvCharacters();
+    }
+    @MemberSupport public String validate0Act(              // <1>
+        final TvCharacter tvCharacter
+    ) {
+        TvShow selectedTvShow = page.getSelectedTvShow();
+        return selectedTvShow == null ||
+               tvCharacter.getTvShow() == selectedTvShow
+                ? null
+                : "TV character doesn't appear in that show";
+    }
+}
+//end::class[]
+
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharacters.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharacters.java
new file mode 100644
index 0000000000..73de7ae7b5
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharacters.java
@@ -0,0 +1,76 @@
+/*
+ *  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 demoapp.dom.domain.actions.progmodel.validate;
+
+import demoapp.dom.domain.actions.progmodel.TvCharacter;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.causeway.applib.annotation.*;
+
+//tag::class[]
+@Action(semantics = SemanticsOf.IDEMPOTENT)
+@RequiredArgsConstructor
+public class ActionValidatePage_selectTvCharacters {
+
+    private final ActionValidatePage page;
+
+    @MemberSupport public ActionValidatePage act(                   // <.>
+        @Parameter(optionality = Optionality.MANDATORY)
+        final List<TvCharacter> maleTvCharacters,
+        @Parameter(optionality = Optionality.MANDATORY)
+        final List<TvCharacter> femaleTvCharacters
+    ) {
+        // ...
+//end::class[]
+        page.getSelectedTvCharacters().clear();
+        page.getSelectedTvCharacters().addAll(maleTvCharacters);
+        page.getSelectedTvCharacters().addAll(femaleTvCharacters);
+        return page;
+//tag::class[]
+    }
+
+    @MemberSupport public Collection<TvCharacter> choicesMaleTvCharacters() {
+        return pageTvCharacters(TvCharacter.Sex.MALE);
+    }
+    @MemberSupport public Collection<TvCharacter> choicesFemaleTvCharacters() {
+        return pageTvCharacters(TvCharacter.Sex.FEMALE);
+    }
+    @MemberSupport public String validateAct(                       // <1>
+            final List<TvCharacter> maleTvCharacters,
+            final List<TvCharacter> femaleTvCharacters
+    ) {
+        return maleTvCharacters.size() == femaleTvCharacters.size() // <.>
+                ? null
+                : "Must have the same number of male and female characters";
+    }
+    // ...
+//end::class[]
+    private List<TvCharacter> pageTvCharacters(TvCharacter.Sex sex) {
+        return page.getTvCharacters().stream()
+                .filter(x -> x.getSex() == sex).collect(Collectors.toList());
+    }
+//tag::class[]
+}
+//end::class[]
+
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharactersByShowAndSexUsingName.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharactersByShowAndSexUsingName.java
new file mode 100644
index 0000000000..3173185cb1
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharactersByShowAndSexUsingName.java
@@ -0,0 +1,90 @@
+/*
+ *  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 demoapp.dom.domain.actions.progmodel.validate;
+
+import demoapp.dom.domain.actions.progmodel.TvCharacter;
+import demoapp.dom.domain.actions.progmodel.TvShow;
+
+import lombok.RequiredArgsConstructor;
+import lombok.Value;
+import lombok.experimental.Accessors;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.causeway.applib.annotation.*;
+
+//tag::class[]
+@Action(semantics = SemanticsOf.SAFE)
+@RequiredArgsConstructor
+public class ActionValidatePage_selectTvCharactersByShowAndSexUsingName {
+
+    private final ActionValidatePage page;
+
+    @MemberSupport public ActionValidatePage act(
+        @Parameter(optionality = Optionality.MANDATORY)
+        final TvShow tvShow,
+        @Parameter(optionality = Optionality.OPTIONAL)
+        final TvCharacter.Sex sex,
+        @Parameter(optionality = Optionality.OPTIONAL)
+        final List<TvCharacter> tvCharacters                        // <.>
+    ) {
+        // ...
+//end::class[]
+        page.getSelectedTvCharacters().clear();
+        page.getSelectedTvCharacters().addAll(tvCharacters);
+        return page;
+//tag::class[]
+    }
+
+    // ...
+//end::class[]
+    @MemberSupport public TvShow defaultTvShow() {                  // <1>
+        return page.getPreselectTvShow2();
+    }
+    @MemberSupport public TvCharacter.Sex defaultSex() {            // <2>
+        return page.getPreselectCharacterSex2();
+    }
+    @MemberSupport public List<TvCharacter> choicesTvCharacters() {
+        return new ArrayList<>(page.getTvCharacters());
+    }
+//tag::class[]
+    @MemberSupport public String validateTvCharacters(              // <1>
+        final TvShow tvShowSelected,                                // <.>
+        final TvCharacter.Sex sexSelected,                          // <2>
+        final List<TvCharacter> tvCharacters                        // <.>
+    ) {
+        // ...
+//end::class[]
+        if (tvCharacters.isEmpty()) {
+            return "Must specify at least one TV character";
+        }
+        List<TvCharacter> collect = choicesTvCharacters()
+                .stream()
+                .filter(tvCharacter -> tvShowSelected == null || 
tvShowSelected == tvCharacter.getTvShow())
+                .filter(tvCharacter -> sexSelected == null || sexSelected == 
tvCharacter.getSex())
+                .collect(Collectors.toList());
+        return collect.containsAll(tvCharacters)
+                ? null
+                : "Not all of the characters were in that TV show/of the 
specified sex";
+//tag::class[]
+    }
+}
+//end::class[]
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharactersByShowAndSexUsingRecord.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharactersByShowAndSexUsingRecord.java
new file mode 100644
index 0000000000..81e5f346cb
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/progmodel/validate/ActionValidatePage_selectTvCharactersByShowAndSexUsingRecord.java
@@ -0,0 +1,99 @@
+/*
+ *  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 demoapp.dom.domain.actions.progmodel.validate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.causeway.applib.annotation.Action;
+import org.apache.causeway.applib.annotation.MemberSupport;
+import org.apache.causeway.applib.annotation.Optionality;
+import org.apache.causeway.applib.annotation.Parameter;
+import org.apache.causeway.applib.annotation.SemanticsOf;
+
+import lombok.RequiredArgsConstructor;
+import lombok.Value;
+import lombok.val;
+import lombok.experimental.Accessors;
+
+import demoapp.dom.domain.actions.progmodel.TvCharacter;
+import demoapp.dom.domain.actions.progmodel.TvShow;
+
+//tag::class[]
+@Action(semantics = SemanticsOf.SAFE)
+@RequiredArgsConstructor
+public class ActionValidatePage_selectTvCharactersByShowAndSexUsingRecord {
+
+    private final ActionValidatePage page;
+
+    @MemberSupport public ActionValidatePage act(
+        @Parameter(optionality = Optionality.MANDATORY)
+        final TvShow tvShow,
+        @Parameter(optionality = Optionality.OPTIONAL)
+        final TvCharacter.Sex sex,
+        @Parameter(optionality = Optionality.OPTIONAL)
+        final List<TvCharacter> tvCharacters                        // <.>
+    ) {
+        // ...
+//end::class[]
+        page.getSelectedTvCharacters().clear();
+        page.getSelectedTvCharacters().addAll(tvCharacters);
+        return page;
+//tag::class[]
+    }
+
+    @Value @Accessors(fluent = true)
+    static class Parameters {                                       // <.>
+        TvShow tvShow;
+        TvCharacter.Sex sex;
+        List<TvCharacter> tvCharacters;
+    }
+    // ...
+//end::class[]
+    @MemberSupport public TvShow defaultTvShow() {
+        return page.getPreselectTvShow2();
+    }
+    @MemberSupport public TvCharacter.Sex defaultSex() {
+        return page.getPreselectCharacterSex2();
+    }
+    @MemberSupport public List<TvCharacter> choicesTvCharacters() {
+        return new ArrayList<>(page.getTvCharacters());
+    }
+//tag::class[]
+    @MemberSupport public String validateTvCharacters(              // <1>
+            final Parameters params                                 // <2>
+    ) {
+        List<TvCharacter> tvCharacters = params.tvCharacters();
+        if (tvCharacters.isEmpty()) {
+            return "Must specify at least one TV character";
+        }
+        val tvShowSelected = params.tvShow();                       // <.>
+        val sexSelected = params.sex;                               // <3>
+        List<TvCharacter> collect = choicesTvCharacters()
+                .stream()
+                .filter(tvCharacter -> tvShowSelected == null || 
tvShowSelected == tvCharacter.getTvShow())
+                .filter(tvCharacter -> sexSelected == null || sexSelected == 
tvCharacter.getSex())
+                .collect(Collectors.toList());
+        return collect.containsAll(tvCharacters)
+                ? null
+                : "Not all of the characters were in that TV show/of the 
specified sex";
+    }
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml 
b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
index 6ae9c6ea2a..3cc84f93f5 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
@@ -92,6 +92,7 @@ For latest we use: 
https://raw.githubusercontent.com/apache/causeway/master/anto
                 <mb3:serviceAction objectType="demo.ActionChoicesMenu" 
id="choices"/>
                 <mb3:serviceAction objectType="demo.ActionAutoCompleteMenu" 
id="autoComplete"/>
                 <mb3:serviceAction objectType="demo.ActionDefaultsMenu" 
id="defaults"/>
+                <mb3:serviceAction objectType="demo.ActionValidateMenu" 
id="validate"/>
                 <mb3:serviceAction objectType="demo.ActionDependentArgsMenu" 
id="dependentArgsActions">
                     <cpt:named>Dependent Arguments</cpt:named>
                 </mb3:serviceAction>


Reply via email to