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/isis.git
The following commit(s) were added to refs/heads/master by this push: new 466cabc5c7 ISIS-3325: move nested classes from CausewayToWicketTreeAdapter to their own files 466cabc5c7 is described below commit 466cabc5c7bfb0a8908c6830c37576344379e2af Author: andi-huber <ahu...@apache.org> AuthorDate: Mon Jan 23 14:03:58 2023 +0100 ISIS-3325: move nested classes from CausewayToWicketTreeAdapter to their own files --- .../tree/CausewayToWicketTreeAdapter.java | 441 +++------------------ .../tree/_LoadableDetachableTreeModel.java | 103 +++++ .../ui/components/tree/_TreeExpansionModel.java | 87 ++++ .../wicket/ui/components/tree/_TreeModel.java | 57 +++ .../ui/components/tree/_TreeModelTreeAdapter.java | 120 ++++++ .../ui/components/tree/_TreeModelTreeProvider.java | 73 ++++ 6 files changed, 492 insertions(+), 389 deletions(-) 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 6849700fe6..830ca9bd3e 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 @@ -19,14 +19,7 @@ package org.apache.causeway.viewer.wicket.ui.components.tree; import java.io.Serializable; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; @@ -37,49 +30,36 @@ 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.basic.Label; import org.apache.wicket.model.IModel; -import org.apache.wicket.model.LoadableDetachableModel; -import org.apache.wicket.model.Model; -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.services.bookmark.Bookmark; -import org.apache.causeway.applib.services.factory.FactoryService; -import org.apache.causeway.commons.functional.IndexedFunction; -import org.apache.causeway.commons.internal.collections._Lists; import org.apache.causeway.core.metamodel.context.MetaModelContext; import org.apache.causeway.core.metamodel.object.ManagedObject; -import org.apache.causeway.core.metamodel.object.ManagedObjects; import org.apache.causeway.viewer.wicket.model.models.ScalarModel; -import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt; import org.apache.causeway.viewer.wicket.model.models.ValueModel; -import org.apache.causeway.viewer.wicket.model.util.WktContext; import org.apache.causeway.viewer.wicket.ui.components.entity.icontitle.EntityIconAndTitlePanel; -import lombok.NonNull; import lombok.val; class CausewayToWicketTreeAdapter { + /** + * @param valueModel - holder of {@link TreeNode} + */ public static Component adapt(final String id, final ValueModel valueModel) { - if(valueModel==null || valueModel.getObject()==null) { - return emptyTreeComponent(id); - } - val commonContext = valueModel.getMetaModelContext(); - val treeNode = valueModel.getObject(); - return new EntityTree(id, toITreeProvider(commonContext, treeNode), - toIModelRepresentingCollapseExpandState(commonContext, treeNode)); + return valueModel==null + || valueModel.getObject()==null + ? emptyTreeComponent(id) + : EntityTree.of(id, valueModel.getObject(), valueModel.getMetaModelContext()); } + /** + * @param scalarModel - holder of {@link TreeNode} + */ public static Component adapt(final String id, final ScalarModel scalarModel) { - if(scalarModel==null || scalarModel.getObject()==null) { - return emptyTreeComponent(id); - } - val commonContext = scalarModel.getMetaModelContext(); - val treeNode = scalarModel.getObject(); - return new EntityTree(id, - toITreeProvider(commonContext, treeNode), - toIModelRepresentingCollapseExpandState(commonContext, treeNode)); + return scalarModel==null + || scalarModel.getObject()==null + ? emptyTreeComponent(id) + : EntityTree.of(id, scalarModel.getObject(), scalarModel.getMetaModelContext()); } // -- FALLBACK @@ -93,14 +73,34 @@ class CausewayToWicketTreeAdapter { /** * Wicket's Tree Component implemented for Causeway */ - private static class EntityTree extends NestedTree<TreeModel> { + private static class EntityTree extends NestedTree<_TreeModel> { private static final long serialVersionUID = 1L; - public EntityTree( + public static EntityTree of( + final String id, final ManagedObject treeNodeObject, final MetaModelContext mmc) { + + val treeNode = (TreeNode<?>) treeNodeObject.getPojo(); + val treeAdapterClass = treeNode.getTreeAdapterClass(); + + val wrappingTreeAdapter = new _TreeModelTreeAdapter(mmc, treeAdapterClass); + + val treeModelTreeProvider = new _TreeModelTreeProvider( + wrappingTreeAdapter.wrap(treeNode.getValue(), treeNode.getPositionAsPath()), + wrappingTreeAdapter); + + val treeExpansionModel = _TreeExpansionModel.of(mmc, + treeNode.getTreeState().getExpandedNodePaths()); + + return new EntityTree(id, + treeModelTreeProvider, + treeExpansionModel); + } + + private EntityTree( final String id, - final ITreeProvider<TreeModel> provider, - final TreeExpansionModel collapseExpandState) { + final ITreeProvider<_TreeModel> provider, + final _TreeExpansionModel collapseExpandState) { super(id, provider, collapseExpandState); } @@ -108,8 +108,8 @@ class CausewayToWicketTreeAdapter { * 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<TreeModel> node) { - final TreeModel treeModel = node.getObject(); + protected Component newContentComponent(final String id, final IModel<_TreeModel> node) { + final _TreeModel treeModel = node.getObject(); final Component entityIconAndTitle = new EntityIconAndTitlePanel(id, treeModel); return entityIconAndTitle; } @@ -118,20 +118,20 @@ class CausewayToWicketTreeAdapter { * To hardcode Node's <pre>AjaxFallbackLink.isEnabledInHierarchy()->true</pre> we override this method. */ @Override - public Component newNodeComponent(final String id, final IModel<TreeModel> model) { + public Component newNodeComponent(final String id, final IModel<_TreeModel> model) { - final Node<TreeModel> node = new Node<TreeModel>(id, this, model) { + final Node<_TreeModel> node = new Node<_TreeModel>(id, this, model) { private static final long serialVersionUID = 1L; @Override - protected Component createContent(final String id, final IModel<TreeModel> model) { + protected Component createContent(final String id, final IModel<_TreeModel> model) { return EntityTree.this.newContentComponent(id, model); } @Override protected MarkupContainer createJunctionComponent(final String id) { - final Node<TreeModel> node = this; + final Node<_TreeModel> node = this; final Runnable toggleExpandCollapse = (Runnable & Serializable) this::toggle; return new AjaxFallbackLink<Void>(id) { @@ -168,9 +168,8 @@ class CausewayToWicketTreeAdapter { * we override this method. */ @Override - public State getState(final TreeModel t) { - final TreeExpansionModel treeExpansionModel = (TreeExpansionModel) getModel(); - return treeExpansionModel.contains(t.getTreePath()) ? State.EXPANDED : State.COLLAPSED; + public State getState(final _TreeModel t) { + return treeExpansionModel().contains(t.getTreePath()) ? State.EXPANDED : State.COLLAPSED; } /** @@ -178,9 +177,8 @@ class CausewayToWicketTreeAdapter { * we override this method. */ @Override - public void expand(final TreeModel t) { - final TreeExpansionModel treeExpansionModel = (TreeExpansionModel) getModel(); - treeExpansionModel.onExpand(t); + public void expand(final _TreeModel t) { + treeExpansionModel().onExpand(t); super.expand(t); } @@ -189,348 +187,13 @@ class CausewayToWicketTreeAdapter { * we override this method. */ @Override - public void collapse(final TreeModel t) { - final TreeExpansionModel treeExpansionModel = (TreeExpansionModel) getModel(); - treeExpansionModel.onCollapse(t); + public void collapse(final _TreeModel t) { + treeExpansionModel().onCollapse(t); super.collapse(t); } - } - - // -- CAUSEWAY' TREE-MODEL - - /** - * Extending the UiObjectWkt to also provide a TreePath. - */ - private static class TreeModel extends UiObjectWkt { - private static final long serialVersionUID = 8916044984628849300L; - - private final TreePath treePath; - private final boolean isTreePathModelOnly; - - public TreeModel(final MetaModelContext commonContext, final TreePath treePath) { - super(commonContext, commonContext.getObjectManager().adapt(0)); // any bookmarkable will do - this.treePath = treePath; - this.isTreePathModelOnly = true; - } - - public TreeModel(final MetaModelContext commonContext, final ManagedObject adapter, final TreePath treePath) { - super(commonContext, Objects.requireNonNull(adapter)); - this.treePath = treePath; - this.isTreePathModelOnly = false; - } - - public TreePath getTreePath() { - return treePath; - } - - public boolean isTreePathModelOnly() { - return isTreePathModelOnly; - } - - } - - // -- CAUSEWAY' TREE ADAPTER (FOR TREES OF TREE-MODEL NODES) - - /** - * TreeAdapter for TreeModel nodes. - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - private static class TreeModelTreeAdapter implements TreeAdapter<TreeModel>, Serializable { - private static final long serialVersionUID = 1L; - - private final Class<? extends TreeAdapter> treeAdapterClass; - - private transient TreeAdapter wrappedTreeAdapter; - private transient MetaModelContext commonContext; - private transient FactoryService factoryService; - private transient Function<Object, ManagedObject> pojoToAdapter; - - - private TreeModelTreeAdapter( - final MetaModelContext commonContext, - final Class<? extends TreeAdapter> treeAdapterClass) { - - this.treeAdapterClass = treeAdapterClass; - init(commonContext); - } - - private void init(final MetaModelContext commonContext) { - this.commonContext = commonContext; - this.factoryService = commonContext.lookupServiceElseFail(FactoryService.class); - this.pojoToAdapter = pojo -> - ManagedObject.adaptSingular(commonContext.getSpecificationLoader(), pojo); - } - - private TreeAdapter wrappedTreeAdapter() { - if(wrappedTreeAdapter!=null) { - return wrappedTreeAdapter; - } - try { - ensureInit(); // in case we were de-serialzed - return wrappedTreeAdapter = factoryService.getOrCreate(treeAdapterClass); - } catch (Exception e) { - throw new RuntimeException("failed to instantiate tree adapter", e); - } - } - - @Override - public Optional<TreeModel> parentOf(final TreeModel treeModel) { - if(treeModel==null) { - return Optional.empty(); - } - return wrappedTreeAdapter().parentOf(unwrap(treeModel)) - .map(pojo->wrap(pojo, treeModel.getTreePath().getParentIfAny())); - } - - @Override - public int childCountOf(final TreeModel treeModel) { - if(treeModel==null) { - return 0; - } - return wrappedTreeAdapter().childCountOf(unwrap(treeModel)); - } - - @Override - public Stream<TreeModel> childrenOf(final TreeModel treeModel) { - if(treeModel==null) { - return Stream.empty(); - } - return wrappedTreeAdapter().childrenOf(unwrap(treeModel)) - .map(newPojoToTreeModelMapper(treeModel)); - } - - private TreeModel wrap(final @NonNull Object pojo, final TreePath treePath) { - ensureInit(); // in case we were de-serialzed - val objectAdapter = pojoToAdapter.apply(pojo); - return new TreeModel(commonContext, objectAdapter, treePath); - } - - private Object unwrap(final TreeModel model) { - Objects.requireNonNull(model); - return model.getObject().getPojo(); - } - - private Function<Object, TreeModel> newPojoToTreeModelMapper(final TreeModel parent) { - return IndexedFunction.zeroBased((indexWithinSiblings, pojo)-> - wrap(pojo, parent.getTreePath().append(indexWithinSiblings))); - } - - // in case we were de-serialzed - private void ensureInit() { - if(commonContext!=null) return; - init(WktContext.getMetaModelContext()); - } - - } - - // -- WICKET'S TREE PROVIDER (FOR TREES OF TREE-MODEL NODES) - - /** - * Wicket's ITreeProvider implemented for Causeway - */ - private static class TreeModelTreeProvider implements ITreeProvider<TreeModel> { - - private static final long serialVersionUID = 1L; - - /** - * tree's root - */ - private final TreeModel primaryValue; - private final TreeModelTreeAdapter treeAdapter; - - private TreeModelTreeProvider(final TreeModel primaryValue, final TreeModelTreeAdapter treeAdapter) { - this.primaryValue = primaryValue; - this.treeAdapter = treeAdapter; - } - - @Override - public void detach() { - } - - @Override - public Iterator<? extends TreeModel> getRoots() { - return _Lists.singleton(primaryValue).iterator(); - } - - @Override - public boolean hasChildren(final TreeModel node) { - return treeAdapter.childCountOf(node)>0; - } - - @Override - public Iterator<? extends TreeModel> getChildren(final TreeModel node) { - return treeAdapter.childrenOf(node).iterator(); - } - - @Override - public IModel<TreeModel> model(final TreeModel treeModel) { - return treeModel.isTreePathModelOnly() - ? Model.of(treeModel) - : new LoadableDetachableTreeModel(treeModel); - } - - } - - /** - * @return Wicket's ITreeProvider - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static ITreeProvider<TreeModel> toITreeProvider( - final MetaModelContext commonContext, - final ManagedObject treeNodeObject) { - - val treeNode = (TreeNode) treeNodeObject.getPojo(); - val treeAdapterClass = treeNode.getTreeAdapterClass(); - - val wrappingTreeAdapter = new TreeModelTreeAdapter(commonContext, treeAdapterClass); - - return new TreeModelTreeProvider( - wrappingTreeAdapter.wrap(treeNode.getValue(), treeNode.getPositionAsPath()), - wrappingTreeAdapter); - } - - // -- WICKET'S LOADABLE/DETACHABLE MODEL FOR TREE-MODEL NODES - - /** - * Wicket's loadable/detachable model for TreeModel nodes. - */ - private static class LoadableDetachableTreeModel extends LoadableDetachableModel<TreeModel> { - private static final long serialVersionUID = 1L; - - private final Bookmark bookmark; - private final TreePath treePath; - private final int hashCode; - - private transient MetaModelContext commonContext; - - public LoadableDetachableTreeModel(final TreeModel tModel) { - super(tModel); - this.treePath = tModel.getTreePath(); - this.bookmark = ManagedObjects.bookmarkElseFail(tModel.getObject()); - - this.hashCode = Objects.hash(bookmark.hashCode(), treePath.hashCode()); - this.commonContext = tModel.getMetaModelContext(); - } - - /* - * loads EntityModel using Oid (id) - */ - @Override - protected TreeModel load() { - - commonContext = WktContext.computeIfAbsent(commonContext); - - val oid = bookmark; - val objAdapter = commonContext.getMetaModelContext().getObjectManager() - .loadObject(oid) - .orElseThrow(()->new NoSuchElementException( - String.format("Tree creation: could not recreate TreeModel from Bookmark: '%s'", bookmark))); - - final Object pojo = objAdapter.getPojo(); - if(pojo==null) { - throw new NoSuchElementException( - String.format("Tree creation: could not recreate Pojo from Oid: '%s'", bookmark)); - } - - return new TreeModel(commonContext, objAdapter, treePath); - } - - /* - * Important! Models must be identifiable by their contained object. Also IDs must be - * unique within a tree structure. - */ - @Override - public boolean equals(final Object obj) { - if (obj instanceof LoadableDetachableTreeModel) { - final LoadableDetachableTreeModel other = (LoadableDetachableTreeModel) obj; - return treePath.equals(other.treePath) && bookmark.equals(other.bookmark); - } - return false; - } - - /* - * Important! Models must be identifiable by their contained object. - */ - @Override - public int hashCode() { - return hashCode; - } - } - - // -- COLLAPSE/EXPAND - - /** - * - * @param actionOrPropertyModel - * @return Wicket's model for collapse/expand state - */ - @SuppressWarnings({ "rawtypes" }) - private static TreeExpansionModel toIModelRepresentingCollapseExpandState( - final MetaModelContext commonContext, - final ManagedObject treeNodeObject) { - - val treeNode = (TreeNode) treeNodeObject.getPojo(); - val treeState = treeNode.getTreeState(); - return TreeExpansionModel.of(commonContext, treeState.getExpandedNodePaths()); - } - - /** - * Wicket's model for collapse/expand state - */ - private static class TreeExpansionModel implements IModel<Set<TreeModel>> { - private static final long serialVersionUID = 648152234030889164L; - - public static TreeExpansionModel of( - final MetaModelContext commonContext, - final Set<TreePath> expandedTreePaths) { - - return new TreeExpansionModel(commonContext, expandedTreePaths); - } - - /** - * Happens on user interaction via UI. - * @param t - */ - public void onExpand(final TreeModel t) { - expandedTreePaths.add(t.getTreePath()); - } - - /** - * Happens on user interaction via UI. - * @param t - */ - public void onCollapse(final TreeModel t) { - expandedTreePaths.remove(t.getTreePath()); - } - - public boolean contains(final TreePath treePath) { - return expandedTreePaths.contains(treePath); - } - - private final Set<TreePath> expandedTreePaths; - private final Set<TreeModel> expandedNodes; - - private TreeExpansionModel( - final MetaModelContext commonContext, - final Set<TreePath> expandedTreePaths) { - - this.expandedTreePaths = expandedTreePaths; - this.expandedNodes = expandedTreePaths.stream() - .map(tPath->new TreeModel(commonContext, tPath)) - .collect(Collectors.toSet()); - } - - @Override - public Set<TreeModel> getObject() { - return expandedNodes; - } - - @Override - public String toString() { - return "{" + expandedTreePaths.stream() - .map(TreePath::toString) - .collect(Collectors.joining(", ")) + "}"; + private _TreeExpansionModel treeExpansionModel() { + return (_TreeExpansionModel) getModel(); } } diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_LoadableDetachableTreeModel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_LoadableDetachableTreeModel.java new file mode 100644 index 0000000000..d7d50156dd --- /dev/null +++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_LoadableDetachableTreeModel.java @@ -0,0 +1,103 @@ +/* + * 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.util.NoSuchElementException; +import java.util.Objects; + +import org.apache.wicket.model.LoadableDetachableModel; + +import org.apache.causeway.applib.graph.tree.TreePath; +import org.apache.causeway.applib.services.bookmark.Bookmark; +import org.apache.causeway.core.metamodel.context.HasMetaModelContext; +import org.apache.causeway.core.metamodel.context.MetaModelContext; +import org.apache.causeway.core.metamodel.object.ManagedObjects; +import org.apache.causeway.viewer.wicket.model.util.WktContext; + +import lombok.val; + +/** + * Wicket's loadable/detachable model for TreeModel nodes. + */ +class _LoadableDetachableTreeModel +extends LoadableDetachableModel<_TreeModel> +implements HasMetaModelContext { + private static final long serialVersionUID = 1L; + + private final Bookmark bookmark; + private final TreePath treePath; + private final int hashCode; + + private transient MetaModelContext metaModelContext; + + public _LoadableDetachableTreeModel(final _TreeModel tModel) { + super(tModel); + this.treePath = tModel.getTreePath(); + this.bookmark = ManagedObjects.bookmarkElseFail(tModel.getObject()); + + this.hashCode = Objects.hash(bookmark.hashCode(), treePath.hashCode()); + this.metaModelContext = tModel.getMetaModelContext(); + } + + /* + * loads EntityModel using Oid (id) + */ + @Override + protected _TreeModel load() { + + val objAdapter = getObjectManager() + .loadObject(bookmark) + .orElseThrow(()->new NoSuchElementException( + String.format("Tree creation: could not recreate TreeModel from Bookmark: '%s'", bookmark))); + + final Object pojo = objAdapter.getPojo(); + if(pojo==null) { + throw new NoSuchElementException( + String.format("Tree creation: could not recreate Pojo from Oid: '%s'", bookmark)); + } + + return new _TreeModel(getMetaModelContext(), objAdapter, treePath); + } + + @Override + public MetaModelContext getMetaModelContext() { + return this.metaModelContext = WktContext.computeIfAbsent(metaModelContext); + } + + /* + * Important! Models must be identifiable by their contained object. Also IDs must be + * unique within a tree structure. + */ + @Override + public boolean equals(final Object obj) { + if (obj instanceof _LoadableDetachableTreeModel) { + final _LoadableDetachableTreeModel other = (_LoadableDetachableTreeModel) obj; + return treePath.equals(other.treePath) && bookmark.equals(other.bookmark); + } + return false; + } + + /* + * Important! Models must be identifiable by their contained object. + */ + @Override + public int hashCode() { + return hashCode; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..8a32e363a4 --- /dev/null +++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeExpansionModel.java @@ -0,0 +1,87 @@ +/* + * 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.util.Set; +import java.util.stream.Collectors; + +import org.apache.wicket.model.IModel; + +import org.apache.causeway.applib.graph.tree.TreePath; +import org.apache.causeway.core.metamodel.context.MetaModelContext; + +/** + * Wicket's model for collapse/expand state + */ +class _TreeExpansionModel implements IModel<Set<_TreeModel>> { + private static final long serialVersionUID = 648152234030889164L; + + public static _TreeExpansionModel of( + final MetaModelContext commonContext, + final Set<TreePath> expandedTreePaths) { + + return new _TreeExpansionModel(commonContext, expandedTreePaths); + } + + /** + * Happens on user interaction via UI. + * @param t + */ + public void onExpand(final _TreeModel t) { + expandedTreePaths.add(t.getTreePath()); + } + + /** + * Happens on user interaction via UI. + * @param t + */ + public void onCollapse(final _TreeModel t) { + expandedTreePaths.remove(t.getTreePath()); + } + + public boolean contains(final TreePath treePath) { + return expandedTreePaths.contains(treePath); + } + + private final Set<TreePath> expandedTreePaths; + private final Set<_TreeModel> expandedNodes; + + private _TreeExpansionModel( + final MetaModelContext commonContext, + final Set<TreePath> expandedTreePaths) { + + this.expandedTreePaths = expandedTreePaths; + this.expandedNodes = expandedTreePaths.stream() + .map(tPath->new _TreeModel(commonContext, tPath)) + .collect(Collectors.toSet()); + } + + @Override + public Set<_TreeModel> getObject() { + return expandedNodes; + } + + @Override + public String toString() { + return "{" + expandedTreePaths.stream() + .map(TreePath::toString) + .collect(Collectors.joining(", ")) + "}"; + } + +} \ No newline at end of file diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModel.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModel.java new file mode 100644 index 0000000000..2234b6c983 --- /dev/null +++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModel.java @@ -0,0 +1,57 @@ +/* + * 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.util.Objects; + +import org.apache.causeway.applib.graph.tree.TreePath; +import org.apache.causeway.core.metamodel.context.MetaModelContext; +import org.apache.causeway.core.metamodel.object.ManagedObject; +import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt; + +/** + * Extending the UiObjectWkt to also provide a TreePath. + */ +class _TreeModel extends UiObjectWkt { + private static final long serialVersionUID = 8916044984628849300L; + + private final TreePath treePath; + private final boolean isTreePathModelOnly; + + public _TreeModel(final MetaModelContext commonContext, final TreePath treePath) { + super(commonContext, commonContext.getObjectManager().adapt(0)); // any bookmarkable will do + this.treePath = treePath; + this.isTreePathModelOnly = true; + } + + public _TreeModel(final MetaModelContext commonContext, final ManagedObject adapter, final TreePath treePath) { + super(commonContext, Objects.requireNonNull(adapter)); + this.treePath = treePath; + this.isTreePathModelOnly = false; + } + + public TreePath getTreePath() { + return treePath; + } + + public boolean isTreePathModelOnly() { + return isTreePathModelOnly; + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000000..6b1bb3e348 --- /dev/null +++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeAdapter.java @@ -0,0 +1,120 @@ +/* + * 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.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.apache.causeway.applib.graph.tree.TreeAdapter; +import org.apache.causeway.applib.graph.tree.TreePath; +import org.apache.causeway.commons.functional.IndexedFunction; +import org.apache.causeway.core.metamodel.context.HasMetaModelContext; +import org.apache.causeway.core.metamodel.context.MetaModelContext; +import org.apache.causeway.core.metamodel.object.ManagedObject; +import org.apache.causeway.viewer.wicket.model.util.WktContext; + +import lombok.NonNull; + +/** + * {@link TreeAdapter} for _TreeModel nodes. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +class _TreeModelTreeAdapter +implements + TreeAdapter<_TreeModel>, + HasMetaModelContext, + Serializable { + + private static final long serialVersionUID = 1L; + + private final Class<? extends TreeAdapter> treeAdapterClass; + + private transient TreeAdapter wrappedTreeAdapter; + private transient MetaModelContext metaModelContext; + + _TreeModelTreeAdapter( + final MetaModelContext mmc, + final Class<? extends TreeAdapter> treeAdapterClass) { + this.metaModelContext = mmc; + this.treeAdapterClass = treeAdapterClass; + } + + @Override + public MetaModelContext getMetaModelContext() { + return this.metaModelContext = WktContext.computeIfAbsent(metaModelContext); + } + + @Override + public Optional<_TreeModel> parentOf(final _TreeModel treeModel) { + if(treeModel==null) { + return Optional.empty(); + } + return wrappedTreeAdapter().parentOf(unwrap(treeModel)) + .map(pojo->wrap(pojo, treeModel.getTreePath().getParentIfAny())); + } + + @Override + public int childCountOf(final _TreeModel treeModel) { + if(treeModel==null) { + return 0; + } + return wrappedTreeAdapter().childCountOf(unwrap(treeModel)); + } + + @Override + public Stream<_TreeModel> childrenOf(final _TreeModel treeModel) { + if(treeModel==null) { + return Stream.empty(); + } + return wrappedTreeAdapter().childrenOf(unwrap(treeModel)) + .map(newPojoToTreeModelMapper(treeModel)); + } + + _TreeModel wrap(final @NonNull Object pojo, final TreePath treePath) { + return new _TreeModel( + getMetaModelContext(), + ManagedObject.adaptSingular(getSpecificationLoader(), pojo), + treePath); + } + + private Object unwrap(final _TreeModel model) { + Objects.requireNonNull(model); + return model.getObject().getPojo(); + } + + private Function<Object, _TreeModel> newPojoToTreeModelMapper(final _TreeModel parent) { + return IndexedFunction.zeroBased((indexWithinSiblings, pojo)-> + wrap(pojo, parent.getTreePath().append(indexWithinSiblings))); + } + + private TreeAdapter wrappedTreeAdapter() { + if(wrappedTreeAdapter!=null) { + return wrappedTreeAdapter; + } + try { + return wrappedTreeAdapter = getFactoryService().getOrCreate(treeAdapterClass); + } catch (Exception e) { + throw new RuntimeException("failed to instantiate tree adapter", e); + } + } + +} \ No newline at end of file diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeProvider.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeProvider.java new file mode 100644 index 0000000000..683231f2c1 --- /dev/null +++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/tree/_TreeModelTreeProvider.java @@ -0,0 +1,73 @@ +/* + * 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.util.Iterator; + +import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; + +import org.apache.causeway.commons.internal.collections._Lists; + +/** + * Wicket's {@link ITreeProvider} implemented for Causeway + */ +class _TreeModelTreeProvider implements ITreeProvider<_TreeModel> { + + private static final long serialVersionUID = 1L; + + /** + * tree's root + */ + private final _TreeModel primaryValue; + private final _TreeModelTreeAdapter treeAdapter; + + _TreeModelTreeProvider(final _TreeModel primaryValue, final _TreeModelTreeAdapter treeAdapter) { + this.primaryValue = primaryValue; + this.treeAdapter = treeAdapter; + } + + @Override + public void detach() { + } + + @Override + public Iterator<? extends _TreeModel> getRoots() { + return _Lists.singleton(primaryValue).iterator(); + } + + @Override + public boolean hasChildren(final _TreeModel node) { + return treeAdapter.childCountOf(node)>0; + } + + @Override + public Iterator<? extends _TreeModel> getChildren(final _TreeModel node) { + return treeAdapter.childrenOf(node).iterator(); + } + + @Override + public IModel<_TreeModel> model(final _TreeModel treeModel) { + return treeModel.isTreePathModelOnly() + ? Model.of(treeModel) + : new _LoadableDetachableTreeModel(treeModel); + } + +} \ No newline at end of file