You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2020/07/15 06:13:18 UTC

[isis] 06/08: ISIS-2222: adds @PropertyLayout describedAs example

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

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit e06e14c4b5b1a83d4373109ef02e50d48dd90450
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jul 14 07:32:29 2020 +0100

    ISIS-2222: adds @PropertyLayout describedAs example
---
 .../PropertyLayout/PropertyLayoutMenu.java         |  34 +++--
 .../PropertyLayoutCssClassVm_annotatedMixin.java   |   7 +-
 ...yLayoutCssClassVm_updateWithMetaAnnotation.java |   3 -
 ...ClassVm_updateWithMetaAnnotationOverridden.java |   3 -
 .../describedAs/DescribedAsMetaAnnotation.java     |  25 ++++
 .../PropertyLayoutDescribedAsVm-description.adoc   | 108 ++++++++++++++++
 .../describedAs/PropertyLayoutDescribedAsVm.java   | 140 +++++++++++++++++++++
 .../PropertyLayoutDescribedAsVm.layout.xml         |  50 ++++++++
 ...ropertyLayoutDescribedAsVm_annotatedMixin.java} |  17 +--
 ...youtDescribedAsVm_updateWithMetaAnnotation.java |  36 ++++++
 ...bedAsVm_updateWithMetaAnnotationOverridden.java |  37 ++++++
 ...outDescribedAsVm_updateWithParameterLayout.java |  36 ++++++
 .../PropertyLayoutMultiLineVm_annotatedMixin.java} |  17 +--
 .../named/PropertyLayoutNamedVm-description.adoc   |   4 +-
 .../src/main/java/demoapp/dom/menubars.layout.xml  |   1 +
 15 files changed, 487 insertions(+), 31 deletions(-)

diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/PropertyLayoutMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/PropertyLayoutMenu.java
index 830483c..22b36c9 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/PropertyLayoutMenu.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/PropertyLayoutMenu.java
@@ -19,7 +19,6 @@
 package demoapp.dom.annotations.PropertyLayout;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.SemanticsOf;
@@ -27,28 +26,47 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
+import demoapp.dom.annotations.PropertyLayout.cssClass.PropertyLayoutCssClassVm;
+import demoapp.dom.annotations.PropertyLayout.describedAs.PropertyLayoutDescribedAsVm;
 import demoapp.dom.annotations.PropertyLayout.multiLine.PropertyLayoutMultiLineVm;
+import demoapp.dom.annotations.PropertyLayout.named.PropertyLayoutNamedVm;
 
 @DomainService(nature=NatureOfService.VIEW, objectType = "demo.PropertyLayoutMenu")
 @Log4j2
 public class PropertyLayoutMenu {
 
     @Action(semantics = SemanticsOf.SAFE)
-    public demoapp.dom.annotations.PropertyLayout.cssClass.PropertyLayoutCssClassVm cssClass(){
-        return new demoapp.dom.annotations.PropertyLayout.cssClass.PropertyLayoutCssClassVm();
+    public PropertyLayoutCssClassVm cssClass(){
+        return new PropertyLayoutCssClassVm();
     }
 
     @Action(semantics = SemanticsOf.SAFE)
-    public demoapp.dom.annotations.PropertyLayout.multiLine.PropertyLayoutMultiLineVm multiLine(){
+    public PropertyLayoutDescribedAsVm describedAs(){
+        return new PropertyLayoutDescribedAsVm();
+    }
+
+    @Action(semantics = SemanticsOf.SAFE)
+    public PropertyLayoutMultiLineVm multiLine(){
         val vm = new PropertyLayoutMultiLineVm();
-        vm.setPropertyUsingAnnotation("A multiline string\nspanning\n5 lines. (click me)");
-        vm.setPropertyUsingAnnotationReadOnly("A readonly string\nspanning\n5 lines. (but allows text select)");
+        vm.setPropertyUsingAnnotation(
+                "A multiline string\n" +
+                "spanning\n" +
+                "5 lines\n" +
+                "\n" +
+                "(click me to edit)\n"
+                );
+        vm.setPropertyUsingAnnotationReadOnly(
+                "A readonly string\n" +
+                "spanning\n" +
+                "5 lines\n" +
+                "\n" +
+                "(Allows text select)");
         return vm;
     }
 
     @Action(semantics = SemanticsOf.SAFE)
-    public demoapp.dom.annotations.PropertyLayout.named.PropertyLayoutNamedVm named(){
-        return new demoapp.dom.annotations.PropertyLayout.named.PropertyLayoutNamedVm();
+    public PropertyLayoutNamedVm named(){
+        return new PropertyLayoutNamedVm();
     }
 
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
index 00a8b30..b445ddd 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
@@ -27,7 +27,12 @@ import lombok.RequiredArgsConstructor;
 
 //tag::class[]
 @Property()
-@PropertyLayout(cssClass = "red", describedAs = "cssClass = \"red\" ", hidden = Where.ALL_TABLES)  
+@PropertyLayout(
+    cssClass = "red"
+    , describedAs =
+        "cssClass = \"red\" "
+    , hidden = Where.ALL_TABLES
+)
 @RequiredArgsConstructor
 public class PropertyLayoutCssClassVm_annotatedMixin {
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotation.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotation.java
index 1c12a26..be66a5a 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotation.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotation.java
@@ -8,9 +8,6 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 
 import lombok.RequiredArgsConstructor;
 
-import demoapp.dom.annotations.PropertyLayout.multiLine.MultiLineMetaAnnotation;
-import demoapp.dom.annotations.PropertyLayout.multiLine.PropertyLayoutMultiLineVm;
-
 @Action(
     semantics = SemanticsOf.IDEMPOTENT,
     associateWith = "propertyUsingMetaAnnotation", associateWithSequence = "1"
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotationOverridden.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotationOverridden.java
index 5154c9c..a105e28 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotationOverridden.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_updateWithMetaAnnotationOverridden.java
@@ -8,9 +8,6 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 
 import lombok.RequiredArgsConstructor;
 
-import demoapp.dom.annotations.PropertyLayout.multiLine.MultiLineMetaAnnotation;
-import demoapp.dom.annotations.PropertyLayout.multiLine.PropertyLayoutMultiLineVm;
-
 @Action(
     semantics = SemanticsOf.IDEMPOTENT,
     associateWith = "propertyUsingMetaAnnotationButOverridden", associateWithSequence = "1"
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/DescribedAsMetaAnnotation.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/DescribedAsMetaAnnotation.java
new file mode 100644
index 0000000..6f29a6b
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/DescribedAsMetaAnnotation.java
@@ -0,0 +1,25 @@
+package demoapp.dom.annotations.PropertyLayout.describedAs;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.PropertyLayout;
+
+//tag::class[]
+@PropertyLayout(describedAs = "Described from meta-annotation")     // <.>
+@ParameterLayout(describedAs = "Described from meta-annotation")    // <.>
+@Inherited
+@Target({
+        ElementType.METHOD, ElementType.FIELD,                      // <.>
+        ElementType.PARAMETER,                                      // <.>
+        ElementType.TYPE                                            // <.>
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DescribedAsMetaAnnotation {
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm-description.adoc
new file mode 100644
index 0000000..cefa71f
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm-description.adoc
@@ -0,0 +1,108 @@
+The description of a property or parameter is used as the tooltip of the field in the UI, providing more information to the end-user:
+
+* for properties, the description can be specified using either the `@PropertyLayout` annotation, or a meta-annotation, or using the `Xxx.layout.xml` layout file.
+
+* for parameters, the description can be specified using either the `@ParameterLayout` annotation, or a meta-annotation.
+
+
+
+== Annotation
+
+The description can be specified explicitly using an annotation:
+
+* for properties, using `@PropertyLayout#describedAs()`:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm.java[tags=annotation]
+----
+<.> explicitly specifies the description of the property
+
+* for parameters, using `@ParameterLayout#describedAs()`:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm_updateWithParameterLayout.java[tags=annotation]
+----
+<.> explicitly specifies the description of the parameter
+
+== Layout File
+
+For properties (not parameters), the description can be specified using the `Xxx.layout.xml` file:
+
+* property definition:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm.java[tags=layout-file]
+----
+<.> `describedAs` attribute is _not_ present.
+
+* layout file snippet:
++
+[source,xml,indent=0,tabsize=4]
+----
+<cpt:property id="propertyUsingLayout">
+    <cpt:describedAs>Described using layout.xml</cpt:describedAs>
+</cpt:property>
+----
+// TODO: for some reason this doesn't include correctly in the app...
+//include::PropertyLayoutDescribedAsVm.layout.xml[tags=layout-file]
+
+One advantage of the layout file over the annotation is that the layout file is dynamic: it can be modified and the changes reloaded in a running application.
+
+TIP: It is sometimes necessary to invoke the `Rebuild Metamodel` action to see this take effect.
+
+== Meta-annotations
+
+The name can also be specified using a custom meta-annotation:
+
+* the meta-annotation is defined as:
++
+[source,java]
+----
+include::DescribedAsMetaAnnotation.java[tags=class]
+----
+<.> annotated for property ...
+<.> \... and also for parameter.
+<.> annotation can be applied to a property (either its getter or if using link:https://projectlombok.org/[Lombok] the field directly)
+<.> annotation can be applied to a parameter
+<.> annotation can be applied to a type (ie, meta-meta annotations recursively)
+
+* and can be applied to a property:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm.java[tags=meta-annotated]
+----
+<.> property's description is inherited from the meta-annotation
+
+* or can be applied to a parameter:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm_updateWithMetaAnnotation.java[tags=meta-annotation]
+----
+<.> parameter's description is inherited from the meta-annotation
+
+The meta-annotation can itself be overridden:
+
+* in a property:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm.java[tags=meta-annotated-overridden]
+----
+<.> description from meta-annotation ...
+<.> \... is overridden by the `@PropertyLayout` annotation
+
+* or a parameter:
++
+[source,java,indent=0]
+----
+include::PropertyLayoutDescribedAsVm_updateWithMetaAnnotationOverridden.java[tags=meta-annotation-overridden]
+----
+<.> description from meta-annotation ...
+<.> \... is overridden by the `@ParameterLayout` annotation
+
+
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm.java
new file mode 100644
index 0000000..c9254ce
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm.java
@@ -0,0 +1,140 @@
+/*
+ *  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.annotations.PropertyLayout.describedAs;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+
+//tag::class[]
+@XmlRootElement(name = "root")
+@XmlType
+@XmlAccessorType(XmlAccessType.FIELD)
+@DomainObject(
+        nature=Nature.VIEW_MODEL,
+        objectType = "demo.PropertyLayoutDescribedAsVm",
+        editing = Editing.ENABLED
+)
+public class PropertyLayoutDescribedAsVm implements HasAsciiDocDescription {
+
+    public String title() {
+        return "PropertyLayout#describedAs";
+    }
+
+//tag::annotation[]
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(
+        describedAs =
+            "@PropertyLayout(describedAs= \"...\")"
+        , hidden = Where.ALL_TABLES
+    )
+    @MemberOrder(name = "properties", sequence = "1")
+    @XmlElement(required = false)
+    @Getter @Setter
+    private String propertyUsingAnnotation;
+//end::annotation[]
+
+//tag::layout-file[]
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(                                        // <.>
+        hidden = Where.ALL_TABLES
+    )
+    @MemberOrder(name = "properties", sequence = "2")
+    @XmlElement(required = false)
+    @Getter @Setter
+    private String propertyUsingLayout;
+//end::layout-file[]
+
+//tag::meta-annotated[]
+    @Property(optionality = Optionality.OPTIONAL)
+    @DescribedAsMetaAnnotation                            // <.>
+    @PropertyLayout(
+        describedAs = "@DescribedAsMetaAnnotation"
+        , hidden = Where.ALL_TABLES
+    )
+    @MemberOrder(name = "meta-annotated", sequence = "1")
+    @XmlElement(required = false)
+    @Getter @Setter
+    private String propertyUsingMetaAnnotation;
+//end::meta-annotated[]
+
+//tag::meta-annotated-overridden[]
+    @Property(optionality = Optionality.OPTIONAL)
+    @DescribedAsMetaAnnotation                                  // <.>
+    @PropertyLayout(
+        describedAs =
+            "@DescribedAsMetaAnnotation @PropertyLayout(...)"   // <.>
+        , hidden = Where.ALL_TABLES
+    )
+    @MemberOrder(name = "meta-annotated", sequence = "2")
+    @XmlElement(required = false)
+    @Getter @Setter
+    private String propertyUsingMetaAnnotationButOverridden;
+//end::meta-annotated-overridden[]
+
+    //tag::markup[]
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(
+        named = "Named <b>uses</b> <i>markup</i>",          // <.>
+        namedEscaped = false                                // <.>
+        , describedAs =
+            "@PropertyLayout(named= \"...\", namedEscaped=false)"
+        , hidden = Where.ALL_TABLES
+    )
+    @MemberOrder(name = "markup", sequence = "1")
+    @XmlElement(required = false)
+    @Getter @Setter
+    private String propertyUsingMarkup;
+//end::markup[]
+
+    //tag::markup-escaped[]
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(
+        named = "Named <b>but</b> <i>escaped</i>",          // <.>
+        namedEscaped = true                                 // <.>
+        , describedAs =
+            "@PropertyLayout(" +
+            "named = \"...\", namedEscaped=true)"
+        , hidden = Where.ALL_TABLES
+    )
+    @MemberOrder(name = "markup", sequence = "2")
+    @XmlElement(required = false)
+    @Getter @Setter
+    private String propertyUsingEscapedMarkup;
+//end::markup-escaped[]
+
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm.layout.xml
new file mode 100644
index 0000000..f4bbb29
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm.layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<bs3:grid
+        xsi:schemaLocation="http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd   http://isis.apache.org/applib/layout/grid/bootstrap3 http://isis.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd"
+        xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3"
+        xmlns:cpt="http://isis.apache.org/applib/layout/component"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+	<bs3:row>
+		<bs3:col span="10" unreferencedActions="true">
+			<cpt:domainObject />
+			<cpt:action id="rebuildMetamodel"/>
+		</bs3:col>
+		<bs3:col span="2">
+			<cpt:fieldSet name="" id="sources" />
+		</bs3:col>
+	</bs3:row>
+
+	<bs3:row>
+		<bs3:col span="6">
+			<cpt:fieldSet name="Properties" id="properties">
+				<cpt:property id="propertyUsingAnnotation"/>
+<!-- tag::layout-file[] -->
+				<cpt:property id="propertyUsingLayout">
+					<cpt:describedAs>Described using layout.xml</cpt:describedAs>
+				</cpt:property>
+<!-- end::layout-file[] -->
+			</cpt:fieldSet>
+			<cpt:fieldSet name="Meta-annotated" id="meta-annotated"/>
+			<cpt:fieldSet name="Contributed" id="contributed"/>
+			<cpt:fieldSet name="Markup" id="markup"/>
+			<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="downloadLayoutXml"  position="PANEL_DROPDOWN"/>
+				<cpt:action id="rebuildMetamodel" position="PANEL_DROPDOWN"/>
+				<cpt:action id="downloadMetamodelXml"  position="PANEL_DROPDOWN"/>
+				<cpt:action id="inspectMetamodel"  position="PANEL_DROPDOWN"/>
+				<cpt:action id="downloadJdoMetadata"  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/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_annotatedMixin.java
similarity index 68%
copy from examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
copy to examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_annotatedMixin.java
index 00a8b30..2895d8a 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_annotatedMixin.java
@@ -16,26 +16,29 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.annotations.PropertyLayout.cssClass;
+package demoapp.dom.annotations.PropertyLayout.describedAs;
 
 import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
 import org.apache.isis.applib.annotation.Where;
 
 import lombok.RequiredArgsConstructor;
 
 //tag::class[]
-@Property()
-@PropertyLayout(cssClass = "red", describedAs = "cssClass = \"red\" ", hidden = Where.ALL_TABLES)  
+//@Property() // TODO: ISIS-2405: this fails as attempts to invoke as an action, and heuristics replace null with the view model object.
+@PropertyLayout(
+    describedAs =
+            "Mixin described using @PropertyLayout(describedAs = \"...\")"
+    , hidden = Where.ALL_TABLES
+)
 @RequiredArgsConstructor
-public class PropertyLayoutCssClassVm_annotatedMixin {
+public class PropertyLayoutDescribedAsVm_annotatedMixin {
 
-    private final PropertyLayoutCssClassVm propertyLayoutCssClassVm;
+    private final PropertyLayoutDescribedAsVm propertyLayoutDescribedAsVm;
 
     @MemberOrder(name = "contributed", sequence = "1")
     public String prop() {
-        return propertyLayoutCssClassVm.getPropertyUsingAnnotation();
+        return propertyLayoutDescribedAsVm.getPropertyUsingAnnotation();
     }
 
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithMetaAnnotation.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithMetaAnnotation.java
new file mode 100644
index 0000000..2e29580
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithMetaAnnotation.java
@@ -0,0 +1,36 @@
+package demoapp.dom.annotations.PropertyLayout.describedAs;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(
+    semantics = SemanticsOf.IDEMPOTENT,
+    associateWith = "propertyUsingMetaAnnotation", associateWithSequence = "1"
+)
+@RequiredArgsConstructor
+public class PropertyLayoutDescribedAsVm_updateWithMetaAnnotation {
+
+    private final PropertyLayoutDescribedAsVm propertyLayoutDescribedAsVm;
+
+//tag::meta-annotation[]
+    public PropertyLayoutDescribedAsVm act(
+            @Parameter(optionality = Optionality.OPTIONAL)
+            @DescribedAsMetaAnnotation                            // <.>
+            @ParameterLayout(
+                describedAs = "@DescribedAsMetaAnnotation"
+            )
+            final String newValue) {
+        propertyLayoutDescribedAsVm.setPropertyUsingMetaAnnotation(newValue);
+        return propertyLayoutDescribedAsVm;
+    }
+//end::meta-annotation[]
+    public String default0Act() {
+        return propertyLayoutDescribedAsVm.getPropertyUsingMetaAnnotation();
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithMetaAnnotationOverridden.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithMetaAnnotationOverridden.java
new file mode 100644
index 0000000..cb047fa
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithMetaAnnotationOverridden.java
@@ -0,0 +1,37 @@
+package demoapp.dom.annotations.PropertyLayout.describedAs;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(
+    semantics = SemanticsOf.IDEMPOTENT,
+    associateWith = "propertyUsingMetaAnnotationButOverridden", associateWithSequence = "1"
+)
+@RequiredArgsConstructor
+public class PropertyLayoutDescribedAsVm_updateWithMetaAnnotationOverridden {
+
+    private final PropertyLayoutDescribedAsVm propertyLayoutDescribedAsVm;
+
+//tag::meta-annotation-overridden[]
+    public PropertyLayoutDescribedAsVm act(
+            @Parameter(optionality = Optionality.OPTIONAL)
+            @DescribedAsMetaAnnotation                                      // <.>
+            @ParameterLayout(
+                describedAs =                                               // <.>
+                    "@DescribedAsMetaAnnotation @ParameterLayout(...)"
+            )
+            final String newValue) {
+        propertyLayoutDescribedAsVm.setPropertyUsingMetaAnnotationButOverridden(newValue);
+        return propertyLayoutDescribedAsVm;
+    }
+//end::meta-annotation-overridden[]
+    public String default0Act() {
+        return propertyLayoutDescribedAsVm.getPropertyUsingMetaAnnotationButOverridden();
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithParameterLayout.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithParameterLayout.java
new file mode 100644
index 0000000..fa766d9
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/describedAs/PropertyLayoutDescribedAsVm_updateWithParameterLayout.java
@@ -0,0 +1,36 @@
+package demoapp.dom.annotations.PropertyLayout.describedAs;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(
+    semantics = SemanticsOf.IDEMPOTENT,
+    associateWith = "propertyUsingAnnotation", associateWithSequence = "1"
+)
+@RequiredArgsConstructor
+public class PropertyLayoutDescribedAsVm_updateWithParameterLayout {
+
+    private final PropertyLayoutDescribedAsVm propertyLayoutDescribedAsVm;
+
+//tag::annotation[]
+    public PropertyLayoutDescribedAsVm act(
+            @Parameter(optionality = Optionality.OPTIONAL)
+            @ParameterLayout(
+                describedAs =
+                    "@ParameterLayout(describedAs = \"...\")"   // <.>
+            )
+            final String newValue) {
+        propertyLayoutDescribedAsVm.setPropertyUsingAnnotation(newValue);
+        return propertyLayoutDescribedAsVm;
+    }
+//end::annotation[]
+    public String default0Act() {
+        return propertyLayoutDescribedAsVm.getPropertyUsingAnnotation();
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/multiLine/PropertyLayoutMultiLineVm_annotatedMixin.java
similarity index 69%
copy from examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
copy to examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/multiLine/PropertyLayoutMultiLineVm_annotatedMixin.java
index 00a8b30..51c15fe 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/cssClass/PropertyLayoutCssClassVm_annotatedMixin.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/multiLine/PropertyLayoutMultiLineVm_annotatedMixin.java
@@ -16,26 +16,29 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.annotations.PropertyLayout.cssClass;
+package demoapp.dom.annotations.PropertyLayout.multiLine;
 
 import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
 import org.apache.isis.applib.annotation.Where;
 
 import lombok.RequiredArgsConstructor;
 
 //tag::class[]
-@Property()
-@PropertyLayout(cssClass = "red", describedAs = "cssClass = \"red\" ", hidden = Where.ALL_TABLES)  
+//@Property() // TODO: ISIS-2405: this fails as attempts to invoke as an action, and heuristics replace null with the view model object.
+@PropertyLayout(
+    multiLine = 5
+    , describedAs = "@PropertyLayout(multiLine = 5)"
+    , hidden = Where.ALL_TABLES
+)
 @RequiredArgsConstructor
-public class PropertyLayoutCssClassVm_annotatedMixin {
+public class PropertyLayoutMultiLineVm_annotatedMixin {
 
-    private final PropertyLayoutCssClassVm propertyLayoutCssClassVm;
+    private final PropertyLayoutMultiLineVm propertyLayoutMultiLineVm;
 
     @MemberOrder(name = "contributed", sequence = "1")
     public String prop() {
-        return propertyLayoutCssClassVm.getPropertyUsingAnnotation();
+        return propertyLayoutMultiLineVm.getPropertyUsingAnnotation();
     }
 
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/named/PropertyLayoutNamedVm-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/named/PropertyLayoutNamedVm-description.adoc
index cf890b7..032a6f5 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/named/PropertyLayoutNamedVm-description.adoc
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotations/PropertyLayout/named/PropertyLayoutNamedVm-description.adoc
@@ -22,7 +22,7 @@ The name can be specified explicitly using an annotation:
 ----
 include::PropertyLayoutNamedVm.java[tags=annotation]
 ----
-<.> explicitly specifies name of the property
+<.> explicitly specifies the name of the property
 
 * for parameters, using `@ParameterLayout#named()`:
 +
@@ -30,7 +30,7 @@ include::PropertyLayoutNamedVm.java[tags=annotation]
 ----
 include::PropertyLayoutNamedVm_updateWithParameterLayout.java[tags=annotation]
 ----
-<.> explicitly specifies name of the parameter
+<.> explicitly specifies the name of the parameter
 
 == Layout File
 
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 df6d811..ffef338 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
@@ -122,6 +122,7 @@ as a replacement for
             <mb3:section>
                 <mb3:named>@PropertyLayout</mb3:named>
                 <mb3:serviceAction objectType="demo.PropertyLayoutMenu" id="cssClass"/>
+                <mb3:serviceAction objectType="demo.PropertyLayoutMenu" id="describedAs"/>
                 <mb3:serviceAction objectType="demo.PropertyLayoutMenu" id="multiLine"/>
                 <mb3:serviceAction objectType="demo.PropertyLayoutMenu" id="named"/>
             </mb3:section>