This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch 3937-grid.api.overhaul in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 835258b7735f8d879ae8117099c83fe167d1caf3 Author: Andi Huber <[email protected]> AuthorDate: Mon Oct 27 08:07:45 2025 +0100 CAUSEWAY-2297: grid refactoring --- .../apache/causeway/applib/layout/grid/Grid.java | 41 +-- .../applib/layout/grid/bootstrap/BSElement.java | 14 +- .../applib/layout/grid/bootstrap/BSGrid.java | 13 +- .../layout/grid/bootstrap/BSGridTransformer.java | 7 +- .../applib/layout/grid/bootstrap/BSUtil.java | 11 +- .../applib/layout/grid/bootstrap/BSWalker.java | 113 +++---- .../applib/services/grid/GridLoaderService.java | 40 +-- .../applib/services/grid/GridMarshaller.java | 13 +- .../causeway/applib/services/grid/GridService.java | 120 +++---- .../applib/services/grid/GridSystemService.java | 71 ++-- .../metamodel/facets/object/grid/BSGridFacet.java | 21 +- .../metamodel/facets/object/grid/GridFacet.java | 5 +- .../core/metamodel/layout/LayoutFacetUtil.java | 372 ++++++++++----------- .../services/grid/GridLoaderServiceDefault.java | 13 +- .../services/grid/GridServiceDefault.java | 18 +- .../services/grid/GridSystemServiceAbstract.java | 25 +- .../grid/XsiSchemaLocationProviderForGrid.java | 39 +-- .../grid/bootstrap/GridInitializationModel.java | 8 +- .../bootstrap/GridMarshallerServiceBootstrap.java | 4 +- .../grid/bootstrap/GridSystemServiceBootstrap.java | 28 +- .../causeway/core/metamodel/util/Facets.java | 1 - .../mmtestsupport/MetaModelContext_forTesting.java | 37 +- .../sitemap/SitemapServiceDefault.java | 4 +- .../help/topics/welcome/WelcomeHelpPage.java | 45 +-- .../resources/DomainObjectResourceServerside.java | 9 +- 25 files changed, 460 insertions(+), 612 deletions(-) diff --git a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/Grid.java b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/Grid.java index 2175f0a2ec7..f3592af66ec 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/Grid.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/Grid.java @@ -18,13 +18,13 @@ */ package org.apache.causeway.applib.layout.grid; +import java.io.Serializable; +import java.util.Map; import java.util.stream.Stream; import org.apache.causeway.applib.annotation.Programmatic; import org.apache.causeway.applib.layout.component.ActionLayoutData; import org.apache.causeway.applib.layout.component.CollectionLayoutData; -import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; -import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; import org.apache.causeway.applib.services.layout.LayoutService; @@ -35,11 +35,19 @@ * * @since 1.x revised for 4.0 {@index} */ +@Deprecated @Programmatic public interface Grid { Class<?> domainClass(); + /** + * Arbitrary additional 'runtime' data attributed to this grid, + * but not part of the DTO specification. + * @since 4.0 + */ + Map<String, Serializable> attributes(); + /** * Indicates whether or not this grid is a fallback. * {@code True}, if this Grid originates from @@ -55,33 +63,4 @@ public interface Grid { Stream<CollectionLayoutData> streamCollectionLayoutData(); Stream<ActionLayoutData> streamActionLayoutData(); - interface Visitor { - default void visit(final DomainObjectLayoutData domainObjectLayoutData) {} - default void visit(final ActionLayoutData actionLayoutData) {} - default void visit(final PropertyLayoutData propertyLayoutData) {} - default void visit(final CollectionLayoutData collectionLayoutData) {} - default void visit(final FieldSet fieldSet) {} - } - - void visit(final Grid.Visitor visitor); - - // -- EMPTY GRID - - final static Grid EMPTY = new EmptyGrid(Object.class); - static Grid empty() { return EMPTY; } - - public record EmptyGrid(Class<?> domainClass) implements Grid { - @Override public boolean isFallback() { return false; } - @Override public boolean isNormalized() { return true; } - @Override public Stream<PropertyLayoutData> streamPropertyLayoutData() { - return Stream.empty(); - } - @Override public Stream<CollectionLayoutData> streamCollectionLayoutData() { - return Stream.empty(); - } - @Override public Stream<ActionLayoutData> streamActionLayoutData() { - return Stream.empty(); - } - @Override public void visit(Visitor visitor) { } - }; } diff --git a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSElement.java b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSElement.java index cf75b3ae304..665f88969fa 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSElement.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSElement.java @@ -20,7 +20,11 @@ import java.io.Serializable; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.component.ActionLayoutData; +import org.apache.causeway.applib.layout.component.CollectionLayoutData; +import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; +import org.apache.causeway.applib.layout.component.FieldSet; +import org.apache.causeway.applib.layout.component.PropertyLayoutData; /** * @since 1.x {@index} @@ -35,7 +39,7 @@ public interface BSElement extends Serializable { String getCssClass(); void setCssClass(final String cssClass); - public interface Visitor extends Grid.Visitor { + public interface BSElementVisitor { default void preVisit(final BSGrid bsGrid) {} default void visit(final BSGrid bsGrid) {} default void postVisit(final BSGrid bsGrid) {} @@ -52,6 +56,12 @@ default void postVisit(final BSTabGroup bsTabGroup) {} default void preVisit(final BSTab bsTab) {} default void visit(final BSTab bsTab) {} default void postVisit(final BSTab bsTab) {} + + default void visit(final DomainObjectLayoutData domainObjectLayoutData) {} + default void visit(final ActionLayoutData actionLayoutData) {} + default void visit(final PropertyLayoutData propertyLayoutData) {} + default void visit(final CollectionLayoutData collectionLayoutData) {} + default void visit(final FieldSet fieldSet) {} } } diff --git a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGrid.java b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGrid.java index bf75467581c..5638d4aa404 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGrid.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGrid.java @@ -18,8 +18,10 @@ */ package org.apache.causeway.applib.layout.grid.bootstrap; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Stream; import jakarta.xml.bind.annotation.XmlAccessType; @@ -54,6 +56,8 @@ public final class BSGrid implements Grid, BSElement, Dto, BSRowOwner { @XmlTransient @Getter @Accessors(fluent=true) @Setter private Class<?> domainClass; @XmlTransient @Getter @Setter private boolean fallback; + @XmlTransient @Getter @Accessors(fluent=true) private final Map<String, Serializable> attributes = Map.of(); + @XmlTransient @Getter @Setter private boolean normalized; @XmlAttribute(required = false) @@ -67,15 +71,14 @@ public final class BSGrid implements Grid, BSElement, Dto, BSRowOwner { @XmlElement(name = "metadataError", required = false) @Getter private final List<String> metadataErrors = new ArrayList<>(); - @Override - public void visit(final Grid.Visitor visitor) { + public void visit(final BSElementVisitor visitor) { new BSWalker(this).walk(visitor); } @Override public Stream<PropertyLayoutData> streamPropertyLayoutData() { final var properties = new ArrayList<PropertyLayoutData>(); - visit(new BSElement.Visitor() { + visit(new BSElementVisitor() { @Override public void visit(final PropertyLayoutData propertyLayoutData) { properties.add(propertyLayoutData); @@ -87,7 +90,7 @@ public void visit(final PropertyLayoutData propertyLayoutData) { @Override public Stream<CollectionLayoutData> streamCollectionLayoutData() { final var collections = new ArrayList<CollectionLayoutData>(); - visit(new BSElement.Visitor() { + visit(new BSElementVisitor() { @Override public void visit(final CollectionLayoutData collectionLayoutData) { collections.add(collectionLayoutData); @@ -99,7 +102,7 @@ public void visit(final CollectionLayoutData collectionLayoutData) { @Override public Stream<ActionLayoutData> streamActionLayoutData() { final var actions = new ArrayList<ActionLayoutData>(); - visit(new BSElement.Visitor() { + visit(new BSElementVisitor() { @Override public void visit(final ActionLayoutData actionLayoutData) { actions.add(actionLayoutData); diff --git a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGridTransformer.java b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGridTransformer.java index 36605c7360d..4bc5ed8da04 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGridTransformer.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSGridTransformer.java @@ -26,6 +26,7 @@ import org.apache.causeway.applib.layout.component.CollectionLayoutData; import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; import org.apache.causeway.applib.layout.component.PropertyLayoutData; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; import org.apache.causeway.commons.internal.base._NullSafe; @FunctionalInterface @@ -45,7 +46,7 @@ public BSGrid apply(final BSGrid bsGrid) { var emptyTabs = new ArrayList<BSTab>(); // first phase: collect all empty tabs for removal - bsGrid.visit(new BSElement.Visitor() { + bsGrid.visit(new BSElementVisitor() { final Stack<Flag> stack = new Stack<Flag>(); @@ -102,7 +103,7 @@ static final class Flag { public BSGrid apply(final BSGrid bsGrid) { var emptyRows = new ArrayList<BSRow>(); - bsGrid.visit(new BSElement.Visitor() { + bsGrid.visit(new BSElementVisitor() { final Stack<Flag> stack = new Stack<Flag>(); @@ -152,7 +153,7 @@ record CollapseIfOneTab() implements BSGridTransformer { @Override public BSGrid apply(final BSGrid bsGrid) { - bsGrid.visit(new BSElement.Visitor() { + bsGrid.visit(new BSElementVisitor() { @Override public void visit(final BSTabGroup bsTabGroup) { if(bsTabGroup.getTabs().size()!=1) return; // when has no tabs is also a no-op diff --git a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSUtil.java b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSUtil.java index e03d859cbdc..235ef3b9c82 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSUtil.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSUtil.java @@ -25,6 +25,7 @@ import org.apache.causeway.applib.layout.component.CollectionLayoutData; import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; import org.apache.causeway.applib.layout.component.PropertyLayoutData; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; import org.apache.causeway.commons.internal.base._Casts; import org.apache.causeway.commons.internal.resources._Serializables; @@ -46,7 +47,7 @@ public class BSUtil { */ public boolean hasContent(final BSTab thisBsTab) { final AtomicBoolean foundContent = new AtomicBoolean(false); - new BSWalker(thisBsTab).walk(new BSElement.Visitor() { + new BSWalker(thisBsTab).walk(new BSElementVisitor() { @Override public void visit(final DomainObjectLayoutData domainObjectLayoutData) { foundContent.set(true); @@ -84,28 +85,28 @@ public BSGrid resolveOwners(final BSGrid grid) { // -- REMOVERS /** removes the tab from its owner and returns the owner */ - public Optional<BSTabOwner> remove(BSTab tab) { + public Optional<BSTabOwner> remove(final BSTab tab) { var ownerOpt = Optional.ofNullable(tab.owner()); ownerOpt.ifPresent(owner->owner.getTabs().remove(tab)); tab.owner(null); return ownerOpt; } /** removes the col from its owner and returns the owner */ - public Optional<BSRowContentOwner> remove(BSCol col) { + public Optional<BSRowContentOwner> remove(final BSCol col) { var ownerOpt = Optional.ofNullable(col.owner()); ownerOpt.ifPresent(owner->owner.getRowContents().remove(col)); col.owner(null); return ownerOpt; } /** removes the tabGroup from its owner and returns the owner */ - public Optional<BSTabGroupOwner> remove(BSTabGroup tabGroup) { + public Optional<BSTabGroupOwner> remove(final BSTabGroup tabGroup) { var ownerOpt = Optional.ofNullable(tabGroup.owner()); ownerOpt.ifPresent(owner->owner.getTabGroups().remove(tabGroup)); tabGroup.owner(null); return ownerOpt; } /** removes the row from its owner and returns the owner */ - public Optional<BSRowOwner> remove(BSRow row) { + public Optional<BSRowOwner> remove(final BSRow row) { var ownerOpt = Optional.ofNullable(row.owner()); ownerOpt.ifPresent(owner->owner.getRows().remove(row)); row.owner(null); diff --git a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSWalker.java b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSWalker.java index 94e81bdf6e1..afe19771755 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSWalker.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/layout/grid/bootstrap/BSWalker.java @@ -29,47 +29,46 @@ import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.FieldSetOwner; import org.apache.causeway.applib.layout.component.PropertyLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; public record BSWalker(BSRowOwner root) { - public void walk(final Grid.Visitor visitor) { - final BSElement.Visitor bsVisitor = asBsVisitor(visitor); + public void walk(final BSElementVisitor visitor) { if(root instanceof BSGrid bsGrid) { - bsVisitor.preVisit(bsGrid); - bsVisitor.visit(bsGrid); - traverseRows(root, bsVisitor); - bsVisitor.postVisit(bsGrid); + visitor.preVisit(bsGrid); + visitor.visit(bsGrid); + traverseRows(root, visitor); + visitor.postVisit(bsGrid); } else { - traverseRows(root, bsVisitor); + traverseRows(root, visitor); } } - private void traverseRows(final BSRowOwner rowOwner, final BSElement.Visitor bsVisitor) { + private void traverseRows(final BSRowOwner rowOwner, final BSElementVisitor visitor) { final List<BSRow> rows = rowOwner.getRows(); for (BSRow bsRow : new ArrayList<>(rows)) { - bsVisitor.preVisit(bsRow); - bsVisitor.visit(bsRow); - traverseCols(bsRow, bsVisitor); - bsVisitor.postVisit(bsRow); + visitor.preVisit(bsRow); + visitor.visit(bsRow); + traverseCols(bsRow, visitor); + visitor.postVisit(bsRow); } } - private void traverseCols(final BSRow bsRow, final BSElement.Visitor bsVisitor) { + private void traverseCols(final BSRow bsRow, final BSElementVisitor visitor) { final List<BSRowContent> cols = bsRow.getRowContents(); for (BSRowContent rowContent : new ArrayList<>(cols)) { if(rowContent instanceof BSCol bsCol) { - bsVisitor.preVisit(bsCol); - bsVisitor.visit(bsCol); - traverseDomainObject(bsCol, bsVisitor); - traverseTabGroups(bsCol, bsVisitor); - traverseActions(bsCol, bsVisitor); - traverseFieldSets(bsCol, bsVisitor); - traverseCollections(bsCol, bsVisitor); - traverseRows(bsCol, bsVisitor); - bsVisitor.postVisit(bsCol); + visitor.preVisit(bsCol); + visitor.visit(bsCol); + traverseDomainObject(bsCol, visitor); + traverseTabGroups(bsCol, visitor); + traverseActions(bsCol, visitor); + traverseFieldSets(bsCol, visitor); + traverseCollections(bsCol, visitor); + traverseRows(bsCol, visitor); + visitor.postVisit(bsCol); } else if (rowContent instanceof BSClearFix bsClearFix) { - bsVisitor.visit(bsClearFix); + visitor.visit(bsClearFix); } else { throw new IllegalStateException( "Unrecognized implementation of BSRowContent, " + rowContent); @@ -77,83 +76,61 @@ private void traverseCols(final BSRow bsRow, final BSElement.Visitor bsVisitor) } } - private void traverseDomainObject(final BSCol bsCol, final BSElement.Visitor bsVisitor) { + private void traverseDomainObject(final BSCol bsCol, final BSElementVisitor visitor) { final DomainObjectLayoutData domainObject = bsCol.getDomainObject(); if(domainObject == null) return; - bsVisitor.visit(domainObject); + visitor.visit(domainObject); } - private void traverseTabGroups(final BSTabGroupOwner bsTabGroupOwner, final BSElement.Visitor bsVisitor) { + private void traverseTabGroups(final BSTabGroupOwner bsTabGroupOwner, final BSElementVisitor visitor) { final List<BSTabGroup> tabGroups = bsTabGroupOwner.getTabGroups(); for (BSTabGroup bsTabGroup : new ArrayList<>(tabGroups)) { - bsVisitor.preVisit(bsTabGroup); - bsVisitor.visit(bsTabGroup); - traverseTabs(bsTabGroup, bsVisitor); - bsVisitor.postVisit(bsTabGroup); + visitor.preVisit(bsTabGroup); + visitor.visit(bsTabGroup); + traverseTabs(bsTabGroup, visitor); + visitor.postVisit(bsTabGroup); } } - private void traverseTabs(final BSTabOwner bsTabOwner, final BSElement.Visitor bsVisitor) { + private void traverseTabs(final BSTabOwner bsTabOwner, final BSElementVisitor visitor) { final List<BSTab> tabs = bsTabOwner.getTabs(); for (BSTab tab : new ArrayList<>(tabs)) { - bsVisitor.preVisit(tab); - bsVisitor.visit(tab); - traverseRows(tab, bsVisitor); - bsVisitor.postVisit(tab); + visitor.preVisit(tab); + visitor.visit(tab); + traverseRows(tab, visitor); + visitor.postVisit(tab); } } - private void traverseActions(final ActionLayoutDataOwner actionLayoutDataOwner, final BSElement.Visitor bsVisitor) { + private void traverseActions(final ActionLayoutDataOwner actionLayoutDataOwner, final BSElementVisitor visitor) { final List<ActionLayoutData> actionLayoutDatas = actionLayoutDataOwner.getActions(); if(actionLayoutDatas == null) return; for (final ActionLayoutData actionLayoutData : new ArrayList<>(actionLayoutDatas)) { - bsVisitor.visit(actionLayoutData); + visitor.visit(actionLayoutData); } } - private void traverseFieldSets(final FieldSetOwner fieldSetOwner, final BSElement.Visitor bsVisitor) { + private void traverseFieldSets(final FieldSetOwner fieldSetOwner, final BSElementVisitor visitor) { final List<FieldSet> fieldSets = fieldSetOwner.getFieldSets(); for (FieldSet fieldSet : new ArrayList<>(fieldSets)) { - bsVisitor.visit(fieldSet); - traverseActions(fieldSet, bsVisitor); + visitor.visit(fieldSet); + traverseActions(fieldSet, visitor); final List<PropertyLayoutData> properties = fieldSet.getProperties(); for (final PropertyLayoutData property : new ArrayList<>(properties)) { - bsVisitor.visit(property); - traverseActions(property, bsVisitor); + visitor.visit(property); + traverseActions(property, visitor); } } } private void traverseCollections( - final CollectionLayoutDataOwner owner, final BSElement.Visitor bsVisitor) { + final CollectionLayoutDataOwner owner, final BSElementVisitor visitor) { final List<CollectionLayoutData> collections = owner.getCollections(); for (CollectionLayoutData collection : new ArrayList<>(collections)) { - bsVisitor.visit(collection); - traverseActions(collection, bsVisitor); + visitor.visit(collection); + traverseActions(collection, visitor); } } - private BSElement.Visitor asBsVisitor(final Grid.Visitor visitor) { - return visitor instanceof BSElement.Visitor bsGridVisistor - ? bsGridVisistor - : new BSElement.Visitor() { - @Override public void visit(final DomainObjectLayoutData domainObjectLayoutData) { - visitor.visit(domainObjectLayoutData); - } - @Override public void visit(final ActionLayoutData actionLayoutData) { - visitor.visit(actionLayoutData); - } - @Override public void visit(final PropertyLayoutData propertyLayoutData) { - visitor.visit(propertyLayoutData); - } - @Override public void visit(final CollectionLayoutData collectionLayoutData) { - visitor.visit(collectionLayoutData); - } - @Override public void visit(final FieldSet fieldSet) { - visitor.visit(fieldSet); - } - }; - } - } diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java index aac3248b272..623f3c67d10 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridLoaderService.java @@ -25,11 +25,12 @@ import org.jspecify.annotations.Nullable; import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.mixins.metamodel.Object_rebuildMetamodel; import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; /** - * Provides the ability to load the XML layout (grid) for a domain class. + * Loads the XML layout (grid) for a domain class. * * @since 1.x - revised for 2.0 {@index} */ @@ -38,56 +39,51 @@ public interface GridLoaderService { /** * Whether dynamic reloading of layouts is enabled. * - * <p> - * The default implementation enables reloading for prototyping mode, - * disables in production - * </p> + * <p> The default implementation enables reloading for prototyping mode, + * disables in production */ boolean supportsReloading(); /** * To support metamodel invalidation/rebuilding of spec. * - * <p> - * This is called by the {@link Object_rebuildMetamodel} mixin action. - * </p> + * <p>This is called by the {@link Object_rebuildMetamodel} mixin action. */ void remove(Class<?> domainClass); /** * Whether any persisted layout metadata (eg a <code>.layout.xml</code> file) exists for this domain class. * - * <p> - * If none exists, will return null (and the calling {@link GridService} will use {@link GridSystemService} - * to obtain a default grid for the domain class). - * </p> + * <p>If none exists, will return null (and the calling {@link GridService} will use {@link GridSystemService} + * to obtain a default grid for the domain class). */ boolean existsFor(Class<?> domainClass, EnumSet<CommonMimeType> supportedFormats); /** * Optionally returns a new instance of a {@link Grid}, * based on whether the underlying resource could be found, loaded and parsed. - * <p> - * The layout alternative will typically be specified through a - * `layout()` method on the domain object, the value of which is used - * for the suffix of the layout file (eg "Customer-layout.archived.xml" - * to use a different layout for customers that have been archived). - * </p> + * + * <p>The layout alternative will typically be specified through a + * `layout()` method on the domain object, the value of which is used + * for the suffix of the layout file (eg "Customer-layout.archived.xml" + * to use a different layout for customers that have been archived). + * * @throws UnsupportedOperationException - when format is not supported */ - <T extends Grid> Optional<T> load( + Optional<BSGrid> load( Class<?> domainClass, @Nullable String layoutIfAny, - @NonNull GridMarshaller<T> marshaller); + @NonNull GridMarshaller marshaller); /** * Optionally returns a new instance of a {@link Grid}, * based on whether the underlying resource could be found, loaded and parsed. + * * @throws UnsupportedOperationException - when format is not supported */ - default <T extends Grid> Optional<T> load( + default Optional<BSGrid> load( final Class<?> domainClass, - final @NonNull GridMarshaller<T> marshaller) { + final @NonNull GridMarshaller marshaller) { return load(domainClass, null, marshaller); } diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java index 4b3d36e807e..6e2678791bd 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridMarshaller.java @@ -24,6 +24,7 @@ import org.jspecify.annotations.Nullable; import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.marshal.Marshaller; import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType; import org.apache.causeway.commons.functional.Try; @@ -35,25 +36,25 @@ * * @since 2.0 revised for 4.0 {@index} */ -public interface GridMarshaller<T extends Grid> { +public interface GridMarshaller { - Class<T> supportedClass(); + Class<BSGrid> supportedClass(); /** - * Supported format(s) for {@link #unmarshal(String, CommonMimeType)} - * and {@link #marshal(Object, CommonMimeType)}. + * Supported format(s) for {@link #unmarshal(Class, String, CommonMimeType)} + * and {@link #marshal(BSGrid, CommonMimeType)}. */ EnumSet<CommonMimeType> supportedFormats(); /** * @throws UnsupportedOperationException when format is not supported */ - String marshal(@NonNull T value, @NonNull CommonMimeType format); + String marshal(@NonNull BSGrid value, @NonNull CommonMimeType format); /** * Returns a new de-serialized instance wrapped in a {@link Try}. * @throws UnsupportedOperationException when format is not supported (not wrapped) */ - Try<T> unmarshal(Class<?> domainClass, @Nullable String content, @NonNull CommonMimeType format); + Try<BSGrid> unmarshal(Class<?> domainClass, @Nullable String content, @NonNull CommonMimeType format); } diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java index 47cbbfab17b..7da28a7fe09 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridService.java @@ -23,12 +23,14 @@ import org.apache.causeway.applib.annotation.DomainObjectLayout; import org.apache.causeway.applib.annotation.PropertyLayout; import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.layout.LayoutExportStyle; import org.apache.causeway.commons.internal.exceptions._Exceptions; /** - * Provides the ability to load the XML layout (grid) for a domain class. + * Loads the layout (grid) for any domain class. * + * <p> Acts on top of {@link GridLoaderService}, {@link GridMarshaller} and any {@link GridSystemService}(s) registered with Spring. * @since 1.x {@index} */ public interface GridService { @@ -36,31 +38,25 @@ public interface GridService { /** * Whether dynamic reloading of layouts is enabled. * - * <p> - * The default implementation just delegates to the configured - * {@link GridLoaderService}; the default implementation of <i>that</i> - * service enables reloading wihle prototyping, disables in production. - * </p> + * <p> The default implementation just delegates to the configured + * {@link GridLoaderService}; the default implementation of <i>that</i> + * service enables reloading while prototyping, disables in production. */ boolean supportsReloading(); /** * To support metamodel invalidation/rebuilding of spec. * - * <p> - * The default implementation just delegates to the configured - * {@link GridLoaderService}. - * </p> + * <p>The default implementation just delegates to the configured + * {@link GridLoaderService}. */ void remove(Class<?> domainClass); /** * Whether any persisted layout metadata (eg a <code>.layout.xml</code> file) exists for this domain class. * - * <p> - * The default implementation just delegates to the configured - * {@link GridLoaderService}. - * </p> + * <p>The default implementation just delegates to the configured + * {@link GridLoaderService}. */ boolean existsFor(Class<?> domainClass); @@ -68,103 +64,85 @@ public interface GridService { * Returns a new instance of a {@link Grid} for the specified domain class, * for example as loaded from a <code>layout.xml</code> file. * - * <p> - * If non exists, returns <code>null</code>. (The caller can then - * use {@link GridService#defaultGridFor(Class)} to obtain a - * default grid if necessary). - * </p> + * <p>If non exists, returns <code>null</code>. (The caller can then + * use {@link GridService#defaultGridFor(Class)} to obtain a + * default grid if necessary). * - * <p> - * The default implementation just delegates to the configured - * {@link GridLoaderService}. - * </p> + * <p>The default implementation just delegates to the configured + * {@link GridLoaderService}. */ - Grid load(final Class<?> domainClass); + BSGrid load(final Class<?> domainClass); /** * Returns an alternative layout for the domain class. * - * <p> - * The alternative layout name can for example be returned by the - * domain object's <code>layout()</code> method, whereby - based on the - * state of the domain object - it requests a different layout be used. - * </p> + * <p>The alternative layout name can for example be returned by the + * domain object's <code>layout()</code> method, whereby - based on the + * state of the domain object - it requests a different layout be used. * - * <p> - * The default implementation just delegates to the configured - * {@link GridLoaderService}; the default implementation of <i>that</i> - * service uses the layout name to search for a differently - * named layout file, <code>[domainClass].layout.[layout].xml</code>. - * </p> + * <p>The default implementation just delegates to the configured + * {@link GridLoaderService}; the default implementation of <i>that</i> + * service uses the layout name to search for a differently + * named layout file, <code>[domainClass].layout.[layout].xml</code>. */ - Grid load(Class<?> domainClass, String layout); + BSGrid load(Class<?> domainClass, String layout); /** * Returns a default grid; eg where none can be loaded using {@link #load(Class)}. * - * <p> - * Used when no existing grid layout exists for a domain class. - * </p> + * <p>Used when no existing grid layout exists for a domain class. * - * <p> - * The default implementation searches through all available - * {@link GridSystemService}s and asks each in turn for a - * {@link GridSystemService#defaultGrid(Class) default grid}. - * </p> + * <p>The default implementation searches through all available + * {@link GridSystemService}s and asks each in turn for a + * {@link GridSystemService#defaultGrid(Class) default grid}. */ - Grid defaultGridFor(Class<?> domainClass); + BSGrid defaultGridFor(Class<?> domainClass); /** * Returns a normalized grid for the domain class obtained previously using {@link #load(Class)}. * - * <p> - * If a 'normalized' grid is persisted as the <code>layout.xml</code>, then the expectation is that - * any ordering metadata from layout annotations can be removed from the domain class - * because the binding of properties/collections/actions will be within the XML. However, the layout - * annotations ({@link DomainObjectLayout}, {@link ActionLayout}, {@link PropertyLayout} and - * {@link CollectionLayout}) (if present) will continue to be used to provide additional layout metadata. Of - * course, there is nothing to prevent the developer from extending the layout XML to also include the - * layout XML (in other words moving towards a {@link #complete(Grid) complete} grid. Metadata within the - * <code>layout.xml</code> file takes precedence over any annotations. - * </p> + * <p>If a 'normalized' grid is persisted as the <code>layout.xml</code>, then the expectation is that + * any ordering metadata from layout annotations can be removed from the domain class + * because the binding of properties/collections/actions will be within the XML. However, the layout + * annotations ({@link DomainObjectLayout}, {@link ActionLayout}, {@link PropertyLayout} and + * {@link CollectionLayout}) (if present) will continue to be used to provide additional layout metadata. Of + * course, there is nothing to prevent the developer from extending the layout XML to also include the + * layout XML (in other words moving towards a {@link #complete(BSGrid) complete} grid. Metadata within the + * <code>layout.xml</code> file takes precedence over any annotations. */ - Grid normalize(final Grid grid); + BSGrid normalize(BSGrid grid); /** * Modifies the provided {@link Grid} with additional metadata, broadly speaking corresponding to the * {@link DomainObjectLayout}, {@link ActionLayout}, {@link PropertyLayout} and {@link CollectionLayout}. * - * <p> - * If a 'complete' grid is persisted as the <code>layout.xml</code>, then there should be no need - * for any of the layout annotations, - * to be required in the domain class itself. - * </p> + * <p>If a 'complete' grid is persisted as the <code>layout.xml</code>, then there should be no need + * for any of the layout annotations, + * to be required in the domain class itself. */ - Grid complete(Grid grid); + BSGrid complete(BSGrid grid); /** * Modifies the provided {@link Grid}, removing all metadata except the basic grid structure. * - * <p> - * If a 'minimal' grid is persisted as the <code>layout.xml</code>, then the expectation is that - * most of the layout annotations ({@link DomainObjectLayout}, {@link ActionLayout}, {@link PropertyLayout}, - * {@link CollectionLayout} will still be retained in the domain class code. - * </p> + * <p>If a 'minimal' grid is persisted as the <code>layout.xml</code>, then the expectation is that + * most of the layout annotations ({@link DomainObjectLayout}, {@link ActionLayout}, {@link PropertyLayout}, + * {@link CollectionLayout} will still be retained in the domain class code. * * @param grid */ - Grid minimal(Grid grid); + BSGrid minimal(BSGrid grid); // -- LAYOUT EXPORT - GridMarshaller<? extends Grid> marshaller(); + GridMarshaller marshaller(); //TODO rename - default Grid toGridForExport( + default BSGrid toGridForExport( final Class<?> domainClass, final LayoutExportStyle style) { // don't use the grid from the facet, because it will be modified subsequently. - Grid grid = load(domainClass); + BSGrid grid = load(domainClass); if(grid == null) { grid = defaultGridFor(domainClass); } diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java index c9cfaaf1d12..7737534c74f 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/grid/GridSystemService.java @@ -19,75 +19,47 @@ package org.apache.causeway.applib.services.grid; import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; /** * Encapsulates a single layout grid system which can be used to customize the layout * of domain objects. * - * <p> - * In particular this means being able to return a "normalized" form + * <p>In particular this means being able to return a "normalized" form * (validating and associating domain object members into the various regions * of the grid) and in providing a default grid if there is no other metadata * available. - * </p> * * @since 1.x {@index} */ -public interface GridSystemService<G extends Grid> { +public interface GridSystemService { /** * The concrete subclass of {@link Grid} supported by this implementation. * - * <p> - * There can be multiple implementations of this service, this indicates - * the base class used by the implementation. - * </p> + * <p>There can be multiple implementations of this service, this indicates + * the base class used by the implementation. */ - Class<G> gridImplementation(); - - /** - * The target namespace for this grid system. - * - * <p> - * This is used when generating the XML. The Bootstrap grid system - * provided by the framework returns the value - * `https://causeway.apache.org/applib/layout/grid/bootstrap3`. - * </p> - */ - String tns(); - - /** - * The schema location for the XSD. - * - * <p> - * Every grid system is expected to provide a schema XSD in order to - * provide code completion in an IDE. The Bootstrap grid system - * provided by the framework returns the value - * `https://causeway.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd`. - * </p> - */ - String schemaLocation(); + Class<BSGrid> gridImplementation(); /** * A default grid, used when no grid layout can be found for the domain * class. * - * <p> - * For example, this layout could define two columns in ratio 4:8. - * </p> + * <p>For example, this layout could define two columns in ratio 4:8. * * @param domainClass */ - G defaultGrid(Class<?> domainClass); + BSGrid defaultGrid(Class<?> domainClass); /** * Validates and normalizes a grid, modifying the grid so that all of the * domain object's members (properties, collections, actions) are bound to * regions of the grid. - * <p> - * E.g. for properties (and similar for collections and actions) the annotation attributes - * {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()} - * and + * + * <p>E.g. for properties (and similar for collections and actions) the annotation attributes + * {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()} + * and * {@link org.apache.causeway.applib.annotation.PropertyLayout#fieldSetId()} * or * {@link org.apache.causeway.applib.annotation.PropertyLayout#fieldSetName()} @@ -96,14 +68,14 @@ public interface GridSystemService<G extends Grid> { * allows the various layout annotation attributes to be unspecified or removed from the source code * of the domain class. */ - void normalize(G grid, Class<?> domainClass); + void normalize(BSGrid grid, Class<?> domainClass); /** * Takes a normalized grid and enriches it with all the available metadata * (taken from Apache Causeway' internal metadata) that can be represented in * the layout XML. - * <p> - * Such a grid, if persisted as the layout XML file for the domain class, + * + * <p>Such a grid, if persisted as the layout XML file for the domain class, * allows all layout annotations * ({@link org.apache.causeway.applib.annotation.ActionLayout}, * {@link org.apache.causeway.applib.annotation.PropertyLayout}, @@ -112,24 +84,25 @@ public interface GridSystemService<G extends Grid> { * @param grid * @param domainClass */ - void complete(G grid, Class<?> domainClass); + void complete(BSGrid grid, Class<?> domainClass); /** * Takes a normalized grid and strips out removes all members, leaving only * the grid structure. - * <p> - * Such a grid, if persisted as the layout XML file for the domain class, + * + * <p>Such a grid, if persisted as the layout XML file for the domain class, * requires that e.g. for properties (and similar for collections and actions) the annotation attributes - * {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()} - * and + * {@link org.apache.causeway.applib.annotation.PropertyLayout#sequence()} + * and * {@link org.apache.causeway.applib.annotation.PropertyLayout#fieldSetId()} * or * {@link org.apache.causeway.applib.annotation.PropertyLayout#fieldSetName()} * are retained in the source code of said class in order to bind * members to the regions of the grid. + * * @param grid * @param domainClass */ - void minimal(G grid, Class<?> domainClass); + void minimal(BSGrid grid, Class<?> domainClass); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridFacet.java index 0e59392558c..e4abfe06f87 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridFacet.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/BSGridFacet.java @@ -26,7 +26,6 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.apache.causeway.applib.layout.grid.Grid; import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.grid.GridService; import org.apache.causeway.commons.internal.base._Lazy; @@ -64,12 +63,7 @@ public static GridFacet create( @Override public FacetHolder getFacetHolder() { return facetHolder(); } @Override - public boolean supports(Class<? extends Grid> gridClass) { - return BSGrid.class.equals(gridClass); - } - - @Override - public Grid getGrid(final @Nullable ManagedObject mo) { + public BSGrid getGrid(final @Nullable ManagedObject mo) { guardAgainstObjectOfDifferentType(mo); return normalized(mo); } @@ -125,7 +119,7 @@ private BSGrid load(final @NonNull String layoutPrefix) { gridService.load(domainClass, _Strings.emptyToNull(layoutPrefix))) // loads from default-XML if available .orElseGet(()->gridService.defaultGridFor(domainClass)); - var bsGrid = (BSGrid) gridService.normalize(grid); + var bsGrid = gridService.normalize(grid); return bsGrid; } @@ -140,17 +134,12 @@ record NoLayout( @Override public Precedence getPrecedence() { return precedence(); } @Override public FacetHolder getFacetHolder() { return facetHolder(); } - @Override public void visitAttributes(BiConsumer<String, Object> visitor) { + @Override public void visitAttributes(final BiConsumer<String, Object> visitor) { visitor.accept("precedence", getPrecedence().name()); } - @Override public Grid getGrid(@Nullable ManagedObject mo) { - return Grid.empty(); + @Override public BSGrid getGrid(@Nullable final ManagedObject mo) { + return null; } - @Override - public boolean supports(Class<? extends Grid> gridClass) { - return false; - } - } } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java index be6fe67eb26..5a74ff77707 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacet.java @@ -20,7 +20,7 @@ import org.jspecify.annotations.Nullable; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.grid.GridSystemService; import org.apache.causeway.applib.services.layout.LayoutService; import org.apache.causeway.core.metamodel.facetapi.Facet; @@ -34,7 +34,6 @@ */ public interface GridFacet extends Facet { - boolean supports(Class<? extends Grid> gridClass); - Grid getGrid(@Nullable ManagedObject mo); + BSGrid getGrid(@Nullable ManagedObject mo); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java index abcb90f87b7..9654b1a156b 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java @@ -35,7 +35,7 @@ import org.apache.causeway.applib.layout.component.HasHidden; import org.apache.causeway.applib.layout.component.HasNamed; import org.apache.causeway.applib.layout.component.PropertyLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; import org.apache.causeway.commons.internal.base._Strings; import org.apache.causeway.commons.internal.functions._Functions; import org.apache.causeway.core.metamodel.facetapi.Facet; @@ -60,21 +60,105 @@ import org.apache.causeway.core.metamodel.facets.objectvalue.typicallen.TypicalLengthFacet; import org.apache.causeway.core.metamodel.spec.ObjectSpecification; -import org.jspecify.annotations.NonNull; -import lombok.RequiredArgsConstructor; import lombok.experimental.UtilityClass; -/** - * - * @since 2.0 - * - */ @UtilityClass public class LayoutFacetUtil { - public void setBookmarkingIfAny( - final HasBookmarking hasBookmarking, - final FacetHolder facetHolder) { + public record LayoutDataFactory(MetamodelToGridOverridingVisitor helper) { + + public LayoutDataFactory(final ObjectSpecification objectSpec) { + this(new MetamodelToGridOverridingVisitor(objectSpec)); + } + + public ActionLayoutData createActionLayoutData(final String id) { + var layoutData = new ActionLayoutData(id); + helper.visit(layoutData); + return layoutData; + } + + public CollectionLayoutData createCollectionLayoutData(final String id) { + var layoutData = new CollectionLayoutData(id); + helper.visit(layoutData); + return layoutData; + } + + public PropertyLayoutData createPropertyLayoutData(final String id) { + var layoutData = new PropertyLayoutData(id); + helper.visit(layoutData); + return layoutData; + } + + public DomainObjectLayoutData createDomainObjectLayoutData() { + var layoutData = new DomainObjectLayoutData(); + helper.visit(layoutData); + return layoutData; + } + + } + + public record MetamodelToGridOverridingVisitor(ObjectSpecification objectSpec) implements BSElementVisitor { + + @Override + public void visit(final ActionLayoutData actionLayoutData) { + objectSpec.getAction(actionLayoutData.getId()) + .ifPresent(objectAction->{ + setCssClassIfAny(actionLayoutData, objectAction); + setCssClassFaIfAny(actionLayoutData, objectAction); + setMemberDescribedIfAny(actionLayoutData, objectAction); + setHiddenIfAny(actionLayoutData, objectAction); + setMemberNamedIfAny(actionLayoutData, objectAction); + setActionPositionIfAny(actionLayoutData, objectAction); + }); + } + + @Override + public void visit(final CollectionLayoutData collectionLayoutData) { + objectSpec.getAssociation(collectionLayoutData.getId()) + .ifPresent(collection->{ + setCssClassIfAny(collectionLayoutData, collection); + setDefaultViewIfAny(collectionLayoutData, collection); + setMemberDescribedIfAny(collectionLayoutData, collection); + setHiddenIfAny(collectionLayoutData, collection); + setMemberNamedIfAny(collectionLayoutData, collection); + setPagedIfAny(collectionLayoutData, collection, objectSpec); + setTableDecoratorIfAny(collectionLayoutData, collection, objectSpec); + setSortedByIfAny(collectionLayoutData, collection); + }); + } + + @Override + public void visit(final PropertyLayoutData propertyLayoutData) { + objectSpec.getAssociation(propertyLayoutData.getId()) + .ifPresent(property->{ + setCssClassIfAny(propertyLayoutData, property); + setMemberDescribedIfAny(propertyLayoutData, property); + setHiddenIfAny(propertyLayoutData, property); + setMemberNamedIfAny(propertyLayoutData, property); + setLabelPositionIfAny(propertyLayoutData, property); + setMultiLineIfAny(propertyLayoutData, property); + setRenderedAsDayBeforeIfAny(propertyLayoutData, property); + setTypicalLengthIfAny(propertyLayoutData, property); + }); + } + + @Override + public void visit(final DomainObjectLayoutData domainObjectLayoutData) { + setBookmarkingIfAny(domainObjectLayoutData, objectSpec); + setCssClassIfAny(domainObjectLayoutData, objectSpec); + setCssClassFaIfAny(domainObjectLayoutData, objectSpec); + setObjectDescribedIfAny(domainObjectLayoutData, objectSpec); + setObjectNamedIfAny(domainObjectLayoutData, objectSpec); + setPagedIfAny(domainObjectLayoutData, objectSpec); + setTableDecoratorIfAny(domainObjectLayoutData, objectSpec); + } + } + + // -- HELPER + + private void setBookmarkingIfAny( + final HasBookmarking hasBookmarking, + final FacetHolder facetHolder) { var bookmarkPolicyFacet = facetHolder.getFacet(BookmarkPolicyFacet.class); if(isNonFallback(bookmarkPolicyFacet)) { @@ -85,9 +169,9 @@ public void setBookmarkingIfAny( } } - public void setCssClassIfAny( - final HasCssClass hasCssClass, - final FacetHolder facetHolder) { + private void setCssClassIfAny( + final HasCssClass hasCssClass, + final FacetHolder facetHolder) { var cssClassFacet = facetHolder.getFacet(CssClassFacet.class); if(isNonFallback(cssClassFacet)) { @@ -103,27 +187,27 @@ public void setCssClassIfAny( } } - public void setCssClassFaIfAny( - final HasCssClassFa hasCssClassFa, - final FacetHolder facetHolder) { + private void setCssClassFaIfAny( + final HasCssClassFa hasCssClassFa, + final FacetHolder facetHolder) { facetHolder.lookupNonFallbackFacet(FaFacet.class) .map(FaFacet::getSpecialization) .ifPresent(specialization-> - specialization.accept( - faStaticFacet->{ - final String cssClassFa = faStaticFacet.getLayers().toQuickNotation(); - if(!_Strings.isNullOrEmpty(cssClassFa)) { - hasCssClassFa.setCssClassFa(cssClassFa); - hasCssClassFa.setCssClassFaPosition(faStaticFacet.getLayers().position()); - } - }, - _Functions.noopConsumer())); // not supported for imperative fa-icons + specialization.accept( + faStaticFacet->{ + final String cssClassFa = faStaticFacet.getLayers().toQuickNotation(); + if(!_Strings.isNullOrEmpty(cssClassFa)) { + hasCssClassFa.setCssClassFa(cssClassFa); + hasCssClassFa.setCssClassFaPosition(faStaticFacet.getLayers().position()); + } + }, + _Functions.noopConsumer())); // not supported for imperative fa-icons } - public void setDefaultViewIfAny( - final CollectionLayoutData collectionLayoutData, - final FacetHolder facetHolder) { + private void setDefaultViewIfAny( + final CollectionLayoutData collectionLayoutData, + final FacetHolder facetHolder) { var defaultViewFacet = facetHolder.getFacet(DefaultViewFacet.class); if(isNonFallback(defaultViewFacet)) { @@ -135,8 +219,8 @@ public void setDefaultViewIfAny( } private void setObjectNamedIfAny( - final HasNamed hasNamed, - final FacetHolder facetHolder) { + final HasNamed hasNamed, + final FacetHolder facetHolder) { facetHolder.lookupNonFallbackFacet(ObjectNamedFacet.class) .filter(ObjectNamedFacet::isNounPresent) @@ -145,8 +229,8 @@ private void setObjectNamedIfAny( } private void setObjectDescribedIfAny( - final HasDescribedAs hasDescribedAs, - final FacetHolder facetHolder) { + final HasDescribedAs hasDescribedAs, + final FacetHolder facetHolder) { facetHolder.lookupNonFallbackFacet(ObjectDescribedFacet.class) .map(ObjectDescribedFacet::translated) @@ -154,9 +238,9 @@ private void setObjectDescribedIfAny( .ifPresent(hasDescribedAs::setDescribedAs); } - public void setPagedIfAny( - final DomainObjectLayoutData domainObjectLayoutData, - final FacetHolder facetHolder) { + private void setPagedIfAny( + final DomainObjectLayoutData domainObjectLayoutData, + final FacetHolder facetHolder) { var pagedFacet = FacetUtil.lookupFacetIn(PagedFacet.class, facetHolder).orElse(null); if(isNonFallback(pagedFacet)) { @@ -167,56 +251,53 @@ public void setPagedIfAny( } } - public void setTableDecoratorIfAny( - final DomainObjectLayoutData domainObjectLayoutData, - final FacetHolder facetHolder) { + private void setTableDecoratorIfAny( + final DomainObjectLayoutData domainObjectLayoutData, + final FacetHolder facetHolder) { - var tableDecoratorFacet = FacetUtil.lookupFacetIn(TableDecoratorFacet.class, facetHolder).orElse(null); - if(isNonFallback(tableDecoratorFacet)) { - final Class<? extends TableDecorator> value = tableDecoratorFacet.value(); - if(value != TableDecorator.Default.class) { - domainObjectLayoutData.setTableDecorator(value); - } - } + facetHolder.lookupNonFallbackFacet(TableDecoratorFacet.class) + .map(TableDecoratorFacet::value) + .filter(it->it!=TableDecorator.Default.class) + .ifPresent(domainObjectLayoutData::setTableDecorator); } private void setMemberNamedIfAny( - final HasNamed hasNamed, - final FacetHolder facetHolder) { + final HasNamed hasNamed, + final FacetHolder facetHolder) { facetHolder.lookupNonFallbackFacet(MemberNamedFacet.class) .map(MemberNamedFacet::getSpecialization) .ifPresent(specialization-> - specialization.accept( - hasStaticText->{ - var describedAs = hasStaticText.translated(); - if(_Strings.isNotEmpty(describedAs)) { - hasNamed.setNamed(describedAs); - } - }, - _Functions.noopConsumer())); // not supported for imperative text + specialization.accept( + hasStaticText->{ + var describedAs = hasStaticText.translated(); + if(_Strings.isNotEmpty(describedAs)) { + hasNamed.setNamed(describedAs); + } + }, + _Functions.noopConsumer())); // not supported for imperative text } private void setMemberDescribedIfAny( - final HasDescribedAs hasDescribedAs, - final FacetHolder facetHolder) { + final HasDescribedAs hasDescribedAs, + final FacetHolder facetHolder) { facetHolder.lookupNonFallbackFacet(MemberDescribedFacet.class) .map(MemberDescribedFacet::getSpecialization) .ifPresent(specialization-> - specialization.accept( - hasStaticText->{ - var describedAs = hasStaticText.translated(); - if(_Strings.isNotEmpty(describedAs)) { - hasDescribedAs.setDescribedAs(describedAs); - } - }, - _Functions.noopConsumer())); // not supported for imperative text + specialization.accept( + hasStaticText->{ + var describedAs = hasStaticText.translated(); + if(_Strings.isNotEmpty(describedAs)) { + hasDescribedAs.setDescribedAs(describedAs); + } + }, + _Functions.noopConsumer())); // not supported for imperative text } - public void setHiddenIfAny( - final HasHidden hasHidden, - final FacetHolder facetHolder) { + private void setHiddenIfAny( + final HasHidden hasHidden, + final FacetHolder facetHolder) { var hiddenFacet = facetHolder.getFacet(HiddenFacet.class); if (isNonFallback(hiddenFacet)) { @@ -227,9 +308,9 @@ public void setHiddenIfAny( } } - public void setLabelPositionIfAny( - final PropertyLayoutData propertyLayoutData, - final FacetHolder facetHolder) { + private void setLabelPositionIfAny( + final PropertyLayoutData propertyLayoutData, + final FacetHolder facetHolder) { var labelAtFacet = facetHolder.getFacet(LabelAtFacet.class); if(isNonFallback(labelAtFacet)) { @@ -240,9 +321,9 @@ public void setLabelPositionIfAny( } } - public void setMultiLineIfAny( - final PropertyLayoutData propertyLayoutData, - final FacetHolder facetHolder) { + private void setMultiLineIfAny( + final PropertyLayoutData propertyLayoutData, + final FacetHolder facetHolder) { var multiLineFacet = facetHolder.getFacet(MultiLineFacet.class); if(isNonFallback(multiLineFacet)) { @@ -253,9 +334,9 @@ public void setMultiLineIfAny( } } - public void setPagedIfAny( - final CollectionLayoutData collectionLayoutData, - final FacetHolder facetHolder, final ObjectSpecification objectSpec) { + private void setPagedIfAny( + final CollectionLayoutData collectionLayoutData, + final FacetHolder facetHolder, final ObjectSpecification objectSpec) { var pagedFacet = FacetUtil.lookupFacetIn(PagedFacet.class, facetHolder, objectSpec).orElse(null); if(isNonFallback(pagedFacet)) { @@ -266,9 +347,9 @@ public void setPagedIfAny( } } - public void setTableDecoratorIfAny( - final CollectionLayoutData collectionLayoutData, - final FacetHolder facetHolder, final ObjectSpecification objectSpec) { + private void setTableDecoratorIfAny( + final CollectionLayoutData collectionLayoutData, + final FacetHolder facetHolder, final ObjectSpecification objectSpec) { var tableDecoratorFacet = FacetUtil.lookupFacetIn(TableDecoratorFacet.class, facetHolder, objectSpec).orElse(null); if(isNonFallback(tableDecoratorFacet)) { @@ -279,9 +360,9 @@ public void setTableDecoratorIfAny( } } - public void setActionPositionIfAny( - final ActionLayoutData actionLayoutData, - final FacetHolder facetHolder) { + private void setActionPositionIfAny( + final ActionLayoutData actionLayoutData, + final FacetHolder facetHolder) { var actionPositionFacet = facetHolder.getFacet(ActionPositionFacet.class); if(isNonFallback(actionPositionFacet)) { @@ -292,32 +373,32 @@ public void setActionPositionIfAny( } } - public void setRenderedAsDayBeforeIfAny( - final PropertyLayoutData propertyLayoutData, - final FacetHolder facetHolder) { + private void setRenderedAsDayBeforeIfAny( + final PropertyLayoutData propertyLayoutData, + final FacetHolder facetHolder) { facetHolder.lookupNonFallbackFacet(DateRenderAdjustFacet.class) .ifPresent(dateRenderAdjustFacet-> - propertyLayoutData.setDateRenderAdjustDays(dateRenderAdjustFacet.getDateRenderAdjustDays())); + propertyLayoutData.setDateRenderAdjustDays(dateRenderAdjustFacet.getDateRenderAdjustDays())); } - public void setSortedByIfAny( - final CollectionLayoutData collectionLayoutData, - final FacetHolder facetHolder) { + private void setSortedByIfAny( + final CollectionLayoutData collectionLayoutData, + final FacetHolder facetHolder) { var sortedByFacet = facetHolder.getFacet(SortedByFacet.class); if(isNonFallback(sortedByFacet)) { final Class<? extends Comparator<?>> cls = sortedByFacet.value(); if(cls != null - && cls.getCanonicalName()!=null) { + && cls.getCanonicalName()!=null) { collectionLayoutData.setSortedBy(cls.getName()); } } } - public void setTypicalLengthIfAny( - final PropertyLayoutData propertyLayoutData, - final FacetHolder facetHolder) { + private void setTypicalLengthIfAny( + final PropertyLayoutData propertyLayoutData, + final FacetHolder facetHolder) { var typicalLengthFacet = facetHolder.getFacet(TypicalLengthFacet.class); if(isNonFallback(typicalLengthFacet)) { @@ -328,108 +409,9 @@ public void setTypicalLengthIfAny( } } - public static class LayoutDataFactory { - - private final MetamodelToGridOverridingVisitor helper; - - public static LayoutDataFactory of(final ObjectSpecification objectSpec) { - return new LayoutDataFactory(objectSpec); - } - - private LayoutDataFactory(final ObjectSpecification objectSpec) { - this.helper = MetamodelToGridOverridingVisitor.of(objectSpec); - } - - public ActionLayoutData createActionLayoutData(final String id) { - var layoutData = new ActionLayoutData(id); - helper.visit(layoutData); - return layoutData; - } - - public CollectionLayoutData createCollectionLayoutData(final String id) { - var layoutData = new CollectionLayoutData(id); - helper.visit(layoutData); - return layoutData; - } - - public PropertyLayoutData createPropertyLayoutData(final String id) { - var layoutData = new PropertyLayoutData(id); - helper.visit(layoutData); - return layoutData; - } - - public DomainObjectLayoutData createDomainObjectLayoutData() { - var layoutData = new DomainObjectLayoutData(); - helper.visit(layoutData); - return layoutData; - } - - } - - @RequiredArgsConstructor(staticName = "of") - public static class MetamodelToGridOverridingVisitor implements Grid.Visitor { - - private final @NonNull ObjectSpecification objectSpec; - - @Override - public void visit(final ActionLayoutData actionLayoutData) { - objectSpec.getAction(actionLayoutData.getId()) - .ifPresent(objectAction->{ - setCssClassIfAny(actionLayoutData, objectAction); - setCssClassFaIfAny(actionLayoutData, objectAction); - setMemberDescribedIfAny(actionLayoutData, objectAction); - setHiddenIfAny(actionLayoutData, objectAction); - setMemberNamedIfAny(actionLayoutData, objectAction); - setActionPositionIfAny(actionLayoutData, objectAction); - }); - } - - @Override - public void visit(final CollectionLayoutData collectionLayoutData) { - objectSpec.getAssociation(collectionLayoutData.getId()) - .ifPresent(collection->{ - setCssClassIfAny(collectionLayoutData, collection); - setDefaultViewIfAny(collectionLayoutData, collection); - setMemberDescribedIfAny(collectionLayoutData, collection); - setHiddenIfAny(collectionLayoutData, collection); - setMemberNamedIfAny(collectionLayoutData, collection); - setPagedIfAny(collectionLayoutData, collection, objectSpec); - setTableDecoratorIfAny(collectionLayoutData, collection, objectSpec); - setSortedByIfAny(collectionLayoutData, collection); - }); - } - - @Override - public void visit(final PropertyLayoutData propertyLayoutData) { - objectSpec.getAssociation(propertyLayoutData.getId()) - .ifPresent(property->{ - setCssClassIfAny(propertyLayoutData, property); - setMemberDescribedIfAny(propertyLayoutData, property); - setHiddenIfAny(propertyLayoutData, property); - setMemberNamedIfAny(propertyLayoutData, property); - setLabelPositionIfAny(propertyLayoutData, property); - setMultiLineIfAny(propertyLayoutData, property); - setRenderedAsDayBeforeIfAny(propertyLayoutData, property); - setTypicalLengthIfAny(propertyLayoutData, property); - }); - } - - @Override - public void visit(final DomainObjectLayoutData domainObjectLayoutData) { - setBookmarkingIfAny(domainObjectLayoutData, objectSpec); - setCssClassIfAny(domainObjectLayoutData, objectSpec); - setCssClassFaIfAny(domainObjectLayoutData, objectSpec); - setObjectDescribedIfAny(domainObjectLayoutData, objectSpec); - setObjectNamedIfAny(domainObjectLayoutData, objectSpec); - setPagedIfAny(domainObjectLayoutData, objectSpec); - } - } - - // -- HELPER - private static boolean isNonFallback(final Facet facet) { return facet != null - && !facet.getPrecedence().isFallback(); + && !facet.getPrecedence().isFallback(); } } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java index ff7048e054b..599495ade14 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridLoaderServiceDefault.java @@ -37,7 +37,7 @@ import org.springframework.stereotype.Service; import org.apache.causeway.applib.annotation.PriorityPrecedence; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.grid.GridLoaderService; import org.apache.causeway.applib.services.grid.GridMarshaller; import org.apache.causeway.applib.services.message.MessageService; @@ -95,7 +95,7 @@ record LayoutKey( // for better logging messages (used only in prototyping mode) private final Map<LayoutKey, String> badContentByKey = _Maps.newHashMap(); // cache (used only in prototyping mode) - private final Map<LayoutKey, Grid> gridCache = _Maps.newHashMap(); + private final Map<LayoutKey, BSGrid> gridCache = _Maps.newHashMap(); @Override public void remove(final Class<?> domainClass) { @@ -114,10 +114,10 @@ public boolean existsFor(final Class<?> domainClass, final EnumSet<CommonMimeTyp } @Override - public <T extends Grid> Optional<T> load( + public Optional<BSGrid> load( final Class<?> domainClass, final String layoutIfAny, - final @NonNull GridMarshaller<T> marshaller) { + final @NonNull GridMarshaller marshaller) { var supportedFormats = marshaller.supportedFormats(); @@ -146,15 +146,14 @@ public <T extends Grid> Optional<T> load( } } else { // if cached, serve from cache - otherwise fall through - @SuppressWarnings("unchecked") - final T grid = (T)gridCache.get(layoutKey); + final BSGrid grid = gridCache.get(layoutKey); if(grid != null) { return Optional.of(grid); } } try { - final T grid = marshaller + final BSGrid grid = marshaller .unmarshal(domainClass, layoutResource.content(), layoutResource.format()) .getValue().orElseThrow(); if(supportsReloading()) { diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java index f995294bca2..00a234e5eae 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridServiceDefault.java @@ -27,7 +27,7 @@ import org.springframework.stereotype.Service; import org.apache.causeway.applib.annotation.PriorityPrecedence; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.grid.GridLoaderService; import org.apache.causeway.applib.services.grid.GridMarshaller; import org.apache.causeway.applib.services.grid.GridService; @@ -46,8 +46,8 @@ @Qualifier("Default") public record GridServiceDefault( GridLoaderService gridLoaderService, - GridMarshaller<? extends Grid> marshaller, - List<GridSystemService<? extends Grid>> gridSystemServices) implements GridService { + GridMarshaller marshaller, + List<GridSystemService> gridSystemServices) implements GridService { @Override public boolean supportsReloading() { @@ -65,19 +65,19 @@ public boolean existsFor(final Class<?> domainClass) { } @Override - public Grid load(final Class<?> domainClass) { + public BSGrid load(final Class<?> domainClass) { return gridLoaderService.load(domainClass, marshaller).orElse(null); } @Override - public Grid load(final Class<?> domainClass, final String layout) { + public BSGrid load(final Class<?> domainClass, final String layout) { return gridLoaderService.load(domainClass, layout, marshaller).orElse(null); } // -- @Override - public Grid defaultGridFor(final Class<?> domainClass) { + public BSGrid defaultGridFor(final Class<?> domainClass) { for (var gridSystemService : gridSystemServices()) { var grid = gridSystemService.defaultGrid(domainClass); if(grid != null) return grid; @@ -87,7 +87,7 @@ public Grid defaultGridFor(final Class<?> domainClass) { } @Override - public Grid normalize(final Grid grid) { + public BSGrid normalize(final BSGrid grid) { if(grid.isNormalized()) return grid; var domainClass = grid.domainClass(); @@ -98,7 +98,7 @@ public Grid normalize(final Grid grid) { } @Override - public Grid complete(final Grid grid) { + public BSGrid complete(final BSGrid grid) { var domainClass = grid.domainClass(); for (var gridSystemService : gridSystemServices()) { gridSystemService.complete(_Casts.uncheckedCast(grid), domainClass); @@ -107,7 +107,7 @@ public Grid complete(final Grid grid) { } @Override - public Grid minimal(final Grid grid) { + public BSGrid minimal(final BSGrid grid) { var domainClass = grid.domainClass(); for (var gridSystemService : gridSystemServices()) { gridSystemService.minimal(_Casts.uncheckedCast(grid), domainClass); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java index 1b3dc7feac4..82f89a3c5d7 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/GridSystemServiceAbstract.java @@ -31,7 +31,8 @@ import org.apache.causeway.applib.layout.component.DomainObjectLayoutDataOwner; import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.services.grid.GridSystemService; import org.apache.causeway.applib.services.i18n.TranslationService; import org.apache.causeway.applib.services.message.MessageService; @@ -85,8 +86,8 @@ @RequiredArgsConstructor(onConstructor_ = {@Inject}, access = AccessLevel.PROTECTED) @Slf4j -public abstract class GridSystemServiceAbstract<G extends org.apache.causeway.applib.layout.grid.Grid> -implements GridSystemService<G> { +public abstract class GridSystemServiceAbstract +implements GridSystemService { protected final Provider<SpecificationLoader> specLoaderProvider; protected final TranslationService translationService; @@ -94,7 +95,7 @@ public abstract class GridSystemServiceAbstract<G extends org.apache.causeway.ap protected final CausewaySystemEnvironment causewaySystemEnvironment; @Override - public void normalize(final G grid, final Class<?> domainClass) { + public void normalize(final BSGrid grid, final Class<?> domainClass) { // ignore any other grid implementations if(!gridImplementation().isAssignableFrom(grid.getClass())) return; @@ -113,7 +114,7 @@ public void normalize(final G grid, final Class<?> domainClass) { } } - protected abstract String toXml(Grid grid); + protected abstract String toXml(BSGrid grid); /** * Mandatory hook method for subclasses, where they must ensure that all object members (properties, collections @@ -121,7 +122,7 @@ public void normalize(final G grid, final Class<?> domainClass) { * (eg facets from annotations) or just by applying default rules. */ protected abstract boolean validateAndNormalize( - final Grid grid, + final BSGrid grid, final Class<?> domainClass); /** @@ -131,7 +132,7 @@ protected abstract boolean validateAndNormalize( * because the layout might be reloaded from XML if reloading is supported. */ private void overwriteFacets( - final G fcGrid, + final BSGrid fcGrid, final Class<?> domainClass) { var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); @@ -146,7 +147,7 @@ private void overwriteFacets( : Facet.Precedence.HIGH; // non-fallback case: XML layout overrules layout from annotations final AtomicInteger propertySequence = new AtomicInteger(0); - fcGrid.visit(new Grid.Visitor() { + fcGrid.visit(new BSElementVisitor() { private int collectionSequence = 1; private int actionDomainObjectSequence = 1; @@ -366,17 +367,17 @@ public void visit(final CollectionLayoutData collectionLayoutData) { @Programmatic @Override - public void complete(final G grid, final Class<?> domainClass) { + public void complete(final BSGrid grid, final Class<?> domainClass) { normalize(grid, domainClass); var objectSpec = specLoaderProvider.get().specForTypeElseFail(domainClass); - grid.visit(MetamodelToGridOverridingVisitor.of(objectSpec)); + grid.visit(new MetamodelToGridOverridingVisitor(objectSpec)); } @Programmatic @Override - public void minimal(final G grid, final Class<?> domainClass) { + public void minimal(final BSGrid grid, final Class<?> domainClass) { normalize(grid, domainClass); - grid.visit(new Grid.Visitor() { + grid.visit(new BSElementVisitor() { @Override public void visit(final ActionLayoutData actionLayoutData) { actionLayoutData.owner().getActions().remove(actionLayoutData); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java index a11ca6e5106..fe18cc31c36 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/XsiSchemaLocationProviderForGrid.java @@ -19,19 +19,11 @@ package org.apache.causeway.core.metamodel.services.grid; import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; import java.util.stream.Collectors; import jakarta.xml.bind.Marshaller; -import org.springframework.stereotype.Service; - -import org.apache.causeway.applib.layout.grid.Grid; -import org.apache.causeway.applib.services.grid.GridSystemService; - -@Service -public record XsiSchemaLocationProviderForGrid(List<GridSystemService<? extends Grid>> gridSystemServices) { +public record XsiSchemaLocationProviderForGrid() { static final String COMPONENT_TNS = "https://causeway.apache.org/applib/layout/component"; static final String COMPONENT_SCHEMA_LOCATION = "https://causeway.apache.org/applib/layout/component/component.xsd"; @@ -39,26 +31,13 @@ public record XsiSchemaLocationProviderForGrid(List<GridSystemService<? extends static final String LINKS_TNS = "https://causeway.apache.org/applib/layout/links"; static final String LINKS_SCHEMA_LOCATION = "https://causeway.apache.org/applib/layout/links/links.xsd"; - public XsiSchemaLocationProviderForGrid { - final var gridImplementations = new HashSet<Class<?>>(); - /* - * For all of the {@link GridSystemService}s available, return only the first one for any that - * are for the same grid implementation. - * - * <p>This allows default implementations (eg for bootstrap3) to be overridden while also allowing for the more - * general idea of multiple implementations. - */ - gridSystemServices = gridSystemServices - .stream() - // true only if gridImplementations did not already contain the specified element - .filter(gridService->gridImplementations.add(gridService.gridImplementation())) - .toList(); - } + static final String BS_TNS = "https://causeway.apache.org/applib/layout/grid/bootstrap3"; + static final String BS_SCHEMA_LOCATION = "https://causeway.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd"; /** * @see Marshaller#JAXB_SCHEMA_LOCATION */ - public String xsiSchemaLocation(final Class<? extends Grid> gridClass) { + public String xsiSchemaLocation() { var parts = new ArrayList<String>(); parts.add(COMPONENT_TNS); @@ -67,13 +46,9 @@ public String xsiSchemaLocation(final Class<? extends Grid> gridClass) { parts.add(LINKS_TNS); parts.add(LINKS_SCHEMA_LOCATION); - for (var gridSystemService : gridSystemServices()) { - var gridImpl = gridSystemService.gridImplementation(); - if(gridImpl.isAssignableFrom(gridClass)) { - parts.add(gridSystemService.tns()); - parts.add(gridSystemService.schemaLocation()); - } - } + parts.add(BS_TNS); + parts.add(BS_SCHEMA_LOCATION); + return parts.stream() .collect(Collectors.joining(" ")); } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java index ab5e84e04de..7f88800c6c3 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridInitializationModel.java @@ -24,12 +24,13 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; + import org.apache.causeway.applib.layout.component.ActionLayoutData; import org.apache.causeway.applib.layout.component.CollectionLayoutData; import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; import org.apache.causeway.applib.layout.grid.bootstrap.BSCol; -import org.apache.causeway.applib.layout.grid.bootstrap.BSElement; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.layout.grid.bootstrap.BSRow; import org.apache.causeway.applib.layout.grid.bootstrap.BSTabGroup; @@ -38,6 +39,7 @@ import org.apache.causeway.commons.internal.collections._Multimaps; import org.apache.causeway.commons.internal.collections._Sets; import org.apache.causeway.core.metamodel.facets.members.layout.group.GroupIdAndName; + import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -67,7 +69,7 @@ public static Optional<GridInitializationModel> createFrom(final BSGrid bsGrid) var gridModel = new GridInitializationModel(); - bsGrid.visit(new BSElement.Visitor() { + bsGrid.visit(new BSElementVisitor() { @Override public void visit(final BSRow bsRow) { final String id = bsRow.getId(); @@ -116,7 +118,7 @@ public void visit(final FieldSet fieldSet) { return Optional.empty(); } - bsGrid.visit(new BSElement.Visitor(){ + bsGrid.visit(new BSElementVisitor(){ @Override public void visit(final BSCol bsCol) { diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java index aa361fa8ae1..36fd1f48a59 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridMarshallerServiceBootstrap.java @@ -58,7 +58,7 @@ public record GridMarshallerServiceBootstrap( JaxbService jaxbService, XsiSchemaLocationProviderForGrid schemaLocationProvider, EnumSet<CommonMimeType> supportedFormats - ) implements GridMarshaller<BSGrid> { + ) implements GridMarshaller { @Inject public GridMarshallerServiceBootstrap(final JaxbService jaxbService, final XsiSchemaLocationProviderForGrid schemaLocationProvider) { @@ -84,7 +84,7 @@ public String marshal(final @NonNull BSGrid bsGrid, final @NonNull CommonMimeTyp case XML:{ return jaxbService.toXml(bsGrid, Map.of(jakarta.xml.bind.Marshaller.JAXB_SCHEMA_LOCATION, - schemaLocationProvider.xsiSchemaLocation(BSGrid.class))); + schemaLocationProvider.xsiSchemaLocation())); } default: throw _Exceptions.unsupportedOperation("supported format %s is not implemented", format.name()); diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java index e3c1928b877..a63e106344b 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/GridSystemServiceBootstrap.java @@ -46,7 +46,6 @@ import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; import org.apache.causeway.applib.layout.grid.bootstrap.BSCol; import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.layout.grid.bootstrap.BSRow; @@ -100,10 +99,7 @@ @Qualifier("Bootstrap") @Slf4j public class GridSystemServiceBootstrap -extends GridSystemServiceAbstract<BSGrid> { - - public static final String TNS = "https://causeway.apache.org/applib/layout/grid/bootstrap3"; - public static final String SCHEMA_LOCATION = "https://causeway.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd"; +extends GridSystemServiceAbstract { /** * SPI to customize layout fallback behavior on a per class basis. @@ -118,7 +114,7 @@ public static interface FallbackLayoutDataSource { @Inject @Lazy // circular dependency (late binding) @Setter @Accessors(chain = true) // JUnit support - private GridMarshaller<BSGrid> marshaller; + private GridMarshaller marshaller; private final CausewayConfiguration config; private final Can<FallbackLayoutDataSource> fallbackLayoutDataSources; @@ -143,18 +139,8 @@ public Class<BSGrid> gridImplementation() { } @Override - public String tns() { - return TNS; - } - - @Override - public String schemaLocation() { - return SCHEMA_LOCATION; - } - - @Override - protected String toXml(Grid grid) { - return marshaller.marshal((BSGrid) grid, CommonMimeType.XML); + protected String toXml(final BSGrid grid) { + return marshaller.marshal(grid, CommonMimeType.XML); } @Override @@ -240,11 +226,9 @@ static void addFieldSetsToColumn( @Override protected boolean validateAndNormalize( - final Grid grid, + final BSGrid bsGrid, final Class<?> domainClass) { - var bsGrid = (BSGrid) grid; - var gridModelIfValid = GridInitializationModel.createFrom(bsGrid); if(!gridModelIfValid.isPresent()) return false; // only present if valid @@ -255,7 +239,7 @@ protected boolean validateAndNormalize( var oneToManyAssociationById = ObjectMember.mapById(objSpec.streamCollections(MixedIn.INCLUDED)); var objectActionById = ObjectMember.mapById(objSpec.streamRuntimeActions(MixedIn.INCLUDED)); - var layoutDataFactory = LayoutDataFactory.of(objSpec); + var layoutDataFactory = new LayoutDataFactory(objSpec); // * left ... those defined in the grid model but not available with the meta-model // * right ... those available with the meta-model but missing in the grid-model diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/Facets.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/Facets.java index 649be57a99f..c779e530803 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/Facets.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/util/Facets.java @@ -124,7 +124,6 @@ public BookmarkPolicy bookmarkPolicyOrElseNotSpecified(final @Nullable FacetHold public Optional<BSGrid> bootstrapGrid( final ObjectSpecification objectSpec, final @Nullable ManagedObject mo) { return objectSpec.lookupFacet(GridFacet.class) - .filter(facet->facet.supports(BSGrid.class)) .map(gridFacet->gridFacet.getGrid(mo)) .flatMap(grid->_Casts.castTo(BSGrid.class, grid)); } diff --git a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java index f6541c9e69b..71af4f7c29d 100644 --- a/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java +++ b/core/mmtestsupport/src/main/java/org/apache/causeway/core/mmtestsupport/MetaModelContext_forTesting.java @@ -34,7 +34,6 @@ import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.util.ClassUtils; -import org.apache.causeway.applib.layout.grid.Grid; import org.apache.causeway.applib.services.factory.FactoryService; import org.apache.causeway.applib.services.grid.GridLoaderService; import org.apache.causeway.applib.services.grid.GridMarshaller; @@ -457,45 +456,43 @@ private final MenuBarsService createMenuBarsService() { } @Getter(lazy = true) - private final GridMarshaller gridMarshallerService = createGridMarshallerService(); - //XXX lombok issue: won't compile if inlined - private final GridMarshaller<? extends Grid> createGridMarshallerService() { - return new GridMarshallerServiceBootstrap(getJaxbService(), new XsiSchemaLocationProviderForGrid(List.of())); + private final GridMarshaller gridMarshaller = createGridMarshaller(); + private final GridMarshaller createGridMarshaller() { + return new GridMarshallerServiceBootstrap(getJaxbService(), new XsiSchemaLocationProviderForGrid()); } @Getter(lazy = true) - private final List<GridSystemService<? extends Grid>> gridSystemServices = List.of( - new GridSystemServiceBootstrap( - getConfiguration(), - ()->getSpecificationLoader(), - getTranslationService(), - getJaxbService(), - getMessageService(), - getSystemEnvironment(), - List.of()) - .setMarshaller(getGridMarshallerService())); + private final List<GridSystemService> gridSystemServices = createGridSystemService(); + private final List<GridSystemService> createGridSystemService() { + return List.of( + new GridSystemServiceBootstrap( + getConfiguration(), + ()->getSpecificationLoader(), + getTranslationService(), + getJaxbService(), + getMessageService(), + getSystemEnvironment(), + List.of()) + .setMarshaller(getGridMarshaller())); + } @Getter(lazy = true) private final GridLoaderService gridLoaderService = createGridLoaderService(); - //XXX lombok issue: won't compile if inlined private final GridLoaderService createGridLoaderService() { return new GridLoaderServiceDefault(getMessageService(), Can.of(new LayoutResourceLoaderDefault()), /*support reloading*/true); } @Getter(lazy = true) private final GridService gridService = createGridService(); - //XXX lombok issue: won't compile if inlined - @SuppressWarnings("unchecked") private final GridService createGridService() { return new GridServiceDefault( getGridLoaderService(), - getGridMarshallerService(), + getGridMarshaller(), getGridSystemServices()); } @Getter(lazy = true) private final LayoutService layoutService = createLayoutService(); - //XXX lombok issue: won't compile if inlined private final LayoutService createLayoutService() { return new LayoutServiceDefault( getSpecificationLoader(), diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/sitemap/SitemapServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/sitemap/SitemapServiceDefault.java index 43b5fb405ce..6f96a0cee82 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/sitemap/SitemapServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/sitemap/SitemapServiceDefault.java @@ -34,7 +34,7 @@ import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; import org.apache.causeway.applib.layout.component.ServiceActionLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars; import org.apache.causeway.applib.services.menu.MenuBarsService; import org.apache.causeway.applib.services.sitemap.SitemapService; @@ -120,7 +120,7 @@ public String toSitemapAdoc(final String title) { var grid = specificationLoader.specForType(actionElementType.getCorrespondingClass()) .flatMap(mo->Facets.bootstrapGrid(mo)) .orElse(null); - grid.visit(new Grid.Visitor() { + grid.visit(new BSElementVisitor() { @Override public void visit(final ActionLayoutData actionLayoutData) { actionElementType.getAction(actionLayoutData.getId(), ActionScope.PRODUCTION_ONLY) .ifPresent(action->{ diff --git a/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/topics/welcome/WelcomeHelpPage.java b/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/topics/welcome/WelcomeHelpPage.java index 0f30c638286..419ee9133fe 100644 --- a/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/topics/welcome/WelcomeHelpPage.java +++ b/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/topics/welcome/WelcomeHelpPage.java @@ -33,7 +33,8 @@ import org.apache.causeway.applib.layout.component.FieldSet; import org.apache.causeway.applib.layout.component.PropertyLayoutData; import org.apache.causeway.applib.layout.component.ServiceActionLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.layout.menubars.MenuBars; import org.apache.causeway.applib.layout.menubars.bootstrap.BSMenuBars; import org.apache.causeway.applib.services.homepage.HomePageResolverService; @@ -169,7 +170,7 @@ private String getDocumentationAsHtml() { private StringBuffer documentationForObjectType(final ObjectSpecification objectSpec) { StringBuffer html = new StringBuffer(); - Grid grid = toGrid(objectSpec.getCorrespondingClass()); + var grid = toGrid(objectSpec.getCorrespondingClass()); html.append("<ul>"); { html.append("<ul>"); @@ -225,7 +226,7 @@ private StringBuffer documentationForObjectType(final ObjectSpecification object html.append("</ul>"); html.append("<ul>"); { - grid.visit(new Grid.Visitor() { + grid.visit(new BSElementVisitor() { @Override public void visit(final FieldSet fieldSet) { if (_NullSafe.isEmpty(fieldSet.getProperties())) { @@ -235,25 +236,25 @@ public void visit(final FieldSet fieldSet) { html.append("<ul>"); for (PropertyLayoutData layout : fieldSet.getProperties()) { objectSpec.getProperty(layout.getId()) - .ifPresent(member -> { - if (!member.isAlwaysHidden()) { - String describedAs = member.getCanonicalDescription() - .map(desc -> String.format("%s", desc)) - .orElse(""); - html.append(String.format("<li><b>%s</b>: %s.", - member.getCanonicalFriendlyName(), - describedAs)); - if (member.getElementType().logicalType().correspondingClass() - .isAnnotationPresent(DomainObject.class)) { - html.append(String.format(" <i> See: <a href='#%s'>%s</a></i>", - member.getElementType().logicalTypeName(), - member.getElementType().getSingularName())); - } else { - //none - } - html.append("</li>\n"); + .ifPresent(member -> { + if (!member.isAlwaysHidden()) { + String describedAs = member.getCanonicalDescription() + .map(desc -> String.format("%s", desc)) + .orElse(""); + html.append(String.format("<li><b>%s</b>: %s.", + member.getCanonicalFriendlyName(), + describedAs)); + if (member.getElementType().logicalType().correspondingClass() + .isAnnotationPresent(DomainObject.class)) { + html.append(String.format(" <i> See: <a href='#%s'>%s</a></i>", + member.getElementType().logicalTypeName(), + member.getElementType().getSingularName())); + } else { + //none } - }); + html.append("</li>\n"); + } + }); } html.append("</ul>"); } @@ -272,7 +273,7 @@ private Optional<ObjectAction> lookupAction(final ServiceActionLayoutData action .map(typeSpec -> typeSpec.getAction(actionLayout.getId(), ActionScope.PRODUCTION_ONLY).orElse(null)); } - private Grid toGrid(final Class<?> domainClass) { + private BSGrid toGrid(final Class<?> domainClass) { return specificationLoader.specForType(domainClass) .flatMap(spec -> spec.lookupFacet(GridFacet.class)) .map(gridFacet -> gridFacet.getGrid(null)) diff --git a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java index f7efa8465b9..042c6b73980 100644 --- a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java +++ b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java @@ -32,7 +32,8 @@ import org.apache.causeway.applib.layout.component.CollectionLayoutData; import org.apache.causeway.applib.layout.component.DomainObjectLayoutData; import org.apache.causeway.applib.layout.component.PropertyLayoutData; -import org.apache.causeway.applib.layout.grid.Grid; +import org.apache.causeway.applib.layout.grid.bootstrap.BSElement.BSElementVisitor; +import org.apache.causeway.applib.layout.grid.bootstrap.BSGrid; import org.apache.causeway.applib.layout.links.Link; import org.apache.causeway.commons.io.UrlUtils; import org.apache.causeway.core.metamodel.consent.Consent; @@ -250,7 +251,7 @@ public ResponseEntity<Object> layout( .orElseGet(ResponseFactory::notFound)); } - private Optional<Grid> layoutAsGrid( + private Optional<BSGrid> layoutAsGrid( final String domainType, final String instanceId) { @@ -267,9 +268,9 @@ public static void addLinks( final ResourceContext resourceContext, final String domainType, final String instanceId, - final Grid grid) { + final BSGrid grid) { - grid.visit(new Grid.Visitor() { + grid.visit(new BSElementVisitor() { @Override public void visit(final DomainObjectLayoutData domainObjectLayoutData) { Link link = newLink(
