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 2021/06/10 13:38:26 UTC

[isis] branch ISIS-2442 updated (31ccd3f -> 0460f57)

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

danhaywood pushed a change to branch ISIS-2442
in repository https://gitbox.apache.org/repos/asf/isis.git.


 discard 31ccd3f  ISIS-2442: improves base subdomain docs a little more
 discard 1305649  ISIS-2442: adds docs for base subdomain; convert types to meta-annotations
     add e93e59d  ISIS-2727: remove Authentication from 'security'
     add fe9d58e  ISIS-2727: remove AnonymousInteractionFactory
     add 7a606bf  ISIS-2727: java-doc fixes
     add 42d29aa  ISIS-2727: remove AuthenticationProvider
     new be2f793  ISIS-2442: adds docs for base subdomain; convert types to meta-annotations
     new 58ab199  ISIS-2442: improves base subdomain docs a little more
     new 856bf05  ISIS-2442: ports docx to latest version of docx4j, and removes guava dependency
     new 0460f57  ISIS-2442: updates docs for docx

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (31ccd3f)
            \
             N -- N -- N   refs/heads/ISIS-2442 (0460f57)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../comguide/modules/ROOT/pages/starter-apps.adoc  |   4 +-
 .../pages/index/docx/applib/DocxService.adoc       |  40 ++
 .../docx/applib/DocxService~MergeDefinition.adoc   |  11 +-
 .../org/apache/isis/applib/clock/VirtualClock.java |   5 +-
 .../applib/services/iactn/InteractionProvider.java |  26 +-
 .../services/iactnlayer/InteractionContext.java    |  70 +--
 .../services/iactnlayer/InteractionService.java    |  24 +-
 .../isis/applib/services/user/UserMemento.java     | 197 ++++----
 .../applib/services/user/UserMemento_Test.java     |  40 +-
 core/adoc/modules/_overview/pages/about.adoc       |  17 +-
 .../interaction/integration/IsisRequestCycle.java  |  33 +-
 .../interaction/session/InteractionFactory.java    |  83 ----
 .../interaction/session/InteractionTracker.java    |  41 +-
 .../_testing/MetaModelContext_forTesting.java      |  22 +-
 .../metamodel/context/HasMetaModelContext.java     |   6 +-
 .../core/metamodel/context/MetaModelContext.java   |   4 +-
 .../context/MetaModelContext_usingIoc.java         |   6 +-
 .../metamodel/interactions/InteractionContext.java |   7 +-
 .../authorization/AuthorizationFacetAbstract.java  |  14 +-
 .../title/TitlesAndTranslationsValidator.java      |   2 +-
 .../facets/AbstractFacetFactoryJUnit4TestCase.java |   8 +-
 .../metamodel/facets/AbstractFacetFactoryTest.java |  18 +-
 .../actions/ActionMethodsFacetFactoryTest.java     |   7 +-
 .../action/ActionAnnotationFacetFactoryTest.java   |   6 +-
 .../TitleAnnotationFacetFactoryTest.java           |  34 +-
 .../NavigableParentAnnotationFacetFactoryTest.java |  23 +-
 .../parseable/ParseableFacetUsingParserTest.java   |   8 +-
 .../ValueSemanticsProviderAbstractTestCase.java    |  22 +-
 .../objects/ObjectActionLayoutXmlDefaultTest.java  |   6 +-
 .../objects/OneToManyAssociationDefaultTest.java   |   6 +-
 .../SpecificationLoaderTestAbstract.java           |  20 +-
 core/pom.xml                                       |  12 +-
 .../core/runtime/context/RuntimeContextBase.java   |  11 +-
 .../IsisModuleCoreRuntimeServices.java             |   4 +-
 .../runtimeservices/clock/ClockServiceDefault.java |   2 +-
 .../session/AnonymousAuthentication.java           |  37 --
 ...Default.java => InteractionServiceDefault.java} |  55 +--
 .../runtimeservices/sudo/SudoServiceDefault.java   |  15 +-
 .../runtimeservices/user/UserServiceDefault.java   |   4 +-
 .../wrapper/WrapperFactoryDefault.java             |  21 +-
 .../_testing/InteractionService_forTesting.java    |  82 ++++
 .../security/authentication/Authentication.java    | 123 -----
 .../authentication/AuthenticationAbstract.java     | 113 -----
 .../authentication/AuthenticationProvider.java     |  51 ---
 .../authentication/AuthenticationRequest.java      |   8 +-
 .../security/authentication/Authenticator.java     |  12 +-
 .../authentication/InteractionContextFactory.java  |  62 +++
 .../health/HealthAuthentication.java               |  41 --
 .../security/authentication/logout/LogoutMenu.java |  17 +-
 .../manager/AnonymousInteractionFactory.java       |  78 ----
 .../manager/AuthenticationManager.java             |  31 +-
 .../singleuser/SingleUserAuthentication.java       |  44 --
 .../standard/AuthenticatorAbstract.java            |  13 +-
 .../standard/SimpleAuthentication.java             |  64 ---
 .../core/security/authorization/Authorizor.java    |  12 +-
 .../manager/AuthorizationManager.java              |   8 +-
 .../apache/isis/core/security/util/XrayUtil.java   |  15 -
 .../isis/security/EncodabilityContractTest.java    |  17 +-
 .../AuthenticationManager_authenticators_Test.java |  14 +-
 .../SimpleSessionEncodabilityNoRolesTest.java      |   7 +-
 .../SimpleSessionEncodabilityTestAbstract.java     |  19 +-
 .../SimpleSessionEncodabilityWithRolesTest.java    |   8 +-
 ...rdAuthenticationManager_AuthenticationTest.java |  16 +-
 .../changetracking/EntityChangeTrackerDefault.java |  17 +-
 .../core/transaction/changetracking/_Xray.java     |  41 +-
 .../HealthIndicatorUsingHealthCheckService.java    |  18 +-
 .../main/java/demoapp/javafx/DemoAppJavaFx.java    |   4 +-
 .../java/demoapp/webapp/vaadin/DemoAppVaadin.java  |   4 +-
 .../secondary/job/ReplicateAndReplayJob.java       |  17 +-
 .../adoc/modules/secman/pages/setting-up.adoc      |   1 -
 .../secman/applib/user/dom/ApplicationUser.java    |   6 +-
 .../authenticator/AuthenticatorSecman.java         |  12 +-
 .../integration/authorizor/AuthorizorSecman.java   |  12 +-
 .../shiro/IsisModuleExtSecmanShiroRealm.java       |   8 +-
 .../viewer/javafx/model/context/UiContextFx.java   |   4 +-
 .../javafx/ui/components/object/ObjectViewFx.java  |   9 +-
 .../viewer/javafx/ui/main/MainViewFx.java          |   6 +-
 .../viewer/javafx/ui/main/UiActionHandlerFx.java   |   6 +-
 .../viewer/javafx/ui/main/UiContextFxDefault.java  |   6 +-
 .../viewer/vaadin/model/context/UiContextVaa.java  |   2 +-
 .../vaadin/ui/auth/AuthSessionStoreUtil.java       |  22 +-
 .../viewer/vaadin/ui/auth/LogoutHandlerVaa.java    |   2 +-
 .../ui/auth/VaadinAuthenticationHandler.java       |  10 +-
 .../vaadin/ui/components/object/ObjectViewVaa.java |   2 +-
 .../vaadin/ui/pages/main/UiActionHandlerVaa.java   |   6 +-
 .../vaadin/ui/pages/main/UiContextVaaDefault.java  |   6 +-
 .../viewer/IsisModuleIncViewerVaadinViewer.java    |   6 +-
 .../viewer/vaadin/viewer/IsisServletForVaadin.java |  13 +-
 .../SpecificationLoaderTestAbstract.java           |  14 +-
 .../testing/AbstractFacetFactoryTest.java          |  20 +-
 .../jdo/JdoExceptionTranslationTest.java           |  76 ++--
 ...xceptionTranslationTest_usingTransactional.java |  14 +-
 ...TransactionRollbackTest_usingTransactional.java |  54 +--
 .../jdo/JdoTransactionScopeListenerTest.java       |  52 +--
 .../jpa/JpaExceptionTranslationTest.java           |  82 ++--
 ...xceptionTranslationTest_usingTransactional.java |  16 +-
 .../jpa/JpaTransactionScopeListenerTest.java       |  52 +--
 .../applayer/ApplicationLayerTestFactory.java      |  25 +-
 .../bypass/authorization/AuthorizorBypass.java     |   6 +-
 .../authentication/AuthenticatorKeycloak.java      |  12 +-
 .../keycloak/webmodule/KeycloakFilter.java         |  23 +-
 .../shiro/authentication/AuthenticatorShiro.java   |  14 +-
 .../shiro/authorization/AuthorizorShiro.java       |  10 +-
 ...AuthenticatorOrAuthorizorTest_authenticate.java |  11 +-
 ...ticatorOrAuthorizorTest_isVisibleInAnyRole.java |   7 +-
 .../spring/authentication/AuthenticatorSpring.java |  12 +-
 .../spring/webmodule/SpringSecurityFilter.java     |  26 +-
 .../adoc/modules/ROOT/partials/component-nav.adoc  |   2 +-
 subdomains/base/adoc/modules/base/nav.adoc         |   3 +-
 .../adoc/modules/docx/attachments/Template.docx    | Bin 0 -> 28756 bytes
 .../modules/docx/examples/input-exact-match.html   |  41 ++
 .../adoc/modules/docx/images/template-docx.png     | Bin 0 -> 472174 bytes
 .../docx/images/word-enable-developer-ribbon.png   | Bin 0 -> 435844 bytes
 subdomains/docx/adoc/modules/docx/nav.adoc         |   3 +-
 subdomains/docx/adoc/modules/docx/pages/about.adoc | 124 ++++-
 subdomains/docx/applib/pom.xml                     |  54 ++-
 .../isis/subdomains/docx/applib/DocxService.java   | 497 +++++++++++++++++++++
 .../applib/exceptions/DocxServiceException.java    |  13 +
 .../docx/applib/exceptions/LoadInputException.java |   8 +
 .../applib/exceptions/LoadTemplateException.java   |   8 +
 .../docx/applib/exceptions/MergeException.java     |  11 +
 .../docx/applib/traverse/AllMatches.java           |  42 ++
 .../docx/applib/traverse/CallbackAbstract.java     |  32 ++
 .../docx/applib/traverse/FirstMatch.java           |  47 ++
 .../isis/subdomains/docx/applib/util/Docx.java     | 109 +++++
 .../isis/subdomains/docx/applib/util/Dump.java     | 181 ++++++++
 .../isis/subdomains/docx/applib/util/Jdom2.java    |  76 ++++
 .../isis/subdomains/docx/applib/util/Types.java    |  17 +
 .../applib/src/main/resources/docx4j.properties    |  31 ++
 .../docx/applib/DocxService_merge_Test.java        | 352 +++++++++++++++
 .../isis/subdomains/docx/applib/IoHelper.java      |  98 ++++
 .../subdomains/docx/applib/Output-Expected.docx    | Bin 0 -> 29005 bytes
 .../subdomains/docx/applib/Output-Expected.pdf     | Bin 0 -> 84424 bytes
 .../isis/subdomains/docx/applib/Template.docx      | Bin 0 -> 28756 bytes
 .../subdomains/docx/applib/input-exact-match.html  |  67 +++
 .../subdomains/docx/applib/input-malformed.html    |  69 +++
 .../isis/subdomains/docx/applib/input-missing.html |  69 +++
 .../isis/subdomains/docx/applib/input-surplus.html |  70 +++
 subdomains/excel/adoc/modules/excel/nav.adoc       |   3 +-
 .../freemarker/adoc/modules/freemarker/nav.adoc    |   3 +-
 subdomains/ognl/adoc/modules/ognl/nav.adoc         |   3 +-
 subdomains/pdfbox/adoc/modules/pdfbox/nav.adoc     |   3 +-
 subdomains/pom.xml                                 |  51 ++-
 subdomains/spring/adoc/modules/spring/nav.adoc     |   3 +-
 .../adoc/modules/spring/partials/module-nav.adoc   |   2 +-
 .../xdocreport/adoc/modules/xdocreport/nav.adoc    |   2 +-
 subdomains/zip/adoc/modules/zip/nav.adoc           |   3 +-
 .../applib/fixturescripts/FixtureScripts.java      |   8 +-
 .../testing/integtestsupport/applib/_Helper.java   |   7 +-
 .../tooling/javamodel/test/ProjectSamples.java     |   2 +-
 .../sse/ui/wkt/services/SseServiceDefault.java     |   6 +-
 .../restfulobjects/rendering/IResourceContext.java |   4 +-
 ..._ensureCompatibleAcceptHeader_ContractTest.java |  20 +-
 .../viewer/resources/HomePageReprRenderer.java     |   6 +-
 .../viewer/resources/SwaggerSpecResource.java      |  10 +-
 .../viewer/resources/UserReprRenderer.java         |  10 +-
 .../viewer/resources/UserResourceServerside.java   |   2 +-
 .../IsisRestfulObjectsInteractionFilter.java       |  16 +-
 .../webmodule/auth/AuthenticationStrategy.java     |  14 +-
 .../auth/AuthenticationStrategyAbstract.java       |   3 +-
 .../auth/AuthenticationStrategyBasicAuth.java      |   8 +-
 .../auth/AuthenticationStrategyDefault.java        |  15 +-
 .../context/ResourceContext_getArg_Test.java       |  14 +-
 .../isis/viewer/wicket/ui/pages/WebPageBase.java   |   8 +-
 .../ui/pages/accmngt/EmailAvailableValidator.java  |  10 +-
 .../pages/accmngt/UsernameAvailableValidator.java  |  10 +-
 .../accmngt/password_reset/PasswordResetPage.java  |   2 +-
 .../accmngt/password_reset/PasswordResetPanel.java |   2 +-
 .../ui/pages/accmngt/register/RegisterPanel.java   |   6 +-
 .../wicket/ui/panels/FormExecutorDefault.java      |   5 +-
 .../isis/viewer/wicket/ui/panels/PanelBase.java    |   8 +-
 .../AuthenticatedWebSessionForIsis.java            |  31 +-
 .../viewer/integration/LocalizerForIsis.java       |  10 +-
 .../viewer/integration/WebRequestCycleForIsis.java |   4 +-
 ...uthenticatedWebSessionForIsis_Authenticate.java |  42 +-
 .../AuthenticatedWebSessionForIsis_SignIn.java     |  34 +-
 ...uthenticatedWebSessionForIsis_TestAbstract.java |  16 +-
 177 files changed, 3261 insertions(+), 1761 deletions(-)
 create mode 100644 antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService.adoc
 copy subdomains/excel/adoc/modules/excel/nav.adoc => antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService~MergeDefinition.adoc (86%)
 delete mode 100644 core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
 delete mode 100644 core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/AnonymousAuthentication.java
 rename core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/session/{InteractionFactoryDefault.java => InteractionServiceDefault.java} (91%)
 create mode 100644 core/security/src/main/java/org/apache/isis/core/security/_testing/InteractionService_forTesting.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/Authentication.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationAbstract.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/AuthenticationProvider.java
 create mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/InteractionContextFactory.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/health/HealthAuthentication.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AnonymousInteractionFactory.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/singleuser/SingleUserAuthentication.java
 delete mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/standard/SimpleAuthentication.java
 create mode 100644 subdomains/docx/adoc/modules/docx/attachments/Template.docx
 create mode 100644 subdomains/docx/adoc/modules/docx/examples/input-exact-match.html
 create mode 100644 subdomains/docx/adoc/modules/docx/images/template-docx.png
 create mode 100644 subdomains/docx/adoc/modules/docx/images/word-enable-developer-ribbon.png
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/DocxServiceException.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadInputException.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadTemplateException.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/MergeException.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/AllMatches.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/CallbackAbstract.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/FirstMatch.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Docx.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Dump.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Jdom2.java
 create mode 100644 subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Types.java
 create mode 100644 subdomains/docx/applib/src/main/resources/docx4j.properties
 create mode 100644 subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java
 create mode 100644 subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.docx
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.pdf
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Template.docx
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-exact-match.html
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-malformed.html
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-missing.html
 create mode 100644 subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-surplus.html

[isis] 02/04: ISIS-2442: improves base subdomain docs a little more

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 58ab1994bff81869be28822b20747d2a1423a7db
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jun 10 11:24:42 2021 +0100

    ISIS-2442: improves base subdomain docs a little more
---
 .../applib/services/calendar/CalendarService.adoc  | 46 +++++++++++++---------
 .../types/{NameType.adoc => Description.adoc}      | 10 +++--
 .../types/{DescriptionType.adoc => Fqcn.adoc}      |  9 +++--
 .../applib/types/{FqcnType.adoc => Money.adoc}     |  9 +++--
 .../base/applib/types/{NameType.adoc => Name.adoc} |  9 +++--
 .../applib/types/{NameType.adoc => Notes.adoc}     | 10 +++--
 .../pages/index/base/applib/types/NotesType.adoc   | 12 ------
 .../types/{NameType.adoc => ObjectIdentifier.adoc} |  9 +++--
 .../base/applib/types/ObjectIdentifierType.adoc    | 12 ------
 .../types/{NameType.adoc => Percentage.adoc}       |  9 +++--
 .../index/base/applib/types/PercentageType.adoc    | 12 ------
 .../types/{NameType.adoc => ProperName.adoc}       |  9 +++--
 .../index/base/applib/types/ProperNameType.adoc    | 12 ------
 .../applib/types/{NameType.adoc => Reference.adoc} | 11 ++++--
 .../index/base/applib/types/ReferenceType.adoc     | 12 ------
 .../applib/types/{MoneyType.adoc => Title.adoc}    |  9 +++--
 .../pages/index/base/applib/types/TitleType.adoc   | 12 ------
 .../types/{NameType.adoc => UrlTemplate.adoc}      |  9 +++--
 .../index/base/applib/types/UrlTemplateType.adoc   | 12 ------
 .../index/base/applib/types/UserNameType.adoc      | 12 ------
 .../applib/types/{NameType.adoc => Username.adoc}  |  9 +++--
 .../pages/index/base/applib/types/XxxType.adoc     | 12 ------
 core/adoc/modules/_overview/pages/about.adoc       |  4 +-
 subdomains/base/adoc/modules/base/pages/about.adoc | 34 ++++++++--------
 24 files changed, 120 insertions(+), 184 deletions(-)

diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/services/calendar/CalendarService.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/services/calendar/CalendarService.adoc
index f4c809e..0ffa385 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/services/calendar/CalendarService.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/services/calendar/CalendarService.adoc
@@ -7,35 +7,43 @@
 .CalendarService.java
 ----
 class CalendarService {
-  CalendarService(ClockService clockService)
-  LocalDate beginningOfMonth()
+  LocalDate beginningOfMonth()     // <.>
   LocalDate beginningOfMonth(final LocalDate date)
-  LocalDate beginningOfQuarter()
-  LocalDate beginningOfNextQuarter()
+  LocalDate beginningOfQuarter()     // <.>
+  LocalDate beginningOfNextQuarter()     // <.>
   LocalDate beginningOfQuarter(final LocalDate date)
-  long timestamp()     // <.>
 }
 ----
 
-<.> xref:#timestamp__[timestamp()]
+<.> xref:#beginningOfMonth__[beginningOfMonth()]
 +
 --
-[WARNING]
-====
-[red]#_deprecated:_#
-
-- use _ClockService#getClock()_ .getEpochMillis()
-====
+Returns the date corresponding to the beginning of the current month.
+--
+<.> xref:#beginningOfQuarter__[beginningOfQuarter()]
++
+--
+Returns the date corresponding to the beginning of the current quarter (typically: January, April, July or October).
+--
+<.> xref:#beginningOfNextQuarter__[beginningOfNextQuarter()]
++
+--
+Returns the date corresponding to the beginning of the quarter following this one.
 --
 
 == Members
 
-[#timestamp__]
-=== timestamp()
+[#beginningOfMonth__]
+=== beginningOfMonth()
+
+Returns the date corresponding to the beginning of the current month.
+
+[#beginningOfQuarter__]
+=== beginningOfQuarter()
+
+Returns the date corresponding to the beginning of the current quarter (typically: January, April, July or October).
 
-[WARNING]
-====
-[red]#_deprecated:_#
+[#beginningOfNextQuarter__]
+=== beginningOfNextQuarter()
 
-- use _ClockService#getClock()_ .getEpochMillis()
-====
+Returns the date corresponding to the beginning of the quarter following this one.
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Description.adoc
similarity index 81%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Description.adoc
index 452c3db..0701c9f 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Description.adoc
@@ -1,12 +1,16 @@
-= NameType
+= @Description
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a description of some sort.
+
 == API
 
 [source,java]
-.NameType.java
+.Description.java
 ----
-class NameType {
+@interface Description {
+  int MAX_LEN;
+  int MULTI_LINE;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/DescriptionType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Fqcn.adoc
similarity index 84%
rename from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/DescriptionType.adoc
rename to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Fqcn.adoc
index ede2e31..53f9bb1 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/DescriptionType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Fqcn.adoc
@@ -1,12 +1,15 @@
-= DescriptionType
+= @Fqcn
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a fully qualified class name
+
 == API
 
 [source,java]
-.DescriptionType.java
+.Fqcn.java
 ----
-class DescriptionType {
+@interface Fqcn {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/FqcnType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Money.adoc
similarity index 83%
rename from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/FqcnType.adoc
rename to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Money.adoc
index d50f9a8..58d638c 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/FqcnType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Money.adoc
@@ -1,12 +1,15 @@
-= FqcnType
+= @Money
 :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 [...]
 
+Meta-annotation for a _java.math.BigDecimal_ property or parameter representing a monetary amount.
+
 == API
 
 [source,java]
-.FqcnType.java
+.Money.java
 ----
-class FqcnType {
+@interface Money {
+  int SCALE;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Name.adoc
similarity index 85%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Name.adoc
index 452c3db..b738171 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Name.adoc
@@ -1,12 +1,15 @@
-= NameType
+= @Name
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a name of some sort.
+
 == API
 
 [source,java]
-.NameType.java
+.Name.java
 ----
-class NameType {
+@interface Name {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Notes.adoc
similarity index 81%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Notes.adoc
index 452c3db..0f20f4e 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Notes.adoc
@@ -1,12 +1,16 @@
-= NameType
+= @Notes
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a notes or comments about some object.
+
 == API
 
 [source,java]
-.NameType.java
+.Notes.java
 ----
-class NameType {
+@interface Notes {
+  int MAX_LEN;
+  int MULTI_LINE;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NotesType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NotesType.adoc
deleted file mode 100644
index 3e72428..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NotesType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= NotesType
-: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 [...]
-
-== API
-
-[source,java]
-.NotesType.java
-----
-class NotesType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ObjectIdentifier.adoc
similarity index 77%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ObjectIdentifier.adoc
index 452c3db..9f59cb3 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ObjectIdentifier.adoc
@@ -1,12 +1,15 @@
-= NameType
+= @ObjectIdentifier
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a the identity of a domain object (with respect to its logical type).
+
 == API
 
 [source,java]
-.NameType.java
+.ObjectIdentifier.java
 ----
-class NameType {
+@interface ObjectIdentifier {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ObjectIdentifierType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ObjectIdentifierType.adoc
deleted file mode 100644
index 6e8c2dd..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ObjectIdentifierType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= ObjectIdentifierType
-: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 [...]
-
-== API
-
-[source,java]
-.ObjectIdentifierType.java
-----
-class ObjectIdentifierType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Percentage.adoc
similarity index 79%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Percentage.adoc
index 452c3db..ab6b410 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Percentage.adoc
@@ -1,12 +1,15 @@
-= NameType
+= @Percentage
 :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 [...]
 
+Meta-annotation for a numeric property or parameter representing a the identity of a domain object (with respect to its logical type).
+
 == API
 
 [source,java]
-.NameType.java
+.Percentage.java
 ----
-class NameType {
+@interface Percentage {
+  int SCALE;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/PercentageType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/PercentageType.adoc
deleted file mode 100644
index e17ff6f..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/PercentageType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= PercentageType
-: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 [...]
-
-== API
-
-[source,java]
-.PercentageType.java
-----
-class PercentageType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ProperName.adoc
similarity index 81%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ProperName.adoc
index 452c3db..a7a353a 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ProperName.adoc
@@ -1,12 +1,15 @@
-= NameType
+= @ProperName
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing the "proper" (or formal) name of some sort.
+
 == API
 
 [source,java]
-.NameType.java
+.ProperName.java
 ----
-class NameType {
+@interface ProperName {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ProperNameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ProperNameType.adoc
deleted file mode 100644
index 9f9e1e0..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ProperNameType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= ProperNameType
-: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 [...]
-
-== API
-
-[source,java]
-.ProperNameType.java
-----
-class ProperNameType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Reference.adoc
similarity index 78%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Reference.adoc
index 452c3db..9430f4b 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Reference.adoc
@@ -1,12 +1,17 @@
-= NameType
+= @Reference
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing A unique reference code of some sort.
+
 == API
 
 [source,java]
-.NameType.java
+.Reference.java
 ----
-class NameType {
+@interface Reference {
+  int MAX_LEN;
+  String REGEX;
+  String REGEX_DESCRIPTION;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ReferenceType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ReferenceType.adoc
deleted file mode 100644
index 16b1477..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/ReferenceType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= ReferenceType
-: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 [...]
-
-== API
-
-[source,java]
-.ReferenceType.java
-----
-class ReferenceType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/MoneyType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Title.adoc
similarity index 83%
rename from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/MoneyType.adoc
rename to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Title.adoc
index 01189c8..6156a11 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/MoneyType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Title.adoc
@@ -1,12 +1,15 @@
-= MoneyType
+= @Title
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a title (or perhaps label) of some sort.
+
 == API
 
 [source,java]
-.MoneyType.java
+.Title.java
 ----
-class MoneyType {
+@interface Title {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/TitleType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/TitleType.adoc
deleted file mode 100644
index 8997a98..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/TitleType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= TitleType
-: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 [...]
-
-== API
-
-[source,java]
-.TitleType.java
-----
-class TitleType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UrlTemplate.adoc
similarity index 76%
copy from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UrlTemplate.adoc
index 452c3db..d1abe04 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UrlTemplate.adoc
@@ -1,12 +1,15 @@
-= NameType
+= @UrlTemplate
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing a URL template, for example for an entity that holds configuration data to access external systems.
+
 == API
 
 [source,java]
-.NameType.java
+.UrlTemplate.java
 ----
-class NameType {
+@interface UrlTemplate {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UrlTemplateType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UrlTemplateType.adoc
deleted file mode 100644
index 167c2e0..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UrlTemplateType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= UrlTemplateType
-: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 [...]
-
-== API
-
-[source,java]
-.UrlTemplateType.java
-----
-class UrlTemplateType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UserNameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UserNameType.adoc
deleted file mode 100644
index f7057aa..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/UserNameType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= UserNameType
-: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 [...]
-
-== API
-
-[source,java]
-.UserNameType.java
-----
-class UserNameType {
-}
-----
-
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Username.adoc
similarity index 80%
rename from antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
rename to antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Username.adoc
index 452c3db..450d857 100644
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/NameType.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/Username.adoc
@@ -1,12 +1,15 @@
-= NameType
+= @Username
 :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 [...]
 
+Meta-annotation for a _String_ property or parameter representing the unique username (or login name) of a user of the system.
+
 == API
 
 [source,java]
-.NameType.java
+.Username.java
 ----
-class NameType {
+@interface Username {
+  int MAX_LEN;
 }
 ----
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/XxxType.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/XxxType.adoc
deleted file mode 100644
index bd1d67d..0000000
--- a/antora/components/refguide-index/modules/subdomains/pages/index/base/applib/types/XxxType.adoc
+++ /dev/null
@@ -1,12 +0,0 @@
-= XxxType
-: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 [...]
-
-== API
-
-[source,java]
-.XxxType.java
-----
-class XxxType {
-}
-----
-
diff --git a/core/adoc/modules/_overview/pages/about.adoc b/core/adoc/modules/_overview/pages/about.adoc
index f18088f..8786da7 100644
--- a/core/adoc/modules/_overview/pages/about.adoc
+++ b/core/adoc/modules/_overview/pages/about.adoc
@@ -4469,7 +4469,6 @@ o.a.i.extensions.secman.jdo.util.RegexReplacer +
 .Dependencies
 ****
 org.apache.isis.core:isis-core-runtime:jar:<managed> +
-org.apache.isis.extensions:isis-extensions-secman-applib:jar:<managed> +
 org.apache.isis.extensions:isis-extensions-secman-integration:jar:<managed> +
 org.apache.isis.persistence:isis-persistence-jdo-applib:jar:<managed> +
 org.apache.isis.persistence:isis-persistence-jdo-datanucleus:jar:<managed> +
@@ -4501,7 +4500,6 @@ o.a.i.extensions.secman.jpa.util.RegexReplacer +
 .Dependencies
 ****
 org.apache.isis.core:isis-core-runtime:jar:<managed> +
-org.apache.isis.extensions:isis-extensions-secman-applib:jar:<managed> +
 org.apache.isis.extensions:isis-extensions-secman-integration:jar:<managed> +
 org.apache.isis.persistence:isis-persistence-jpa-applib:jar:<managed> +
 org.apache.isis.testing:isis-testing-fixtures-applib:jar:<managed> +
@@ -5256,7 +5254,7 @@ org.datanucleus:javax.jdo:jar:<managed> +
 
 .Document Index Entries
 ****
-xref:refguide:subdomains:index/base/applib/Chained.adoc[Chained], xref:refguide:subdomains:index/base/applib/Dflt.adoc[Dflt], xref:refguide:subdomains:index/base/applib/IsisModuleSubdomainsBaseApplib.adoc[IsisModuleSubdomainsBaseApplib], xref:refguide:subdomains:index/base/applib/PowerType.adoc[PowerType], xref:refguide:subdomains:index/base/applib/Titled.adoc[Titled], xref:refguide:subdomains:index/base/applib/TitledEnum.adoc[TitledEnum], xref:refguide:subdomains:index/base/applib/servi [...]
+xref:refguide:subdomains:index/base/applib/Chained.adoc[Chained], xref:refguide:subdomains:index/base/applib/Dflt.adoc[Dflt], xref:refguide:subdomains:index/base/applib/IsisModuleSubdomainsBaseApplib.adoc[IsisModuleSubdomainsBaseApplib], xref:refguide:subdomains:index/base/applib/PowerType.adoc[PowerType], xref:refguide:subdomains:index/base/applib/Titled.adoc[Titled], xref:refguide:subdomains:index/base/applib/TitledEnum.adoc[TitledEnum], xref:refguide:subdomains:index/base/applib/servi [...]
 ****
 |===
 
diff --git a/subdomains/base/adoc/modules/base/pages/about.adoc b/subdomains/base/adoc/modules/base/pages/about.adoc
index 5a1f449..08c0c6b 100644
--- a/subdomains/base/adoc/modules/base/pages/about.adoc
+++ b/subdomains/base/adoc/modules/base/pages/about.adoc
@@ -85,9 +85,7 @@ xref:refguide:subdomains:index/base/applib/valuetypes/VT.adoc[]
 
 * "with" interfaces (useful to write contract tests against):
 +
-:chk: icon:check[]
-+
-[cols="2a,^2a,^2a,^2a",options="header"]
+[cols="1a,^2a,^2a,^2a",options="header"]
 |===
 |With
 | Getter
@@ -95,29 +93,29 @@ xref:refguide:subdomains:index/base/applib/valuetypes/VT.adoc[]
 | Unique
 
 | Code
-| xref:refguide:subdomains:index/base/applib/with/WithCodeGetter.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithCodeComparable.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithCodeUnique.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithCodeGetter.adoc[WithCodeGetter]
+| xref:refguide:subdomains:index/base/applib/with/WithCodeComparable.adoc[WithCodeComparable]
+| xref:refguide:subdomains:index/base/applib/with/WithCodeUnique.adoc[WithCodeUnique]
 
 | Description
-| xref:refguide:subdomains:index/base/applib/with/WithDescriptionGetter.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithDescriptionComparable.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithDescriptionUnique.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithDescriptionGetter.adoc[WithDescriptionGetter]
+| xref:refguide:subdomains:index/base/applib/with/WithDescriptionComparable.adoc[WithDescriptionComparable]
+| xref:refguide:subdomains:index/base/applib/with/WithDescriptionUnique.adoc[WithDescriptionUnique]
 
 | Name
-| xref:refguide:subdomains:index/base/applib/with/WithNameGetter.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithNameComparable.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithNameUnique.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithNameGetter.adoc[WithNameGetter]
+| xref:refguide:subdomains:index/base/applib/with/WithNameComparable.adoc[WithNameComparable]
+| xref:refguide:subdomains:index/base/applib/with/WithNameUnique.adoc[WithNameUnique]
 
 | Reference
-| xref:refguide:subdomains:index/base/applib/with/WithReferenceGetter.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithReferenceComparable.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithReferenceUnique.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithReferenceGetter.adoc[WithReferenceGetter]
+| xref:refguide:subdomains:index/base/applib/with/WithReferenceComparable.adoc[WithReferenceComparable]
+| xref:refguide:subdomains:index/base/applib/with/WithReferenceUnique.adoc[WithReferenceUnique]
 
 | Title
-| xref:refguide:subdomains:index/base/applib/with/WithTitleGetter.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithTitleComparable.adoc[{chk}]
-| xref:refguide:subdomains:index/base/applib/with/WithTitleUnique.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithTitleGetter.adoc[WithTitleGetter]
+| xref:refguide:subdomains:index/base/applib/with/WithTitleComparable.adoc[WithTitleComparable]
+| xref:refguide:subdomains:index/base/applib/with/WithTitleUnique.adoc[WithTitleUnique]
 
 
 |===

[isis] 01/04: ISIS-2442: adds docs for base subdomain; convert types to meta-annotations

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit be2f793d1b79ca3825f23b6fc87ba893474d7ace
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jun 10 11:13:25 2021 +0100

    ISIS-2442: adds docs for base subdomain; convert types to meta-annotations
---
 .../adoc/modules/secman/pages/setting-up.adoc      |  16 +--
 .../setup-and-configure-dependencyManagement.adoc  |   2 +-
 subdomains/base/adoc/modules/base/pages/about.adoc | 152 ++++++++++++++++++++-
 .../applib/services/calendar/CalendarService.java  |  33 +++--
 .../{DescriptionType.java => Description.java}     |  31 +++--
 .../base/applib/types/{FqcnType.java => Fqcn.java} |  25 ++--
 .../applib/types/{NameType.java => Money.java}     |  25 ++--
 .../applib/types/{MoneyType.java => Name.java}     |  25 ++--
 .../types/{DescriptionType.java => Notes.java}     |  32 +++--
 .../subdomains/base/applib/types/NotesType.java    |  36 -----
 ...{DescriptionType.java => ObjectIdentifier.java} |  27 ++--
 .../base/applib/types/ObjectIdentifierType.java    |  36 -----
 .../{DescriptionType.java => Percentage.java}      |  24 ++--
 .../base/applib/types/PercentageType.java          |  36 -----
 .../{DescriptionType.java => ProperName.java}      |  26 ++--
 .../base/applib/types/ProperNameType.java          |  36 -----
 .../subdomains/base/applib/types/Reference.java    |  47 +++++++
 .../base/applib/types/ReferenceType.java           |  39 ------
 .../types/{DescriptionType.java => Title.java}     |  26 ++--
 .../subdomains/base/applib/types/TitleType.java    |  34 -----
 .../{DescriptionType.java => UrlTemplate.java}     |  26 ++--
 .../base/applib/types/UrlTemplateType.java         |  34 -----
 .../subdomains/base/applib/types/UserNameType.java |  34 -----
 .../types/{DescriptionType.java => Username.java}  |  26 ++--
 .../isis/subdomains/base/applib/types/XxxType.java |  34 -----
 .../base/applib/valuetypes/LocalDateInterval.java  |   1 -
 26 files changed, 410 insertions(+), 453 deletions(-)

diff --git a/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc b/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
index 2c3d762..6498f9b 100644
--- a/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
+++ b/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
@@ -40,14 +40,14 @@ In the webapp module of your application, add the following dependency:
 .pom.xml
 ----
 <dependencies>
-        <dependency>
-            <groupId>org.apache.isis.extensions</groupId>
-            <artifactId>isis-extensions-secman-persistence-XXX</artifactId> <!--.-->
-        </dependency>
-        <dependency>
-            <groupId>org.apache.isis.extensions</groupId>
-            <artifactId>isis-extensions-secman-encryption-jbcrypt</artifactId> <!--.-->
-        </dependency>
+    <dependency>
+        <groupId>org.apache.isis.extensions</groupId>
+        <artifactId>isis-extensions-secman-persistence-XXX</artifactId>    <!--.-->
+    </dependency>
+    <dependency>
+        <groupId>org.apache.isis.extensions</groupId>
+        <artifactId>isis-extensions-secman-encryption-jbcrypt</artifactId> <!--.-->
+    </dependency>
 </dependencies>
 ----
 <.> specify either `isis-extensions-secman-persistence-jpa` or `isis-extensions-secman-persistence-jdo`, as required
diff --git a/mavendeps/adoc/modules/mavendeps/partials/setup-and-configure-dependencyManagement.adoc b/mavendeps/adoc/modules/mavendeps/partials/setup-and-configure-dependencyManagement.adoc
index a3737e7..c09f781 100644
--- a/mavendeps/adoc/modules/mavendeps/partials/setup-and-configure-dependencyManagement.adoc
+++ b/mavendeps/adoc/modules/mavendeps/partials/setup-and-configure-dependencyManagement.adoc
@@ -5,7 +5,7 @@
 
 
 
-If your application inherits from the Apache Isis starter app (`org.apache.isis.app:isis-app-starter-parent` then that will define the version automatically:
+If your application inherits from the Apache Isis starter app (`org.apache.isis.app:isis-app-starter-parent`) then that will define the version automatically:
 
 [source,xml,subs="attributes+"]
 .pom.xml
diff --git a/subdomains/base/adoc/modules/base/pages/about.adoc b/subdomains/base/adoc/modules/base/pages/about.adoc
index 00c83c4..5a1f449 100644
--- a/subdomains/base/adoc/modules/base/pages/about.adoc
+++ b/subdomains/base/adoc/modules/base/pages/about.adoc
@@ -1,5 +1,153 @@
-= Base Library
+= Base subdomain
 
 :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 [...]
 
-WARNING: TODO
+This module provides a base library of utility classes.
+
+include::docs:mavendeps:partial$setup-and-configure-dependencyManagement.adoc[leveloffset=+1]
+
+In addition, add a section for the BOM of all subdomains:
+
+[source,xml,subs="attributes+"]
+.pom.xml
+----
+<dependencyManagement>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.isis.subdomains</groupId>
+            <artifactId>isis-subdomains</artifactId>
+            <scope>import</scope>
+            <type>pom</type>
+            <version>{page-isisrel}</version>
+        </dependency>
+    </dependencies>
+</dependencyManagement>
+----
+
+[#dependencies]
+== Dependencies
+
+In the domain module(s) of your application, add the following dependency:
+
+[source,xml]
+.pom.xml
+----
+<dependencies>
+    <dependency>
+        <groupId>org.apache.isis.subdomains</groupId>
+        <artifactId>isis-subdomains-base-applib</artifactId>
+    </dependency>
+</dependencies>
+----
+
+
+== Usage
+
+Details:
+
+* xref:refguide:subdomains:index/base/applib/services/calendar/CalendarService.adoc[CalendarService]
++
+to look up beginning of months and quarters based on a date.
++
+To use, `@Import` the xref:refguide:subdomains:index/base/applib/services/BaseServicesModule.adoc[BaseServicesModule] into your app's _AppManifest_.
+
+* Meta-annotations for types:
++
+@xref:refguide:subdomains:index/base/applib/types/Description.adoc[Description],
+@xref:refguide:subdomains:index/base/applib/types/Fqcn.adoc[Fqcn],
+@xref:refguide:subdomains:index/base/applib/types/Money.adoc[Money],
+@xref:refguide:subdomains:index/base/applib/types/Name.adoc[Name],
+@xref:refguide:subdomains:index/base/applib/types/Notes.adoc[Notes],
+@xref:refguide:subdomains:index/base/applib/types/ObjectIdentifier.adoc[ObjectIdentifier],
+@xref:refguide:subdomains:index/base/applib/types/Percentage.adoc[Percentage],
+@xref:refguide:subdomains:index/base/applib/types/ProperName.adoc[ProperName],
+@xref:refguide:subdomains:index/base/applib/types/Reference.adoc[Reference],
+@xref:refguide:subdomains:index/base/applib/types/Title.adoc[Title],
+@xref:refguide:subdomains:index/base/applib/types/UrlTemplate.adoc[UrlTemplate]
+
+* Various utility classes:
++
+xref:refguide:subdomains:index/base/applib/utils/ClassUtils.adoc[],
+xref:refguide:subdomains:index/base/applib/utils/JodaPeriodUtils.adoc[],
+xref:refguide:subdomains:index/base/applib/utils/MathUtils.adoc[],
+xref:refguide:subdomains:index/base/applib/utils/MessageUtils.adoc[],
+xref:refguide:subdomains:index/base/applib/utils/StringUtils.adoc[],
+xref:refguide:subdomains:index/base/applib/utils/TitleBuilder.adoc[]
+
+* value types:
++
+xref:refguide:subdomains:index/base/applib/valuetypes/LocalDateInterval.adoc[] (a specialization of
+xref:refguide:subdomains:index/base/applib/valuetypes/AbstractInterval.adoc[])
+
+* value type factory (useful for tests):
++
+xref:refguide:subdomains:index/base/applib/valuetypes/VT.adoc[]
+
+* "with" interfaces (useful to write contract tests against):
++
+:chk: icon:check[]
++
+[cols="2a,^2a,^2a,^2a",options="header"]
+|===
+|With
+| Getter
+| Comparable
+| Unique
+
+| Code
+| xref:refguide:subdomains:index/base/applib/with/WithCodeGetter.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithCodeComparable.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithCodeUnique.adoc[{chk}]
+
+| Description
+| xref:refguide:subdomains:index/base/applib/with/WithDescriptionGetter.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithDescriptionComparable.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithDescriptionUnique.adoc[{chk}]
+
+| Name
+| xref:refguide:subdomains:index/base/applib/with/WithNameGetter.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithNameComparable.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithNameUnique.adoc[{chk}]
+
+| Reference
+| xref:refguide:subdomains:index/base/applib/with/WithReferenceGetter.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithReferenceComparable.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithReferenceUnique.adoc[{chk}]
+
+| Title
+| xref:refguide:subdomains:index/base/applib/with/WithTitleGetter.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithTitleComparable.adoc[{chk}]
+| xref:refguide:subdomains:index/base/applib/with/WithTitleUnique.adoc[{chk}]
+
+
+|===
+
+** also:
+xref:refguide:subdomains:index/base/applib/with/WithInterval.adoc[WithInterval],
+xref:refguide:subdomains:index/base/applib/with/WithIntervalContiguous.adoc[WithIntervalContiguous],
+xref:refguide:subdomains:index/base/applib/with/WithIntervalMutable.adoc[WithIntervalMutable]
+
+** also:
+xref:refguide:subdomains:index/base/applib/with/WithSequence.adoc[WithSequence]
+
+** also:
+xref:refguide:subdomains:index/base/applib/with/WithStartDate.adoc[WithStartDate]
+
+** also:
+xref:refguide:subdomains:index/base/applib/Chained.adoc[Chained] - declares the objects are linked together with next and previous
+
+* titles:
+
+** xref:refguide:subdomains:index/base/applib/Titled.adoc[Titled] - require an object explicitly implement the `title()` method
+
+** xref:refguide:subdomains:index/base/applib/TitledEnum.adoc[TitledEnum] - require an enum explicitly implement the `title()` method.
+Used by xref:refguide:subdomains:index/base/applib/utils/StringUtils.adoc[StringUtils]
+
+* misc:
+
+** xref:refguide:subdomains:index/base/applib/Dflt.adoc[Dflt] - useful for obtaining a default from a list of choices
+
+** xref:refguide:subdomains:index/base/applib/PowerType.adoc[PowerType] - for enums that act as factories of a subtype
+
+
+
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/services/calendar/CalendarService.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/services/calendar/CalendarService.java
index 48a2455..201ea6b 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/services/calendar/CalendarService.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/services/calendar/CalendarService.java
@@ -32,6 +32,8 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.clock.ClockService;
 
+import lombok.RequiredArgsConstructor;
+
 /**
  * @since 2.0 {@index}
  */
@@ -40,17 +42,16 @@ import org.apache.isis.applib.services.clock.ClockService;
 @Order(OrderPrecedence.MIDPOINT)
 @Primary
 @Qualifier("Default")
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
 public class CalendarService {
 
     private static final int MONTHS_IN_QUARTER = 3;
 
-    private ClockService clockService;
-
-    @Inject
-    public CalendarService(ClockService clockService) {
-        this.clockService = clockService;
-    }
+    private final ClockService clockService;
 
+    /**
+     * Returns the date corresponding to the beginning of the current month.
+     */
     public LocalDate beginningOfMonth() {
         return beginningOfMonth(nowAsLocalDate());
     }
@@ -60,10 +61,21 @@ public class CalendarService {
         return date.minusDays(dayOfMonth-1L);
     }
 
+    /**
+     * Returns the date corresponding to the beginning of the current quarter (typically: January, April, July or October).
+     *
+     * @see #beginningOfQuarter(LocalDate)
+     * @see #beginningOfNextQuarter()
+     */
     public LocalDate beginningOfQuarter() {
         return beginningOfQuarter(nowAsLocalDate());
     }
 
+    /**
+     * Returns the date corresponding to the beginning of the quarter following this one.
+     *
+     * @see #beginningOfQuarter()
+     */
     public LocalDate beginningOfNextQuarter() {
         return beginningOfQuarter(nowAsLocalDate().plusMonths(3));
     }
@@ -81,13 +93,4 @@ public class CalendarService {
         return clockService.getClock().localDate(ZoneId.systemDefault());
     }
 
-    /**
-     * @deprecated - use {@link ClockService#getClock()}.getEpochMillis()
-     */
-    @Deprecated
-    public long timestamp() {
-        return clockService.getClock().getEpochMillis();
-    }
-
-
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Description.java
similarity index 50%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Description.java
index 2c08e75..96aeb62 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Description.java
@@ -18,20 +18,31 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * description of some sort.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = Description.MAX_LEN)
+@PropertyLayout(multiLine = Description.MULTI_LINE)
+@Parameter(maxLength = Description.MAX_LEN)
+@ParameterLayout(multiLine = Description.MULTI_LINE)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Description {
 
-    }
+    int MAX_LEN = 254;
+    int MULTI_LINE = 5;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/FqcnType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Fqcn.java
similarity index 59%
rename from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/FqcnType.java
rename to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Fqcn.java
index f91a4ea..aa6ecd2 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/FqcnType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Fqcn.java
@@ -18,19 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * fully qualified class name
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class FqcnType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
+@Property(maxLength = Fqcn.MAX_LEN)
+@Parameter(maxLength = Fqcn.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Fqcn {
 
-    }
+    int MAX_LEN = 254;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/NameType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Money.java
similarity index 59%
rename from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/NameType.java
rename to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Money.java
index bdea9bf..6b82993 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/NameType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Money.java
@@ -18,19 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link java.math.BigDecimal} property or parameter
+ * representing a monetary amount.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class NameType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 50;
+@Property(maxLength = Fqcn.MAX_LEN)
+@Parameter(maxLength = Fqcn.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Money {
 
-    }
+    int SCALE = 2;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/MoneyType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Name.java
similarity index 59%
rename from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/MoneyType.java
rename to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Name.java
index 42e2490..9e24e98 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/MoneyType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Name.java
@@ -18,19 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * name of some sort.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class MoneyType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int SCALE = 2;
+@Property(maxLength = Name.MAX_LEN)
+@Parameter(maxLength = Name.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Name {
 
-    }
+    int MAX_LEN = 50;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Notes.java
similarity index 51%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Notes.java
index 2c08e75..00ea7ba 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Notes.java
@@ -18,20 +18,32 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * notes or comments about some object.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = Notes.MAX_LEN)
+@PropertyLayout(multiLine = Notes.MULTI_LINE)
+@Parameter(maxLength = Notes.MAX_LEN)
+@ParameterLayout(multiLine = Notes.MULTI_LINE)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Notes {
 
-    }
+    int MAX_LEN = 4000;
+    int MULTI_LINE = 10;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/NotesType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/NotesType.java
deleted file mode 100644
index db77526..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/NotesType.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class NotesType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 4000;
-
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ObjectIdentifier.java
similarity index 56%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ObjectIdentifier.java
index 2c08e75..2b40374 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ObjectIdentifier.java
@@ -18,20 +18,27 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * the identity of a domain object (with respect to its logical type).
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = ObjectIdentifier.MAX_LEN)
+@Parameter(maxLength = ObjectIdentifier.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ObjectIdentifier {
 
-    }
+    int MAX_LEN = 50;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ObjectIdentifierType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ObjectIdentifierType.java
deleted file mode 100644
index 4abf1e1..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ObjectIdentifierType.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class ObjectIdentifierType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 20;
-
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Percentage.java
similarity index 60%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Percentage.java
index 2c08e75..83b06cc 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Percentage.java
@@ -18,20 +18,24 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a numeric property or parameter representing a
+ * the identity of a domain object (with respect to its logical type).
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Percentage {
 
-    }
+    int SCALE = 2;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/PercentageType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/PercentageType.java
deleted file mode 100644
index 5280f44..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/PercentageType.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class PercentageType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int SCALE = 2;
-
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ProperName.java
similarity index 57%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ProperName.java
index 2c08e75..85d698b 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ProperName.java
@@ -18,20 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing the
+ * &quot;proper&quot; (or formal) name of some sort.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = ProperName.MAX_LEN)
+@Parameter(maxLength = ProperName.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ProperName {
 
-    }
+    int MAX_LEN = 50;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ProperNameType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ProperNameType.java
deleted file mode 100644
index 08ef75b..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ProperNameType.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class ProperNameType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 50;
-
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Reference.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Reference.java
new file mode 100644
index 0000000..7f73ce8
--- /dev/null
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Reference.java
@@ -0,0 +1,47 @@
+/*
+ *  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.subdomains.base.applib.types;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+
+/**
+ * Meta-annotation for a {@link String} property or parameter representing A
+ * unique reference code of some sort.
+ *
+ * @since 2.0 {@index}
+ */
+@Property(maxLength = Reference.MAX_LEN, regexPattern = Reference.REGEX, regexPatternReplacement = Reference.REGEX_DESCRIPTION)
+@Parameter(maxLength = Reference.MAX_LEN, regexPattern = Reference.REGEX, regexPatternReplacement = Reference.REGEX_DESCRIPTION)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Reference {
+
+    int MAX_LEN = 24;
+
+    String REGEX = "[ -/_A-Z0-9]+";
+    String REGEX_DESCRIPTION = "Only capital letters, numbers and 3 symbols being: \"_\" , \"-\" and \"/\" are allowed";
+
+}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ReferenceType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ReferenceType.java
deleted file mode 100644
index 25a0656..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/ReferenceType.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class ReferenceType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 24;
-
-        public static final String REGEX = "[ -/_A-Z0-9]+";
-        public static final String REGEX_DESCRIPTION = "Only capital letters, numbers and 3 symbols being: \"_\" , \"-\" and \"/\" are allowed";
-
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Title.java
similarity index 58%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Title.java
index 2c08e75..83e12cd 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Title.java
@@ -18,20 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * title (or perhaps label) of some sort.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = Title.MAX_LEN)
+@Parameter(maxLength = Title.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Title {
 
-    }
+    int MAX_LEN = 50;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/TitleType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/TitleType.java
deleted file mode 100644
index 4e27d1b..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/TitleType.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class TitleType {
-
-    @UtilityClass
-    public static class Meta {
-        public static final int MAX_LEN = 50;
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UrlTemplate.java
similarity index 55%
copy from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
copy to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UrlTemplate.java
index 2c08e75..730dc51 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UrlTemplate.java
@@ -18,20 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing a
+ * URL template, for example for an entity that holds configuration data to access external systems.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = UrlTemplate.MAX_LEN)
+@Parameter(maxLength = UrlTemplate.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UrlTemplate {
 
-    }
+    int MAX_LEN = 254;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UrlTemplateType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UrlTemplateType.java
deleted file mode 100644
index f350eb9..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UrlTemplateType.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class UrlTemplateType {
-
-    @UtilityClass
-    public static class Meta {
-        public static final int MAX_LEN = 254;
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UserNameType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UserNameType.java
deleted file mode 100644
index c19b026..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/UserNameType.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class UserNameType {
-
-    @UtilityClass
-    public static class Meta {
-        public static final int MAX_LEN = 50;
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Username.java
similarity index 57%
rename from subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
rename to subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Username.java
index 2c08e75..97fcf11 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/DescriptionType.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/Username.java
@@ -18,20 +18,26 @@
  */
 package org.apache.isis.subdomains.base.applib.types;
 
-import lombok.experimental.UtilityClass;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
 
 /**
+ * Meta-annotation for a {@link String} property or parameter representing the
+ * unique username (or login name) of a user of the system.
+ *
  * @since 2.0 {@index}
  */
-@UtilityClass
-public class DescriptionType {
-
-    @UtilityClass
-    public static class Meta {
-
-        public static final int MAX_LEN = 254;
-        public static final int MULTI_LINE = 5;
+@Property(maxLength = Username.MAX_LEN)
+@Parameter(maxLength = Username.MAX_LEN)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Username {
 
-    }
+    int MAX_LEN = 120;
 
 }
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/XxxType.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/XxxType.java
deleted file mode 100644
index c11108e..0000000
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/types/XxxType.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  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.subdomains.base.applib.types;
-
-import lombok.experimental.UtilityClass;
-
-/**
- * @since 2.0 {@index}
- */
-@UtilityClass
-public class XxxType {
-
-    @UtilityClass
-    public static class Meta {
-
-    }
-
-}
diff --git a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/valuetypes/LocalDateInterval.java b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/valuetypes/LocalDateInterval.java
index 44a468d..3a25aff 100644
--- a/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/valuetypes/LocalDateInterval.java
+++ b/subdomains/base/applib/src/main/java/org/apache/isis/subdomains/base/applib/valuetypes/LocalDateInterval.java
@@ -36,7 +36,6 @@ public class LocalDateInterval extends AbstractInterval<LocalDateInterval>{
         return new LocalDateInterval(startDate, endDate, IntervalEnding.INCLUDING_END_DATE);
     }
 
-    // //////////////////////////////////////
 
     public LocalDateInterval() {
     }

[isis] 03/04: ISIS-2442: ports docx to latest version of docx4j, and removes guava dependency

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 856bf056e0fa4bcf92878c9e7b900351dfa06e3c
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jun 10 13:39:01 2021 +0100

    ISIS-2442: ports docx to latest version of docx4j, and removes guava dependency
---
 core/pom.xml                                       |  12 +-
 .../docx/adoc/modules/docx/examples/Template.docx  | Bin 0 -> 28756 bytes
 .../modules/docx/examples/input-exact-match.html   |  67 +++
 subdomains/docx/adoc/modules/docx/pages/about.adoc |  81 +++-
 subdomains/docx/applib/pom.xml                     |  54 ++-
 .../isis/subdomains/docx/applib/DocxService.java   | 497 +++++++++++++++++++++
 .../applib/exceptions/DocxServiceException.java    |  13 +
 .../docx/applib/exceptions/LoadInputException.java |   8 +
 .../applib/exceptions/LoadTemplateException.java   |   8 +
 .../docx/applib/exceptions/MergeException.java     |  11 +
 .../docx/applib/traverse/AllMatches.java           |  42 ++
 .../docx/applib/traverse/CallbackAbstract.java     |  32 ++
 .../docx/applib/traverse/FirstMatch.java           |  47 ++
 .../isis/subdomains/docx/applib/util/Docx.java     | 109 +++++
 .../isis/subdomains/docx/applib/util/Dump.java     | 181 ++++++++
 .../isis/subdomains/docx/applib/util/Jdom2.java    |  76 ++++
 .../isis/subdomains/docx/applib/util/Types.java    |  17 +
 .../applib/src/main/resources/docx4j.properties    |  31 ++
 .../docx/applib/DocxService_merge_Test.java        | 352 +++++++++++++++
 .../isis/subdomains/docx/applib/IoHelper.java      |  98 ++++
 .../subdomains/docx/applib/Output-Expected.docx    | Bin 0 -> 29005 bytes
 .../subdomains/docx/applib/Output-Expected.pdf     | Bin 0 -> 84424 bytes
 .../isis/subdomains/docx/applib/Template.docx      | Bin 0 -> 28756 bytes
 .../subdomains/docx/applib/input-exact-match.html  |  67 +++
 .../subdomains/docx/applib/input-malformed.html    |  69 +++
 .../isis/subdomains/docx/applib/input-missing.html |  69 +++
 .../isis/subdomains/docx/applib/input-surplus.html |  70 +++
 subdomains/pom.xml                                 |  51 ++-
 28 files changed, 2032 insertions(+), 30 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 0fd76fd..fe37753 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -111,7 +111,7 @@
 		<datanucleus-maven-plugin.version>5.2.1</datanucleus-maven-plugin.version>
 		<datanucleus-rdbms.version>5.2.8</datanucleus-rdbms.version>
 
-		<docx4j.version>6.1.2</docx4j.version>
+		<docx4j.version>8.3.0</docx4j.version>
 
 		<easymock.version>4.3</easymock.version>
 		<error_prone_annotations.version>2.7.1</error_prone_annotations.version>
@@ -190,9 +190,9 @@
 		<wicket-fullcalendar.version>4.2</wicket-fullcalendar.version>
 		<wicket-source.version>7.0.0</wicket-source.version>
 		<wicket-webjars.version>2.0.20</wicket-webjars.version>
-		
+
 		<!-- org.webjars:boostrap, NOTE: dictates popper-js.version and jquery.version -->
-		<bootstrap.version>4.6.0-1</bootstrap.version> 
+		<bootstrap.version>4.6.0-1</bootstrap.version>
 		<popper-js.version>1.16.1-lts</popper-js.version> <!-- org.webjars:popper-js, dictated by bootstrap.version -->
 
 		<wicketstuff.version>8.12.0</wicketstuff.version> <!-- org.wicketstuff:wicketstuff-select2 -->
@@ -999,7 +999,7 @@
 			</dependency>
 			<dependency>
 			    <groupId>org.webjars</groupId>
-			    <artifactId>popper.js</artifactId>		    
+			    <artifactId>popper.js</artifactId>
 				<version>${popper-js.version}</version>
 			</dependency>
 
@@ -1342,7 +1342,7 @@
 
 			<dependency>
 				<groupId>org.docx4j</groupId>
-				<artifactId>docx4j</artifactId>
+				<artifactId>docx4j-JAXB-internal</artifactId>
 				<version>${docx4j.version}</version>
 			</dependency>
 
@@ -1480,7 +1480,7 @@
 				<artifactId>jquery-ui</artifactId>
 				<version>${jquery-ui.version}</version>
 			</dependency>
- 			
+
 			<dependency>
 				<groupId>org.webjars</groupId>
 				<artifactId>select2</artifactId>
diff --git a/subdomains/docx/adoc/modules/docx/examples/Template.docx b/subdomains/docx/adoc/modules/docx/examples/Template.docx
new file mode 100644
index 0000000..d6cef7c
Binary files /dev/null and b/subdomains/docx/adoc/modules/docx/examples/Template.docx differ
diff --git a/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html b/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html
new file mode 100644
index 0000000..2a68d04
--- /dev/null
+++ b/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<html>
+	<body>
+		<p id="PPSN" class="plain">1234567A</p>
+		<p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
+		<p id="Date" class="date">31/1/2012</p>
+		<p id="Decision" class="rich">
+			I am writing to you about your claim for jobseeker's credits. I have
+			decided that you are not entitled to this benefit.
+		</p>
+		<p id="Decision2" class="rich">
+			What follows below is a table that has been merged in, adding 
+			additional rows dynamically as necessary based on the input data.
+		</p>
+		<ul id="Reasons2">
+			<li>
+				<p>This is some reason text (without a following
+					paragraph)</p>
+			</li>
+			<li>
+				<p>This would be some additional the reason text</p>
+				<p>This reason has one additional text paragraph, eg documenting
+					the grounds</p>
+			</li>
+			<li>
+				<p>This is a final reason text</p>
+				<p>It has two additional paragraphs below it, eg one for the
+O					grounds...</p>
+				<p>... and another one for the legislation.</p>
+			</li>
+		</ul>
+		<table id="Relatives">
+			<tr>
+				<td>Charlie O'Leary</td>
+				<td>Husband</td>
+				<td></td>
+			</tr>
+			<tr>
+				<td>Mary O'Leary</td>
+				<td>Daughter</td>
+				<td>14</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Billy O'Leary</td>
+				<td>Son</td>
+				<td>11</td>
+				<td>this td is ignored</td>
+			</tr>
+			<tr>
+				<td>Laura O'Leary</td>
+				<td>Daughter</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Betty O'Leary</td>
+				<td>Daughter</td>
+				<td>6</td>
+			</tr>
+			<tr>
+				<td>Maud Donoghue</td>
+				<td>Mother</td>
+				<td></td>
+			</tr>
+		</table>
+	</body>
+</html>
\ No newline at end of file
diff --git a/subdomains/docx/adoc/modules/docx/pages/about.adoc b/subdomains/docx/adoc/modules/docx/pages/about.adoc
index 495dff0..56058e5 100644
--- a/subdomains/docx/adoc/modules/docx/pages/about.adoc
+++ b/subdomains/docx/adoc/modules/docx/pages/about.adoc
@@ -1,5 +1,82 @@
-= Docx Library
+= Docx Subdomain
 
 :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 [...]
 
-WARNING: TODO
+This module provides a mail-merge capability of input data into an MS Word `.docx` templates.
+The generated output document is either Word `.docx` or Acrobat `.pdf`.
+
+[IMPORTANT]
+====
+Exporting to PDF requires more memory, both heap and permgen.
+If the PDF generation hangs then increase the memory settings, eg `-Xmx1024m -XX:MaxPermSize=128m`
+====
+
+The module consists of a single domain service, `DocxService`.
+This provides an API to merge a `.docx` template against its input data. The input data is represented as a simple HTML file.
+
+
+The service supports several data types:
+
+* plain text
+* rich text
+* date
+* bulleted list
+* tables
+
+The implementation uses link:http://www.docx4java.org[docx4j], link:https://code.google.com/p/guava-libraries/[guava] and link:http://www.jdom.org[jdom2].
+Databinding to custom XML parts (the `.docx` file format's in-built support) is *not* used (as repeating datasets - required for lists and tables - was not supported prior to Word 2013).
+
+
+include::docs:mavendeps:partial$setup-and-configure-dependencyManagement.adoc[leveloffset=+1]
+
+In addition, add a section for the BOM of all subdomains:
+
+[source,xml,subs="attributes+"]
+.pom.xml
+----
+<dependencyManagement>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.isis.subdomains</groupId>
+            <artifactId>isis-subdomains</artifactId>
+            <scope>import</scope>
+            <type>pom</type>
+            <version>{page-isisrel}</version>
+        </dependency>
+    </dependencies>
+</dependencyManagement>
+----
+
+[#dependencies]
+== Dependencies
+
+In the domain module(s) of your application, add the following dependency:
+
+[source,xml]
+.pom.xml
+----
+<dependencies>
+    <dependency>
+        <groupId>org.apache.isis.subdomains</groupId>
+        <artifactId>isis-subdomains-docx-applib</artifactId>
+    </dependency>
+</dependencies>
+----
+
+To output to PDF, the following dependency must also be added:
+
+[source,xml]
+.pom.xml
+----
+<dependency>
+    <groupId>org.docx4j</groupId>
+    <artifactId>docx4j-export-fo</artifactId>
+    <scope>test</scope>
+</dependency>
+----
+
+
+
+== Usage
+
+
diff --git a/subdomains/docx/applib/pom.xml b/subdomains/docx/applib/pom.xml
index 875c9d1..27f335e 100644
--- a/subdomains/docx/applib/pom.xml
+++ b/subdomains/docx/applib/pom.xml
@@ -1,13 +1,13 @@
 <?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 
+<!-- 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"
@@ -33,12 +33,40 @@
 			<groupId>org.apache.isis.core</groupId>
 			<artifactId>isis-applib</artifactId>
 		</dependency>
-<!--
+
+		<dependency>
+			<groupId>org.docx4j</groupId>
+			<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.jdom</groupId>
+			<artifactId>jdom2</artifactId>
+		</dependency>
+
 		<dependency>
-			<groupId>org.apache.isis.subdomains</groupId>
-			<artifactId>isis-subdomains-base-applib</artifactId>
+			<groupId>org.apache.isis.testing</groupId>
+			<artifactId>isis-testing-unittestsupport-applib</artifactId>
+			<exclusions>
+				<exclusion>
+                    <groupId>org.jmock</groupId>
+					<artifactId>jmock-junit4</artifactId>
+                </exclusion>
+			</exclusions>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.docx4j</groupId>
+			<artifactId>docx4j-export-fo</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<scope>test</scope>
 		</dependency>
--->
 	</dependencies>
 
+
 </project>
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java
new file mode 100644
index 0000000..78c6aca
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java
@@ -0,0 +1,497 @@
+package org.apache.isis.subdomains.docx.applib;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.io.IOUtils;
+import org.docx4j.Docx4J;
+import org.docx4j.XmlUtils;
+import org.docx4j.com.google.common.base.Objects;
+import org.docx4j.convert.out.FOSettings;
+import org.docx4j.fonts.IdentityPlusMapper;
+import org.docx4j.fonts.Mapper;
+import org.docx4j.openpackaging.exceptions.Docx4JException;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.docx4j.wml.Body;
+import org.docx4j.wml.P;
+import org.docx4j.wml.R;
+import org.docx4j.wml.SdtElement;
+import org.docx4j.wml.Tbl;
+import org.docx4j.wml.Tc;
+import org.docx4j.wml.Tr;
+import org.jdom2.Content;
+import org.jdom2.Element;
+import org.jdom2.input.DOMBuilder;
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+
+import org.apache.isis.subdomains.docx.applib.exceptions.LoadInputException;
+import org.apache.isis.subdomains.docx.applib.exceptions.LoadTemplateException;
+import org.apache.isis.subdomains.docx.applib.exceptions.MergeException;
+import org.apache.isis.subdomains.docx.applib.traverse.AllMatches;
+import org.apache.isis.subdomains.docx.applib.traverse.FirstMatch;
+import org.apache.isis.subdomains.docx.applib.util.Docx;
+import org.apache.isis.subdomains.docx.applib.util.Jdom2;
+import org.apache.isis.subdomains.docx.applib.util.Types;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.val;
+
+/**
+ * Provides a mail-merge capability.
+ *
+ * @since 2.x {@index}
+ */
+@Service
+public class DocxService {
+
+    /**
+     * @since 2.x {@index}
+     */
+    @Getter
+    @Builder(builderClassName = "Builder")
+    public static class MergeDefinition {
+
+        /**
+         * Defines the policy for matching input to placeholders.
+         *
+         * <p>
+         *     Does not need to be specified, will default to {@link MatchingPolicy#STRICT strict}
+         * </p>
+         */
+        @lombok.Builder.Default private MatchingPolicy matchingPolicy = MatchingPolicy.STRICT;
+
+        /**
+         * Defines whether to output as Word docx or PDF.
+         *
+         * <p>
+         *     Does not need to be specified, will default to {@link OutputType#DOCX docx}.
+         * </p>
+         */
+        @lombok.Builder.Default private OutputType outputType = OutputType.DOCX;
+
+        /**
+         * Holds the input arguments to be merged into the template.
+         *
+         * <p>
+         * Either this or {@link #getInputAsHtmlDoc()} must be specified.
+         * Preference is given to {@link #getInputAsHtmlDoc()}.
+         * </p>
+         *
+         * @see #getInputAsHtmlDoc()
+         */
+        private String inputAsHtml;
+
+        /**
+         * Holds the input arguments to be merged into the template.
+         *
+         * <p>
+         * Either this or {@link #getInputAsHtml()} must be specified, with
+         * preference given to this.
+         * </p>
+         *
+         * @see #getInputAsHtml()
+         */
+        private org.w3c.dom.Document inputAsHtmlDoc;
+
+        /**
+         * Refers to the template with place holders to be merged into.
+         *
+         * <p>
+         *     Either this or {@link #getDocxTemplateAsWpMlPackage()} myst be
+         *     specified, with preference given to {@link #getDocxTemplateAsWpMlPackage()}
+         * </p>
+         *
+         * @see #getDocxTemplateAsWpMlPackage()
+         */
+        private InputStream docxTemplate;
+
+        /**
+         * Refers to the template with place holders to be merged into.
+         *
+         * <p>
+         *     Either this or {@link #getDocxTemplate()} myst be
+         *     specified, with preference given to this.
+         * </p>
+         *
+         * @see #getDocxTemplate()
+         */
+        private WordprocessingMLPackage docxTemplateAsWpMlPackage;
+
+        /**
+         * The output stream to write to.
+         */
+        private OutputStream output;
+
+    }
+
+    /**
+     * Load and return an in-memory representation of a docx.
+     *
+     * <p>
+     * This is public API because building the in-memory structure can be
+     * quite slow.  Thus, clients can use this method to cache the in-memory
+     * structure, and pass it in the {@link MergeDefinition} (through the
+     * {@link MergeDefinition.Builder#docxTemplateAsWpMlPackage(WordprocessingMLPackage) builder method})
+     */
+    public WordprocessingMLPackage loadPackage(final InputStream docxTemplate) throws LoadTemplateException {
+        final WordprocessingMLPackage docxPkg;
+        try {
+            docxPkg = WordprocessingMLPackage.load(docxTemplate);
+        } catch (final Docx4JException ex) {
+            throw new LoadTemplateException("Unable to load docx template from input stream", ex);
+        }
+        return docxPkg;
+    }
+
+    /**
+     * Merge the input arguments (as HTML) against the Docx template, writing out as a Word docx..
+     */
+    public void merge(final MergeDefinition mergeDefn) throws LoadInputException, LoadTemplateException, MergeException {
+
+        final org.jdom2.Document htmlJdomDoc;
+        final Document inputAsHtmlDoc = mergeDefn.getInputAsHtmlDoc();
+        final String inputAsHtml = mergeDefn.getInputAsHtml();
+        if(inputAsHtmlDoc != null) {
+            htmlJdomDoc = new DOMBuilder().build(inputAsHtmlDoc);
+        } else if (inputAsHtml != null) {
+            htmlJdomDoc = Jdom2.loadInput(inputAsHtml);
+        } else {
+            throw new IllegalArgumentException("Input HTML must be provided");
+        }
+
+        final DefensiveCopy defensiveCopy;
+        final WordprocessingMLPackage docxPkg;
+        final WordprocessingMLPackage docxTemplateAsWpMlPackage = mergeDefn.getDocxTemplateAsWpMlPackage();
+        final InputStream docxTemplate = mergeDefn.getDocxTemplate();
+        if(docxTemplateAsWpMlPackage != null) {
+            docxPkg = docxTemplateAsWpMlPackage;
+            defensiveCopy = DefensiveCopy.REQUIRED;
+        } else if (docxTemplate != null) {
+            docxPkg = loadPackage(docxTemplate);
+            defensiveCopy = DefensiveCopy.NOT_REQUIRED;
+        } else {
+            throw new IllegalArgumentException("Docx template HTML must be provided");
+        }
+
+        val output = mergeDefn.getOutput();
+        if(output == null) {
+            throw new IllegalArgumentException("Output stream must be provided");
+        }
+        merge(htmlJdomDoc, docxPkg, output, mergeDefn.getMatchingPolicy(), defensiveCopy, mergeDefn.getOutputType());
+    }
+
+    private void merge(
+            final org.jdom2.Document htmlDoc,
+            final WordprocessingMLPackage docxTemplateInput,
+            final OutputStream docxTarget,
+            final MatchingPolicy matchingPolicy,
+            final DefensiveCopy defensiveCopy,
+            final OutputType outputType)
+            throws MergeException {
+
+        final WordprocessingMLPackage docxTemplate =
+                defensiveCopy == DefensiveCopy.REQUIRED
+                        ? Docx.clone(docxTemplateInput)
+                        : docxTemplateInput;
+
+        try {
+            final Element bodyEl = Jdom2.htmlBodyFor(htmlDoc);
+            final Body docXBody = Docx.docxBodyFor(docxTemplate);
+
+            merge(bodyEl, docXBody, matchingPolicy);
+
+            if (outputType == OutputType.PDF) {
+
+
+                final FOSettings foSettings = Docx4J.createFOSettings();
+                foSettings.setWmlPackage(docxTemplate);
+
+                try {
+                    final Mapper fontMapper = new IdentityPlusMapper();
+                    docxTemplate.setFontMapper(fontMapper, true);
+                } catch (final Exception e) {
+                    throw new MergeException("unable to set font mapper for PDF generation", e);
+                }
+
+                // according to the documentation/examples the XSL transformation
+                // is slower but more feature complete than Docx4J.FLAG_EXPORT_PREFER_NONXSL
+
+                final int flags = Docx4J.FLAG_EXPORT_PREFER_XSL;
+
+                Docx4J.toFO(foSettings, docxTarget, flags);
+
+            } else {
+                final File tempTargetFile = createTempFile();
+                FileInputStream tempTargetFis = null;
+                try {
+                    docxTemplate.save(tempTargetFile);
+                    tempTargetFis = new FileInputStream(tempTargetFile);
+                    IOUtils.copy(tempTargetFis, docxTarget);
+                } finally {
+                    IOUtils.closeQuietly(tempTargetFis);
+                    tempTargetFile.delete();
+                }
+            }
+        } catch (final Docx4JException e) {
+            throw new MergeException("unable to write to target file", e);
+        } catch (final FileNotFoundException e) {
+            throw new MergeException("unable to read back from target file", e);
+        } catch (final IOException e) {
+            throw new MergeException("unable to generate output stream from temporary file", e);
+        }
+    }
+
+    /**
+     * Defines the strategy as to whether placeholders must exactly input data
+     * (or whether there can be unmatched placeholders, or conversely unused input data).
+     */
+    public enum MatchingPolicy {
+        STRICT(false, false),
+        ALLOW_UNMATCHED_INPUT(true, false),
+        ALLOW_UNMATCHED_PLACEHOLDERS(false, true),
+        /**
+         * Combination of both {@link #ALLOW_UNMATCHED_INPUT} and {@link #ALLOW_UNMATCHED_PLACEHOLDERS}.
+         */
+        LAX(true, true);
+        private final boolean allowUnmatchedInput;
+        private final boolean allowUnmatchedPlaceholders;
+
+        private MatchingPolicy(final boolean allowUnmatchedInput, final boolean allowUnmatchedPlaceholders) {
+            this.allowUnmatchedInput = allowUnmatchedInput;
+            this.allowUnmatchedPlaceholders = allowUnmatchedPlaceholders;
+        }
+
+        public void unmatchedInputs(final List<String> unmatched) throws MergeException {
+            if (!allowUnmatchedInput && !unmatched.isEmpty()) {
+                throw new MergeException("Input elements " + unmatched + " were not matched to placeholders");
+            }
+        }
+
+        public void unmatchedPlaceholders(final List<String> unmatched) throws MergeException {
+            if (!allowUnmatchedPlaceholders && !unmatched.isEmpty()) {
+                throw new MergeException("Placeholders " + unmatched + " were not matched to input");
+            }
+        }
+    }
+
+    private enum DefensiveCopy {
+        REQUIRED,
+        NOT_REQUIRED
+    }
+
+    /**
+     * The type of the file to generate
+     */
+    public enum OutputType {
+        DOCX,
+        /**
+         * Support for PDF should be considered experimental.
+         */
+        PDF
+    }
+
+    private enum MergeType {
+        PLAIN("p.plain"),
+        RICH("p.rich"),
+        DATE("p.date"),
+        UL("ul") {
+            @Override
+            boolean merge(final Element htmlUl, final SdtElement sdtElement) {
+                final List<Element> htmlLiList = htmlUl.getChildren("li"); // can be empty
+
+                final List<P> docxPOrigList = AllMatches.<P>matching(sdtElement, Types.withType(P.class));
+                if (docxPOrigList.isEmpty()) {
+                    return false;
+                }
+
+                final List<P> docxPNewList = new ArrayList<P>();
+                for (final Element htmlLi : htmlLiList) {
+                    final List<Element> htmlPList = htmlLi.getChildren("p");
+
+                    for (int htmlPNum = 0; htmlPNum < htmlPList.size(); htmlPNum++) {
+                        final int numDocxPNum = docxPOrigList.size();
+                        final int docxPNum = numDocxPNum == 1 || htmlPNum == 0 ? 0 : 1;
+                        final P docxP = XmlUtils.deepCopy(docxPOrigList.get(docxPNum));
+                        docxPNewList.add(docxP);
+                        final R docxR = FirstMatch.<R>matching(docxP, Types.withType(R.class));
+                        final Element htmlP = htmlPList.get(htmlPNum);
+                        Docx.setText(docxR, Jdom2.textValueOf(htmlP));
+                    }
+                }
+
+                // remove original and replace with new
+                final List<Object> content = sdtElement.getSdtContent().getContent();
+                for (final P docxP : docxPOrigList) {
+                    content.remove(docxP);
+                }
+                for (final P docxP : docxPNewList) {
+                    content.add(docxP);
+                }
+                return true;
+            }
+        },
+        TABLE("table") {
+            @Override
+            boolean merge(final Element htmlTable, final SdtElement sdtElement) {
+
+                final List<Element> htmlTrOrigList = htmlTable.getChildren("tr"); // can be empty
+
+                final List<Object> docxContents = sdtElement.getSdtContent().getContent();
+                final Tbl docxTbl = FirstMatch.matching(docxContents, Types.withType(Tbl.class));
+                if (docxTbl == null) {
+                    return false;
+                }
+                final List<Tr> docxTrList = AllMatches.matching(docxTbl, Types.withType(Tr.class));
+                if (docxTrList.size() < 2) {
+                    // require a header row and one other
+                    return false;
+                }
+
+                final List<Tr> docxTrNewList = Lists.newArrayList();
+                for (int htmlRowNum = 0; htmlRowNum < htmlTrOrigList.size(); htmlRowNum++) {
+                    final Element htmlTr = htmlTrOrigList.get(htmlRowNum);
+
+                    final int numDocxBodyTr = docxTrList.size() - 1;
+                    final int docxTrNum = (htmlRowNum % numDocxBodyTr) + 1;
+                    final Tr docxTr = XmlUtils.deepCopy(docxTrList.get(docxTrNum));
+                    docxTrNewList.add(docxTr);
+                    final List<Tc> docxTcList = AllMatches.matching(docxTr.getContent(), Types.withType(Tc.class));
+                    final List<Element> htmlTdList = htmlTr.getChildren("td");
+                    final List<String> htmlCellValues =
+                            htmlTdList.stream().map(x -> Jdom2.textValue().apply(x))
+                            .collect(Collectors.toList());
+                    for (int cellNum = 0; cellNum < docxTcList.size(); cellNum++) {
+                        final Tc docxTc = docxTcList.get(cellNum);
+                        final String value = cellNum < htmlCellValues.size() ? htmlCellValues.get(cellNum) : "";
+                        final P docxP = FirstMatch.matching(docxTc.getContent(), Types.withType(P.class));
+                        if (docxP == null) {
+                            return false;
+                        }
+                        final R docxR = FirstMatch.matching(docxP, Types.withType(R.class));
+                        if (docxR == null) {
+                            return false;
+                        }
+                        Docx.setText(docxR, value);
+                    }
+                }
+                docxReplaceRows(docxTbl, docxTrList, docxTrNewList);
+                return true;
+            }
+
+            private void docxReplaceRows(final Tbl docxTbl, final List<Tr> docxTrList, final List<Tr> docxTrToAdd) {
+                final List<Object> docxTblContent = docxTbl.getContent();
+                boolean first = true;
+                for (final Tr docxTr : docxTrList) {
+                    if (first) {
+                        // header, do NOT remove
+                        first = false;
+                    } else {
+                        docxTblContent.remove(docxTr);
+                    }
+                }
+                for (final Tr docxTr : docxTrToAdd) {
+                    docxTblContent.add(docxTr);
+                }
+            }
+        };
+
+        private final String type;
+
+
+        private MergeType(final String type) {
+            this.type = type;
+        }
+
+        public static MergeType lookup(final String name, final String clazz) {
+            final String type = name + (clazz != null ? "." + clazz : "");
+            for (final MergeType mt : values()) {
+                if (Objects.equal(mt.type, type)) {
+                    return mt;
+                }
+            }
+            return null;
+        }
+
+        boolean merge(final Element htmlElement, final SdtElement docxElement) {
+            final String htmlTextValue = Jdom2.textValueOf(htmlElement);
+            if (htmlTextValue == null) {
+                return false;
+            }
+
+            final R docxR = FirstMatch.matching(docxElement, Types.withType(R.class));
+            if (docxR == null) {
+                return false;
+            }
+            return Docx.setText(docxR, htmlTextValue);
+        }
+    }
+
+    private static void merge(final Element htmlBody, final Body docXBody, final MatchingPolicy matchingPolicy) throws MergeException {
+        final List<String> matchedInputIds = Lists.newArrayList();
+        final List<String> unmatchedInputIds = Lists.newArrayList();
+
+        final List<Content> htmlBodyContents = htmlBody.getContent();
+        for (final Content input : htmlBodyContents) {
+            if (!(input instanceof Element)) {
+                continue;
+            }
+            mergeInto((Element) input, docXBody, matchedInputIds, unmatchedInputIds);
+        }
+
+        final List<String> unmatchedPlaceHolders = unmatchedPlaceholders(docXBody, matchedInputIds);
+
+        matchingPolicy.unmatchedInputs(unmatchedInputIds);
+        matchingPolicy.unmatchedPlaceholders(unmatchedPlaceHolders);
+    }
+
+    private static void mergeInto(final Element input, final Body docXBody, final List<String> matchedInputs, final List<String> unmatchedInputs) throws MergeException {
+
+        final String id = Jdom2.attrOf(input, "id");
+        if (id == null) {
+            throw new MergeException("Missing 'id' attribute for element within body of input HTML");
+        }
+
+        final MergeType mergeType = MergeType.lookup(input.getName(), Jdom2.attrOf(input, "class"));
+        if (mergeType == null) {
+            unmatchedInputs.add(id);
+            return;
+        }
+
+        final SdtElement docxElement = FirstMatch.matching(docXBody, Docx.withTagVal(id));
+        if (docxElement == null) {
+            unmatchedInputs.add(id);
+            return;
+        }
+
+        if (mergeType.merge(input, docxElement)) {
+            matchedInputs.add(id);
+        } else {
+            unmatchedInputs.add(id);
+        }
+    }
+
+    private static List<String> unmatchedPlaceholders(final Body docXBody, final List<String> matchedIds) {
+        final List<SdtElement> taggedElements = AllMatches.matching(docXBody, Docx.withAnyTag());
+        final List<String> unmatchedPlaceHolders = taggedElements.stream().map(x -> Docx.tagToValue().apply(x)).collect(Collectors.toList());
+        unmatchedPlaceHolders.removeAll(matchedIds);
+        return unmatchedPlaceHolders;
+    }
+
+    private static File createTempFile() throws MergeException {
+        try {
+            return File.createTempFile("docx", null);
+        } catch (final IOException ex) {
+            throw new MergeException("Unable to create temporary working file", ex);
+        }
+    }
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/DocxServiceException.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/DocxServiceException.java
new file mode 100644
index 0000000..a91e7df
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/DocxServiceException.java
@@ -0,0 +1,13 @@
+package org.apache.isis.subdomains.docx.applib.exceptions;
+
+public class DocxServiceException extends Exception {
+    public DocxServiceException(String message) {
+        super(message);
+    }
+
+    public DocxServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    private static final long serialVersionUID = 1L;
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadInputException.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadInputException.java
new file mode 100644
index 0000000..b9062bc
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadInputException.java
@@ -0,0 +1,8 @@
+package org.apache.isis.subdomains.docx.applib.exceptions;
+
+public class LoadInputException extends DocxServiceException {
+    private static final long serialVersionUID = 1L;
+    public LoadInputException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadTemplateException.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadTemplateException.java
new file mode 100644
index 0000000..bb21680
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/LoadTemplateException.java
@@ -0,0 +1,8 @@
+package org.apache.isis.subdomains.docx.applib.exceptions;
+
+public class LoadTemplateException extends DocxServiceException {
+    private static final long serialVersionUID = 1L;
+    public LoadTemplateException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/MergeException.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/MergeException.java
new file mode 100644
index 0000000..7025dfe
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/exceptions/MergeException.java
@@ -0,0 +1,11 @@
+package org.apache.isis.subdomains.docx.applib.exceptions;
+
+public class MergeException extends DocxServiceException {
+    private static final long serialVersionUID = 1L;
+    public MergeException(String message) {
+        super(message);
+    }
+    public MergeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/AllMatches.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/AllMatches.java
new file mode 100644
index 0000000..8426d4b
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/AllMatches.java
@@ -0,0 +1,42 @@
+package org.apache.isis.subdomains.docx.applib.traverse;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.apache.commons.compress.utils.Lists;
+import org.docx4j.TraversalUtil;
+import org.docx4j.XmlUtils;
+
+public class AllMatches<T> extends CallbackAbstract {
+
+    public static <T> List<T> matching(Object docxObject, Predicate<Object> predicate) {
+        return new AllMatches<T>(docxObject, predicate).getResult();
+    }
+
+    private final Object parent;
+    private final Predicate<Object> predicate;
+
+    private final List<T> result = Lists.newArrayList();
+
+    AllMatches(Object parent, Predicate<Object> predicate) {
+        this.parent = parent;
+        this.predicate = predicate;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<Object> apply(Object o) {
+        o = XmlUtils.unwrap(o);
+
+        if(predicate.test(o)) {
+            this.result.add((T) o);
+            return null;
+        }
+        return null;
+    }
+
+    public List<T> getResult() {
+        new TraversalUtil(parent, this);
+        return this.result;
+    }
+
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/CallbackAbstract.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/CallbackAbstract.java
new file mode 100644
index 0000000..55c8982
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/CallbackAbstract.java
@@ -0,0 +1,32 @@
+package org.apache.isis.subdomains.docx.applib.traverse;
+
+import java.util.List;
+
+import org.docx4j.TraversalUtil;
+import org.docx4j.TraversalUtil.Callback;
+import org.docx4j.XmlUtils;
+
+public abstract class CallbackAbstract implements Callback {
+    public void walkJAXBElements(Object parent) {
+        List<Object> children = getChildren(parent);
+        if (children == null) {
+            return;
+        }
+        for (Object o : children) {
+            // if wrapped in javax.xml.bind.JAXBElement, get its value
+            o = XmlUtils.unwrap(o);
+            apply(o);
+            if (shouldTraverse(o)) {
+                walkJAXBElements(o);
+            }
+        }
+    }
+
+    public List<Object> getChildren(Object o) {
+        return TraversalUtil.getChildrenImpl(o);
+    }
+
+    public boolean shouldTraverse(Object o) {
+        return true;
+    }
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/FirstMatch.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/FirstMatch.java
new file mode 100644
index 0000000..832e614
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/traverse/FirstMatch.java
@@ -0,0 +1,47 @@
+package org.apache.isis.subdomains.docx.applib.traverse;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.docx4j.TraversalUtil;
+import org.docx4j.XmlUtils;
+
+
+public class FirstMatch<T> extends CallbackAbstract {
+
+    public static <T> T matching(Object docxObject, Predicate<Object> predicate) {
+        return new FirstMatch<T>(docxObject, predicate).getResult();
+    }
+
+    private final Object parent;
+    private final Predicate<Object> predicate;
+
+    private T result;
+
+    FirstMatch(Object parent, Predicate<Object> predicate) {
+        this.parent = parent;
+        this.predicate = predicate;
+    }
+
+    @Override
+    public boolean shouldTraverse(Object o) {
+        return result == null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<Object> apply(Object o) {
+        o = XmlUtils.unwrap(o);
+
+        if(predicate.test(o)) {
+            this.result = (T) o;
+            return null;
+        }
+        return null;
+    }
+
+    public T getResult() {
+        new TraversalUtil(parent, this);
+        return this.result;
+    }
+
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Docx.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Docx.java
new file mode 100644
index 0000000..29662bd
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Docx.java
@@ -0,0 +1,109 @@
+package org.apache.isis.subdomains.docx.applib.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.xml.bind.JAXBException;
+
+import org.docx4j.com.google.common.base.Objects;
+import org.docx4j.convert.in.FlatOpcXmlImporter;
+import org.docx4j.convert.out.flatOpcXml.FlatOpcXmlCreator;
+import org.docx4j.openpackaging.exceptions.Docx4JException;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
+import org.docx4j.wml.Body;
+import org.docx4j.wml.R;
+import org.docx4j.wml.SdtElement;
+import org.docx4j.wml.Tag;
+
+import org.apache.isis.subdomains.docx.applib.exceptions.MergeException;
+
+public final class Docx {
+    private Docx() {
+    }
+
+    public static Function<SdtElement, String> tagToValue() {
+        return  new Function<SdtElement, String>(){
+            public String apply(SdtElement input) {
+                return input.getSdtPr().getTag().getVal();
+            }
+        };
+    }
+
+    public static Predicate<Object> withAnyTag() {
+        return new Predicate<Object>(){
+            public boolean test(Object object) {
+                if(!(object instanceof SdtElement)) {
+                    return false;
+                }
+                SdtElement sdtBlock = (SdtElement) object;
+                Tag tag = sdtBlock.getSdtPr().getTag();
+                return tag != null;
+            }
+        };
+    }
+
+    public static Predicate<Object> withTagVal(final String tagVal) {
+        return new Predicate<Object>(){
+            public boolean test(Object object) {
+                if(!(object instanceof SdtElement)) {
+                    return false;
+                }
+                SdtElement sdtBlock = (SdtElement) object;
+                Tag tag = sdtBlock.getSdtPr().getTag();
+                return tag != null && Objects.equal(tagVal, tag.getVal());
+            }
+
+        };
+    }
+
+    @SuppressWarnings({ "rawtypes", "restriction" })
+    public
+    static boolean setText(R run, String value) {
+        List<Object> runContent = run.getContent();
+        if(runContent.isEmpty()) {
+            return false;
+        }
+        Object jaxbElObj = runContent.get(0);
+
+        if(!(jaxbElObj instanceof javax.xml.bind.JAXBElement)) {
+            return false;
+        }
+        javax.xml.bind.JAXBElement jaxbElement = (javax.xml.bind.JAXBElement) jaxbElObj;
+        Object textObj = jaxbElement.getValue();
+        if(!(textObj instanceof org.docx4j.wml.Text)) {
+            return false;
+        }
+        org.docx4j.wml.Text text = (org.docx4j.wml.Text) textObj;
+        text.setValue(value);
+        return true;
+    }
+
+    public static Body docxBodyFor(WordprocessingMLPackage docxPkg) {
+        MainDocumentPart docxMdp = docxPkg.getMainDocumentPart();
+
+        org.docx4j.wml.Document docxDoc = (org.docx4j.wml.Document) docxMdp.getJaxbElement();
+        return docxDoc.getBody();
+    }
+
+    public static WordprocessingMLPackage clone(WordprocessingMLPackage docxTemplate) throws MergeException {
+        FlatOpcXmlCreator foxc = new FlatOpcXmlCreator(docxTemplate);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            foxc.marshal(baos);
+            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+            FlatOpcXmlImporter foxi = new FlatOpcXmlImporter(bais);
+            docxTemplate = (WordprocessingMLPackage) foxi.get();
+        } catch (Docx4JException e) {
+            throw new MergeException("unable to defensive copy (problem exporting)", e);
+        } catch (@SuppressWarnings("restriction") JAXBException e) {
+            throw new MergeException("unable to defensive copy (problem importing)", e);
+        }
+        return docxTemplate;
+    }
+
+
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Dump.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Dump.java
new file mode 100644
index 0000000..df3b642
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Dump.java
@@ -0,0 +1,181 @@
+package org.apache.isis.subdomains.docx.applib.util;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.docx4j.TraversalUtil;
+import org.docx4j.TraversalUtil.Callback;
+import org.docx4j.XmlUtils;
+import org.docx4j.openpackaging.exceptions.Docx4JException;
+import org.docx4j.openpackaging.packages.OpcPackage;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.docx4j.openpackaging.parts.JaxbXmlPart;
+import org.docx4j.openpackaging.parts.Part;
+import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
+import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
+import org.docx4j.relationships.Relationship;
+import org.docx4j.wml.Body;
+
+public class Dump {
+
+    public static void main(String[] args) throws Exception {
+
+        //String filename = "helloWorld.docx";
+        String filename = "TypicalDocument.docx";
+        File file = new File(System.getProperty("user.dir") + "/" +
+        		filename
+                );
+
+        System.out.println(file);
+
+        Dump dump = new Dump(file);
+        System.out.println("\nPARTS LIST");
+        dump.partsList(System.out);
+        System.out.println("\n\nDOCUMENT TRAVERSE");
+        dump.documentTraverse(System.out);
+    }
+
+    private final File file;
+
+    private Map<Part, Part> handled = new HashMap<Part, Part>();
+
+    public Dump(File file) {
+        this.file = file;
+    }
+
+    public void partsList(PrintStream out) throws Exception {
+
+        OpcPackage opcPackage = OpcPackage.load(file);
+
+        // printContentTypes(opcPackage);
+
+        RelationshipsPart rp = opcPackage.getRelationshipsPart();
+        StringBuilder sb = new StringBuilder();
+        appendInfo(rp, sb, "");
+        traverseRelationships(opcPackage, rp, sb, "  ");
+
+        out.println(sb.toString());
+
+        // SaveToZipFile saver = new SaveToZipFile(opcPackage);
+        // saver.save(System.getProperty("user.dir") + "/out.docx");
+    }
+
+    @SuppressWarnings({ "restriction", "rawtypes" })
+    private void appendInfo(Part p, StringBuilder sb, String indent) {
+
+        String relationshipType = "";
+        if (p.getSourceRelationships().size() > 0) {
+            relationshipType = p.getSourceRelationships().get(0).getType();
+        }
+
+        sb.append("\n" + indent + "Part " + p.getPartName() + " [" + p.getClass().getName() + "] " + relationshipType);
+
+        if (p instanceof JaxbXmlPart) {
+            Object o = ((JaxbXmlPart) p).getJaxbElement();
+            if (o instanceof javax.xml.bind.JAXBElement) {
+                sb.append(" containing JaxbElement:" + XmlUtils.JAXBElementDebug((javax.xml.bind.JAXBElement) o));
+            } else {
+                sb.append(" containing JaxbElement:" + o.getClass().getName());
+            }
+        }
+    }
+
+    private void traverseRelationships(OpcPackage opcPackage, RelationshipsPart rp, StringBuilder sb, String indent) {
+
+        // TODO: order by rel id
+
+        for (Relationship r : rp.getRelationships().getRelationship()) {
+
+            // log.info("\nFor Relationship Id=" + r.getId()
+            // + " Source is " + rp.getSourceP().getPartName()
+            // + ", Target is " + r.getTarget()
+            // + " type " + r.getType() + "\n");
+
+            if (r.getTargetMode() != null && r.getTargetMode().equals("External")) {
+
+                sb.append("\n" + indent + "external resource " + r.getTarget() + " of type " + r.getType());
+                continue;
+            }
+
+            Part part = rp.getPart(r);
+
+            appendInfo(part, sb, indent);
+            if (handled.get(part) != null) {
+                sb.append(" [additional reference] ");
+                continue;
+            }
+            handled.put(part, part);
+            if (part.getRelationshipsPart(false) == null) {
+                // sb.append(".. no rels" );
+            } else {
+                traverseRelationships(opcPackage, part.getRelationshipsPart(false), sb, indent + "    ");
+            }
+        }
+    }
+
+    public void documentTraverse(PrintStream out) throws Docx4JException {
+        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(file);
+        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
+
+        org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document) documentPart.getJaxbElement();
+        Body body = wmlDocumentEl.getBody();
+
+        new TraversalUtil(body,
+
+            new Callback() {
+
+                String indent = "";
+
+                public List<Object> apply(Object o) {
+
+                    String text = "";
+                    if (o instanceof org.docx4j.wml.Text)
+                        text = ((org.docx4j.wml.Text) o).getValue();
+
+                    System.out.println(indent + o.getClass().getName() + "  \"" + text + "\"");
+                    return null;
+                }
+
+                public boolean shouldTraverse(Object o) {
+                    return true;
+                }
+
+                // Depth first
+                public void walkJAXBElements(Object parent) {
+
+                    indent += "    ";
+
+                    List<Object> children = getChildren(parent);
+                    if (children != null) {
+
+                        for (Object o : children) {
+
+                            // if its wrapped in javax.xml.bind.JAXBElement, get its
+                            // value
+                            o = XmlUtils.unwrap(o);
+
+                            this.apply(o);
+
+                            if (this.shouldTraverse(o)) {
+                                walkJAXBElements(o);
+                            }
+
+                        }
+                    }
+
+                    indent = indent.substring(0, indent.length() - 4);
+                }
+
+                public List<Object> getChildren(Object o) {
+                    return TraversalUtil.getChildrenImpl(o);
+                }
+            }
+
+        );
+
+    }
+
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Jdom2.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Jdom2.java
new file mode 100644
index 0000000..e331526
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Jdom2.java
@@ -0,0 +1,76 @@
+package org.apache.isis.subdomains.docx.applib.util;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+import java.util.function.Function;
+
+import org.jdom2.Attribute;
+import org.jdom2.Content;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.Text;
+import org.jdom2.input.SAXBuilder;
+
+import org.apache.isis.subdomains.docx.applib.exceptions.LoadInputException;
+import org.apache.isis.subdomains.docx.applib.exceptions.MergeException;
+
+public final class Jdom2 {
+
+    private Jdom2(){}
+
+    public static String textValueOf(Element htmlElement) {
+        List<Content> htmlContent = htmlElement.getContent();
+        if(htmlContent.isEmpty()) {
+            return null;
+        }
+        Content content = htmlContent.get(0);
+        if(!(content instanceof Text)) {
+            return null;
+        }
+        Text htmlText = (Text) content;
+        return normalized(htmlText.getValue());
+    }
+
+
+    private static String normalized(String value) {
+        String replaceAll = value.replaceAll("\\s+", " ");
+        return replaceAll;
+    }
+
+    public static Function<Element, String> textValue() {
+        return  new Function<Element, String>(){
+        public String apply(Element input) {
+            return textValueOf(input);
+        }};
+    }
+
+    public static String attrOf(Element input, String attname) {
+        Attribute attribute = input.getAttribute(attname);
+        if(attribute == null) {
+            return null;
+        }
+        return attribute.getValue();
+    }
+
+    public static Document loadInput(String html) throws LoadInputException {
+        try {
+            return new SAXBuilder().build(new StringReader(html));
+        } catch (JDOMException e) {
+            throw new LoadInputException("Unable to parse input", e);
+        } catch (IOException e) {
+            throw new LoadInputException("Unable to parse input", e);
+        }
+    }
+
+    public static Element htmlBodyFor(Document htmlDoc) throws MergeException {
+        Element htmlEl = htmlDoc.getRootElement();
+        Element bodyEl = htmlEl.getChild("body");
+        if (bodyEl == null) {
+            throw new MergeException("cannot locate body element within the input HTML");
+        }
+        return bodyEl;
+    }
+
+}
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Types.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Types.java
new file mode 100644
index 0000000..cda1565
--- /dev/null
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/util/Types.java
@@ -0,0 +1,17 @@
+package org.apache.isis.subdomains.docx.applib.util;
+
+import java.util.function.Predicate;
+
+public final class Types {
+
+    private Types(){}
+
+    public static Predicate<Object> withType(final Class<?> cls) {
+        return new Predicate<Object>(){
+            public boolean test(Object object) {
+                return cls.isAssignableFrom(object.getClass());
+            }
+        };
+    }
+
+}
diff --git a/subdomains/docx/applib/src/main/resources/docx4j.properties b/subdomains/docx/applib/src/main/resources/docx4j.properties
new file mode 100644
index 0000000..467ca35
--- /dev/null
+++ b/subdomains/docx/applib/src/main/resources/docx4j.properties
@@ -0,0 +1,31 @@
+# Page size: use a value from org.docx4j.model.structure.PageSizePaper enum
+# eg A4, LETTER
+docx4j.PageSize=A4
+
+# Page size: use a value from org.docx4j.model.structure.MarginsWellKnown enum
+docx4j.PageMargins=NORMAL
+docx4j.PageOrientationLandscape=false
+
+# Page size: use a value from org.pptx4j.model.SlideSizesWellKnown enum
+# eg A4, LETTER
+pptx4j.PageSize=A4
+pptx4j.PageOrientationLandscape=false
+
+# These will be injected into docProps/app.xml
+# if App.Write=true
+docx4j.App.write=true
+docx4j.Application=docx4j
+docx4j.AppVersion=2.7.1
+# of the form XX.YYYY where X and Y represent numerical values
+
+# These will be injected into docProps/core.xml
+docx4j.dc.write=true
+docx4j.dc.creator.value=docx4j
+docx4j.dc.lastModifiedBy.value=docx4j
+
+#
+#docx4j.McPreprocessor=true
+
+# If you haven't configured log4j yourself
+# docx4j will autoconfigure it.  Set this to true to disable that
+docx4j.Log4j.Configurator.disabled=true
\ No newline at end of file
diff --git a/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java
new file mode 100644
index 0000000..61a963a
--- /dev/null
+++ b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java
@@ -0,0 +1,352 @@
+package org.apache.isis.subdomains.docx.applib;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+
+import org.assertj.core.data.Percentage;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assumptions.assumeThat;
+
+import org.apache.isis.subdomains.docx.applib.exceptions.LoadInputException;
+import org.apache.isis.subdomains.docx.applib.exceptions.MergeException;
+
+import lombok.val;
+
+class DocxService_merge_Test {
+
+    final IoHelper io = new IoHelper(this.getClass());
+
+    DocxService docxService;
+    WordprocessingMLPackage docxTemplate;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        docxService = new DocxService();
+
+        // given
+        final ByteArrayInputStream docxInputTemplate = io.asBais("Template.docx");
+        docxTemplate = docxService.loadPackage(docxInputTemplate);
+
+    }
+
+    @Nested
+    public class Strict {
+
+        private DocxService.MatchingPolicy matchingPolicy = DocxService.MatchingPolicy.STRICT;
+
+        @Test
+        public void exactMatch() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-exact-match.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .output(baos)
+                    .build());
+
+            // then
+            final byte[] docxActual = baos.toByteArray();
+
+            // ... for manual inspection
+            final File docxExpectedFile = io.asFile("Output-Expected.docx");
+
+            final File docxActualFile = io.asFileInSameDir(docxExpectedFile, "Output-Actual.docx");
+            io.write(docxActual, docxActualFile);
+
+            System.out.println("docx expected: " + docxExpectedFile.getAbsolutePath());
+            System.out.println("docx actual: " + docxActualFile.getAbsolutePath());
+
+
+            // ... and automated
+            // a simple binary comparison finds differences, even though a manual check using MS Word itself shows
+            // no differences; for now just do a heuristic check on file size
+            final byte[] docxExpected = io.asBytes(docxExpectedFile);
+            assertThat(docxActual.length).isCloseTo(docxExpected.length, Percentage.withPercentage(40));
+        }
+
+        @Test
+        public void whenSurplusInput() throws Exception {
+
+            // then
+            Assertions.assertThrows(MergeException.class, () -> {
+
+                // when
+                docxService.merge(DocxService.MergeDefinition.builder()
+                        .inputAsHtml(io.asString("input-surplus.html"))
+                        .docxTemplateAsWpMlPackage(docxTemplate)
+                        .output(new ByteArrayOutputStream())
+                        .build());
+
+            }, "Input elements [SURPLUS] were not matched to placeholders");
+
+        }
+
+        @Test
+        public void whenMissingInput() throws Exception {
+
+            // then
+            Assertions.assertThrows(MergeException.class, () -> {
+
+                // when
+                docxService.merge(DocxService.MergeDefinition.builder()
+                        .inputAsHtml(io.asString("input-missing.html"))
+                        .docxTemplateAsWpMlPackage(docxTemplate)
+                        .output(new ByteArrayOutputStream())
+                        .build());
+
+            }, "Placeholders [Decision2] were not matched to input");
+
+        }
+    }
+
+    @Nested
+    public class AllowUnmatchedInput {
+
+        private DocxService.MatchingPolicy matchingPolicy = DocxService.MatchingPolicy.ALLOW_UNMATCHED_INPUT;
+
+        @Test
+        public void exactMatch() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-exact-match.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+
+        @Test
+        public void whenSurplusInput() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-surplus.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then no exceptions
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+
+        @Test
+        public void whenMissingInput() throws Exception {
+
+            // then
+            Assertions.assertThrows(MergeException.class, () -> {
+
+                // when
+                docxService.merge(DocxService.MergeDefinition.builder()
+                        .inputAsHtml(io.asString("input-missing.html"))
+                        .docxTemplateAsWpMlPackage(docxTemplate)
+                        .output(new ByteArrayOutputStream())
+                        .build());
+
+            }, "Placeholders [Decision2] were not matched to input");
+        }
+
+    }
+
+    @Nested
+    public class AllowUnmatchedPlaceholders {
+
+        private DocxService.MatchingPolicy matchingPolicy = DocxService.MatchingPolicy.ALLOW_UNMATCHED_PLACEHOLDERS;
+
+        @Test
+        public void exactMatch() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-exact-match.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+
+        @Test
+        public void whenSurplusInput() throws Exception {
+
+            // then
+            Assertions.assertThrows(MergeException.class, () -> {
+
+                // when
+                docxService.merge(DocxService.MergeDefinition.builder()
+                        .inputAsHtml(io.asString("input-surplus.html"))
+                        .docxTemplateAsWpMlPackage(docxTemplate)
+                        .output(new ByteArrayOutputStream())
+                        .build());
+
+            }, "Input elements [SURPLUS] were not matched to placeholders");
+        }
+
+        @Test
+        public void whenMissingInput() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-missing.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then no exceptions
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+    }
+
+    @Nested
+    public class Lax {
+
+        private DocxService.MatchingPolicy matchingPolicy = DocxService.MatchingPolicy.LAX;
+
+        @Test
+        public void exactMatch() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-exact-match.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+
+        @Test
+        public void whenSurplusInput() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-surplus.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then no exceptions
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+
+        @Test
+        public void whenMissingInput() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-missing.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(matchingPolicy)
+                    .output(baos)
+                    .build());
+
+            // then no exceptions
+            final byte[] actual = baos.toByteArray();
+            assertThat(actual.length).isGreaterThan(0);
+        }
+
+    }
+
+
+    @Nested
+    public class BadInput {
+
+        @Test
+        public void whenBadInput() throws Exception {
+
+            Assertions.assertThrows(LoadInputException.class, () -> {
+
+                docxService.merge(DocxService.MergeDefinition.builder()
+                        .inputAsHtml(io.asString("input-malformed.html"))
+                        .matchingPolicy(DocxService.MatchingPolicy.LAX)
+                        .docxTemplateAsWpMlPackage(docxTemplate)
+                        .output(new ByteArrayOutputStream())
+                        .build());
+
+            }, "Unable to parse input");
+
+            // when
+        }
+    }
+
+    @Nested
+    public class GeneratePdf {
+
+        private DocxService.MatchingPolicy matchingPolicy = DocxService.MatchingPolicy.STRICT;
+
+        @BeforeEach
+        public void setUp() throws Exception {
+
+            // :-( font mapping issues when running in CI environments
+            assumeThat(System.getenv("TRAVIS")).isNull();
+            assumeThat(System.getenv("JENKINS_URL")).isNull();
+            assumeThat(System.getenv("GITLAB_CI")).isNull();
+        }
+
+        @Test
+        public void exactMatch() throws Exception {
+
+            // when
+            val baos = new ByteArrayOutputStream();
+
+            docxService.merge(DocxService.MergeDefinition.builder()
+                    .inputAsHtml(io.asString("input-exact-match.html"))
+                    .docxTemplateAsWpMlPackage(docxTemplate)
+                    .outputType(DocxService.OutputType.PDF)
+                    .output(baos)
+                    .build());
+
+            // then
+            final byte[] pdfActual = baos.toByteArray();
+
+            // ... for manual inspection
+            final File pdfExpectedFile = io.asFile("Output-Expected.pdf");
+
+            final File pdfActualFile = io.asFileInSameDir(pdfExpectedFile, "Output-Actual.pdf");
+            io.write(pdfActual, pdfActualFile);
+
+            System.out.println("pdf expected: " + pdfExpectedFile.getAbsolutePath());
+            System.out.println("pdf actual: " + pdfActualFile.getAbsolutePath());
+
+
+            // ... and automated
+            // a simple binary comparison finds differences, even though a manual check using MS Word itself shows
+            // no differences; for now just do a heuristic check on file size
+            final byte[] pdfExpected = io.asBytes(pdfExpectedFile);
+            assertThat(pdfActual.length).isCloseTo(pdfExpected.length, Percentage.withPercentage(20));
+        }
+    }
+
+}
diff --git a/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java
new file mode 100644
index 0000000..e6a3315
--- /dev/null
+++ b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java
@@ -0,0 +1,98 @@
+package org.apache.isis.subdomains.docx.applib;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.Charset;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Resources;
+
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+class IoHelper {
+
+    private final Class<?> baseClass;
+
+    public byte[] asBytes(String fileName) throws IOException {
+        final ByteArrayOutputStream baos = asBaos(fileName);
+        return baos.toByteArray();
+    }
+
+    public byte[] asBytes(File file) throws IOException {
+        final FileInputStream fis = new FileInputStream(file);
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ByteStreams.copy(fis, baos);
+        return baos.toByteArray();
+    }
+
+
+    public ByteArrayOutputStream asBaos(String fileName) throws IOException {
+        final ByteArrayInputStream bais = asBais(fileName);
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ByteStreams.copy(bais, baos);
+        return baos;
+    }
+
+    public String asString(final String fileName) throws IOException {
+        final URL fileUrl = asUrl(fileName);
+        return Resources.toString(fileUrl, Charset.forName("UTF-8"));
+    }
+
+    public ByteArrayInputStream asBais(final String fileName) throws IOException {
+        final URL fileUrl = asUrl(fileName);
+        return new ByteArrayInputStream(Resources.toByteArray(fileUrl));
+    }
+
+    public URL asUrl(final String fileName) {
+        return Resources.getResource(baseClass, fileName);
+    }
+
+    public File asFile(String fileName) {
+        return toFile(asUrl(fileName));
+    }
+
+    public File asFileInSameDir(final String existingFileName, String newFile) {
+        final File existingFile = asFile(existingFileName);
+        return asFileInSameDir(existingFile, newFile);
+    }
+
+    public File asFileInSameDir(File existingFile, String newFile) {
+        final File dir = existingFile.getParentFile();
+        return new File(dir, newFile);
+    }
+
+
+    public void write(byte[] bytes, File file) throws IOException {
+        final FileOutputStream targetFos = new FileOutputStream(file);
+        ByteStreams.copy(new ByteArrayInputStream(bytes), targetFos);
+    }
+
+
+    static File toFile(URL url) {
+        File file;
+        String path;
+
+        try {
+            path = url.toURI().getSchemeSpecificPart();
+            if ((file = new File(path)).exists()) return file;
+        } catch (URISyntaxException e) {
+        }
+
+        try {
+            path = url.toExternalForm();
+            if (path.startsWith("file:")) path = path.substring("file:".length());
+            if ((file = new File(path)).exists()) return file;
+
+        } catch (Exception e) {
+        }
+
+        return null;
+    }
+}
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.docx b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.docx
new file mode 100644
index 0000000..e2b23f5
Binary files /dev/null and b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.docx differ
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.pdf b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.pdf
new file mode 100644
index 0000000..78112ee
Binary files /dev/null and b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Output-Expected.pdf differ
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Template.docx b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Template.docx
new file mode 100644
index 0000000..d6cef7c
Binary files /dev/null and b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/Template.docx differ
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-exact-match.html b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-exact-match.html
new file mode 100644
index 0000000..2a68d04
--- /dev/null
+++ b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-exact-match.html
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<html>
+	<body>
+		<p id="PPSN" class="plain">1234567A</p>
+		<p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
+		<p id="Date" class="date">31/1/2012</p>
+		<p id="Decision" class="rich">
+			I am writing to you about your claim for jobseeker's credits. I have
+			decided that you are not entitled to this benefit.
+		</p>
+		<p id="Decision2" class="rich">
+			What follows below is a table that has been merged in, adding 
+			additional rows dynamically as necessary based on the input data.
+		</p>
+		<ul id="Reasons2">
+			<li>
+				<p>This is some reason text (without a following
+					paragraph)</p>
+			</li>
+			<li>
+				<p>This would be some additional the reason text</p>
+				<p>This reason has one additional text paragraph, eg documenting
+					the grounds</p>
+			</li>
+			<li>
+				<p>This is a final reason text</p>
+				<p>It has two additional paragraphs below it, eg one for the
+O					grounds...</p>
+				<p>... and another one for the legislation.</p>
+			</li>
+		</ul>
+		<table id="Relatives">
+			<tr>
+				<td>Charlie O'Leary</td>
+				<td>Husband</td>
+				<td></td>
+			</tr>
+			<tr>
+				<td>Mary O'Leary</td>
+				<td>Daughter</td>
+				<td>14</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Billy O'Leary</td>
+				<td>Son</td>
+				<td>11</td>
+				<td>this td is ignored</td>
+			</tr>
+			<tr>
+				<td>Laura O'Leary</td>
+				<td>Daughter</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Betty O'Leary</td>
+				<td>Daughter</td>
+				<td>6</td>
+			</tr>
+			<tr>
+				<td>Maud Donoghue</td>
+				<td>Mother</td>
+				<td></td>
+			</tr>
+		</table>
+	</body>
+</html>
\ No newline at end of file
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-malformed.html b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-malformed.html
new file mode 100644
index 0000000..0f9fac8
--- /dev/null
+++ b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-malformed.html
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<html>
+	<body>
+        <!-- missing closing tag -->
+		<p id="PPSN" class="plain">1234567A
+
+		<p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
+		<p id="Date" class="date">31/1/2012</p>
+		<p id="Decision" class="rich">
+			I am writing to you about your claim for jobseeker's credits. I have
+			decided that you are not entitled to this benefit.
+		</p>
+		<p id="Decision2" class="rich">
+			What follows below is a table that has been merged in, adding 
+			additional rows dynamically as necessary based on the input data.
+		</p>
+		<ul id="Reasons2">
+			<li>
+				<p>This is some reason text (without a following
+					paragraph)</p>
+			</li>
+			<li>
+				<p>This would be some additional the reason text</p>
+				<p>This reason has one additional text paragraph, eg documenting
+					the grounds</p>
+			</li>
+			<li>
+				<p>This is a final reason text</p>
+				<p>It has two additional paragraphs below it, eg one for the
+O					grounds...</p>
+				<p>... and another one for the legislation.</p>
+			</li>
+		</ul>
+		<table id="Relatives">
+			<tr>
+				<td>Charlie O'Leary</td>
+				<td>Husband</td>
+				<td></td>
+			</tr>
+			<tr>
+				<td>Mary O'Leary</td>
+				<td>Daughter</td>
+				<td>14</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Billy O'Leary</td>
+				<td>Son</td>
+				<td>11</td>
+				<td>this td is ignored</td>
+			</tr>
+			<tr>
+				<td>Laura O'Leary</td>
+				<td>Daughter</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Betty O'Leary</td>
+				<td>Daughter</td>
+				<td>6</td>
+			</tr>
+			<tr>
+				<td>Maud Donoghue</td>
+				<td>Mother</td>
+				<td></td>
+			</tr>
+		</table>
+	</body>
+</html>
\ No newline at end of file
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-missing.html b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-missing.html
new file mode 100644
index 0000000..4e8791a
--- /dev/null
+++ b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-missing.html
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<html>
+	<body>
+		<p id="PPSN" class="plain">1234567A</p>
+		<p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
+		<p id="Date" class="date">31/1/2012</p>
+		<p id="Decision" class="rich">
+			I am writing to you about your claim for jobseeker's credits. I have
+			decided that you are not entitled to this benefit.
+		</p>
+        <!-- MISSING
+		<p id="Decision2" class="rich">
+			What follows below is a table that has been merged in, adding 
+			additional rows dynamically as necessary based on the input data.
+		</p>
+		-->
+		<ul id="Reasons2">
+			<li>
+				<p>This is some reason text (without a following
+					paragraph)</p>
+			</li>
+			<li>
+				<p>This would be some additional the reason text</p>
+				<p>This reason has one additional text paragraph, eg documenting
+					the grounds</p>
+			</li>
+			<li>
+				<p>This is a final reason text</p>
+				<p>It has two additional paragraphs below it, eg one for the
+O					grounds...</p>
+				<p>... and another one for the legislation.</p>
+			</li>
+		</ul>
+		<table id="Relatives">
+			<tr>
+				<td>Charlie O'Leary</td>
+				<td>Husband</td>
+				<td></td>
+			</tr>
+			<tr>
+				<td>Mary O'Leary</td>
+				<td>Daughter</td>
+				<td>14</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Billy O'Leary</td>
+				<td>Son</td>
+				<td>11</td>
+				<td>this td is ignored</td>
+			</tr>
+			<tr>
+				<td>Laura O'Leary</td>
+				<td>Daughter</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Betty O'Leary</td>
+				<td>Daughter</td>
+				<td>6</td>
+			</tr>
+			<tr>
+				<td>Maud Donoghue</td>
+				<td>Mother</td>
+				<td></td>
+			</tr>
+		</table>
+	</body>
+</html>
\ No newline at end of file
diff --git a/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-surplus.html b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-surplus.html
new file mode 100644
index 0000000..635e3fc
--- /dev/null
+++ b/subdomains/docx/applib/src/test/resources/org/apache/isis/subdomains/docx/applib/input-surplus.html
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<html>
+	<body>
+		<p id="PPSN" class="plain">1234567A</p>
+		<p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
+		<p id="Date" class="date">31/1/2012</p>
+		<p id="Decision" class="rich">
+			I am writing to you about your claim for jobseeker's credits. I have
+			decided that you are not entitled to this benefit.
+		</p>
+		<p id="Decision2" class="rich">
+			What follows below is a table that has been merged in, adding 
+			additional rows dynamically as necessary based on the input data.
+		</p>
+        <p id="SURPLUS" class="rich">
+            This doesn't appear in the template!!!!
+        </p>
+		<ul id="Reasons2">
+			<li>
+				<p>This is some reason text (without a following
+					paragraph)</p>
+			</li>
+			<li>
+				<p>This would be some additional the reason text</p>
+				<p>This reason has one additional text paragraph, eg documenting
+					the grounds</p>
+			</li>
+			<li>
+				<p>This is a final reason text</p>
+				<p>It has two additional paragraphs below it, eg one for the
+O					grounds...</p>
+				<p>... and another one for the legislation.</p>
+			</li>
+		</ul>
+		<table id="Relatives">
+			<tr>
+				<td>Charlie O'Leary</td>
+				<td>Husband</td>
+				<td></td>
+			</tr>
+			<tr>
+				<td>Mary O'Leary</td>
+				<td>Daughter</td>
+				<td>14</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Billy O'Leary</td>
+				<td>Son</td>
+				<td>11</td>
+				<td>this td is ignored</td>
+			</tr>
+			<tr>
+				<td>Laura O'Leary</td>
+				<td>Daughter</td>
+				<!-- missing a td -->
+			</tr>
+			<tr>
+				<td>Betty O'Leary</td>
+				<td>Daughter</td>
+				<td>6</td>
+			</tr>
+			<tr>
+				<td>Maud Donoghue</td>
+				<td>Mother</td>
+				<td></td>
+			</tr>
+		</table>
+	</body>
+</html>
\ No newline at end of file
diff --git a/subdomains/pom.xml b/subdomains/pom.xml
index a1c5e03..08e3237 100644
--- a/subdomains/pom.xml
+++ b/subdomains/pom.xml
@@ -1,13 +1,13 @@
 <?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 
+<!-- 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"
@@ -33,6 +33,8 @@
 	<packaging>pom</packaging>
 
 	<properties>
+		<jdom2.version>2.0.5</jdom2.version>
+
 		<jar-plugin.automaticModuleName>org.apache.isis.subdomains</jar-plugin.automaticModuleName>
 		<git-plugin.propertiesDir>org/apache/isis/subdomains</git-plugin.propertiesDir>
 	</properties>
@@ -131,6 +133,37 @@
 				<version>2.0.0-SNAPSHOT</version>
 			</dependency>
 
+			<!-- 3rd party dependencies -->
+			<dependency>
+				<groupId>org.jdom</groupId>
+				<artifactId>jdom2</artifactId>
+				<version>${jdom2.version}</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.docx4j</groupId>
+				<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
+				<version>${docx4j.version}</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.docx4j</groupId>
+				<artifactId>docx4j-JAXB-Internal</artifactId>
+				<version>${docx4j.version}</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.docx4j</groupId>
+				<artifactId>docx4j-JAXB-MOXy</artifactId>
+				<version>${docx4j.version}</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.docx4j</groupId>
+				<artifactId>docx4j-export-fo</artifactId>
+				<version>${docx4j.version}</version>
+			</dependency>
+
 			<!-- testing -->
 			<dependency>
 				<groupId>org.apache.isis.testing</groupId>

[isis] 04/04: ISIS-2442: updates docs for docx

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0460f575254ff5b79f3153a385503b9628c5224d
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jun 10 14:37:56 2021 +0100

    ISIS-2442: updates docs for docx
---
 .../comguide/modules/ROOT/pages/starter-apps.adoc  |   4 +-
 .../pages/index/docx/applib/DocxService.adoc       |  40 ++++++++
 .../docx/applib/DocxService~MergeDefinition.adoc   |  11 ++-
 core/adoc/modules/_overview/pages/about.adoc       |  17 +++-
 .../adoc/modules/secman/pages/setting-up.adoc      |   1 -
 .../adoc/modules/ROOT/partials/component-nav.adoc  |   2 +-
 subdomains/base/adoc/modules/base/nav.adoc         |   3 +-
 .../docx/{examples => attachments}/Template.docx   | Bin
 .../modules/docx/examples/input-exact-match.html   | 104 ++++++++-------------
 .../adoc/modules/docx/images/template-docx.png     | Bin 0 -> 472174 bytes
 .../docx/images/word-enable-developer-ribbon.png   | Bin 0 -> 435844 bytes
 subdomains/docx/adoc/modules/docx/nav.adoc         |   3 +-
 subdomains/docx/adoc/modules/docx/pages/about.adoc |  61 ++++++++++--
 .../isis/subdomains/docx/applib/DocxService.java   |   8 +-
 .../docx/applib/DocxService_merge_Test.java        |  66 ++++++-------
 .../isis/subdomains/docx/applib/IoHelper.java      |   6 +-
 subdomains/excel/adoc/modules/excel/nav.adoc       |   3 +-
 .../freemarker/adoc/modules/freemarker/nav.adoc    |   3 +-
 subdomains/ognl/adoc/modules/ognl/nav.adoc         |   3 +-
 subdomains/pdfbox/adoc/modules/pdfbox/nav.adoc     |   3 +-
 subdomains/spring/adoc/modules/spring/nav.adoc     |   3 +-
 .../adoc/modules/spring/partials/module-nav.adoc   |   2 +-
 .../xdocreport/adoc/modules/xdocreport/nav.adoc    |   2 +-
 subdomains/zip/adoc/modules/zip/nav.adoc           |   3 +-
 24 files changed, 209 insertions(+), 139 deletions(-)

diff --git a/antora/components/comguide/modules/ROOT/pages/starter-apps.adoc b/antora/components/comguide/modules/ROOT/pages/starter-apps.adoc
index 3240cb7..0faa400 100644
--- a/antora/components/comguide/modules/ROOT/pages/starter-apps.adoc
+++ b/antora/components/comguide/modules/ROOT/pages/starter-apps.adoc
@@ -23,9 +23,9 @@ Doing it this way makes it easy to keep the branches in sync.
 . merge jdo-SNAPSHOT to jpa-SNAPSHOT, and fix any persistence differences
 
 [NOTE]
-----
+====
 If you do want to update the antora docs and have them updated on-line, then the procedure is to check out the released branch (M5) and make changes there.
 Then we build and copy over to the apache/isis-site repo to publish up to isis.apache.org.
-----
+====
 
 
diff --git a/antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService.adoc
new file mode 100644
index 0000000..fd7bbbc
--- /dev/null
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService.adoc
@@ -0,0 +1,40 @@
+= DocxService
+: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 [...]
+
+Provides a mail-merge capability.
+
+== API
+
+[source,java]
+.DocxService.java
+----
+class DocxService {
+  WordprocessingMLPackage loadPackage(final InputStream docxTemplate)     // <.>
+  void merge(final MergeDefinition mergeDefn)     // <.>
+}
+----
+
+<.> xref:#loadPackage__InputStream[loadPackage(InputStream)]
++
+--
+Load and return an in-memory representation of a docx.
+--
+<.> xref:#merge__MergeDefinition[merge(MergeDefinition)]
++
+--
+Merge the input arguments (as HTML) against the Docx template, writing out as a Word docx..
+--
+
+== Members
+
+[#loadPackage__InputStream]
+=== loadPackage(InputStream)
+
+Load and return an in-memory representation of a docx.
+
+This is public API because building the in-memory structure can be quite slow. Thus, clients can use this method to cache the in-memory structure, and pass it in the _MergeDefinition_ (through the _MergeDefinition.Builder#docxTemplateAsWpMlPackage(WordprocessingMLPackage) builder method_ )
+
+[#merge__MergeDefinition]
+=== merge(MergeDefinition)
+
+Merge the input arguments (as HTML) against the Docx template, writing out as a Word docx..
diff --git a/subdomains/freemarker/adoc/modules/freemarker/nav.adoc b/antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService~MergeDefinition.adoc
similarity index 86%
copy from subdomains/freemarker/adoc/modules/freemarker/nav.adoc
copy to antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService~MergeDefinition.adoc
index adfee28..2e98e0c 100644
--- a/subdomains/freemarker/adoc/modules/freemarker/nav.adoc
+++ b/antora/components/refguide-index/modules/subdomains/pages/index/docx/applib/DocxService~MergeDefinition.adoc
@@ -1,5 +1,12 @@
-
+= DocxService.MergeDefinition
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
+== API
+
+[source,java]
+.DocxService~MergeDefinition.java
+----
+class MergeDefinition {
+}
+----
 
diff --git a/core/adoc/modules/_overview/pages/about.adoc b/core/adoc/modules/_overview/pages/about.adoc
index 8786da7..1ddb743 100644
--- a/core/adoc/modules/_overview/pages/about.adoc
+++ b/core/adoc/modules/_overview/pages/about.adoc
@@ -5074,9 +5074,24 @@ Artifact: isis-subdomains-docx-applib
 Type: jar
 Directory: /subdomains/docx/applib
 ----
-|.Dependencies
+|.Components
+****
+o.a.i.subdomains.docx.applib.DocxService +
+****
+
+.Dependencies
 ****
+com.google.guava:guava:jar:<managed> +
 org.apache.isis.core:isis-applib:jar:<managed> +
+org.apache.isis.testing:isis-testing-unittestsupport-applib:jar:<managed> +
+org.docx4j:docx4j-JAXB-ReferenceImpl:jar:<managed> +
+org.docx4j:docx4j-export-fo:jar:<managed> +
+org.jdom:jdom2:jar:<managed> +
+****
+
+.Document Index Entries
+****
+xref:refguide:subdomains:index/docx/applib/DocxService.adoc[DocxService], xref:refguide:subdomains:index/docx/applib/DocxService~MergeDefinition.adoc[DocxService.MergeDefinition]
 ****
 
 |Apache Isis Sub - Freemarker (parent)
diff --git a/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc b/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
index 6498f9b..ce9a0b5 100644
--- a/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
+++ b/extensions/security/secman/adoc/modules/secman/pages/setting-up.adoc
@@ -427,7 +427,6 @@ public class UserToRole__joe_UserRw_but_NoDelete
 ----
 <.> application-specific roles
 <.> regular user access (always required)
-<.> specific access to framework features, see <<default-roles,above>>
 
 To seed in fixture scripts we could create a top-level `CustomRolesAndUsers` script (as mentioned in <<custom-seed-service,above>>).
 This would then look something like:
diff --git a/subdomains/adoc/modules/ROOT/partials/component-nav.adoc b/subdomains/adoc/modules/ROOT/partials/component-nav.adoc
index 5fd1e23..9f53811 100644
--- a/subdomains/adoc/modules/ROOT/partials/component-nav.adoc
+++ b/subdomains/adoc/modules/ROOT/partials/component-nav.adoc
@@ -3,7 +3,7 @@ include::subdomains:ROOT:partial$module-nav.adoc[]
 // commented out the libraries that have not yet been brought over from incode-platform
 
 include::subdomains:base:partial$module-nav.adoc[]
-//include::subdomains:docx:partial$module-nav.adoc[]
+include::subdomains:docx:partial$module-nav.adoc[]
 include::subdomains:excel:partial$module-nav.adoc[]
 //include::subdomains:freemarker:partial$module-nav.adoc[]
 //include::subdomains:ognl:partial$module-nav.adoc[]
diff --git a/subdomains/base/adoc/modules/base/nav.adoc b/subdomains/base/adoc/modules/base/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/base/adoc/modules/base/nav.adoc
+++ b/subdomains/base/adoc/modules/base/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/docx/adoc/modules/docx/examples/Template.docx b/subdomains/docx/adoc/modules/docx/attachments/Template.docx
similarity index 100%
rename from subdomains/docx/adoc/modules/docx/examples/Template.docx
rename to subdomains/docx/adoc/modules/docx/attachments/Template.docx
diff --git a/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html b/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html
index 2a68d04..e28bfb4 100644
--- a/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html
+++ b/subdomains/docx/adoc/modules/docx/examples/input-exact-match.html
@@ -1,67 +1,41 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <html>
-	<body>
-		<p id="PPSN" class="plain">1234567A</p>
-		<p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
-		<p id="Date" class="date">31/1/2012</p>
-		<p id="Decision" class="rich">
-			I am writing to you about your claim for jobseeker's credits. I have
-			decided that you are not entitled to this benefit.
-		</p>
-		<p id="Decision2" class="rich">
-			What follows below is a table that has been merged in, adding 
-			additional rows dynamically as necessary based on the input data.
-		</p>
-		<ul id="Reasons2">
-			<li>
-				<p>This is some reason text (without a following
-					paragraph)</p>
-			</li>
-			<li>
-				<p>This would be some additional the reason text</p>
-				<p>This reason has one additional text paragraph, eg documenting
-					the grounds</p>
-			</li>
-			<li>
-				<p>This is a final reason text</p>
-				<p>It has two additional paragraphs below it, eg one for the
-O					grounds...</p>
-				<p>... and another one for the legislation.</p>
-			</li>
-		</ul>
-		<table id="Relatives">
-			<tr>
-				<td>Charlie O'Leary</td>
-				<td>Husband</td>
-				<td></td>
-			</tr>
-			<tr>
-				<td>Mary O'Leary</td>
-				<td>Daughter</td>
-				<td>14</td>
-				<!-- missing a td -->
-			</tr>
-			<tr>
-				<td>Billy O'Leary</td>
-				<td>Son</td>
-				<td>11</td>
-				<td>this td is ignored</td>
-			</tr>
-			<tr>
-				<td>Laura O'Leary</td>
-				<td>Daughter</td>
-				<!-- missing a td -->
-			</tr>
-			<tr>
-				<td>Betty O'Leary</td>
-				<td>Daughter</td>
-				<td>6</td>
-			</tr>
-			<tr>
-				<td>Maud Donoghue</td>
-				<td>Mother</td>
-				<td></td>
-			</tr>
-		</table>
-	</body>
-</html>
\ No newline at end of file
+  <body>
+    <p id="PPSN" class="plain">1234567A</p>
+    <p id="CustomerName" class="plain">Mrs Fidelma O'Leary</p>
+    <p id="Date" class="date">31/1/2012</p>
+    <p id="Decision" class="rich">
+      I am writing to you about your claim for jobseeker's credits. I have
+      decided that you are not entitled to this benefit.
+    </p>
+    <p id="Decision2" class="rich">
+      What follows below is a table that has been merged in, adding
+      additional rows dynamically as necessary based on the input data.
+    </p>
+    <ul id="Reasons2">
+      <li>
+        <p>This is some reason text (without a following
+          paragraph)</p>
+      </li>
+      <li>
+        <p>This would be some additional the reason text</p>
+        <p>This reason has one additional text paragraph, eg documenting
+          the grounds</p>
+      </li>
+			<!-- ... -->
+    </ul>
+    <table id="Relatives">
+      <tr>
+        <td>Charlie O'Leary</td>
+        <td>Husband</td>
+        <td></td>
+      </tr>
+      <tr>
+        <td>Mary O'Leary</td>
+        <td>Daughter</td>
+        <td>14</td>
+      </tr>
+			<!-- ... -->
+    </table>
+  </body>
+</html>
diff --git a/subdomains/docx/adoc/modules/docx/images/template-docx.png b/subdomains/docx/adoc/modules/docx/images/template-docx.png
new file mode 100644
index 0000000..bd007c8
Binary files /dev/null and b/subdomains/docx/adoc/modules/docx/images/template-docx.png differ
diff --git a/subdomains/docx/adoc/modules/docx/images/word-enable-developer-ribbon.png b/subdomains/docx/adoc/modules/docx/images/word-enable-developer-ribbon.png
new file mode 100644
index 0000000..d5325d2
Binary files /dev/null and b/subdomains/docx/adoc/modules/docx/images/word-enable-developer-ribbon.png differ
diff --git a/subdomains/docx/adoc/modules/docx/nav.adoc b/subdomains/docx/adoc/modules/docx/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/docx/adoc/modules/docx/nav.adoc
+++ b/subdomains/docx/adoc/modules/docx/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/docx/adoc/modules/docx/pages/about.adoc b/subdomains/docx/adoc/modules/docx/pages/about.adoc
index 56058e5..be0bd40 100644
--- a/subdomains/docx/adoc/modules/docx/pages/about.adoc
+++ b/subdomains/docx/adoc/modules/docx/pages/about.adoc
@@ -4,12 +4,7 @@
 
 This module provides a mail-merge capability of input data into an MS Word `.docx` templates.
 The generated output document is either Word `.docx` or Acrobat `.pdf`.
-
-[IMPORTANT]
-====
-Exporting to PDF requires more memory, both heap and permgen.
-If the PDF generation hangs then increase the memory settings, eg `-Xmx1024m -XX:MaxPermSize=128m`
-====
+(Be aware that exporting to PDF requires more memory).
 
 The module consists of a single domain service, `DocxService`.
 This provides an API to merge a `.docx` template against its input data. The input data is represented as a simple HTML file.
@@ -23,10 +18,11 @@ The service supports several data types:
 * bulleted list
 * tables
 
-The implementation uses link:http://www.docx4java.org[docx4j], link:https://code.google.com/p/guava-libraries/[guava] and link:http://www.jdom.org[jdom2].
+The implementation uses link:http://www.docx4java.org[docx4j] and link:http://www.jdom.org[jdom2].
 Databinding to custom XML parts (the `.docx` file format's in-built support) is *not* used (as repeating datasets - required for lists and tables - was not supported prior to Word 2013).
 
 
+
 include::docs:mavendeps:partial$setup-and-configure-dependencyManagement.adoc[leveloffset=+1]
 
 In addition, add a section for the BOM of all subdomains:
@@ -71,12 +67,59 @@ To output to PDF, the following dependency must also be added:
 <dependency>
     <groupId>org.docx4j</groupId>
     <artifactId>docx4j-export-fo</artifactId>
-    <scope>test</scope>
 </dependency>
 ----
 
 
-
 == Usage
 
+The `.docx` templates use Word custom content controls as placeholders.
+The actions to work with these placeholders can be enabled by toggling on the "Developer" menu:
+
+image::word-enable-developer-ribbon.png[width=400px]
+
+You can then toggle on _Design Mode_ to create/edit/remove custom content controls.
+
+[[template-docx]]
+For example, see link:{attachmentsdir}/Template.docx[Template.docx].
+
+image::template-docx.png[width=600px]
+
+To programmatically mail-merge into the template, we create a HTML document that provides the input.
+For example:
+
+[[input-html]]
+[source,xhtml]
+.input HTML
+----
+include::example$input-exact-match.html[]
+----
+
+We also parse the template into an internal data structure.
+This is usually done during bootstrapping as it is almost certainly immutable, and the parsing can take a second or two:
+
+[source,java]
+----
+WordprocessingMLPackage docxTemplate =
+    docxService.loadPackage(io.openInputStream("Template.docx"));
+----
+
+We then merge in the input to the template as follows:
+
+[source,java]
+----
+val baos = new ByteArrayOutputStream();
+val params = DocxService.MergeParams.builder()
+        .docxTemplateAsWpMlPackage(docxTemplate)            // <.>
+        .inputAsHtml(inputHtml)                             // <.>
+        .matchingPolicy(DocxService.MatchingPolicy.STRICT)
+        .outputType(DocxService.OutputType.DOCX)
+        .output(baos)
+        .build();
+docxService.merge(params);
+
+final byte[] docxActual = baos.toByteArray();
+----
 
+<.> docx template, as shown <<template-docx,above>>
+<.> input HTML, as shown <<input-html,above>>
diff --git a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java
index 78c6aca..3b4b7a9 100644
--- a/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java
+++ b/subdomains/docx/applib/src/main/java/org/apache/isis/subdomains/docx/applib/DocxService.java
@@ -59,7 +59,7 @@ public class DocxService {
      */
     @Getter
     @Builder(builderClassName = "Builder")
-    public static class MergeDefinition {
+    public static class MergeParams {
 
         /**
          * Defines the policy for matching input to placeholders.
@@ -140,8 +140,8 @@ public class DocxService {
      * <p>
      * This is public API because building the in-memory structure can be
      * quite slow.  Thus, clients can use this method to cache the in-memory
-     * structure, and pass it in the {@link MergeDefinition} (through the
-     * {@link MergeDefinition.Builder#docxTemplateAsWpMlPackage(WordprocessingMLPackage) builder method})
+     * structure, and pass it in the {@link MergeParams} (through the
+     * {@link MergeParams.Builder#docxTemplateAsWpMlPackage(WordprocessingMLPackage) builder method})
      */
     public WordprocessingMLPackage loadPackage(final InputStream docxTemplate) throws LoadTemplateException {
         final WordprocessingMLPackage docxPkg;
@@ -156,7 +156,7 @@ public class DocxService {
     /**
      * Merge the input arguments (as HTML) against the Docx template, writing out as a Word docx..
      */
-    public void merge(final MergeDefinition mergeDefn) throws LoadInputException, LoadTemplateException, MergeException {
+    public void merge(final MergeParams mergeDefn) throws LoadInputException, LoadTemplateException, MergeException {
 
         final org.jdom2.Document htmlJdomDoc;
         final Document inputAsHtmlDoc = mergeDefn.getInputAsHtmlDoc();
diff --git a/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java
index 61a963a..3569cda 100644
--- a/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java
+++ b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/DocxService_merge_Test.java
@@ -1,6 +1,5 @@
 package org.apache.isis.subdomains.docx.applib;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 
@@ -31,8 +30,7 @@ class DocxService_merge_Test {
         docxService = new DocxService();
 
         // given
-        final ByteArrayInputStream docxInputTemplate = io.asBais("Template.docx");
-        docxTemplate = docxService.loadPackage(docxInputTemplate);
+        docxTemplate = docxService.loadPackage(io.openInputStream("Template.docx"));
 
     }
 
@@ -46,12 +44,14 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-exact-match.html"))
+            val params = DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-exact-match.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
+                    .matchingPolicy(DocxService.MatchingPolicy.STRICT)
+                    .outputType(DocxService.OutputType.DOCX)
                     .output(baos)
-                    .build());
+                    .build();
+            docxService.merge(params);
 
             // then
             final byte[] docxActual = baos.toByteArray();
@@ -80,8 +80,8 @@ class DocxService_merge_Test {
             Assertions.assertThrows(MergeException.class, () -> {
 
                 // when
-                docxService.merge(DocxService.MergeDefinition.builder()
-                        .inputAsHtml(io.asString("input-surplus.html"))
+                docxService.merge(DocxService.MergeParams.builder()
+                        .inputAsHtml(io.readFileAsString("input-surplus.html"))
                         .docxTemplateAsWpMlPackage(docxTemplate)
                         .output(new ByteArrayOutputStream())
                         .build());
@@ -97,8 +97,8 @@ class DocxService_merge_Test {
             Assertions.assertThrows(MergeException.class, () -> {
 
                 // when
-                docxService.merge(DocxService.MergeDefinition.builder()
-                        .inputAsHtml(io.asString("input-missing.html"))
+                docxService.merge(DocxService.MergeParams.builder()
+                        .inputAsHtml(io.readFileAsString("input-missing.html"))
                         .docxTemplateAsWpMlPackage(docxTemplate)
                         .output(new ByteArrayOutputStream())
                         .build());
@@ -118,8 +118,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-exact-match.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-exact-match.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -135,8 +135,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-surplus.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-surplus.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -154,8 +154,8 @@ class DocxService_merge_Test {
             Assertions.assertThrows(MergeException.class, () -> {
 
                 // when
-                docxService.merge(DocxService.MergeDefinition.builder()
-                        .inputAsHtml(io.asString("input-missing.html"))
+                docxService.merge(DocxService.MergeParams.builder()
+                        .inputAsHtml(io.readFileAsString("input-missing.html"))
                         .docxTemplateAsWpMlPackage(docxTemplate)
                         .output(new ByteArrayOutputStream())
                         .build());
@@ -175,8 +175,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-exact-match.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-exact-match.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -194,8 +194,8 @@ class DocxService_merge_Test {
             Assertions.assertThrows(MergeException.class, () -> {
 
                 // when
-                docxService.merge(DocxService.MergeDefinition.builder()
-                        .inputAsHtml(io.asString("input-surplus.html"))
+                docxService.merge(DocxService.MergeParams.builder()
+                        .inputAsHtml(io.readFileAsString("input-surplus.html"))
                         .docxTemplateAsWpMlPackage(docxTemplate)
                         .output(new ByteArrayOutputStream())
                         .build());
@@ -208,8 +208,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-missing.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-missing.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -231,8 +231,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-exact-match.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-exact-match.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -248,8 +248,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-surplus.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-surplus.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -265,8 +265,8 @@ class DocxService_merge_Test {
 
             // when
             val baos = new ByteArrayOutputStream();
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-missing.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-missing.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .matchingPolicy(matchingPolicy)
                     .output(baos)
@@ -288,8 +288,8 @@ class DocxService_merge_Test {
 
             Assertions.assertThrows(LoadInputException.class, () -> {
 
-                docxService.merge(DocxService.MergeDefinition.builder()
-                        .inputAsHtml(io.asString("input-malformed.html"))
+                docxService.merge(DocxService.MergeParams.builder()
+                        .inputAsHtml(io.readFileAsString("input-malformed.html"))
                         .matchingPolicy(DocxService.MatchingPolicy.LAX)
                         .docxTemplateAsWpMlPackage(docxTemplate)
                         .output(new ByteArrayOutputStream())
@@ -321,8 +321,8 @@ class DocxService_merge_Test {
             // when
             val baos = new ByteArrayOutputStream();
 
-            docxService.merge(DocxService.MergeDefinition.builder()
-                    .inputAsHtml(io.asString("input-exact-match.html"))
+            docxService.merge(DocxService.MergeParams.builder()
+                    .inputAsHtml(io.readFileAsString("input-exact-match.html"))
                     .docxTemplateAsWpMlPackage(docxTemplate)
                     .outputType(DocxService.OutputType.PDF)
                     .output(baos)
diff --git a/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java
index e6a3315..df6700e 100644
--- a/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java
+++ b/subdomains/docx/applib/src/test/java/org/apache/isis/subdomains/docx/applib/IoHelper.java
@@ -34,18 +34,18 @@ class IoHelper {
 
 
     public ByteArrayOutputStream asBaos(String fileName) throws IOException {
-        final ByteArrayInputStream bais = asBais(fileName);
+        final ByteArrayInputStream bais = openInputStream(fileName);
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ByteStreams.copy(bais, baos);
         return baos;
     }
 
-    public String asString(final String fileName) throws IOException {
+    public String readFileAsString(final String fileName) throws IOException {
         final URL fileUrl = asUrl(fileName);
         return Resources.toString(fileUrl, Charset.forName("UTF-8"));
     }
 
-    public ByteArrayInputStream asBais(final String fileName) throws IOException {
+    public ByteArrayInputStream openInputStream(final String fileName) throws IOException {
         final URL fileUrl = asUrl(fileName);
         return new ByteArrayInputStream(Resources.toByteArray(fileUrl));
     }
diff --git a/subdomains/excel/adoc/modules/excel/nav.adoc b/subdomains/excel/adoc/modules/excel/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/excel/adoc/modules/excel/nav.adoc
+++ b/subdomains/excel/adoc/modules/excel/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/freemarker/adoc/modules/freemarker/nav.adoc b/subdomains/freemarker/adoc/modules/freemarker/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/freemarker/adoc/modules/freemarker/nav.adoc
+++ b/subdomains/freemarker/adoc/modules/freemarker/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/ognl/adoc/modules/ognl/nav.adoc b/subdomains/ognl/adoc/modules/ognl/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/ognl/adoc/modules/ognl/nav.adoc
+++ b/subdomains/ognl/adoc/modules/ognl/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/pdfbox/adoc/modules/pdfbox/nav.adoc b/subdomains/pdfbox/adoc/modules/pdfbox/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/pdfbox/adoc/modules/pdfbox/nav.adoc
+++ b/subdomains/pdfbox/adoc/modules/pdfbox/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/spring/adoc/modules/spring/nav.adoc b/subdomains/spring/adoc/modules/spring/nav.adoc
index afdc817..dbad73b 100644
--- a/subdomains/spring/adoc/modules/spring/nav.adoc
+++ b/subdomains/spring/adoc/modules/spring/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:spring:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/spring/adoc/modules/spring/partials/module-nav.adoc b/subdomains/spring/adoc/modules/spring/partials/module-nav.adoc
index 2d9bc19..5e19816 100644
--- a/subdomains/spring/adoc/modules/spring/partials/module-nav.adoc
+++ b/subdomains/spring/adoc/modules/spring/partials/module-nav.adoc
@@ -1,6 +1,6 @@
 
 
 
-* xref:subdomains:spring:about.adoc[Spring]
+* xref:subdomains:spring:about.adoc[Spring Library]
 
 
diff --git a/subdomains/xdocreport/adoc/modules/xdocreport/nav.adoc b/subdomains/xdocreport/adoc/modules/xdocreport/nav.adoc
index 173ce99..dbad73b 100644
--- a/subdomains/xdocreport/adoc/modules/xdocreport/nav.adoc
+++ b/subdomains/xdocreport/adoc/modules/xdocreport/nav.adoc
@@ -1,4 +1,4 @@
 
 :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 [...]
-include::extensions:ROOT:partial$component-nav.adoc[]
 
+include::subdomains:ROOT:partial$component-nav.adoc[]
diff --git a/subdomains/zip/adoc/modules/zip/nav.adoc b/subdomains/zip/adoc/modules/zip/nav.adoc
index adfee28..dbad73b 100644
--- a/subdomains/zip/adoc/modules/zip/nav.adoc
+++ b/subdomains/zip/adoc/modules/zip/nav.adoc
@@ -1,5 +1,4 @@
 
 :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 [...]
 
-include::extensions:ROOT:partial$component-nav.adoc[]
-
+include::subdomains:ROOT:partial$component-nav.adoc[]