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 2019/01/09 07:25:58 UTC

[isis] 03/04: ISIS-2081: adds @DomainObjectLayout(layoutUiEvent=...)

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 c4bb64e11c187ed15f0f4f6da21b7f2e525b1dac
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jan 8 17:54:10 2019 +0000

    ISIS-2081: adds @DomainObjectLayout(layoutUiEvent=...)
    
    ... similar to TitleUiEvent, IconUiEvent and ClassCssUiEvent.  This means that subscribers can switch the layout of a domain object.
---
 .../_rgant-DomainObjectLayout_cssClassUiEvent.adoc |   2 +-
 .../_rgant-DomainObjectLayout_iconUiEvent.adoc     |   2 +-
 ...> _rgant-DomainObjectLayout_layoutUiEvent.adoc} |  36 +++---
 .../org/apache/isis/applib/IsisApplibModule.java   |   2 +
 .../isis/applib/annotation/DomainObjectLayout.java |  15 ++-
 .../isis/applib/annotation/ViewModelLayout.java    |   4 +-
 .../isis/applib/domain/DomainObjectList.java       |   4 +-
 .../applib/services/eventbus/LayoutUiEvent.java    | 102 +++++++++++++++++
 .../DomainObjectLayoutFacetFactory.java            |   3 +
 ...inObjectLayoutAnnotationUsingLayoutUiEvent.java | 123 +++++++++++++++++++++
 10 files changed, 268 insertions(+), 25 deletions(-)

diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc
index 20d5da0..c44e31e 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc
@@ -111,6 +111,6 @@ And, conversely, the framework also provides `CssClassUiEvent.Noop`; if `cssClas
 == Raising events programmatically
 
 Normally events are only raised for interactions through the UI.
-However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or as a result of calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_DomainObjectContainer[`DomainObjectContainer`]'s `cssClassOf(...)` method.
+However, events can be raised programmatically by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly.
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc
index 854d822..24bdad2 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_iconUiEvent.adoc
@@ -106,6 +106,6 @@ And, conversely, the framework also provides `IconUiEvent.Noop`; if `iconUiEvent
 == Raising events programmatically
 
 Normally events are only raised for interactions through the UI.
-However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or as a result of calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_DomainObjectContainer[`DomainObjectContainer`]'s `iconNameOf(...)` method.
+However, events can be raised programmatically by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly.
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_layoutUiEvent.adoc
similarity index 65%
copy from adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc
copy to adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_layoutUiEvent.adoc
index 20d5da0..07eed8e 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_cssClassUiEvent.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObjectLayout_layoutUiEvent.adoc
@@ -1,13 +1,13 @@
-[[_rgant-DomainObjectLayout_cssClassUiEvent]]
-= cssClassUiEvent()
+[[_rgant-DomainObjectLayout_layoutUiEvent]]
+= layoutUiEvent()
 :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 [...]
 :_basedir: ../../
 :_imagesdir: images/
 
 
 Whenever a domain object is to be rendered, the framework fires off an CSS class UI event to obtain a CSS class to use in any wrapping ``<div>``s and ``<span>``s that render the domain object.
-This is as an alternative to implementing xref:../rgcms/rgcms.adoc#_rgcms_methods_reserved_cssClass[`cssClass()`] reserved method.
-(If `cssClass()` is present, then it will take precedence).
+This is as an alternative to implementing xref:../rgcms/rgcms.adoc#_rgcms_methods_reserved_layout[`layout()`] reserved method.
+(If `layout()` is present, then it will take precedence).
 
 Subscribers subscribe through the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] and can use obtain a reference to the domain object from the event.
 From this they can, if they wish, specify a CSS class for the domain object using the event's API.
@@ -18,7 +18,7 @@ The feature was originally introduced so that xref:../rgant/rgant.adoc#_rgant-Xm
 UI events allow subscribers to provide UI hints, while xref:../ugfun/ugfun.adoc#_ugfun_building-blocks_types-of-domain-objects_mixins[mixin]s can be used to provide the behaviour.
 ====
 
-By default the event raised is `CssClassUiEvent.Default`.
+By default the event raised is `LayoutUiEvent.Default`.
 For example:
 
 [source,java]
@@ -29,7 +29,7 @@ public class ToDoItemDto {
 }
 ----
 
-The purpose of the `cssClassUiEvent()` attribute is to allows a custom subclass to be emitted instead.
+The purpose of the `layoutUiEvent()` attribute is to allows a custom subclass to be emitted instead.
 A similar attribute is available for titles and icons.
 
 For example:
@@ -37,11 +37,11 @@ For example:
 [source,java]
 ----
 @DomainObjectLayout(
-    iconUiEvent=ToDoItemDto.CssClassUiEvent.class
+    iconUiEvent=ToDoItemDto.LayoutUiEvent.class
 )
 public class ToDoItemDto {
-    public static class CssClassUiEvent
-        extends org.apache.isis.applib.services.eventbus.CssClassUiEvent<ToDoItemDto> { }
+    public static class LayoutUiEvent
+        extends org.apache.isis.applib.services.eventbus.LayoutUiEvent<ToDoItemDto> { }
     ...
 }
 ----
@@ -66,7 +66,7 @@ Subscribers can be either coarse-grained (if they subscribe to the top-level eve
 public class SomeSubscriber extends AbstractSubscriber {
     @org.axonframework.eventhandling.annotation.EventHandler // if using axon
     @com.google.common.eventbus.Subscribe                    // if using guava
-    public void on(CssClassUiEvent ev) {
+    public void on(LayoutUiEvent ev) {
         if(ev.getSource() instanceof ToDoItemDto) { ... }
     }
 }
@@ -80,13 +80,13 @@ or can be fine-grained (by subscribing to specific event subtypes):
 public class SomeSubscriber extends AbstractSubscriber {
     @org.axonframework.eventhandling.annotation.EventHandler // if using axon
     @com.google.common.eventbus.Subscribe                    // if using guava
-    public void on(ToDoItemDto.CssClassUiEvent ev) {
+    public void on(ToDoItemDto.LayoutUiEvent ev) {
         ...
     }
 }
 ----
 
-The subscriber should then use `CssClassUiEvent#setCssClass(...)` to actually specify the CSS class to be used.
+The subscriber should then use `LayoutUiEvent#setLayout(...)` to actually specify the CSS class to be used.
 
 
 
@@ -94,14 +94,14 @@ The subscriber should then use `CssClassUiEvent#setCssClass(...)` to actually sp
 
 == Default, Doop and Noop events
 
-If the `cssClassUiEvent` attribute is not explicitly specified (is left as its default value, `CssClassUiEvent.Default`), then the framework will, by default, post an event.
+If the `layoutUiEvent` attribute is not explicitly specified (is left as its default value, `LayoutUiEvent.Default`), then the framework will, by default, post an event.
 
-If this is not required, then the `isis.reflector.facet.domainObjectLayoutAnnotation.cssClassUiEvent.postForDefault` configuration property can be set to "false"; this will disable posting.
+If this is not required, then the `isis.reflector.facet.domainObjectLayoutAnnotation.layoutUiEvent.postForDefault` configuration property can be set to "false"; this will disable posting.
 
-On the other hand, if the `cssClassUiEvent` has been explicitly specified to some subclass, then an event will be posted.
-The framework provides `CssClassUiEvent.Doop` as such a subclass, so setting the `cssClassUiEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.
+On the other hand, if the `layoutUiEvent` has been explicitly specified to some subclass, then an event will be posted.
+The framework provides `LayoutUiEvent.Doop` as such a subclass, so setting the `layoutUiEvent` attribute to this class will ensure that the event to be posted, irrespective of the configuration property setting.
 
-And, conversely, the framework also provides `CssClassUiEvent.Noop`; if `cssClassUiEvent` attribute is set to this class, then no event will be posted.
+And, conversely, the framework also provides `LayoutUiEvent.Noop`; if `layoutUiEvent` attribute is set to this class, then no event will be posted.
 
 
 
@@ -111,6 +111,6 @@ And, conversely, the framework also provides `CssClassUiEvent.Noop`; if `cssClas
 == Raising events programmatically
 
 Normally events are only raised for interactions through the UI.
-However, events can be raised programmatically either by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly, or as a result of calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_DomainObjectContainer[`DomainObjectContainer`]'s `cssClassOf(...)` method.
+However, events can be raised programmatically by calling the xref:../rgsvc/rgsvc.adoc#_rgsvc_core-domain-api_EventBusService[`EventBusService`] API directly.
 
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/IsisApplibModule.java b/core/applib/src/main/java/org/apache/isis/applib/IsisApplibModule.java
index 20771a0..e2955b9 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/IsisApplibModule.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/IsisApplibModule.java
@@ -32,6 +32,8 @@ public class IsisApplibModule extends ModuleAbstract {
             extends org.apache.isis.applib.services.eventbus.IconUiEvent<S> { }
     public abstract static class CssClassUiEvent<S>
             extends org.apache.isis.applib.services.eventbus.CssClassUiEvent<S> { }
+    public abstract static class LayoutUiEvent<S>
+            extends org.apache.isis.applib.services.eventbus.LayoutUiEvent<S> { }
     //endregion
 
     //region > domain event classes
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObjectLayout.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObjectLayout.java
index 84a8fca..dfaf5be 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObjectLayout.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObjectLayout.java
@@ -27,6 +27,7 @@ import java.lang.annotation.Target;
 
 import org.apache.isis.applib.services.eventbus.CssClassUiEvent;
 import org.apache.isis.applib.services.eventbus.IconUiEvent;
+import org.apache.isis.applib.services.eventbus.LayoutUiEvent;
 import org.apache.isis.applib.services.eventbus.TitleUiEvent;
 
 /**
@@ -69,6 +70,17 @@ public @interface DomainObjectLayout {
      */
     Class<? extends CssClassUiEvent<?>> cssClassUiEvent() default CssClassUiEvent.Default.class;
 
+    // //////////////////////////////////////
+
+    /**
+     * Which subclass of {@link LayoutUiEvent} should be used to obtain a layout.
+     *
+     * <p>
+     * This subclass must provide a no-arg constructor; the fields are set reflectively.
+     * </p>
+     */
+    Class<? extends LayoutUiEvent<?>> layoutUiEvent() default LayoutUiEvent.Default.class;
+
     /**
      * Indicates the css class that a domain class (type) should have.
      */
@@ -89,13 +101,14 @@ public @interface DomainObjectLayout {
      * <p>
      *     This attribute is currently ignored by Isis viewers.
      * </p>
-s     */
+     */
     CssClassFaPosition cssClassFaPosition() default CssClassFaPosition.LEFT;
 
     enum CssClassFaPosition {
         LEFT, RIGHT
     }
     
+
     // //////////////////////////////////////
 
     /**
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/ViewModelLayout.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/ViewModelLayout.java
index bbc0c22..626497c 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/ViewModelLayout.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/ViewModelLayout.java
@@ -58,8 +58,6 @@ public @interface ViewModelLayout {
      */
     String cssClassFa() default "";
 
-    // //////////////////////////////////////
-
     /**
      * Indicates the position of the <a href="http://fortawesome.github.io/Font-Awesome/">Font Awesome</a>
      * icon. The icon could be rendered on the left or the right of the object's title.
@@ -105,7 +103,7 @@ public @interface ViewModelLayout {
      * If annotated on a type, then the page size refers to standalone
      * collections (eg as returned from a repository query).
      */
-    public int paged() default -1;
+    int paged() default -1;
 
 
     // //////////////////////////////////////
diff --git a/core/applib/src/main/java/org/apache/isis/applib/domain/DomainObjectList.java b/core/applib/src/main/java/org/apache/isis/applib/domain/DomainObjectList.java
index 34e485b..8c4b4c2 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/domain/DomainObjectList.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/domain/DomainObjectList.java
@@ -56,7 +56,8 @@ import org.apache.isis.schema.utils.jaxbadapters.PersistentEntitiesAdapter;
 @DomainObjectLayout(
         titleUiEvent = DomainObjectList.TitleUiEvent.class,
         iconUiEvent = DomainObjectList.IconUiEvent.class,
-        cssClassUiEvent = DomainObjectList.CssClassUiEvent.class
+        cssClassUiEvent = DomainObjectList.CssClassUiEvent.class,
+        layoutUiEvent = DomainObjectList.LayoutUiEvent.class
 )
 public class DomainObjectList {
 
@@ -64,6 +65,7 @@ public class DomainObjectList {
     public static class TitleUiEvent extends IsisApplibModule.TitleUiEvent<DomainObjectList>{}
     public static class IconUiEvent extends IsisApplibModule.IconUiEvent<DomainObjectList>{}
     public static class CssClassUiEvent extends IsisApplibModule.CssClassUiEvent<DomainObjectList>{}
+    public static class LayoutUiEvent extends IsisApplibModule.LayoutUiEvent<DomainObjectList>{}
     //endregion
 
     //region > domain event classes
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/LayoutUiEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/LayoutUiEvent.java
new file mode 100644
index 0000000..0ab989d
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/LayoutUiEvent.java
@@ -0,0 +1,102 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services.eventbus;
+
+import java.util.EventObject;
+
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+
+/**
+ * Emitted for subscribers to obtain a layout hint (equivalent to the <tt>layout()</tt> supporting method).
+ */
+public abstract class LayoutUiEvent<S> extends AbstractUiEvent<S> {
+
+    private static final long serialVersionUID = 1L;
+
+    //region > constructors
+    /**
+     * If used then the framework will set state via (non-API) setters.
+     *
+     * <p>
+     *     Because the {@link EventObject} superclass prohibits a null source, a dummy value is temporarily used.
+     * </p>
+     */
+    public LayoutUiEvent() {
+        this(null);
+    }
+
+    public LayoutUiEvent(final S source) {
+        super(source);
+    }
+
+    //endregion
+
+    //region > Default class
+    /**
+     * This class is the default for the
+     * {@link DomainObjectLayout#layoutUiEvent()} annotation attribute.  Whether this
+     * raises an event or not depends upon the "isis.reflector.facet.domainObjectLayoutAnnotation.layoutUiEvent.postForDefault"
+     * configuration property.
+     */
+    public static class Default extends LayoutUiEvent<Object> {
+        private static final long serialVersionUID = 1L;
+    }
+    //endregion
+
+    //region > Noop class
+
+    /**
+     * Convenience class to use indicating that an event should <i>not</i> be posted (irrespective of the configuration
+     * property setting for the {@link Default} event.
+     */
+    public static class Noop extends LayoutUiEvent<Object> {
+        private static final long serialVersionUID = 1L;
+    }
+    //endregion
+
+    //region > Doop class
+
+    /**
+     * Convenience class meaning that an event <i>should</i> be posted (irrespective of the configuration
+     * property setting for the {@link Default} event..
+     */
+    public static class Doop extends LayoutUiEvent<Object> {
+        private static final long serialVersionUID = 1L;
+    }
+    //endregion
+
+    //region > layout
+    private String layout;
+
+    /**
+     * The name of the alternate layout to use, as provided by a subscriber using {@link #setLayout(String)}.
+     */
+    public String getLayout() {
+        return layout;
+    }
+
+    /**
+     * For subscribers to call to provide a layout for this object.
+     */
+    public void setLayout(final String layout) {
+        this.layout = layout;
+    }
+    //endregion
+
+}
\ No newline at end of file
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFacetFactory.java
index bbeac06..7c1280a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFacetFactory.java
@@ -49,6 +49,9 @@ public class DomainObjectLayoutFacetFactory extends FacetFactoryAbstract {
         FacetUtil.addFacet(
                 CssClassFacetViaDomainObjectLayoutAnnotationUsingCssClassUiEvent.create(
                         domainObjectLayout, servicesInjector, getConfiguration(), facetHolder));
+        FacetUtil.addFacet(
+                LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent.create(
+                        domainObjectLayout, servicesInjector, getConfiguration(), facetHolder));
 
         FacetUtil.addFacet(
                 CssClassFacetForDomainObjectLayoutAnnotation.create(domainObjectLayout, facetHolder));
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent.java
new file mode 100644
index 0000000..b2bd9bd
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobjectlayout/LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.metamodel.facets.object.domainobjectlayout;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.isis.applib.NonRecoverableException;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.services.eventbus.EventBusService;
+import org.apache.isis.applib.services.eventbus.LayoutUiEvent;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.layout.LayoutFacet;
+import org.apache.isis.core.metamodel.facets.object.layout.LayoutFacetAbstract;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.metamodel.util.EventUtil;
+
+public class LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent extends FacetAbstract implements
+        LayoutFacet {
+
+    private static final Logger LOG = LoggerFactory.getLogger(
+            LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent.class);
+
+    public static Facet create(
+            final DomainObjectLayout domainObjectLayout,
+            final ServicesInjector servicesInjector,
+            final IsisConfiguration configuration, final FacetHolder facetHolder) {
+        if(domainObjectLayout == null) {
+            return null;
+        }
+        final Class<? extends LayoutUiEvent<?>> layoutUiEventClass = domainObjectLayout.layoutUiEvent();
+
+        if(!EventUtil.eventTypeIsPostable(
+                layoutUiEventClass,
+                LayoutUiEvent.Noop.class,
+                LayoutUiEvent.Default.class,
+                "isis.reflector.facet.domainObjectLayoutAnnotation.layoutUiEvent.postForDefault",
+                configuration)) {
+            return null;
+        }
+
+        final EventBusService eventBusService = servicesInjector.lookupService(EventBusService.class);
+
+        return new LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent(
+                layoutUiEventClass, eventBusService, facetHolder);
+    }
+
+    private final Class<? extends LayoutUiEvent<?>> layoutUiEventClass;
+    private final EventBusService eventBusService;
+
+    public LayoutFacetViaDomainObjectLayoutAnnotationUsingLayoutUiEvent(
+            final Class<? extends LayoutUiEvent<?>> layoutUiEventClass,
+            final EventBusService eventBusService,
+            final FacetHolder holder) {
+        super(LayoutFacetAbstract.type(), holder, Derivation.NOT_DERIVED);
+        this.layoutUiEventClass = layoutUiEventClass;
+        this.eventBusService = eventBusService;
+    }
+
+    @Override
+    public String layout(final ObjectAdapter owningAdapter) {
+
+        final LayoutUiEvent<Object> layoutUiEvent = newLayoutUiEvent(owningAdapter);
+
+        eventBusService.post(layoutUiEvent);
+
+        final String layout = layoutUiEvent.getLayout();
+
+        if(layout == null) {
+            // ie no subscribers out there...
+            final Facet underlyingFacet = getUnderlyingFacet();
+            if(underlyingFacet instanceof LayoutFacet) {
+                final LayoutFacet underlyingLayoutFacet = (LayoutFacet) underlyingFacet;
+                return underlyingLayoutFacet.layout(owningAdapter);
+            }
+        }
+
+        return layout;
+    }
+
+    private LayoutUiEvent<Object> newLayoutUiEvent(final ObjectAdapter owningAdapter) {
+        final Object domainObject = owningAdapter.getObject();
+        return newLayoutUiEvent(domainObject);
+    }
+
+    private LayoutUiEvent<Object> newLayoutUiEvent(final Object domainObject) {
+        try {
+            final LayoutUiEvent<Object> layoutUiEvent = (LayoutUiEvent<Object>) layoutUiEventClass.newInstance();
+            layoutUiEvent.setSource(domainObject);
+            return layoutUiEvent;
+        } catch (InstantiationException | IllegalAccessException ex) {
+            throw new NonRecoverableException(ex);
+        }
+    }
+
+    @Override public void appendAttributesTo(final Map<String, Object> attributeMap) {
+        super.appendAttributesTo(attributeMap);
+        attributeMap.put("layoutUiEventClass", layoutUiEventClass);
+    }
+}