This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v3
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/v3 by this push:
new 979df66176b CAUSEWAY-2297: work on simplified tree model (part 5)
979df66176b is described below
commit 979df66176b9da82de8e2f808262df30a7247a89
Author: Andi Huber <[email protected]>
AuthorDate: Tue Dec 10 11:40:08 2024 +0100
CAUSEWAY-2297: work on simplified tree model (part 5)
- makes TreeAdapterWithConverter package private
---
.../userguide/modules/ROOT/pages/overview.adoc | 4 +-
.../causeway/applib/annotation/Navigable.java | 10 +-
.../causeway/applib/graph/tree/TreeAdapter.java | 4 +
.../graph/tree/TreeAdapterWithConverter.java | 2 +-
.../causeway/applib/graph/tree/TreeNode.java | 78 ++++----
.../causeway/applib/graph/tree/TreeState.java | 21 +--
.../tree/CausewayToWicketTreeAdapter.java | 204 ---------------------
.../ui/components/tree/DomainObjectTree.java | 162 ++++++++++++++++
.../ui/components/tree/StandaloneTreePanel.java | 2 +-
.../ui/components/tree/TreeAttributePanel.java | 4 +-
.../ui/components/tree/TreeExpansionModel.java | 10 +-
...CausewayTreeProvider.java => TreeProvider.java} | 21 ++-
12 files changed, 239 insertions(+), 283 deletions(-)
diff --git a/antora/components/userguide/modules/ROOT/pages/overview.adoc
b/antora/components/userguide/modules/ROOT/pages/overview.adoc
index 8a0494bfbef..38dd35bef1b 100644
--- a/antora/components/userguide/modules/ROOT/pages/overview.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/overview.adoc
@@ -96,8 +96,8 @@ To receive the events, the domain service should subscribe to
the xref:refguide:
Enabling and ensuring modularity is a
xref:background-context-and-theory.adoc#modular[key principle] for the Apache
Causeway framework.
Modularity is the only way to ensure that a complex application domain does
not over time degenerate into the infamous "big ball of mud", software that is
difficult, dangerous and expensive to change.
-xref:modules.adoc[Modules] chunk up the overall application into smaller
pieces, usually a pacakge and subpackages.
-The smaller pieces can be either "horizontal" tiers (presentation / domain /
persistence) or "vertial" functional slices (eg customer vs orders vs products
vs invoice etc).
+xref:modules.adoc[Modules] chunk up the overall application into smaller
pieces, usually a package and subpackages.
+The smaller pieces can be either "horizontal" tiers (presentation / domain /
persistence) or "vertical" functional slices (eg customer vs orders vs products
vs invoice etc).
Because the framework takes care in large part of the presentation and
persistence tiers, modules in an Apache Causeway application are oriented
around vertical slices, and determining the dependencies between those modules.
These modules will form a directed acyclic graph (DAG)
diff --git
a/api/applib/src/main/java/org/apache/causeway/applib/annotation/Navigable.java
b/api/applib/src/main/java/org/apache/causeway/applib/annotation/Navigable.java
index 3d76eb9613e..658f60c7975 100644
---
a/api/applib/src/main/java/org/apache/causeway/applib/annotation/Navigable.java
+++
b/api/applib/src/main/java/org/apache/causeway/applib/annotation/Navigable.java
@@ -19,21 +19,15 @@
package org.apache.causeway.applib.annotation;
/**
- *
* Tells the framework which method or field to use in order to construct a
navigable chain of
* parent domain object instances. The Navigable.PARENT 'flag' can only be
used once per class declaration.
*
* @since 2.0 {@index}
*/
public enum Navigable {
-
NOT_SPECIFIED,
IGNORE,
- PARENT,
- ;
-
- public boolean isParent() {
- return this == PARENT;
- }
+ PARENT;
+ public boolean isParent() { return this == PARENT; }
}
diff --git
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapter.java
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapter.java
index 3a1678df009..68cb08c971f 100644
---
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapter.java
+++
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapter.java
@@ -85,4 +85,8 @@ public interface TreeAdapter<T> {
: childNode;
}
+ default <R> TreeAdapter<R> convert(final TreeConverter<T, R> converter) {
+ return new TreeAdapterWithConverter<T, R>(this, converter);
+ }
+
}
diff --git
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapterWithConverter.java
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapterWithConverter.java
index 16a5e0e2047..e5fa6534521 100644
---
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapterWithConverter.java
+++
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeAdapterWithConverter.java
@@ -26,7 +26,7 @@ import org.springframework.lang.Nullable;
import org.apache.causeway.commons.functional.IndexedFunction;
-public record TreeAdapterWithConverter<U, T>(
+record TreeAdapterWithConverter<U, T>(
TreeAdapter<U> underlyingAdapter,
TreeConverter<U, T> converter)
implements TreeAdapter<T>{
diff --git
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeNode.java
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeNode.java
index b8ccdb7186d..31cbb932a12 100644
---
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeNode.java
+++
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeNode.java
@@ -55,21 +55,21 @@ public class TreeNode<T> implements Vertex<T> {
@Getter private final TreeNode<T> rootNode;
@Getter private final TreeAdapter<T> treeAdapter;
-
+
private final TreePath treePath;
private final T value;
private final TreeState sharedState;
public static <T> TreeNode<T> root(
- final T value,
- final TreeAdapter<T> treeAdapter,
+ final T value,
+ final TreeAdapter<T> treeAdapter,
final TreeState sharedState) {
return new TreeNode<T>(value, treeAdapter, sharedState);
}
-
+
public static <T> TreeNode<T> root(
- final T value,
- final Class<? extends TreeAdapter<T>> treeAdapterClass,
+ final T value,
+ final Class<? extends TreeAdapter<T>> treeAdapterClass,
final TreeState sharedState,
final FactoryService factoryService
) {
@@ -80,8 +80,8 @@ public class TreeNode<T> implements Vertex<T> {
protected TreeNode(
final @NonNull TreeNode<T> rootNode,
final @NonNull TreePath treePath,
- final @NonNull T value,
- final @NonNull TreeAdapter<T> treeAdapter,
+ final @NonNull T value,
+ final @NonNull TreeAdapter<T> treeAdapter,
final @NonNull TreeState sharedState) {
this.rootNode = rootNode;
this.treePath = treePath;
@@ -92,8 +92,8 @@ public class TreeNode<T> implements Vertex<T> {
// root-node constructor
private TreeNode(
- final @NonNull T value,
- final @NonNull TreeAdapter<T> treeAdapter,
+ final @NonNull T value,
+ final @NonNull TreeAdapter<T> treeAdapter,
final @NonNull TreeState sharedState) {
this.rootNode = this;
this.treePath = TreePath.root();
@@ -101,11 +101,11 @@ public class TreeNode<T> implements Vertex<T> {
this.treeAdapter = treeAdapter;
this.sharedState = sharedState;
}
-
+
public T getRootValue() {
return getRootNode().getValue();
}
-
+
@Override
public T getValue() {
return value;
@@ -116,7 +116,7 @@ public class TreeNode<T> implements Vertex<T> {
@Override
public int getIncomingCount() {
return getPositionAsPath().isRoot()
- ? 0
+ ? 0
: 1;
}
@Override
@@ -141,15 +141,15 @@ public class TreeNode<T> implements Vertex<T> {
/**
* Resolves given path relative to the root of this tree.
*/
- public Optional<TreeNode<T>> resolve(TreePath absolutePath) {
- /*
+ public Optional<TreeNode<T>> resolve(final TreePath absolutePath) {
+ /*
* Optimize if absolutePath starts with this.treePath:
- *
- * If current path is
- * /p0/p1/p2/p3
+ *
+ * If current path is
+ * /p0/p1/p2/p3
* and we want to resolve
* /p0/p1/p2/p3/p4/p5
- * then instead of starting from root, we can start from here,
resolving sub-node
+ * then instead of starting from root, we can start from here,
resolving sub-node
* /p3/p4/p5
* observe: the relative path /p3 would point to the sub-node itself
*/
@@ -157,7 +157,7 @@ public class TreeNode<T> implements Vertex<T> {
? resolveRelative(absolutePath.subPath(treePath.size() - 1))
: rootNode.resolveRelative(absolutePath);
}
-
+
/**
* Resolves given path relative to this node.
* <p>
@@ -165,24 +165,24 @@ public class TreeNode<T> implements Vertex<T> {
* starting from root, '/0/2' will return the 3rd child of root;<br>
* starting from sub-node '/0/2', '/2/9' will resolve the 10th child
('/0/2/9') of this sub-node;<br>
*/
- private Optional<TreeNode<T>> resolveRelative(TreePath relativePath) {
-
+ private Optional<TreeNode<T>> resolveRelative(final TreePath relativePath)
{
+
if(Objects.equals(this.treePath, relativePath)) {
return Optional.of(this);
}
-
+
final int childIndex = relativePath.childIndex().orElse(-1);
if(childIndex<0) return Optional.empty();
final Optional<TreeNode<T>> childNode =
streamChildren().skip(childIndex).findFirst();
if(!childNode.isPresent()) return Optional.empty();
-
+
return relativePath.size()>2
? childNode.get().resolveRelative(relativePath.subPath(1))
: childNode;
}
-
+
// -- PARENT
-
+
public Optional<TreeNode<T>> lookupParent() {
return Optional.ofNullable(treePath.getParentIfAny())
.flatMap(this::resolve);
@@ -225,7 +225,7 @@ public class TreeNode<T> implements Vertex<T> {
}
public boolean isExpanded(final TreePath treePath) {
- final Set<TreePath> expandedPaths =
getTreeState().getExpandedNodePaths();
+ final Set<TreePath> expandedPaths = getTreeState().expandedNodePaths();
return expandedPaths.contains(treePath);
}
@@ -235,7 +235,7 @@ public class TreeNode<T> implements Vertex<T> {
*/
@Programmatic
public void expand(final TreePath ... treePaths) {
- final Set<TreePath> expandedPaths =
getTreeState().getExpandedNodePaths();
+ final Set<TreePath> expandedPaths = getTreeState().expandedNodePaths();
_NullSafe.stream(treePaths).forEach(expandedPaths::add);
}
@@ -244,7 +244,7 @@ public class TreeNode<T> implements Vertex<T> {
*/
@Programmatic
public void expand() {
- final Set<TreePath> expandedPaths =
getTreeState().getExpandedNodePaths();
+ final Set<TreePath> expandedPaths = getTreeState().expandedNodePaths();
streamHierarchyUp()
.map(TreeNode::getPositionAsPath)
.forEach(expandedPaths::add);
@@ -256,7 +256,7 @@ public class TreeNode<T> implements Vertex<T> {
*/
@Programmatic
public void collapse(final TreePath ... treePaths) {
- final Set<TreePath> expandedPaths =
getTreeState().getExpandedNodePaths();
+ final Set<TreePath> expandedPaths = getTreeState().expandedNodePaths();
_NullSafe.stream(treePaths).forEach(expandedPaths::remove);
}
@@ -264,11 +264,11 @@ public class TreeNode<T> implements Vertex<T> {
/**
* Clears all selection markers.
- * @see #select(TreePath...)
+ * @see #select(TreePath...)
*/
@Programmatic
public void clearSelection() {
- getTreeState().getSelectedNodePaths().clear();
+ getTreeState().selectedNodePaths().clear();
}
/**
@@ -277,25 +277,25 @@ public class TreeNode<T> implements Vertex<T> {
*/
@Programmatic
public boolean isSelected(final TreePath treePath) {
- final Set<TreePath> selectedPaths =
getTreeState().getSelectedNodePaths();
+ final Set<TreePath> selectedPaths = getTreeState().selectedNodePaths();
return selectedPaths.contains(treePath);
}
/**
* Select nodes by their corresponding {@link TreePath}, that is, activate
their selection marker.
* <p>
- * With the <i>Wicket Viewer</i> corresponds to expressing CSS class
{@code tree-node-selected}
+ * With the <i>Wicket Viewer</i> corresponds to expressing CSS class
{@code tree-node-selected}
* on the rendered tree node, which has default bg-color {@code
lightgrey}. Color can be customized
* by setting CSS var {@code--tree-node-selected-bg-color}
* <pre>
* .tree-theme-bootstrap .tree-node-selected {
* background-color: var(--tree-node-selected-bg-color, lightgrey);
* }
- * </pre>
+ * </pre>
*/
@Programmatic
public void select(final TreePath ... treePaths) {
- final Set<TreePath> selectedPaths =
getTreeState().getSelectedNodePaths();
+ final Set<TreePath> selectedPaths = getTreeState().selectedNodePaths();
_NullSafe.stream(treePaths).forEach(selectedPaths::add);
}
@@ -305,16 +305,16 @@ public class TreeNode<T> implements Vertex<T> {
* Creates the root node of a tree structure as inferred from given
treeAdapter.
*/
public static <T> TreeNode<T> root(
- final @NonNull T rootNode,
+ final @NonNull T rootNode,
final @NonNull TreeAdapter<T> treeAdapter) {
return TreeNode.root(rootNode, treeAdapter, TreeState.rootCollapsed());
}
-
+
/**
* Creates the root node of a tree structure as inferred from given
treeAdapter.
*/
public static <T> TreeNode<T> root(
- final @NonNull T rootNode,
+ final @NonNull T rootNode,
final @NonNull Class<? extends TreeAdapter<T>> treeAdapterClass,
final @NonNull FactoryService factoryService) {
return root(rootNode, factoryService.getOrCreate(treeAdapterClass));
diff --git
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState.java
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState.java
index 046762dd93c..7fbd9d7a900 100644
---
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState.java
+++
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState.java
@@ -26,31 +26,24 @@ import org.apache.causeway.applib.annotation.Programmatic;
/**
* Holds information for a tree, which nodes are expanded and which are
selected.
- *
+ *
* @since 2.0 {@index}
*/
@Programmatic
-public class TreeState implements Serializable {
+public record TreeState(
+ Set<TreePath> expandedNodePaths,
+ Set<TreePath> selectedNodePaths) implements Serializable {
// -- FACTORIES
-
+
public static TreeState rootCollapsed() {
return new TreeState();
}
// -- CONSTRUCTION
-
- private static final long serialVersionUID = 7971539034663543462L;
-
- private final Set<TreePath> expandedNodes = new HashSet<>();
- private final Set<TreePath> selectedNodes = new HashSet<>();
-
- public Set<TreePath> getExpandedNodePaths() {
- return expandedNodes;
- }
- public Set<TreePath> getSelectedNodePaths() {
- return selectedNodes;
+ public TreeState() {
+ this(new HashSet<>(), new HashSet<>());
}
}
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/CausewayToWicketTreeAdapter.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/CausewayToWicketTreeAdapter.java
deleted file mode 100644
index 0397cecdda5..00000000000
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/CausewayToWicketTreeAdapter.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.causeway.viewer.wicket.ui.components.tree;
-
-import java.io.Serializable;
-import java.util.Optional;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
-import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
-import org.apache.wicket.extensions.markup.html.repeater.tree.NestedTree;
-import org.apache.wicket.extensions.markup.html.repeater.tree.Node;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.model.IModel;
-
-import org.apache.causeway.applib.graph.tree.TreeNode;
-import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
-import org.apache.causeway.core.metamodel.object.ManagedObject;
-import org.apache.causeway.core.metamodel.object.ManagedObjects;
-import org.apache.causeway.core.metamodel.tree.TreeNodeMemento;
-import org.apache.causeway.viewer.wicket.model.models.UiAttributeWkt;
-import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
-import org.apache.causeway.viewer.wicket.model.models.ValueModel;
-import
org.apache.causeway.viewer.wicket.ui.components.object.icontitle.ObjectIconAndTitlePanelFactory;
-import org.apache.causeway.viewer.wicket.ui.util.Wkt;
-
-class CausewayToWicketTreeAdapter {
-
- /**
- * @param valueModel - holder of {@link TreeNode}
- */
- public static MarkupContainer adapt(final String id, final ValueModel
valueModel) {
- return valueModel==null
- ||
ManagedObjects.isNullOrUnspecifiedOrEmpty(valueModel.getObject())
- ? emptyTreeComponent(id)
- : DomainObjectTree.of(id, valueModel.getObject());
- }
-
- /**
- * @param attributeModel - holder of {@link TreeNode}
- */
- public static MarkupContainer adapt(final String id, final UiAttributeWkt
attributeModel) {
- return attributeModel==null
- ||
ManagedObjects.isNullOrUnspecifiedOrEmpty(attributeModel.getObject())
- ? emptyTreeComponent(id)
- : DomainObjectTree.of(id, attributeModel.getObject());
- }
-
- // -- FALLBACK
-
- private static MarkupContainer emptyTreeComponent(final String id) {
- return new WebMarkupContainer(id);
- }
-
- // -- RENDERING
-
- /**
- * Wicket's Tree Component implemented for Causeway
- */
- private static class DomainObjectTree extends NestedTree<TreeNodeMemento>
- implements HasMetaModelContext {
-
- private static final long serialVersionUID = 1L;
-
- public static DomainObjectTree of(
- final String id, final ManagedObject treeNodeObject) {
-
- var treeNode = (TreeNode<?>) treeNodeObject.getPojo();
-
- var treeModelTreeProvider = new CausewayTreeProvider(
- TreeNodeMemento.mementify(treeNode.getValue(),
treeNode.getPositionAsPath()),
- treeNode.getTreeAdapter());
-
- var treeState = treeNode.getTreeState();
-
- var treeExpansionModel = TreeExpansionModel.of(treeState);
-
- return new DomainObjectTree(id,
- treeModelTreeProvider,
- treeExpansionModel);
- }
-
- private DomainObjectTree(
- final String id,
- final ITreeProvider<TreeNodeMemento> provider,
- final TreeExpansionModel collapseExpandState) {
- super(id, provider, collapseExpandState);
- }
-
- /**
- * To use a custom component for the representation of a node's
content we override this method.
- */
- @Override
- protected Component newContentComponent(final String id, final
IModel<TreeNodeMemento> node) {
- final TreeNodeMemento treeModel = node.getObject();
- final Component entityIconAndTitle =
ObjectIconAndTitlePanelFactory.entityIconAndTitlePanel(
- id, UiObjectWkt.ofBookmark(treeModel.bookmark()));
- if(treeExpansionModel().isSelected(treeModel.treePath())) {
- Wkt.cssAppend(entityIconAndTitle, "tree-node-selected");
- }
- return entityIconAndTitle;
- }
-
- /**
- * To hardcode Node's
<pre>AjaxFallbackLink.isEnabledInHierarchy()->true</pre> we override this
method.
- */
- @Override
- public Component newNodeComponent(final String id, final
IModel<TreeNodeMemento> model) {
-
- final Node<TreeNodeMemento> node = new Node<TreeNodeMemento>(id,
this, model) {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected Component createContent(final String id, final
IModel<TreeNodeMemento> model) {
- return DomainObjectTree.this.newContentComponent(id,
model);
- }
-
- @Override
- protected MarkupContainer createJunctionComponent(final String
id) {
-
- final Node<TreeNodeMemento> node = this;
- final Runnable toggleExpandCollapse = (Runnable &
Serializable) this::toggle;
-
- return new AjaxFallbackLink<Void>(id) {
- private static final long serialVersionUID = 1L;
-
- @Override
- public void onClick(final Optional<AjaxRequestTarget>
target) {
- toggleExpandCollapse.run();
- }
-
- @Override
- public boolean isEnabled() {
- return
DomainObjectTree.this.getProvider().hasChildren(node.getModelObject());
- }
-
- @Override
- public boolean isEnabledInHierarchy() {
- return true; // hardcoded -> true
- }
-
- };
- }
-
- };
-
- node.setOutputMarkupId(true);
- return node;
- }
-
- /**
- * To utilize the custom TreeExpansionModel for deciding a node's
collapse/expand state,
- * we override this method.
- */
- @Override
- public State getState(final TreeNodeMemento t) {
- return treeExpansionModel().contains(t.treePath()) ?
State.EXPANDED : State.COLLAPSED;
- }
-
- /**
- * To utilize the custom TreeExpansionModel for hooking into a node's
expand event,
- * we override this method.
- */
- @Override
- public void expand(final TreeNodeMemento t) {
- treeExpansionModel().onExpand(t);
- super.expand(t);
- }
-
- /**
- * To utilize the custom TreeExpansionModel for hooking into a node's
collapse event,
- * we override this method.
- */
- @Override
- public void collapse(final TreeNodeMemento t) {
- treeExpansionModel().onCollapse(t);
- super.collapse(t);
- }
-
- private TreeExpansionModel treeExpansionModel() {
- return (TreeExpansionModel) getModel();
- }
-
- }
-
-}
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/DomainObjectTree.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/DomainObjectTree.java
new file mode 100644
index 00000000000..3399e2cfc66
--- /dev/null
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/DomainObjectTree.java
@@ -0,0 +1,162 @@
+package org.apache.causeway.viewer.wicket.ui.components.tree;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
+import org.apache.wicket.extensions.markup.html.repeater.tree.NestedTree;
+import org.apache.wicket.extensions.markup.html.repeater.tree.Node;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.IModel;
+
+import org.apache.causeway.applib.graph.tree.TreeNode;
+import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
+import org.apache.causeway.core.metamodel.object.ManagedObjects;
+import org.apache.causeway.core.metamodel.tree.TreeNodeMemento;
+import org.apache.causeway.viewer.wicket.model.models.UiAttributeWkt;
+import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
+import org.apache.causeway.viewer.wicket.model.models.ValueModel;
+import
org.apache.causeway.viewer.wicket.ui.components.object.icontitle.ObjectIconAndTitlePanelFactory;
+import org.apache.causeway.viewer.wicket.ui.util.Wkt;
+
+/**
+ * Wicket's Tree Component implemented for Causeway,
+ * where the tree is modeled by a root {@link TreeNode} (and its child-nodes
recursively).
+ */
+class DomainObjectTree extends NestedTree<TreeNodeMemento>
+implements HasMetaModelContext {
+
+ private static final long serialVersionUID = 1L;
+
+ // -- FACTORIES
+
+ /**
+ * @param valueModel - holder of {@link TreeNode}
+ */
+ static MarkupContainer createComponent(final String id, final ValueModel
valueModel) {
+ return valueModel==null
+ ||
ManagedObjects.isNullOrUnspecifiedOrEmpty(valueModel.getObject())
+ ? emptyTreeComponent(id)
+ : new DomainObjectTree(id,
(TreeNode<?>)valueModel.getObject().getPojo());
+ }
+
+ /**
+ * @param attributeModel - holder of {@link TreeNode}
+ */
+ static MarkupContainer createComponent(final String id, final
UiAttributeWkt attributeModel) {
+ return attributeModel==null
+ ||
ManagedObjects.isNullOrUnspecifiedOrEmpty(attributeModel.getObject())
+ ? emptyTreeComponent(id)
+ : new DomainObjectTree(id,
(TreeNode<?>)attributeModel.getObject().getPojo());
+ }
+
+ private static MarkupContainer emptyTreeComponent(final String id) {
+ return new WebMarkupContainer(id);
+ }
+
+ // -- CONSTRUCTION
+
+ private DomainObjectTree(
+ final String id,
+ final TreeNode<?> treeNode) {
+ super(id, new TreeProvider(treeNode),
TreeExpansionModel.of(treeNode.getTreeState()));
+ }
+
+ /**
+ * To use a custom component for the representation of a node's content we
override this method.
+ */
+ @Override
+ protected Component newContentComponent(final String id, final
IModel<TreeNodeMemento> node) {
+ final TreeNodeMemento treeModel = node.getObject();
+ final Component entityIconAndTitle =
ObjectIconAndTitlePanelFactory.entityIconAndTitlePanel(
+ id, UiObjectWkt.ofBookmark(treeModel.bookmark()));
+ if(treeExpansionModel().isSelected(treeModel.treePath())) {
+ Wkt.cssAppend(entityIconAndTitle, "tree-node-selected");
+ }
+ return entityIconAndTitle;
+ }
+
+ /**
+ * To hardcode Node's
<pre>AjaxFallbackLink.isEnabledInHierarchy()->true</pre> we override this
method.
+ */
+ @Override
+ public Component newNodeComponent(final String id, final
IModel<TreeNodeMemento> model) {
+
+ final Node<TreeNodeMemento> node = new Node<TreeNodeMemento>(id,
this, model) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected Component createContent(final String id, final
IModel<TreeNodeMemento> model) {
+ return DomainObjectTree.this.newContentComponent(id, model);
+ }
+
+ @Override
+ protected MarkupContainer createJunctionComponent(final String id)
{
+
+ final Node<TreeNodeMemento> node = this;
+ final Runnable toggleExpandCollapse = (Runnable &
Serializable) this::toggle;
+
+ return new AjaxFallbackLink<Void>(id) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void onClick(final Optional<AjaxRequestTarget>
target) {
+ toggleExpandCollapse.run();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return
DomainObjectTree.this.getProvider().hasChildren(node.getModelObject());
+ }
+
+ @Override
+ public boolean isEnabledInHierarchy() {
+ return true; // hardcoded -> true
+ }
+
+ };
+ }
+
+ };
+
+ node.setOutputMarkupId(true);
+ return node;
+ }
+
+ /**
+ * To utilize the custom TreeExpansionModel for deciding a node's
collapse/expand state,
+ * we override this method.
+ */
+ @Override
+ public State getState(final TreeNodeMemento t) {
+ return treeExpansionModel().contains(t.treePath()) ? State.EXPANDED :
State.COLLAPSED;
+ }
+
+ /**
+ * To utilize the custom TreeExpansionModel for hooking into a node's
expand event,
+ * we override this method.
+ */
+ @Override
+ public void expand(final TreeNodeMemento t) {
+ treeExpansionModel().onExpand(t);
+ super.expand(t);
+ }
+
+ /**
+ * To utilize the custom TreeExpansionModel for hooking into a node's
collapse event,
+ * we override this method.
+ */
+ @Override
+ public void collapse(final TreeNodeMemento t) {
+ treeExpansionModel().onCollapse(t);
+ super.collapse(t);
+ }
+
+ private TreeExpansionModel treeExpansionModel() {
+ return (TreeExpansionModel) getModel();
+ }
+
+}
\ No newline at end of file
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
index e7224e42932..766a6e0312d 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
@@ -34,7 +34,7 @@ extends PanelAbstract<ManagedObject, ValueModel> {
public StandaloneTreePanel(final String id, final ValueModel valueModel) {
super(id, valueModel);
- final Component tree = CausewayToWicketTreeAdapter.adapt(ID_TREE,
valueModel);
+ final Component tree = DomainObjectTree.createComponent(ID_TREE,
valueModel);
final Behavior treeTheme =
super.getTreeThemeProvider().treeThemeFor(valueModel);
add(tree.add(treeTheme));
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeAttributePanel.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeAttributePanel.java
index dbcd0875742..c164ec3ec38 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeAttributePanel.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeAttributePanel.java
@@ -22,8 +22,8 @@ import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.causeway.viewer.wicket.model.models.UiAttributeWkt;
-import
org.apache.causeway.viewer.wicket.ui.components.attributes.AttributePanel;
import
org.apache.causeway.viewer.wicket.ui.components.attributes.AttributeFragmentFactory.FrameFragment;
+import
org.apache.causeway.viewer.wicket.ui.components.attributes.AttributePanel;
/**
* Renders a non-editable tree.
@@ -59,7 +59,7 @@ extends AttributePanel {
private MarkupContainer createTreeComponent(final String id) {
var container = getScalarFrameContainer();
var attributeModel = attributeModel();
- var tree = CausewayToWicketTreeAdapter.adapt(id, attributeModel);
+ var tree = DomainObjectTree.createComponent(id, attributeModel);
container.add(tree);
// adds the tree-theme behavior to the tree's parent
container.add(getTreeThemeProvider().treeThemeFor(attributeModel));
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeExpansionModel.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeExpansionModel.java
index 6c384c118be..63ae905ddf0 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeExpansionModel.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeExpansionModel.java
@@ -41,7 +41,7 @@ record TreeExpansionModel(
private TreeExpansionModel(
final TreeState treeState) {
- this(treeState, treeState.getExpandedNodePaths().stream()
+ this(treeState, treeState.expandedNodePaths().stream()
.map(TreeNodeMemento::new)
.collect(Collectors.toSet()));
}
@@ -51,7 +51,7 @@ record TreeExpansionModel(
* @param t
*/
public void onExpand(final TreeNodeMemento t) {
- treeState.getExpandedNodePaths().add(t.treePath());
+ treeState.expandedNodePaths().add(t.treePath());
}
/**
@@ -59,15 +59,15 @@ record TreeExpansionModel(
* @param t
*/
public void onCollapse(final TreeNodeMemento t) {
- treeState.getExpandedNodePaths().remove(t.treePath());
+ treeState.expandedNodePaths().remove(t.treePath());
}
public boolean contains(final TreePath treePath) {
- return treeState.getExpandedNodePaths().contains(treePath);
+ return treeState.expandedNodePaths().contains(treePath);
}
public boolean isSelected(final TreePath treePath){
- return treeState.getSelectedNodePaths().contains(treePath);
+ return treeState.selectedNodePaths().contains(treePath);
}
@Override
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/CausewayTreeProvider.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeProvider.java
similarity index 83%
rename from
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/CausewayTreeProvider.java
rename to
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeProvider.java
index 46025b48391..8c13ad05348 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/CausewayTreeProvider.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/TreeProvider.java
@@ -28,8 +28,8 @@ import org.apache.wicket.model.Model;
import org.springframework.lang.Nullable;
import org.apache.causeway.applib.graph.tree.TreeAdapter;
-import org.apache.causeway.applib.graph.tree.TreeAdapterWithConverter;
import org.apache.causeway.applib.graph.tree.TreeConverter;
+import org.apache.causeway.applib.graph.tree.TreeNode;
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.core.metamodel.tree.TreeAdapterRecord;
import org.apache.causeway.core.metamodel.tree.TreeNodeMemento;
@@ -37,7 +37,7 @@ import
org.apache.causeway.core.metamodel.tree.TreeNodeMemento;
/**
* Wicket's {@link ITreeProvider} implemented for Causeway.
*/
-record CausewayTreeProvider(
+record TreeProvider(
/** tree's single root */
TreeNodeMemento primaryValue,
TreeAdapterRecord<Object> treeAdapterRecord)
@@ -47,8 +47,10 @@ implements
private static final long serialVersionUID = 1L;
- CausewayTreeProvider(final TreeNodeMemento primaryValue, final
TreeAdapter<?> treeAdapter) {
- this(primaryValue,_Casts.<TreeAdapterRecord<Object>>uncheckedCast(new
TreeAdapterRecord<>(treeAdapter)));
+ TreeProvider(final TreeNode<?> rootNode) {
+ this(
+ TreeNodeMemento.mementify(rootNode.getValue(),
rootNode.getPositionAsPath()),
+ _Casts.uncheckedCast(new
TreeAdapterRecord<>(rootNode.getTreeAdapter())));
}
@Override
@@ -62,13 +64,12 @@ implements
@Override
public boolean hasChildren(final TreeNodeMemento node) {
- return new TreeAdapterWithConverter<Object,
TreeNodeMemento>(treeAdapterRecord.treeAdapter(), this)
- .childCountOf(node)>0;
+ return treeAdapter().childCountOf(node)>0;
}
@Override
public Iterator<? extends TreeNodeMemento> getChildren(final
TreeNodeMemento node) {
- var children = new TreeAdapterWithConverter<Object,
TreeNodeMemento>(treeAdapterRecord.treeAdapter(), this)
+ var children = treeAdapter()
.childrenOf(node)
.toList();
return children.iterator();
@@ -95,4 +96,10 @@ implements
: null;
}
+ // -- HELPER
+
+ private TreeAdapter<TreeNodeMemento> treeAdapter() {
+ return treeAdapterRecord.treeAdapter().convert(this);
+ }
+
}
\ No newline at end of file