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 56d1c22d302311c92b6eb01309df5337c71c7c33
Author: Dan Haywood <[email protected]>
AuthorDate: Mon Aug 1 19:02:30 2022 +0100

    ISIS-3104: moves Object_clearHints to own module
    
    also add a bunch of regression tests for layout.
---
 core/pom.xml                                       |   5 +
 examples/demo/wicket/common/pom.xml                |   4 +
 regressiontests/pom.xml                            |   1 +
 regressiontests/stable-layouts/log4j2-test.xml     |  78 +++
 regressiontests/stable-layouts/pom.xml             |  87 +++
 .../layouts/integtest/Layout_IntegTest.java        | 780 +++++++++++++++++++++
 .../layouts/integtest/model/Counter.java           | 227 ++++++
 .../layouts/integtest/model/Counter.layout.xml     |  72 ++
 .../model/Counter_updateNameUsingMixin.java        |  37 +
 .../integtest/model/LayoutTestDomainModel.java     |  24 +
 viewers/wicket/{ => applib}/pom.xml                |  41 +-
 .../applib/IsisModuleViewerWicketApplibMixins.java |  39 ++
 .../wicket/applib}/mixins/Object_clearHints.java   |  25 +-
 viewers/wicket/pom.xml                             |   5 +-
 .../viewer/IsisModuleViewerWicketViewer.java       |   4 -
 15 files changed, 1392 insertions(+), 37 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 3a6a4507ec..f43d342c73 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -627,6 +627,11 @@
                                
<artifactId>isis-viewer-restfulobjects-jaxrsresteasy4</artifactId>
                                <version>2.0.0-SNAPSHOT</version>
                        </dependency>
+                       <dependency>
+                               <groupId>org.apache.isis.viewer</groupId>
+                               
<artifactId>isis-viewer-wicket-applib</artifactId>
+                               <version>2.0.0-SNAPSHOT</version>
+                       </dependency>
                        <dependency>
                                <groupId>org.apache.isis.viewer</groupId>
                                
<artifactId>isis-viewer-wicket-model</artifactId>
diff --git a/examples/demo/wicket/common/pom.xml 
b/examples/demo/wicket/common/pom.xml
index 0205522416..3d35b0fc9d 100644
--- a/examples/demo/wicket/common/pom.xml
+++ b/examples/demo/wicket/common/pom.xml
@@ -70,6 +70,10 @@
                        <artifactId>isis-viewer-wicket-ui</artifactId>
                </dependency>
 
+               <dependency>
+                       <groupId>org.apache.isis.viewer</groupId>
+                       <artifactId>isis-viewer-wicket-applib</artifactId>
+               </dependency>
                <dependency>
                        <groupId>org.apache.isis.viewer</groupId>
                        <artifactId>isis-viewer-wicket-viewer</artifactId>
diff --git a/regressiontests/pom.xml b/regressiontests/pom.xml
index 999079fce4..ab2b35ad81 100644
--- a/regressiontests/pom.xml
+++ b/regressiontests/pom.xml
@@ -295,6 +295,7 @@
                <module>stable-cucumber</module>
                <module>stable-domainmodel</module>
                <module>stable-eventhandling</module>
+               <module>stable-layouts</module>
                <module>stable-factory</module>
                <module>stable-interact</module>
                <module>stable-persistence-jdo</module>
diff --git a/regressiontests/stable-layouts/log4j2-test.xml 
b/regressiontests/stable-layouts/log4j2-test.xml
new file mode 100644
index 0000000000..0ed7986e24
--- /dev/null
+++ b/regressiontests/stable-layouts/log4j2-test.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<Configuration status="WARN">
+       <Properties>
+               <Property name="PID">????</Property>
+               <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
+               <Property name="LOG_LEVEL_PATTERN">%5p</Property>
+               <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd 
HH:mm:ss.SSS</Property>
+               <Property 
name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} 
%clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} 
%clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} 
%m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+               <Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} 
${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : 
%m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+       </Properties>
+       <Appenders>
+               <Console name="Console" target="SYSTEM_OUT" follow="true">
+                       <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
+               </Console>
+       </Appenders>
+       <Loggers>
+               <Logger name="org.apache.catalina.startup.DigesterFactory" 
level="error" />
+               <Logger name="org.apache.catalina.util.LifecycleBase" 
level="error" />
+               <Logger name="org.apache.coyote.http11.Http11NioProtocol" 
level="warn" />
+               <logger name="org.apache.sshd.common.util.SecurityUtils" 
level="warn"/>
+               <Logger name="org.apache.tomcat.util.net.NioSelectorPool" 
level="warn" />
+               <Logger 
name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
+               <Logger name="org.hibernate.validator.internal.util.Version" 
level="warn" />
+               <logger name="org.springframework.boot.actuate.endpoint.jmx" 
level="warn"/>
+               
+               <logger name="org.apache.directory" level="warn"/>
+               <logger name="org.apache.directory.api.ldap.model.entry.Value" 
level="off"/>
+               
+               <logger name="DataNucleus.Persistence" level="info"/>
+               <logger name="DataNucleus.Transaction" level="info"/>
+               <logger name="DataNucleus.Datastore.Schema" level="info"/>
+               <logger name="DataNucleus.Datastore.Native" level="info"/>
+               
+               <Root level="info">
+                       <AppenderRef ref="Console" />
+               </Root>
+       </Loggers>
+</Configuration>
+
+<!-- # DataNucleus Logging Categories -->
+<!-- DataNucleus.Persistence - All messages relating to the persistence 
process -->
+<!-- DataNucleus.Transaction - All messages relating to transactions -->
+<!-- DataNucleus.Connection - All messages relating to Connections. -->
+<!-- DataNucleus.Query - All messages relating to queries -->
+<!-- DataNucleus.Cache - All messages relating to the DataNucleus Cache -->
+<!-- DataNucleus.MetaData - All messages relating to MetaData -->
+<!-- DataNucleus.Datastore - All general datastore messages -->
+<!-- DataNucleus.Datastore.Schema - All schema related datastore log messages 
-->
+<!-- DataNucleus.Datastore.Persist - All datastore persistence messages -->
+<!-- DataNucleus.Datastore.Retrieve - All datastore retrieval messages -->
+<!-- DataNucleus.Datastore.Native - Log of all 'native' statements sent to the 
datastore -->
+<!-- DataNucleus.General - All general operational messages -->
+<!-- DataNucleus.Lifecycle - All messages relating to object lifecycle changes 
-->
+<!-- DataNucleus.ValueGeneration - All messages relating to value generation 
-->
+<!-- DataNucleus.Enhancer - All messages from the DataNucleus Enhancer. -->
+<!-- DataNucleus.SchemaTool - All messages from DataNucleus SchemaTool -->
+<!-- DataNucleus.JDO - All messages general to JDO -->
+<!-- DataNucleus.JPA - All messages general to JPA -->
+<!-- DataNucleus.JCA - All messages relating to Connector JCA. -->
+<!-- DataNucleus.IDE - Messages from the DataNucleus IDE. -->
\ No newline at end of file
diff --git a/regressiontests/stable-layouts/pom.xml 
b/regressiontests/stable-layouts/pom.xml
new file mode 100644
index 0000000000..7c7828abfe
--- /dev/null
+++ b/regressiontests/stable-layouts/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.regressiontests</groupId>
+        <artifactId>isis-regressiontests</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>isis-regressiontests-stable-layouts</artifactId>
+    <name>Apache Isis - Regression Tests (stable) - Layouts</name>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.isis.extensions</groupId>
+                <artifactId>isis-extensions</artifactId>
+                <version>2.0.0-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.isis.testing</groupId>
+                <artifactId>isis-testing</artifactId>
+                <version>2.0.0-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.isis.viewer</groupId>
+                <artifactId>isis-viewer-wicket</artifactId>
+                <version>2.0.0-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.isis.regressiontests</groupId>
+                
<artifactId>isis-regressiontests-cmdexecauditsess-generic</artifactId>
+                <version>2.0.0-SNAPSHOT</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-applib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.viewer</groupId>
+            <artifactId>isis-viewer-wicket-applib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-config</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.testing</groupId>
+            <artifactId>isis-testing-fixtures-applib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-runtimeservices</artifactId>
+        </dependency>
+
+
+    </dependencies>
+
+</project>
diff --git 
a/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/Layout_IntegTest.java
 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/Layout_IntegTest.java
new file mode 100644
index 0000000000..cca8034282
--- /dev/null
+++ 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/Layout_IntegTest.java
@@ -0,0 +1,780 @@
+/*
+ *  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.regressiontests.layouts.integtest;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionException;
+import org.springframework.transaction.TransactionStatus;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.apache.isis.applib.IsisModuleApplibMixins;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.id.LogicalType;
+import org.apache.isis.applib.layout.LayoutConstants;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.metamodel.Config;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+import org.apache.isis.core.config.beans.IsisBeanTypeRegistry;
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import 
org.apache.isis.core.metamodel.facets.actions.position.ActionPositionFacet;
+import 
org.apache.isis.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
+import 
org.apache.isis.core.metamodel.facets.members.layout.order.LayoutOrderFacet;
+import org.apache.isis.core.metamodel.spec.feature.MixedIn;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.regressiontests.layouts.integtest.model.Counter;
+import 
org.apache.isis.regressiontests.layouts.integtest.model.LayoutTestDomainModel;
+import org.apache.isis.schema.metamodel.v2.Action;
+import org.apache.isis.schema.metamodel.v2.DomainClassDto;
+import org.apache.isis.schema.metamodel.v2.FacetAttr;
+import org.apache.isis.schema.metamodel.v2.MetamodelDto;
+import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
+import 
org.apache.isis.testing.integtestsupport.applib.IsisIntegrationTestAbstract;
+import org.apache.isis.viewer.wicket.applib.IsisModuleViewerWicketApplibMixins;
+
+import lombok.val;
+
+@SpringBootTest(
+        classes = Layout_IntegTest.AppManifest.class
+)
+@ActiveProfiles("test")
+public class Layout_IntegTest extends IsisIntegrationTestAbstract {
+
+    @SpringBootConfiguration
+    @EnableAutoConfiguration
+    @Import({
+            IsisModuleApplibMixins.class,
+            IsisModuleViewerWicketApplibMixins.class,
+            IsisModuleCoreRuntimeServices.class,
+            IsisModuleSecurityBypass.class,
+    })
+    @PropertySources({
+            @PropertySource(IsisPresets.UseLog4j2Test)
+    })
+    @ComponentScan(basePackageClasses = {AppManifest.class, 
LayoutTestDomainModel.class})
+    public static class AppManifest {
+
+        @Bean
+        @Singleton
+        public PlatformTransactionManager platformTransactionManager() {
+            return new PlatformTransactionManager() {
+
+                @Override
+                public void rollback(final TransactionStatus status) throws 
TransactionException {
+                }
+
+                @Override
+                public TransactionStatus getTransaction(final 
TransactionDefinition definition) throws TransactionException {
+                    return null;
+                }
+
+                @Override
+                public void commit(final TransactionStatus status) throws 
TransactionException {
+                }
+            };
+        }
+
+    }
+
+    @BeforeAll
+    static void beforeAll() {
+        IsisPresets.forcePrototyping();
+    }
+
+    Bookmark target1;
+
+    @BeforeEach
+    void beforeEach() {
+        interactionService.nextInteraction();
+
+        Optional<Bookmark> bookmark = 
bookmarkService.bookmarkFor(newCounter("counter-1"));
+        target1 = bookmark.orElseThrow();
+
+        interactionService.nextInteraction();
+
+    }
+
+    protected Counter newCounter(String name) {
+        return Counter.builder().name(name).build();
+    }
+
+
+    @Test
+    void actionNoPosition() {
+
+        // given
+        val objectSpecification = 
specificationLoader.loadSpecification(Counter.class);
+
+        // when
+        val action = lookupAction("actionNoPosition");
+
+        // then
+        assertThat(action).isNotNull();
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNull())
+        ;
+    }
+
+    @Test
+    void actionPositionBelow() {
+
+        // given
+        val action = lookupAction("actionPositionBelow");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNull())
+        ;
+    }
+
+    @Test
+    void actionPositionPanel() {
+
+        // given
+        val action = lookupAction("actionPositionPanel");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNull())
+        ;
+    }
+
+    @Test
+    void actionDetailsFieldSetNoPosition() {
+
+        // given
+        val action = lookupAction("actionDetailsFieldSetNoPosition");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNotNull())
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))
+        ;
+    }
+
+    @Test
+    void actionDetailsFieldSetPositionBelow() {
+
+        // given
+        val action = lookupAction("actionDetailsFieldSetPositionBelow");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("2"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNotNull())
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))
+        ;
+    }
+
+    @Test
+    void actionDetailsFieldSetPositionPanel() {
+
+        // given
+        val action = lookupAction("actionDetailsFieldSetPositionPanel");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("3"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNotNull())
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))
+        ;
+    }
+
+    @Test
+    void actionDetailsFieldSetPositionPanelDropdown() {
+
+        // given
+        val action = 
lookupAction("actionDetailsFieldSetPositionPanelDropdown");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL_DROPDOWN));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("4"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> assertThat(f).isNotNull())
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))
+        ;
+    }
+
+    @Test
+    void actionEmptyFieldSetNoPosition() {
+
+        // given
+        val action = lookupAction("actionEmptyFieldSetNoPosition");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("empty"))
+        ;
+    }
+
+    @Test
+    void actionEmptyFieldSetPositionBelow() {
+
+        // given
+        val action = lookupAction("actionEmptyFieldSetPositionBelow");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("2"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("empty"))
+        ;
+    }
+
+    @Test
+    void actionEmptyFieldSetPositionPanel() {
+
+        // given
+        val action = lookupAction("actionEmptyFieldSetPositionPanel");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("3"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("empty"))
+        ;
+    }
+
+    @Test
+    void actionEmptyFieldSetPositionPanelDropdown() {
+
+        // given
+        val action = lookupAction("actionEmptyFieldSetPositionPanelDropdown");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL_DROPDOWN));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("4"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("empty"))
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyNoPosition() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyNoPosition");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("name"))  // 
TODO: ?? strange, because 'name' is not a fieldset; should be 'details' ??
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyBelow() {
+
+        // given
+        val action = lookupAction("actionAssociatedWithNamePropertyBelow");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("name"))  // 
TODO: ?? strange, because 'name' is not a fieldset; should be 'details' ??
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyPanel() {
+
+        // given
+        val action = lookupAction("actionAssociatedWithNamePropertyPanel");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("name"))  // 
TODO: ?? strange, because 'name' is not a fieldset; should be 'details' ??
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyPanelDropdown() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyPanelDropdown");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL_DROPDOWN));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo(""))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("name"))  // 
TODO: ?? strange, because 'name' is not a fieldset; should be 'details' ??
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyAndDetailsFieldSetNoPosition() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyAndDetailsFieldSetNoPosition");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNameAndDetailsFieldSetPropertyBelow() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNameAndDetailsFieldSetPropertyBelow");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("2"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyAndDetailsFieldSetPanel() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyAndDetailsFieldSetPanel");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("3"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))    
// because "name" is in this fieldSet
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyAndDetailsFieldSetPanelDropdown() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyAndDetailsFieldSetPanelDropdown");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL_DROPDOWN));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("4"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("details"))    
// because "name" is in this fieldSet
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyButEmptyFieldSetNoPosition() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyButEmptyFieldSetNoPosition");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("empty"))    
// overrides the 'associateWith' ???
+        ;
+    }
+
+    @Test
+    void actionAssociatedWithNamePropertyAndSequenceNoPosition() {
+
+        // given
+        val action = 
lookupAction("actionAssociatedWithNamePropertyAndSequenceNoPosition");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.FALLBACK))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.BELOW));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo("name"))  // 
TODO: ?? strange, because 'name' is not a fieldset
+        ;
+    }
+
+    @Test
+    void openRestApi() {
+
+        // given
+        val action = lookupAction("openRestApi");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL_DROPDOWN));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("750.1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo(LayoutConstants.FieldSetId.METADATA))
+        ;
+    }
+
+    @Test
+    void clearHints() {
+
+        // given
+        val action = lookupAction("clearHints");
+
+        // when, then
+        List<Facet> facets = 
action.getFacetHolder().streamFacets().collect(Collectors.toList());
+
+        val actionPositionFacet = action.getFacet(ActionPositionFacet.class);
+        assertThat(actionPositionFacet)
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(ActionPositionFacet::position).isEqualTo(ActionLayout.Position.PANEL));
+
+        val layoutOrderFacet = action.getFacet(LayoutOrderFacet.class);
+        assertThat(layoutOrderFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getPrecedence).isEqualTo(Facet.Precedence.DEFAULT))
+                .satisfies(f -> 
assertThat(f).extracting(LayoutOrderFacet::getSequence).isEqualTo("400.1"))
+        ;
+
+        val layoutGroupFacet = action.getFacet(LayoutGroupFacet.class);
+        assertThat(layoutGroupFacet)
+                .satisfies(f -> 
assertThat(f).extracting(LayoutGroupFacet::getGroupId).isEqualTo(LayoutConstants.FieldSetId.METADATA))
+        ;
+    }
+
+    private ObjectAction lookupAction(String id) {
+        val objectSpecification = 
specificationLoader.loadSpecification(Counter.class);
+        List<ObjectAction> objectActions = 
objectSpecification.streamAnyActions(MixedIn.INCLUDED).collect(Collectors.toList());
+        return objectSpecification.streamAnyActions(MixedIn.INCLUDED).filter(x 
-> x.getId().equals(id)).findFirst().orElseThrow();
+    }
+
+
+    private void extracted(Class<?> cls) {
+        LogicalType logicalType = 
metaModelService.lookupLogicalTypeByClass(cls).orElseThrow();
+        MetamodelDto metamodelDto = metaModelService.exportMetaModel(new 
Config().withNamespacePrefix("layouts.test."));
+        Map<String, DomainClassDto> metaModelDtoById = 
metamodelDto.getDomainClassDto().stream().collect(Collectors.toMap(DomainClassDto::getId,
 Function.identity()));
+        DomainClassDto domainClassDto = 
metaModelDtoById.get(cls.getCanonicalName());
+        Map<String, Action> actionById = 
domainClassDto.getActions().getAct().stream().collect(Collectors.toMap(Action::getId,
 Function.identity()));
+        List<org.apache.isis.schema.metamodel.v2.Facet> facets = 
actionById.get("updateNameUsingDeclaredAction").getFacets().getFacet();
+        Map<String, org.apache.isis.schema.metamodel.v2.Facet> facetById = 
facets.stream().collect(Collectors.toMap(org.apache.isis.schema.metamodel.v2.Facet::getId,
 Function.identity()));
+        Map<String, String> facetAttrByName = 
facetById.get(LayoutGroupFacet.class.getCanonicalName()).getAttr().stream().collect(Collectors.toMap(FacetAttr::getName,
 FacetAttr::getValue));
+        facetAttrByName.get("Name");
+    }
+
+
+    @Inject InteractionService interactionService;
+    @Inject MetaModelService metaModelService;
+    @Inject SpecificationLoader specificationLoader;
+    @Inject BookmarkService bookmarkService;
+
+    @Inject IsisBeanTypeRegistry isisBeanTypeRegistry;
+
+}
diff --git 
a/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter.java
 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter.java
new file mode 100644
index 0000000000..ab6f5e8837
--- /dev/null
+++ 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter.java
@@ -0,0 +1,227 @@
+/*
+ *  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.regressiontests.layouts.integtest.model;
+
+import javax.inject.Named;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.layout.LayoutConstants;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@XmlRootElement()
+@XmlAccessorOrder
+@Named("layouts.test.Counter")
+@DomainObject(nature = Nature.VIEW_MODEL)
+@NoArgsConstructor
+@Builder
+@AllArgsConstructor
+public class Counter implements Comparable<Counter> {
+
+    @PropertyLayout(fieldSetId = LayoutConstants.FieldSetId.IDENTITY)
+    @Getter @Setter
+    private String name;
+
+    @PropertyLayout(fieldSetId = LayoutConstants.FieldSetId.DETAILS)
+    @Getter @Setter
+    private Long num;
+
+    @PropertyLayout(fieldSetId = LayoutConstants.FieldSetId.IDENTITY)
+    @Getter @Setter
+    private String propertyInFieldSetIdIdentity;
+
+    @PropertyLayout(fieldSetId = LayoutConstants.FieldSetId.DETAILS)
+    @Getter @Setter
+    private String propertyInFieldSetIdDetails;
+
+    @Action()
+    @ActionLayout()
+    public Counter actionNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(position = ActionLayout.Position.BELOW)
+    public Counter actionPositionBelow(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(position = ActionLayout.Position.PANEL)
+    public Counter actionPositionPanel(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(position = ActionLayout.Position.PANEL_DROPDOWN)
+    public Counter actionPositionPanelDropdown(String newName) {
+        return doUpdateName(newName);
+    }
+
+
+    // with details fieldset (which contains at least one property) + sequence
+
+    @Action()
+    @ActionLayout(fieldSetId = LayoutConstants.FieldSetId.DETAILS, sequence = 
"1")
+    public Counter actionDetailsFieldSetNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(fieldSetId = LayoutConstants.FieldSetId.DETAILS, sequence = 
"2", position = ActionLayout.Position.BELOW)
+    public Counter actionDetailsFieldSetPositionBelow(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(fieldSetId = LayoutConstants.FieldSetId.DETAILS, sequence = 
"3", position = ActionLayout.Position.PANEL)
+    public Counter actionDetailsFieldSetPositionPanel(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(fieldSetId = LayoutConstants.FieldSetId.DETAILS, sequence = 
"4", position = ActionLayout.Position.PANEL_DROPDOWN)
+    public Counter actionDetailsFieldSetPositionPanelDropdown(String newName) {
+        return doUpdateName(newName);
+    }
+
+
+    // with empty fieldset (that has no properties) + sequence
+
+    @Action()
+    @ActionLayout(fieldSetId = "empty", sequence = "1")
+    public Counter actionEmptyFieldSetNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(fieldSetId = "empty", sequence = "2", position = 
ActionLayout.Position.BELOW)
+    public Counter actionEmptyFieldSetPositionBelow(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(fieldSetId = "empty", sequence = "3", position = 
ActionLayout.Position.PANEL)
+    public Counter actionEmptyFieldSetPositionPanel(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(fieldSetId = "empty", sequence = "4", position = 
ActionLayout.Position.PANEL_DROPDOWN)
+    public Counter actionEmptyFieldSetPositionPanelDropdown(String newName) {
+        return doUpdateName(newName);
+    }
+
+
+    // with associateWith
+
+    @Action()
+    @ActionLayout(associateWith = "name")
+    public Counter actionAssociatedWithNamePropertyNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(associateWith = "name", position = 
ActionLayout.Position.BELOW)
+    public Counter actionAssociatedWithNamePropertyBelow(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(associateWith = "name", position = 
ActionLayout.Position.PANEL)
+    public Counter actionAssociatedWithNamePropertyPanel(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(associateWith = "name", position = 
ActionLayout.Position.PANEL_DROPDOWN)
+    public Counter actionAssociatedWithNamePropertyPanelDropdown(String 
newName) {
+        return doUpdateName(newName);
+    }
+
+
+    // with associateWith + correct fieldSet for the associated property
+
+    @Action()
+    @ActionLayout(associateWith = "name", fieldSetId = "details", sequence = 
"1")
+    public Counter 
actionAssociatedWithNamePropertyAndDetailsFieldSetNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(associateWith = "name", fieldSetId = "details", sequence = 
"2", position = ActionLayout.Position.BELOW)
+    public Counter 
actionAssociatedWithNameAndDetailsFieldSetPropertyBelow(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(associateWith = "name", fieldSetId = "details", sequence = 
"3", position = ActionLayout.Position.PANEL)
+    public Counter 
actionAssociatedWithNamePropertyAndDetailsFieldSetPanel(String newName) {
+        return doUpdateName(newName);
+    }
+
+    @Action()
+    @ActionLayout(associateWith = "name", fieldSetId = "details", sequence = 
"4", position = ActionLayout.Position.PANEL_DROPDOWN)
+    public Counter 
actionAssociatedWithNamePropertyAndDetailsFieldSetPanelDropdown(String newName) 
{
+        return doUpdateName(newName);
+    }
+
+
+    // with associateWith + incorrect fieldSet for the associated property
+
+    @Action()
+    @ActionLayout(associateWith = "name", fieldSetId = "empty", sequence = "1")
+    public Counter 
actionAssociatedWithNamePropertyButEmptyFieldSetNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+
+    // associateWith + only the sequence
+
+    @Action()
+    @ActionLayout(associateWith = "name", sequence = "1")
+    public Counter 
actionAssociatedWithNamePropertyAndSequenceNoPosition(String newName) {
+        return doUpdateName(newName);
+    }
+
+
+
+    Counter doUpdateName(String newName) {
+        setName(newName);
+        return this;
+    }
+
+    @Override
+    public int compareTo(final Counter o) {
+        return this.getName().compareTo(o.getName());
+    }
+}
diff --git 
a/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter.layout.xml
 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter.layout.xml
new file mode 100644
index 0000000000..9ac67c27bc
--- /dev/null
+++ 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter.layout.xml
@@ -0,0 +1,72 @@
+<?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="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:c="http://isis.apache.org/applib/layout/component"; 
xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+
+    <bs3:row>
+        <bs3:col span="12" unreferencedActions="true">
+            <c:domainObject bookmarking="AS_ROOT"/>
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="4">
+            <bs3:row>
+                <bs3:col span="12">
+                    <bs3:tabGroup>
+                        <bs3:tab name="Identity">
+                            <bs3:row>
+                                <bs3:col span="12">
+                                    <c:fieldSet name="Identity" id="identity"/>
+                                </bs3:col>
+                            </bs3:row>
+                        </bs3:tab>
+                        <bs3:tab name="Other">
+                            <bs3:row>
+                                <bs3:col span="12">
+                                    <c:fieldSet name="Other" id="other" 
unreferencedProperties="true"/>
+                                </bs3:col>
+                            </bs3:row>
+                        </bs3:tab>
+                        <bs3:tab name="Metadata">
+                            <bs3:row>
+                                <bs3:col span="12">
+                                    <c:fieldSet name="Metadata" id="metadata"/>
+                                </bs3:col>
+                            </bs3:row>
+                        </bs3:tab>
+                    </bs3:tabGroup>
+                </bs3:col>
+            </bs3:row>
+            <bs3:row>
+                <bs3:col span="12">
+                    <c:fieldSet name="Details" id="details"/>
+                </bs3:col>
+            </bs3:row>
+            <bs3:row>
+                <bs3:col span="12">
+                    <c:fieldSet name="Empty FieldSet" id="empty"/>
+                </bs3:col>
+            </bs3:row>
+        </bs3:col>
+        <bs3:col span="8">
+            <bs3:tabGroup  unreferencedCollections="true">
+            </bs3:tabGroup>
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git 
a/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter_updateNameUsingMixin.java
 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter_updateNameUsingMixin.java
new file mode 100644
index 0000000000..e3cee6dd93
--- /dev/null
+++ 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/Counter_updateNameUsingMixin.java
@@ -0,0 +1,37 @@
+/*
+ *  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.regressiontests.layouts.integtest.model;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.services.wrapper.Mixin;
+
+import lombok.RequiredArgsConstructor;
+
+@Action()
+@RequiredArgsConstructor
+public class Counter_updateNameUsingMixin implements Mixin<Counter> {
+
+    private final Counter counter;
+
+    public Counter act(String name) {
+        return counter.doUpdateName(name);
+    }
+}
diff --git 
a/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/LayoutTestDomainModel.java
 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/LayoutTestDomainModel.java
new file mode 100644
index 0000000000..79eb334070
--- /dev/null
+++ 
b/regressiontests/stable-layouts/src/main/java/org/apache/isis/regressiontests/layouts/integtest/model/LayoutTestDomainModel.java
@@ -0,0 +1,24 @@
+/*
+ *  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.regressiontests.layouts.integtest.model;
+
+public class LayoutTestDomainModel {
+}
diff --git a/viewers/wicket/pom.xml b/viewers/wicket/applib/pom.xml
similarity index 57%
copy from viewers/wicket/pom.xml
copy to viewers/wicket/applib/pom.xml
index 1d12d0295e..177f0bacd4 100644
--- a/viewers/wicket/pom.xml
+++ b/viewers/wicket/applib/pom.xml
@@ -7,9 +7,9 @@
   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
@@ -22,22 +22,35 @@
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
-        <groupId>org.apache.isis.core</groupId>
-        <artifactId>isis-core</artifactId>
+        <groupId>org.apache.isis.viewer</groupId>
+        <artifactId>isis-viewer-wicket</artifactId>
         <version>2.0.0-SNAPSHOT</version>
-        <relativePath>../../core/pom.xml</relativePath>
     </parent>
 
-    <groupId>org.apache.isis.viewer</groupId>
-    <artifactId>isis-viewer-wicket</artifactId>
-    <name>Apache Isis Viewer - Wicket</name>
+    <artifactId>isis-viewer-wicket-applib</artifactId>
+    <name>Apache Isis Viewer - Wicket (Applib)</name>
+
+    <properties>
+        
<jar-plugin.automaticModuleName>org.apache.isis.viewer.wicket.applib</jar-plugin.automaticModuleName>
+        
<git-plugin.propertiesDir>org/apache/isis/viewer/wicket/applib</git-plugin.propertiesDir>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-applib</artifactId>
+        </dependency>
+
+               <!-- TESTING -->
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-internaltestsupport</artifactId>
+            <scope>test</scope>
+        </dependency>
 
-    <packaging>pom</packaging>
+    </dependencies>
 
-    <modules>
-        <module>model</module>
-        <module>viewer</module>
-        <module>ui</module>
-    </modules>
 
 </project>
diff --git 
a/viewers/wicket/applib/src/main/java/org/apache/isis/viewer/wicket/applib/IsisModuleViewerWicketApplibMixins.java
 
b/viewers/wicket/applib/src/main/java/org/apache/isis/viewer/wicket/applib/IsisModuleViewerWicketApplibMixins.java
new file mode 100644
index 0000000000..16125e90cf
--- /dev/null
+++ 
b/viewers/wicket/applib/src/main/java/org/apache/isis/viewer/wicket/applib/IsisModuleViewerWicketApplibMixins.java
@@ -0,0 +1,39 @@
+/*
+ *  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.viewer.wicket.applib;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import org.apache.isis.viewer.wicket.applib.mixins.Object_clearHints;
+
+
+/**
+ * @since 1.x {@index}
+ */
+@Configuration
+@Import({
+
+        // @Mixin's
+        Object_clearHints.class,
+
+})
+public class IsisModuleViewerWicketApplibMixins {
+
+}
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/mixins/Object_clearHints.java
 
b/viewers/wicket/applib/src/main/java/org/apache/isis/viewer/wicket/applib/mixins/Object_clearHints.java
similarity index 80%
rename from 
viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/mixins/Object_clearHints.java
rename to 
viewers/wicket/applib/src/main/java/org/apache/isis/viewer/wicket/applib/mixins/Object_clearHints.java
index c5f5a4fd37..f98fe618d0 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/mixins/Object_clearHints.java
+++ 
b/viewers/wicket/applib/src/main/java/org/apache/isis/viewer/wicket/applib/mixins/Object_clearHints.java
@@ -15,11 +15,14 @@
  *  KIND, either express or implied.  See the License for the
  *  specific language governing permissions and limitations
  *  under the License.
+ *
  */
-package org.apache.isis.viewer.wicket.viewer.mixins;
+package org.apache.isis.viewer.wicket.applib.mixins;
 
 import javax.inject.Inject;
 
+import org.springframework.beans.factory.annotation.Autowired;
+
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.MemberSupport;
@@ -28,10 +31,8 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.layout.LayoutConstants;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.hint.HintStore;
-import 
org.apache.isis.viewer.wicket.viewer.services.HintStoreUsingWicketSession;
 
 import lombok.RequiredArgsConstructor;
-import lombok.val;
 
 /**
  * Provides the ability for the end-user to discard UI hints so that the
@@ -71,7 +72,7 @@ import lombok.val;
 @RequiredArgsConstructor
 public class Object_clearHints {
 
-    @Inject HintStore hintStore;
+    @Autowired(required = false) HintStore hintStore;
     @Inject BookmarkService bookmarkService;
 
     private final Object holder;
@@ -79,26 +80,16 @@ public class Object_clearHints {
     public static class ActionDomainEvent
         extends org.apache.isis.applib.events.domain.ActionDomainEvent<Object> 
{}
     @MemberSupport public Object act() {
-        if (getHintStoreUsingWicketSession() != null) {
-            val bookmark = bookmarkService.bookmarkForElseFail(holder);
-            val hintStore = getHintStoreUsingWicketSession();
-            if(hintStore!=null) { // just in case
-                hintStore.removeAll(bookmark);
-            }
+        if (hintStore != null) {
+            bookmarkService.bookmarkFor(holder).ifPresent(bookmark -> 
this.hintStore.removeAll(bookmark));
         }
         return holder;
     }
 
     @MemberSupport public boolean hideAct() {
-        return getHintStoreUsingWicketSession() == null;
+        return hintStore == null;
     }
 
     // -- HELPER
 
-    private HintStoreUsingWicketSession getHintStoreUsingWicketSession() {
-        return hintStore instanceof HintStoreUsingWicketSession
-                ? (HintStoreUsingWicketSession) hintStore
-                : null;
-    }
-
 }
diff --git a/viewers/wicket/pom.xml b/viewers/wicket/pom.xml
index 1d12d0295e..00228165fd 100644
--- a/viewers/wicket/pom.xml
+++ b/viewers/wicket/pom.xml
@@ -7,9 +7,9 @@
   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
@@ -35,6 +35,7 @@
     <packaging>pom</packaging>
 
     <modules>
+        <module>applib</module>
         <module>model</module>
         <module>viewer</module>
         <module>ui</module>
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisModuleViewerWicketViewer.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisModuleViewerWicketViewer.java
index a3dacf59a8..bc8f7d2124 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisModuleViewerWicketViewer.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisModuleViewerWicketViewer.java
@@ -22,7 +22,6 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
 import org.apache.isis.viewer.wicket.ui.IsisModuleViewerWicketUi;
-import org.apache.isis.viewer.wicket.viewer.mixins.Object_clearHints;
 import 
org.apache.isis.viewer.wicket.viewer.registries.components.ComponentFactoryRegistrarDefault;
 import 
org.apache.isis.viewer.wicket.viewer.registries.components.ComponentFactoryRegistryDefault;
 import 
org.apache.isis.viewer.wicket.viewer.registries.pages.PageClassListDefault;
@@ -72,9 +71,6 @@ import 
org.apache.isis.viewer.wicket.viewer.wicketapp.config.WicketViewerCssBund
         WebModuleWicket.class,
         WicketViewerSettingsDefault.class,
 
-        // @Mixin's
-        Object_clearHints.class,
-
 })
 public class IsisModuleViewerWicketViewer {
 

Reply via email to