This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/main by this push:
new 2400f4189c2 CAUSEWAY-2297: fixes grid models for incubating viewers
2400f4189c2 is described below
commit 2400f4189c2802df46958f8c3e00158e408000f8
Author: Andi Huber <[email protected]>
AuthorDate: Thu Oct 23 13:34:18 2025 +0200
CAUSEWAY-2297: fixes grid models for incubating viewers
---
.../facets/object/grid/GridFacetDefault.java | 51 ++++++++--
.../grid/bootstrap/CollapseIfOneTabProcessor.java | 52 +++++++++++
.../grid/bootstrap/GridSystemServiceBootstrap.java | 98 +++++++++----------
.../services/grid/bootstrap/_GridModel.java | 57 +++++++----
.../causeway/core/metamodel/util/Facets.java | 5 +-
.../applib/services/menu/model/MenuAction.java | 41 +++++++-
.../viewer/commons/model/layout/UiGridLayout.java | 104 +++++++--------------
7 files changed, 259 insertions(+), 149 deletions(-)
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacetDefault.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacetDefault.java
index 4c2440a27e0..af3f01dc475 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacetDefault.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/grid/GridFacetDefault.java
@@ -20,16 +20,21 @@
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
+import org.apache.causeway.applib.layout.component.ActionLayoutData;
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._Casts;
import org.apache.causeway.commons.internal.base._Lazy;
import org.apache.causeway.commons.internal.base._Strings;
+import org.apache.causeway.commons.internal.collections._Sets;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.facetapi.Facet;
import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
@@ -37,6 +42,8 @@
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
record GridFacetDefault(
GridService gridService,
@@ -63,17 +70,21 @@ public static GridFacet create(
@Override public FacetHolder getFacetHolder() { return facetHolder(); }
@Override
- public Grid getGrid(final @Nullable ManagedObject objectAdapter) {
- guardAgainstObjectOfDifferentType(objectAdapter);
+ public Grid getGrid(final @Nullable ManagedObject mo) {
+ guardAgainstObjectOfDifferentType(mo);
// gridByLayoutName is used as cache, unless
gridService.supportsReloading() returns true
- return gridByLayoutPrefix.compute(layoutPrefixFor(objectAdapter),
- (layoutPrefix, cachedLayout)->
- (cachedLayout==null
- || gridService.supportsReloading())
- ? this.load(layoutPrefix)
- : cachedLayout
- );
+ var grid = gridByLayoutPrefix.compute(layoutPrefixFor(mo),
+ (layoutPrefix, cachedLayout)->
+ (cachedLayout==null
+ || gridService.supportsReloading())
+ ? this.load(layoutPrefix)
+ : cachedLayout);
+
+ _Casts.castTo(BSGrid.class, grid)
+ .ifPresent(bsGrid->attachAssociatedActions(bsGrid, mo));
+
+ return grid;
}
@Override
@@ -83,6 +94,28 @@ public void visitAttributes(final BiConsumer<String, Object>
visitor) {
// -- HELPER
+ private void attachAssociatedActions(BSGrid bsGrid, @Nullable
ManagedObject mo) {
+ if(ManagedObjects.isNullOrUnspecifiedOrEmpty(mo)) return;
+
+ var primedActions = bsGrid.getAllActionsById();
+ final Set<String> actionIdsAlreadyAdded =
_Sets.newHashSet(primedActions.keySet());
+
+ mo.objSpec().streamProperties(MixedIn.INCLUDED)
+ .forEach(property->{
+ Optional.ofNullable(
+ bsGrid.getAllPropertiesById().get(property.getId()))
+ .ifPresent(pl->{
+ ObjectAction.Util.findForAssociation(mo, property)
+ .map(action->action.getId())
+ .filter(id->!actionIdsAlreadyAdded.contains(id))
+ .peek(actionIdsAlreadyAdded::add)
+ .map(ActionLayoutData::new)
+ .forEach(pl.getActions()::add);
+ });
+
+ });
+ }
+
private void guardAgainstObjectOfDifferentType(final @Nullable
ManagedObject objectAdapter) {
if(ManagedObjects.isNullOrUnspecifiedOrEmpty(objectAdapter)) return;
// cannot introspect
if(!getSpecification().equals(objectAdapter.objSpec())) {
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/CollapseIfOneTabProcessor.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/CollapseIfOneTabProcessor.java
new file mode 100644
index 00000000000..50c9b84bdcd
--- /dev/null
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/CollapseIfOneTabProcessor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.causeway.core.metamodel.services.grid.bootstrap;
+
+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.BSTabGroup;
+
+/**
+ * Conditionally collapses tab groups.
+ *
+ * <p> honors {@code <bs:tabGroup collapseIfOne="true">}
+ */
+record CollapseIfOneTabProcessor(BSGrid bsGrid) {
+
+ public void run() {
+ bsGrid.visit(new BSGrid.VisitorAdapter() {
+ @Override
+ public void visit(BSTabGroup bsTabGroup) {
+ if(bsTabGroup.isCollapseIfOne() == null
+ || !bsTabGroup.isCollapseIfOne()
+ || bsTabGroup.getTabs().size()>1) {
+ return;
+ }
+ var parent = (BSCol) bsTabGroup.getOwner();
+ parent.getTabGroups().remove(bsTabGroup);
+ // relocate rows from tab to owning col
+ bsTabGroup.getTabs().get(0).getRows()
+ .forEach(row->{
+ parent.getRows().add(row);
+ row.setOwner(parent);
+ });
+ }
+ });
+ }
+}
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 dcb5b126526..293c889efa0 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
@@ -20,6 +20,7 @@
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -341,22 +342,20 @@ protected boolean validateAndNormalize(
}
if(!unboundPropertyIds.isEmpty()) {
- var fieldSet =
gridModel.getFieldSetForUnreferencedPropertiesRef();
- if(fieldSet != null) {
-
unboundPropertyIds.removeAll(unboundMetadataContributingIds);
+ var fieldSet = gridModel.nodeForUnreferencedProperties();
+ unboundPropertyIds.removeAll(unboundMetadataContributingIds);
- // add unbound properties respecting configured sequence
policy
- var sortedUnboundPropertyIds = _UnreferencedSequenceUtil
- .sortProperties(config, unboundPropertyIds.stream()
- .map(oneToOneAssociationById::get)
- .filter(_NullSafe::isPresent));
+ // add unbound properties respecting configured sequence policy
+ var sortedUnboundPropertyIds = _UnreferencedSequenceUtil
+ .sortProperties(config, unboundPropertyIds.stream()
+ .map(oneToOneAssociationById::get)
+ .filter(_NullSafe::isPresent));
- addPropertiesTo(
- fieldSet,
- sortedUnboundPropertyIds,
- layoutDataFactory::createPropertyLayoutData,
- propertyLayoutDataById::put);
- }
+ addPropertiesTo(
+ fieldSet,
+ sortedUnboundPropertyIds,
+ layoutDataFactory::createPropertyLayoutData,
+ propertyLayoutDataById::put);
}
}
@@ -378,23 +377,22 @@ protected boolean validateAndNormalize(
.map(oneToManyAssociationById::get)
.filter(_NullSafe::isPresent));
- final BSTabGroup bsTabGroup =
gridModel.getTabGroupForUnreferencedCollectionsRef();
- if(bsTabGroup != null) {
- addCollectionsTo(
- bsTabGroup,
- sortedMissingCollectionIds,
- objectSpec,
- layoutDataFactory::createCollectionLayoutData);
- } else {
- final BSCol bsCol =
gridModel.getColForUnreferencedCollectionsRef();
- if(bsCol != null) {
- addCollectionsTo(
+ gridModel.nodeForUnreferencedCollections()
+ .accept(
+ bsCol->{
+ addUnreferencedCollectionsTo(
bsCol,
sortedMissingCollectionIds,
layoutDataFactory::createCollectionLayoutData,
collectionLayoutDataById::put);
- }
- }
+ },
+ bsTabGroup->{
+ addUnreferencedCollectionsTo(
+ bsTabGroup,
+ sortedMissingCollectionIds,
+ objectSpec,
+ layoutDataFactory::createCollectionLayoutData);
+ });
}
// any missing actions will be added as actions in the specified column
@@ -502,25 +500,25 @@ protected boolean validateAndNormalize(
}
if(!missingActionIds.isEmpty()) {
- final BSCol bsCol = gridModel.getColForUnreferencedActionsRef();
- if(bsCol != null) {
- addActionsTo(
- bsCol,
- missingActionIds,
- layoutDataFactory::createActionLayoutData,
- actionLayoutDataById::put);
- } else {
- final FieldSet fieldSet =
gridModel.getFieldSetForUnreferencedActionsRef();
- if(fieldSet != null) {
- addActionsTo(
+ gridModel.nodeForUnreferencedActions()
+ .accept(
+ bsCol->{
+ addActionsTo(
+ bsCol,
+ missingActionIds,
+ layoutDataFactory::createActionLayoutData,
+ actionLayoutDataById::put);
+ },
+ fieldSet->{
+ addActionsTo(
fieldSet,
missingActionIds,
layoutDataFactory::createActionLayoutData,
actionLayoutDataById::put);
- }
- }
+ });
}
+ new CollapseIfOneTabProcessor(bsGrid).run();
return true;
}
@@ -546,7 +544,7 @@ private void addPropertiesTo(
}
}
- private void addCollectionsTo(
+ private void addUnreferencedCollectionsTo(
final BSCol tabRowCol,
final Collection<String> collectionIds,
final Function<String, CollectionLayoutData> layoutFactory,
@@ -559,21 +557,27 @@ private void addCollectionsTo(
}
}
- private void addCollectionsTo(
+ private void addUnreferencedCollectionsTo(
final BSTabGroup tabGroup,
final Collection<String> collectionIds,
final ObjectSpecification objectSpec,
final Function<String, CollectionLayoutData> layoutFactory) {
- for (final String collectionId : collectionIds) {
- final BSTab bsTab = new BSTab();
+ // prevent multiple tabs with the same name
+ var tabsByName = new HashMap<String, BSTab>();
+ tabGroup.getTabs().forEach(tab->tabsByName.put(tab.getName(), tab));
+ for (final String collectionId : collectionIds) {
var feature = objectSpec.getCollectionElseFail(collectionId);
var featureCanonicalFriendlyName =
feature.getCanonicalFriendlyName();
- bsTab.setName(featureCanonicalFriendlyName);
- tabGroup.getTabs().add(bsTab);
- bsTab.setOwner(tabGroup);
+ final BSTab bsTab =
tabsByName.computeIfAbsent(featureCanonicalFriendlyName, __->{
+ var newTab = new BSTab();
+ newTab.setName(featureCanonicalFriendlyName);
+ tabGroup.getTabs().add(newTab);
+ newTab.setOwner(tabGroup);
+ return newTab;
+ });
final BSRow tabRow = new BSRow();
tabRow.setOwner(bsTab);
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_GridModel.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_GridModel.java
index 582c9052f1f..71f0b099590 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_GridModel.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/grid/bootstrap/_GridModel.java
@@ -21,6 +21,7 @@
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.Objects;
import java.util.Optional;
import org.apache.causeway.applib.layout.component.FieldSet;
@@ -28,12 +29,12 @@
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;
+import org.apache.causeway.commons.functional.Either;
import org.apache.causeway.commons.internal.collections._Maps;
import org.apache.causeway.commons.internal.collections._Sets;
import
org.apache.causeway.core.metamodel.facets.members.layout.group.GroupIdAndName;
import lombok.AccessLevel;
-import lombok.Getter;
import lombok.NoArgsConstructor;
/**
@@ -47,11 +48,32 @@ final class _GridModel {
private final LinkedHashMap<String, BSCol> cols =
_Maps.newLinkedHashMap();
private final LinkedHashMap<String, FieldSet> fieldSets =
_Maps.newLinkedHashMap();
- @Getter private BSCol colForUnreferencedActionsRef;
- @Getter private BSCol colForUnreferencedCollectionsRef;
- @Getter private FieldSet fieldSetForUnreferencedActionsRef;
- @Getter private FieldSet fieldSetForUnreferencedPropertiesRef;
- @Getter private BSTabGroup tabGroupForUnreferencedCollectionsRef;
+ private BSCol colForUnreferencedActionsRef;
+ private FieldSet fieldSetForUnreferencedActionsRef;
+
+ private BSCol colForUnreferencedCollectionsRef;
+ private BSTabGroup tabGroupForUnreferencedCollectionsRef;
+
+ private FieldSet fieldSetForUnreferencedPropertiesRef;
+
+ // safe to call once validated
+ FieldSet nodeForUnreferencedProperties() {
+ return
Objects.requireNonNull(fieldSetForUnreferencedPropertiesRef);
+ }
+
+ // safe to call once validated
+ Either<BSCol, FieldSet> nodeForUnreferencedActions() {
+ return colForUnreferencedActionsRef!=null
+ ? Either.left(colForUnreferencedActionsRef)
+ : Either.right(fieldSetForUnreferencedActionsRef);
+ }
+
+ // safe to call once validated
+ Either<BSCol, BSTabGroup> nodeForUnreferencedCollections() {
+ return colForUnreferencedCollectionsRef!=null
+ ? Either.left(colForUnreferencedCollectionsRef)
+ : Either.right(tabGroupForUnreferencedCollectionsRef);
+ }
private boolean gridErrorsDetected = false;
@@ -190,25 +212,26 @@ public void visit(final BSTabGroup bsTabGroup) {
}
});
- if(gridModel.colForUnreferencedActionsRef == null &&
gridModel.fieldSetForUnreferencedActionsRef == null) {
+ boolean isValid = true;
+
+ if(gridModel.colForUnreferencedActionsRef == null
+ && gridModel.fieldSetForUnreferencedActionsRef == null) {
bsGrid.getMetadataErrors().add("No column and also no fieldset
found with the 'unreferencedActions' attribute set");
+ isValid = false;
}
if(gridModel.fieldSetForUnreferencedPropertiesRef == null) {
bsGrid.getMetadataErrors().add("No fieldset found with the
'unreferencedProperties' attribute set");
+ isValid = false;
}
- if(gridModel.colForUnreferencedCollectionsRef == null &&
gridModel.tabGroupForUnreferencedCollectionsRef == null) {
+ if(gridModel.colForUnreferencedCollectionsRef == null
+ && gridModel.tabGroupForUnreferencedCollectionsRef == null) {
bsGrid.getMetadataErrors().add("No column and also no tabgroup
found with the 'unreferencedCollections' attribute set");
+ isValid = false;
}
- final boolean hasErrors =
- gridModel.colForUnreferencedActionsRef == null
- && gridModel.fieldSetForUnreferencedActionsRef == null
- || gridModel.fieldSetForUnreferencedPropertiesRef == null
- || gridModel.colForUnreferencedCollectionsRef == null
- && gridModel.tabGroupForUnreferencedCollectionsRef == null;
-
- return hasErrors ? Optional.empty() : Optional.of(gridModel);
-
+ return isValid
+ ? Optional.of(gridModel)
+ : Optional.empty();
}
private void putRow(final String id, final BSRow bsRow) {
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 10b800ad103..784f05524dd 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
@@ -24,6 +24,7 @@
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
+
import org.springframework.util.ClassUtils;
import org.apache.causeway.applib.annotation.BookmarkPolicy;
@@ -121,9 +122,9 @@ public BookmarkPolicy
bookmarkPolicyOrElseNotSpecified(final @Nullable FacetHold
}
public Optional<BSGrid> bootstrapGrid(
- final ObjectSpecification objectSpec, final @Nullable
ManagedObject objectAdapter) {
+ final ObjectSpecification objectSpec, final @Nullable
ManagedObject mo) {
return objectSpec.lookupFacet(GridFacet.class)
- .map(gridFacet->gridFacet.getGrid(objectAdapter))
+ .map(gridFacet->gridFacet.getGrid(mo))
.flatMap(grid->_Casts.castTo(BSGrid.class, grid));
}
diff --git
a/viewers/commons/applib/src/main/java/org/apache/causeway/viewer/commons/applib/services/menu/model/MenuAction.java
b/viewers/commons/applib/src/main/java/org/apache/causeway/viewer/commons/applib/services/menu/model/MenuAction.java
index 845baec005f..a98beadb512 100644
---
a/viewers/commons/applib/src/main/java/org/apache/causeway/viewer/commons/applib/services/menu/model/MenuAction.java
+++
b/viewers/commons/applib/src/main/java/org/apache/causeway/viewer/commons/applib/services/menu/model/MenuAction.java
@@ -20,30 +20,61 @@
import java.util.Optional;
-import org.jspecify.annotations.Nullable;
+import org.jspecify.annotations.NonNull;
import org.apache.causeway.applib.Identifier;
import org.apache.causeway.applib.annotation.Where;
+import org.apache.causeway.applib.fa.FontAwesomeLayers;
import org.apache.causeway.applib.services.bookmark.Bookmark;
+import org.apache.causeway.core.metamodel.consent.Consent.VetoReason;
import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import
org.apache.causeway.core.metamodel.facets.members.iconfa.FaLayersProvider;
import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
+import org.apache.causeway.core.metamodel.util.Facets;
-import org.jspecify.annotations.NonNull;
+import lombok.AccessLevel;
+import lombok.Builder;
public record MenuAction (
@NonNull Bookmark serviceBookmark,
@NonNull Identifier actionId,
@NonNull String name,
- @Nullable String cssClassFa
+ @NonNull DecorationModel decorationModel
) implements MenuEntry {
+ @Builder(access = AccessLevel.PRIVATE)
+ public record DecorationModel(
+ boolean isPrototype,
+ int paramCount,
+ Optional<VetoReason> interactionVetoOpt,
+ Optional<FontAwesomeLayers> fontAwesomeLayersOpt,
+ Optional<String> describedAsOpt,
+ Optional<String> additionalCssClassOpt) {
+ static DecorationModel of(final @NonNull ManagedAction managedAction) {
+ var action = managedAction.getAction();
+ return DecorationModel.builder()
+ .isPrototype(action.isPrototype())
+ .paramCount(action.getParameterCount())
+ .interactionVetoOpt(managedAction.checkUsability()
+ .flatMap(veto->veto.getReason()))
+ .fontAwesomeLayersOpt(ObjectAction.Util.cssClassFaFactoryFor(
+ managedAction.getAction(),
+ managedAction.getOwner())
+ .map(FaLayersProvider::getLayers)
+ .map(FontAwesomeLayers::emptyToBlank))
+ .describedAsOpt(managedAction.getDescription())
+ .additionalCssClassOpt(Facets.cssClass(action,
managedAction.getOwner()))
+ .build();
+ }
+ }
+
public static MenuAction of(final @NonNull ManagedAction managedAction) {
- // TODO missing cssClass
return new MenuAction(
managedAction.getOwner().getBookmark().orElseThrow(),
managedAction.getIdentifier(),
managedAction.getFriendlyName(),
- null);
+ DecorationModel.of(managedAction));
}
public Optional<ManagedAction> managedAction(){
diff --git
a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/layout/UiGridLayout.java
b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/layout/UiGridLayout.java
index 7591aa2ce25..1e9959533fe 100644
---
a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/layout/UiGridLayout.java
+++
b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/layout/UiGridLayout.java
@@ -19,8 +19,6 @@
package org.apache.causeway.viewer.commons.model.layout;
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.DomainObjectLayoutData;
@@ -32,20 +30,16 @@
import org.apache.causeway.applib.layout.grid.bootstrap.BSRow;
import org.apache.causeway.applib.layout.grid.bootstrap.BSTab;
import org.apache.causeway.applib.layout.grid.bootstrap.BSTabGroup;
-import org.apache.causeway.commons.internal.base._Lazy;
import org.apache.causeway.commons.internal.base._NullSafe;
-import org.apache.causeway.commons.internal.collections._Sets;
import org.apache.causeway.core.metamodel.object.ManagedObject;
-import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
import org.apache.causeway.core.metamodel.util.Facets;
import org.apache.causeway.viewer.commons.model.UiModel;
-import org.jspecify.annotations.NonNull;
import lombok.RequiredArgsConstructor;
-@RequiredArgsConstructor(staticName = "bind")
-public class UiGridLayout implements UiModel {
+public record UiGridLayout(
+ BSGrid bsGrid
+ ) implements UiModel {
@RequiredArgsConstructor
public static abstract class Visitor<C, T> {
@@ -61,53 +55,23 @@ public static abstract class Visitor<C, T> {
protected abstract void onAction(C container, ActionLayoutData
actionData);
protected abstract void onProperty(C container, PropertyLayoutData
propertyData);
protected abstract void onCollection(C container, CollectionLayoutData
collectionData);
-
}
- @NonNull private final ManagedObject managedObject;
- private _Lazy<Optional<BSGrid>> gridData =
_Lazy.threadSafe(this::initGridData);
-
- public <C, T> void visit(final Visitor<C, T> visitor) {
-
- // recursively visit the grid
- gridData.get()
- .ifPresent(bsGrid->{
- for(var bsRow: bsGrid.getRows()) {
- visitRow(bsRow, visitor.rootContainer, visitor);
- }
- });
-
+ public static Optional<UiGridLayout> createGrid(ManagedObject mo) {
+ return Facets.bootstrapGrid(mo.objSpec(), mo)
+ .map(UiGridLayout::new);
}
- private Optional<BSGrid> initGridData() {
- return Facets.bootstrapGrid(managedObject.objSpec(), managedObject)
- .map(this::attachAssociatedActions);
+ /**
+ * recursively visits the grid
+ */
+ public <C, T> void visit(final Visitor<C, T> visitor) {
+ for(var bsRow: bsGrid.getRows()) {
+ visitRow(bsRow, visitor.rootContainer, visitor);
+ }
}
- //TODO[refactor] this should not be necessary here, the GridFacet should
already have done that for us
- private BSGrid attachAssociatedActions(final BSGrid bSGrid) {
-
- var primedActions = bSGrid.getAllActionsById();
- final Set<String> actionIdsAlreadyAdded =
_Sets.newHashSet(primedActions.keySet());
-
- managedObject.objSpec().streamProperties(MixedIn.INCLUDED)
- .forEach(property->{
- Optional.ofNullable(
- bSGrid.getAllPropertiesById().get(property.getId()))
- .ifPresent(pl->{
-
- ObjectAction.Util.findForAssociation(managedObject, property)
- .map(action->action.getId())
- .filter(id->!actionIdsAlreadyAdded.contains(id))
- .peek(actionIdsAlreadyAdded::add)
- .map(ActionLayoutData::new)
- .forEach(pl.getActions()::add);
-
- });
-
- });
- return bSGrid;
- }
+ // -- HELPER
private <C, T> void visitRow(final BSRow bsRow, final C container, final
Visitor<C, T> visitor) {
@@ -115,9 +79,7 @@ private <C, T> void visitRow(final BSRow bsRow, final C
container, final Visitor
for(var bsRowContent: bsRow.getCols()) {
if(bsRowContent instanceof BSCol) {
-
visitCol((BSCol) bsRowContent, uiRow, visitor);
-
} else if (bsRowContent instanceof BSClearFix) {
visitor.onClearfix(uiRow, (BSClearFix) bsRowContent);
} else {
@@ -126,55 +88,59 @@ private <C, T> void visitRow(final BSRow bsRow, final C
container, final Visitor
}
}
- private <C, T> void visitCol(final BSCol bSCol, final C container, final
Visitor<C, T> visitor) {
- var uiCol = visitor.newCol(container, bSCol);
+ private <C, T> void visitCol(final BSCol bsCol, final C container, final
Visitor<C, T> visitor) {
+
+ if(bsCol.getSpan() == 0) return; // skip
+
+ var uiCol = visitor.newCol(container, bsCol);
- var hasDomainObject = bSCol.getDomainObject()!=null;
- var hasActions = _NullSafe.size(bSCol.getActions())>0;
- var hasRows = _NullSafe.size(bSCol.getRows())>0;
+ var hasDomainObject = bsCol.getDomainObject()!=null;
+ var hasActions = _NullSafe.size(bsCol.getActions())>0;
+ var hasRows = _NullSafe.size(bsCol.getRows())>0;
if(hasDomainObject || hasActions) {
var uiActionPanel = visitor.newActionPanel(uiCol);
if(hasDomainObject) {
- visitor.onObjectTitle(uiActionPanel, bSCol.getDomainObject());
+ visitor.onObjectTitle(uiActionPanel, bsCol.getDomainObject());
}
if(hasActions) {
- for(var action : bSCol.getActions()) {
+ for(var action : bsCol.getActions()) {
visitor.onAction(uiActionPanel, action);
}
}
}
- for(var fieldSet : bSCol.getFieldSets()) {
+ for(var fieldSet : bsCol.getFieldSets()) {
+ if(_NullSafe.isEmpty(fieldSet.getProperties())) continue; // skip
empty fieldsets
visitFieldSet(fieldSet, uiCol, visitor);
}
- for(var tabGroup : bSCol.getTabGroups()) {
+ for(var tabGroup : bsCol.getTabGroups()) {
visitTabGroup(tabGroup, uiCol, visitor);
}
if(hasRows) {
- for(var bsRow: bSCol.getRows()) {
+ for(var bsRow: bsCol.getRows()) {
visitRow(bsRow, uiCol, visitor);
}
}
- for(var collectionData : bSCol.getCollections()) {
+ for(var collectionData : bsCol.getCollections()) {
visitor.onCollection(uiCol, collectionData);
}
}
- private <C, T> void visitTabGroup(final BSTabGroup BSColTabGroup, final C
container, final Visitor<C, T> visitor) {
- var uiTabGroup = visitor.newTabGroup(container, BSColTabGroup);
- for(var bsTab: BSColTabGroup.getTabs()) {
+ private <C, T> void visitTabGroup(final BSTabGroup bsTabGroup, final C
container, final Visitor<C, T> visitor) {
+ var uiTabGroup = visitor.newTabGroup(container, bsTabGroup);
+ for(var bsTab: bsTabGroup.getTabs()) {
visitTab(bsTab, uiTabGroup, visitor);
}
}
- private <C, T> void visitTab(final BSTab bSTab, final T container, final
Visitor<C, T> visitor) {
- var uiTab = visitor.newTab(container, bSTab);
- for(var bsRow: bSTab.getRows()) {
+ private <C, T> void visitTab(final BSTab bsTab, final T container, final
Visitor<C, T> visitor) {
+ var uiTab = visitor.newTab(container, bsTab);
+ for(var bsRow: bsTab.getRows()) {
visitRow(bsRow, uiTab, visitor);
}
}