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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new 09b5cbc42b CAUSEWAY-3711: TreeNode should not be responsible for 
creating new tree-adapter instances
09b5cbc42b is described below

commit 09b5cbc42b387da1cc86126b13cd567411f7e016
Author: andi-huber <[email protected]>
AuthorDate: Sat Mar 30 11:23:00 2024 +0100

    CAUSEWAY-3711: TreeNode should not be responsible for creating new
    tree-adapter instances
---
 .../causeway/applib/graph/tree/TreeNode.java       | 87 +++++++++-------------
 .../causeway/applib/graph/tree/TreeState.java      | 23 +++++-
 .../applib/graph/tree/TreeState_Default.java       | 43 -----------
 .../metamodel/inspect/Object_inspectMetamodel.java |  7 +-
 .../valuesemantics/TreeNodeValueSemantics.java     | 11 ++-
 .../docgen/help/helptree/HelpNodeVm.java           |  6 +-
 .../model/valuetypes/ValueTypeExample.java         | 19 ++++-
 .../tree/CausewayToWicketTreeAdapter.java          |  2 +-
 .../ui/components/tree/_TreeModelTreeAdapter.java  |  9 ++-
 9 files changed, 95 insertions(+), 112 deletions(-)

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 4a887366e2..ddb372a9be 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
@@ -34,15 +34,12 @@ import org.apache.causeway.applib.annotation.Value;
 import org.apache.causeway.applib.graph.Edge;
 import org.apache.causeway.applib.graph.SimpleEdge;
 import org.apache.causeway.applib.graph.Vertex;
-import org.apache.causeway.applib.services.inject.ServiceInjector;
+import org.apache.causeway.applib.services.factory.FactoryService;
 import org.apache.causeway.commons.functional.IndexedFunction;
-import org.apache.causeway.commons.internal.base._Lazy;
 import org.apache.causeway.commons.internal.base._NullSafe;
-import org.apache.causeway.commons.internal.context._Context;
 
 import lombok.Getter;
 import lombok.NonNull;
-import lombok.SneakyThrows;
 
 /**
  * Fundamental building block of Tree structures.
@@ -55,19 +52,27 @@ import lombok.SneakyThrows;
 @Value
 public class TreeNode<T> implements Vertex<T> {
 
-    @Getter
-    private final TreeNode<T> rootNode;
+    @Getter private final TreeNode<T> rootNode;
+    @Getter private final TreeAdapter<T> treeAdapter;
+    
     private final TreePath treePath;
     private final T value;
     private final TreeState sharedState;
-    private final Class<? extends TreeAdapter<T>> treeAdapterClass;
-    private final _Lazy<TreeAdapter<T>> treeAdapter = 
_Lazy.of(this::newTreeAdapter);
 
     public static <T> TreeNode<T> root(
             final T value, 
-            final Class<? extends TreeAdapter<T>> treeAdapterClass, 
+            final TreeAdapter<T> treeAdapter, 
             final TreeState sharedState) {
-        return new TreeNode<T>(value, treeAdapterClass, sharedState);
+        return new TreeNode<T>(value, treeAdapter, sharedState);
+    }
+    
+    public static <T> TreeNode<T> root(
+            final T value, 
+            final Class<? extends TreeAdapter<T>> treeAdapterClass, 
+            final TreeState sharedState,
+            final FactoryService factoryService
+            ) {
+        return root(value, factoryService.getOrCreate(treeAdapterClass));
     }
 
     // generic node constructor, with reference to root
@@ -75,24 +80,24 @@ public class TreeNode<T> implements Vertex<T> {
             final @NonNull TreeNode<T> rootNode,
             final @NonNull TreePath treePath,
             final @NonNull T value, 
-            final @NonNull Class<? extends TreeAdapter<T>> treeAdapterClass, 
+            final @NonNull TreeAdapter<T> treeAdapter, 
             final @NonNull TreeState sharedState) {
         this.rootNode = rootNode;
         this.treePath = treePath;
         this.value = value;
-        this.treeAdapterClass = treeAdapterClass;
+        this.treeAdapter = treeAdapter;
         this.sharedState = sharedState;
     }
 
     // root-node constructor
     private TreeNode(
             final @NonNull T value, 
-            final @NonNull Class<? extends TreeAdapter<T>> treeAdapterClass, 
+            final @NonNull TreeAdapter<T> treeAdapter, 
             final @NonNull TreeState sharedState) {
         this.rootNode = this;
         this.treePath = TreePath.root();
         this.value = value;
-        this.treeAdapterClass = treeAdapterClass;
+        this.treeAdapter = treeAdapter;
         this.sharedState = sharedState;
     }
     
@@ -167,14 +172,14 @@ public class TreeNode<T> implements Vertex<T> {
     // -- CHILDREN
 
     public int getChildCount() {
-        return treeAdapter().childCountOf(value);
+        return treeAdapter.childCountOf(value);
     }
 
     public Stream<TreeNode<T>> streamChildren() {
         if(isLeaf()) {
             return Stream.empty();
         }
-        return treeAdapter().childrenOf(value)
+        return treeAdapter.childrenOf(value)
                 .map(IndexedFunction.zeroBased((siblingIndex, childPojo)->
                     toTreeNode(treePath.append(siblingIndex), childPojo)));
     }
@@ -278,15 +283,22 @@ public class TreeNode<T> implements Vertex<T> {
     // -- CONSTRUCTION
 
     /**
-     * Convenient shortcut.
-     * @param rootNode
-     * @param treeAdapterClass
-     * @return new LazyTreeNode
+     * 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 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 T rootNode, 
-            final Class<? extends TreeAdapter<T>> treeAdapterClass) {
-        return TreeNode.root(rootNode, treeAdapterClass, 
TreeState.rootCollapsed());
+            final @NonNull T rootNode, 
+            final @NonNull Class<? extends TreeAdapter<T>> treeAdapterClass,
+            final @NonNull FactoryService factoryService) {
+        return root(rootNode, factoryService.getOrCreate(treeAdapterClass));
     }
 
     // -- PARENT NODE ITERATION
@@ -327,37 +339,10 @@ public class TreeNode<T> implements Vertex<T> {
                 false); // not parallel
     }
 
-    // -- TREE NODE ADAPTING
-
-    /**
-     * @apiNote a class rather than an instance, because otherwise
-     * the adapter would need to be serializable for Wicket's trees to work 
correctly.
-     */
-    public Class<? extends TreeAdapter<T>> getTreeAdapterClass() {
-        return treeAdapterClass;
-    }
-
     // -- HELPER
 
-    @SneakyThrows
-    private TreeAdapter<T> newTreeAdapter() {
-        try {
-            var adapter = 
treeAdapterClass.getDeclaredConstructor().newInstance();
-            return _Context.lookup(ServiceInjector.class) 
//TODO[CAUSEWAY-3711] requires some cooperator that provides it
-                
.map(serviceInjector->serviceInjector.injectServicesInto(adapter))
-                .orElse(adapter);
-        } catch (InstantiationException | IllegalAccessException e) {
-            throw new IllegalArgumentException(
-                    String.format("failed to instantiate TreeAdapter '%s'", 
treeAdapterClass.getName()), e);
-        }
-    }
-
-    private TreeAdapter<T> treeAdapter() {
-        return treeAdapter.get();
-    }
-
     private TreeNode<T> toTreeNode(final TreePath treePath, final T value){
-        return new TreeNode<>(rootNode, treePath, value, 
getTreeAdapterClass(), sharedState);
+        return new TreeNode<>(rootNode, treePath, value, treeAdapter, 
sharedState);
     }
 
 }
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 d1dba0c6b6..70ccfa2e6e 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
@@ -19,18 +19,33 @@
 package org.apache.causeway.applib.graph.tree;
 
 import java.io.Serializable;
+import java.util.HashSet;
 import java.util.Set;
 
 /**
  * @since 2.0 {@index}
  */
-public interface TreeState extends Serializable {
+public class TreeState implements Serializable {
 
+    // -- FACTORIES
+    
     public static TreeState rootCollapsed() {
-        return new TreeState_Default();
+        return new TreeState();
     }
 
-    public Set<TreePath> getExpandedNodePaths();
-    public Set<TreePath> getSelectedNodePaths();
+    // -- 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;
+    }
 
 }
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState_Default.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState_Default.java
deleted file mode 100644
index e44c458ba4..0000000000
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/graph/tree/TreeState_Default.java
+++ /dev/null
@@ -1,43 +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.applib.graph.tree;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import lombok.ToString;
-
-@ToString
-class TreeState_Default implements TreeState {
-    private static final long serialVersionUID = 7971539034663543462L;
-
-    private final Set<TreePath> expandedNodes = new HashSet<>();
-    private final Set<TreePath> selectedNodes = new HashSet<>();
-
-    @Override
-    public Set<TreePath> getExpandedNodePaths() {
-        return expandedNodes;
-    }
-
-    @Override
-    public Set<TreePath> getSelectedNodePaths() {
-        return selectedNodes;
-    }
-
-}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/Object_inspectMetamodel.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/Object_inspectMetamodel.java
index d291affc3f..26e3b554ce 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/Object_inspectMetamodel.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/Object_inspectMetamodel.java
@@ -33,6 +33,7 @@ import org.apache.causeway.applib.graph.tree.TreeNode;
 import org.apache.causeway.applib.graph.tree.TreePath;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.layout.LayoutConstants;
+import org.apache.causeway.applib.services.factory.FactoryService;
 import org.apache.causeway.applib.services.message.MessageService;
 import org.apache.causeway.applib.services.metamodel.Config;
 import org.apache.causeway.applib.services.metamodel.MetaModelService;
@@ -62,6 +63,8 @@ import lombok.val;
 @RequiredArgsConstructor
 public class Object_inspectMetamodel {
 
+    @Inject FactoryService factoryService;
+    
     private final Object domainObject; // mixee
 
     public static class ActionDomainEvent
@@ -98,8 +101,8 @@ public class Object_inspectMetamodel {
             .orElseThrow(_Exceptions::noSuchElement);
 
         val root = MMNodeFactory.type(domainClassDto, null);
-
-        val tree = TreeNode.root(root, MMTreeAdapter.class);
+        
+        val tree = TreeNode.root(root, 
factoryService.getOrCreate(MMTreeAdapter.class));
 
         // Initialize view-model nodes of the entire tree,
         // because as it stands, all the type information gets cleared,
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/TreeNodeValueSemantics.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/TreeNodeValueSemantics.java
index c363ca251d..1dff074f2d 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/TreeNodeValueSemantics.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/TreeNodeValueSemantics.java
@@ -31,6 +31,7 @@ import org.apache.causeway.applib.graph.tree.TreeAdapter;
 import org.apache.causeway.applib.graph.tree.TreeNode;
 import org.apache.causeway.applib.graph.tree.TreePath;
 import org.apache.causeway.applib.graph.tree.TreeState;
+import org.apache.causeway.applib.services.factory.FactoryService;
 import org.apache.causeway.applib.services.urlencoding.UrlEncodingService;
 import org.apache.causeway.applib.value.semantics.Renderer;
 import org.apache.causeway.applib.value.semantics.ValueDecomposition;
@@ -52,6 +53,7 @@ implements
 
     @Inject UrlEncodingService urlEncodingService;
     @Inject SerializingAdapter serializingAdapter;
+    @Inject FactoryService factoryService;
 
     @Override
     public Class<TreeNode<?>> getCorrespondingClass() {
@@ -78,7 +80,7 @@ implements
     private String toEncodedString(final TreeNode<?> treeNode) {
         final Memento memento = newMemento();
         memento.put("rootValue", treeNode.getRootValue());
-        memento.put("adapterClass", treeNode.getTreeAdapterClass());
+        memento.put("adapterClass", treeNode.getTreeAdapter().getClass());
         memento.put("treeState", treeNode.getTreeState());
         memento.put("treePath", treeNode.getPositionAsPath());
         return memento.asString();
@@ -90,7 +92,8 @@ implements
         final TreeNode<?> rootNode = TreeNode.root(
                 memento.get("rootValue", Object.class),
                 memento.get("adapterClass", Class.class),
-                memento.get("treeState", TreeState.class));
+                memento.get("treeState", TreeState.class),
+                factoryService);
         return rootNode.resolve(memento.get("treePath", TreePath.class))
                 .orElse(null);
     }
@@ -120,8 +123,8 @@ implements
         }
 
         return Can.of(
-                TreeNode.root("TreeRoot", TreeAdapterString.class, 
TreeState.rootCollapsed()),
-                TreeNode.root("another TreeRoot", TreeAdapterString.class, 
TreeState.rootCollapsed()));
+                TreeNode.root("TreeRoot", new TreeAdapterString(), 
TreeState.rootCollapsed()),
+                TreeNode.root("another TreeRoot", new TreeAdapterString(), 
TreeState.rootCollapsed()));
     }
 
     // -- HELPER
diff --git 
a/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/helptree/HelpNodeVm.java
 
b/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/helptree/HelpNodeVm.java
index 6b70c3b491..db91799447 100644
--- 
a/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/helptree/HelpNodeVm.java
+++ 
b/extensions/core/docgen/help/src/main/java/org/apache/causeway/extensions/docgen/help/helptree/HelpNodeVm.java
@@ -36,6 +36,7 @@ import org.apache.causeway.applib.annotation.PropertyLayout;
 import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.applib.graph.tree.TreeNode;
 import org.apache.causeway.applib.graph.tree.TreePath;
+import org.apache.causeway.applib.services.factory.FactoryService;
 import org.apache.causeway.extensions.docgen.help.CausewayModuleExtDocgenHelp;
 import org.apache.causeway.extensions.docgen.help.applib.HelpNode;
 import org.apache.causeway.extensions.docgen.help.applib.HelpNode.HelpTopic;
@@ -59,6 +60,8 @@ public class HelpNodeVm implements ViewModel {
     public static HelpNodeVm forRootTopic(final HelpTopic rootTopic) {
         return new HelpNodeVm(rootTopic, rootTopic);
     }
+    
+    @Inject FactoryService factoryService;
 
     @Getter @Programmatic
     private final HelpTopic rootTopic;
@@ -97,7 +100,8 @@ public class HelpNodeVm implements ViewModel {
     @Property
     @PropertyLayout(labelPosition = LabelPosition.NONE, fieldSetId = "tree", 
sequence = "1")
     public TreeNode<HelpNodeVm> getTree() {
-        final TreeNode<HelpNodeVm> tree = 
TreeNode.root(HelpNodeVm.forRootTopic(rootTopic), HelpTreeAdapter.class);
+        final TreeNode<HelpNodeVm> tree = TreeNode.root(
+                HelpNodeVm.forRootTopic(rootTopic), HelpTreeAdapter.class, 
factoryService);
 
         // expand the current node
         helpNode.getPath().streamUpTheHierarchyStartingAtSelf()
diff --git 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
index 5570d7dc9c..b20e88850b 100644
--- 
a/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
+++ 
b/regressiontests/base/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
@@ -53,6 +53,7 @@ import org.apache.causeway.applib.annotation.ValueSemantics;
 import 
org.apache.causeway.applib.exceptions.recoverable.TextEntryParseException;
 import org.apache.causeway.applib.graph.tree.TreeAdapter;
 import org.apache.causeway.applib.graph.tree.TreeNode;
+import org.apache.causeway.applib.graph.tree.TreePath;
 import org.apache.causeway.applib.graph.tree.TreeState;
 import org.apache.causeway.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
@@ -753,6 +754,20 @@ public abstract class ValueTypeExample<T> {
     }
 
     // -- EXAMPLES - DATA STRUCTURE
+    
+    @Named("causeway.testdomain.valuetypes.ValueTypeExampleTreePath")
+    @DomainObject(
+            nature = Nature.BEAN) @Scope("prototype")
+    public static class ValueTypeExampleTreePath
+    extends ValueTypeExample<TreePath> {
+        @Property @Getter @Setter
+        private TreePath value = TreePath.root();
+        @Getter
+        private TreePath updateValue = TreePath.of(0, 1, 2, 3, 4);
+        @Action @Override
+        public TreePath sampleAction(@Parameter @Nullable final TreePath 
value) { return value; }
+    }
+    
 
     //TODO    TreeNode
 //    @DomainObject(
@@ -762,10 +777,10 @@ public abstract class ValueTypeExample<T> {
     extends ValueTypeExample<TreeNode<String>> {
         @Property @Getter @Setter
         private TreeNode<String> value = TreeNode.root(
-                "root", TreeAdapterString.class, TreeState.rootCollapsed());
+                "root", new TreeAdapterString(), TreeState.rootCollapsed());
         @Getter
         private TreeNode<String> updateValue = TreeNode.root(
-                "anotherRoot", TreeAdapterString.class, 
TreeState.rootCollapsed());
+                "anotherRoot", new TreeAdapterString(), 
TreeState.rootCollapsed());
 
         private static class TreeAdapterString implements TreeAdapter<String> {
             @Override public Stream<String> childrenOf(final String value) {
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
index 81e826eb79..8bc9248d5b 100644
--- 
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
@@ -84,7 +84,7 @@ class CausewayToWicketTreeAdapter {
                 final String id, final ManagedObject treeNodeObject) {
 
             val treeNode = (TreeNode<?>) treeNodeObject.getPojo();
-            val treeAdapterClass = treeNode.getTreeAdapterClass();
+            val treeAdapterClass = treeNode.getTreeAdapter().getClass();
 
             val wrappingTreeAdapter = new 
_TreeModelTreeAdapter(treeAdapterClass);
 
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeAdapter.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeAdapter.java
index 6743e98edd..023b0869ef 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeAdapter.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeAdapter.java
@@ -47,7 +47,8 @@ implements
 
     private final Class<? extends TreeAdapter> treeAdapterClass;
 
-    private transient TreeAdapter wrappedTreeAdapter;
+    /** non serializable delegate */
+    private transient TreeAdapter delegate;
 
     _TreeModelTreeAdapter(
             final Class<? extends TreeAdapter> treeAdapterClass) {
@@ -93,11 +94,11 @@ implements
     }
 
     private TreeAdapter wrappedTreeAdapter() {
-        if(wrappedTreeAdapter!=null) {
-            return wrappedTreeAdapter;
+        if(delegate!=null) {
+            return delegate;
         }
         try {
-            return wrappedTreeAdapter = 
getFactoryService().getOrCreate(treeAdapterClass);
+            return delegate = 
getFactoryService().getOrCreate(treeAdapterClass);
         } catch (Exception e) {
             throw new RuntimeException("failed to instantiate tree adapter", 
e);
         }

Reply via email to