You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/04/17 07:26:07 UTC

[isis] 11/11: ISIS-898 initial (naive) lazy tree feature

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

ahuber pushed a commit to branch dev/2.0.0/ISIS-898-treeview
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 0e3abd90395665e94d76bacf55e39b3286ec39c3
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Apr 17 09:24:52 2018 +0200

    ISIS-898 initial (naive) lazy tree feature
    
    fails when lazily fetching child nodes
---
 .../org/apache/isis/applib/tree/LazyTreeNode.java  |   7 +-
 .../java/org/apache/isis/applib/tree/TreeNode.java |  51 ++---
 .../facets/value/treenode/TreeNodeValueFacet.java} |  17 +-
 ...deValueFacetUsingSemanticsProviderFactory.java} |  30 +--
 .../treenode/TreeNodeValueSemanticsProvider.java   | 133 +++++++++++++
 .../ComponentFactoryRegistrarDefault.java          |   3 +-
 .../wicket/model/models/tree/TreeViewModel.java    |   1 +
 .../components/tree/IsisToWicketTreeAdapter.java   | 209 +++++++++++++++++++++
 .../ui/components/tree/StandaloneTreePanel.html    |   4 +-
 .../ui/components/tree/StandaloneTreePanel.java    |  14 +-
 .../wicket/ui/components/tree/TreePanel.html       |  66 +++++++
 .../wicket/ui/components/tree/TreePanel.java       |  54 ++++--
 .../ui/components/tree/TreePanelFactories.java     |  14 +-
 13 files changed, 519 insertions(+), 84 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/tree/LazyTreeNode.java b/core/applib/src/main/java/org/apache/isis/applib/tree/LazyTreeNode.java
index e464373..1d75d33 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/tree/LazyTreeNode.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/tree/LazyTreeNode.java
@@ -21,15 +21,17 @@ package org.apache.isis.applib.tree;
 import java.util.Objects;
 import java.util.stream.Stream;
 
+import org.apache.isis.applib.annotation.Value;
 import org.apache.isis.applib.internal.base._Lazy;
 
+@Value(semanticsProviderName="org.apache.isis.core.metamodel.facets.value.treenode.TreeNodeValueSemanticsProvider")
 public class LazyTreeNode<T> implements TreeNode<T> {
 	
 	private final T value;
 	private final Class<? extends TreeAdapter<T>> treeAdapterClass;
 	private final _Lazy<TreeAdapter<T>> treeAdapter = _Lazy.of(this::newTreeAdapter);
 	
-	static <T> TreeNode<T> of(T value, Class<? extends TreeAdapter<T>> treeAdapterClass) {
+	public static <T> TreeNode<T> of(T value, Class<? extends TreeAdapter<T>> treeAdapterClass) {
 		return new LazyTreeNode<T>(value, treeAdapterClass);
 	}
 	
@@ -66,6 +68,7 @@ public class LazyTreeNode<T> implements TreeNode<T> {
 				;
 	}
 
+	@Override
 	public Class<? extends TreeAdapter<T>> getTreeAdapterClass() {
 		return treeAdapterClass;
 	}
@@ -77,7 +80,7 @@ public class LazyTreeNode<T> implements TreeNode<T> {
 			return treeAdapterClass.newInstance();
 		} catch (InstantiationException | IllegalAccessException e) {
 			throw new IllegalArgumentException(
-					String.format("failed to instanciate treeAdapter '%s'", treeAdapterClass.getName()), e);
+					String.format("failed to instantiate TreeAdapter '%s'", treeAdapterClass.getName()), e);
 		}
 	}
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/tree/TreeNode.java b/core/applib/src/main/java/org/apache/isis/applib/tree/TreeNode.java
index 0b07924..e387c0c 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/tree/TreeNode.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/tree/TreeNode.java
@@ -27,33 +27,33 @@ import java.util.stream.StreamSupport;
 import javax.annotation.Nullable;
 
 public interface TreeNode<T> {
-	
+
 	// -- VALUE
-	
+
 	public T getValue();
-	
+
 	// -- PARENT
-	
+
 	public @Nullable TreeNode<T> getParentIfAny();
-	
+
 	// -- CHILDREN
-	
+
 	public int getChildCount();
 
 	public Stream<TreeNode<T>> streamChildren();
-	
+
 	// -- BASIC PREDICATES
-	
+
 	public default boolean isRoot() {
 		return getParentIfAny() == null;
 	}
-	
+
 	public default boolean isLeaf() {
 		return getChildCount() == 0;
 	}
 
 	// -- CONSTRUCTION
-	
+
 	/**
 	 * Convenient shortcut.
 	 * @param node
@@ -63,43 +63,52 @@ public interface TreeNode<T> {
 	public static <T> TreeNode<T> lazy(T node, Class<? extends TreeAdapter<T>> treeAdapterClass) {
 		return LazyTreeNode.of(node, treeAdapterClass);
 	}
-	
+
 	// -- PARENT NODE ITERATION
-	
+
 	public default Iterator<TreeNode<T>> iteratorHierarchyUp(){
 		return new TreeNode_iteratorHierarchyUp<>(this);
 	}
-	
+
 	// -- PARENT NODE STREAMING
-	
+
 	public default Stream<TreeNode<T>> streamHierarchyUp(){
 		return StreamSupport.stream(
 				Spliterators.spliteratorUnknownSize(iteratorHierarchyUp(), Spliterator.ORDERED), 
 				false); // not parallel
 	}
-	
+
 	// -- CHILD NODE ITERATION
-	
+
 	public default Iterator<TreeNode<T>> iteratorDepthFirst(){
 		return new TreeNode_iteratorDepthFirst<>(this);
 	}
-	
+
 	public default Iterator<TreeNode<T>> iteratorBreadthFirst(){
 		return new TreeNode_iteratorBreadthFirst<>(this);
 	}
-	
+
 	// -- CHILD NODE STREAMING
-	
+
 	public default Stream<TreeNode<T>> streamDepthFirst(){
 		return StreamSupport.stream(
 				Spliterators.spliteratorUnknownSize(iteratorDepthFirst(), Spliterator.ORDERED), 
 				false); // not parallel
 	}
-	
+
 	public default Stream<TreeNode<T>> streamBreadthFirst(){
 		return StreamSupport.stream(
 				Spliterators.spliteratorUnknownSize(iteratorBreadthFirst(), Spliterator.ORDERED), 
 				false); // not parallel
 	}
-	
+
+	// -- LAZY NODE ADAPTING
+
+	/**
+	 * @return
+	 */
+	// [ahuber] Implementation Note: 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();
+
 }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueFacet.java
similarity index 56%
copy from core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueFacet.java
index 7375410..90c1796 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueFacet.java
@@ -17,20 +17,11 @@
  *  under the License.
  */
 
-package org.apache.isis.viewer.wicket.ui.components.tree;
+package org.apache.isis.core.metamodel.facets.value.treenode;
 
-import org.apache.isis.viewer.wicket.model.models.ValueModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupComponent;
-import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.core.metamodel.facetapi.Facet;
 
-public class StandaloneTreePanel extends PanelAbstract<ValueModel> {
-
-    private static final long serialVersionUID = 1L;
-    private static final String ID_STANDALONE_VALUE = "standaloneValue";
-
-    public StandaloneTreePanel(final String id, final ValueModel valueModel) {
-        super(id, valueModel);
-        add(new MarkupComponent(ID_STANDALONE_VALUE, getModel()));
-    }
+public interface TreeNodeValueFacet extends Facet {
+	
 
 }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueFacetUsingSemanticsProviderFactory.java
similarity index 52%
copy from core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueFacetUsingSemanticsProviderFactory.java
index 7375410..53281e7 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueFacetUsingSemanticsProviderFactory.java
@@ -17,20 +17,24 @@
  *  under the License.
  */
 
-package org.apache.isis.viewer.wicket.ui.components.tree;
+package org.apache.isis.core.metamodel.facets.value.treenode;
 
-import org.apache.isis.viewer.wicket.model.models.ValueModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupComponent;
-import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.applib.tree.TreeNode;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.value.vsp.ValueFacetUsingSemanticsProviderFactory;
 
-public class StandaloneTreePanel extends PanelAbstract<ValueModel> {
-
-    private static final long serialVersionUID = 1L;
-    private static final String ID_STANDALONE_VALUE = "standaloneValue";
-
-    public StandaloneTreePanel(final String id, final ValueModel valueModel) {
-        super(id, valueModel);
-        add(new MarkupComponent(ID_STANDALONE_VALUE, getModel()));
-    }
+@SuppressWarnings("rawtypes")
+public class TreeNodeValueFacetUsingSemanticsProviderFactory 
+extends ValueFacetUsingSemanticsProviderFactory<TreeNode> {
+	
+	@Override
+	public void process(final ProcessClassContext processClassContext) {
+		final Class<?> type = processClassContext.getCls();
+		final FacetHolder holder = processClassContext.getFacetHolder();
 
+		if (!TreeNode.class.isAssignableFrom(type)) {
+			return;
+		}
+		addFacets(new TreeNodeValueSemanticsProvider(holder, getContext()));
+	}
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueSemanticsProvider.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueSemanticsProvider.java
new file mode 100644
index 0000000..66f3a8b
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/value/treenode/TreeNodeValueSemanticsProvider.java
@@ -0,0 +1,133 @@
+/*
+ *  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.isis.core.metamodel.facets.value.treenode;
+
+import org.apache.isis.applib.adapters.DefaultsProvider;
+import org.apache.isis.applib.adapters.EncoderDecoder;
+import org.apache.isis.applib.adapters.Parser;
+import org.apache.isis.applib.internal.memento._Mementos;
+import org.apache.isis.applib.internal.memento._Mementos.Memento;
+import org.apache.isis.applib.internal.memento._Mementos.SerializingAdapter;
+import org.apache.isis.applib.services.urlencoding.UrlEncodingService;
+import org.apache.isis.applib.tree.TreeNode;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.object.value.vsp.ValueSemanticsProviderAndFacetAbstract;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+
+@SuppressWarnings("rawtypes")
+public class TreeNodeValueSemanticsProvider extends ValueSemanticsProviderAndFacetAbstract<TreeNode> 
+implements TreeNodeValueFacet {
+
+	private static final int TYPICAL_LENGTH = 0;
+
+	private static Class<? extends Facet> type() {
+		return TreeNodeValueFacet.class;
+	}
+
+	private static final TreeNode DEFAULT_VALUE = null;
+	private static final Class<TreeNode> VALUE_TYPE = TreeNode.class;
+	
+	 /**
+     * Required because implementation of {@link Parser} and
+     * {@link EncoderDecoder}.
+     */
+    public TreeNodeValueSemanticsProvider() {
+        this(null, null);
+    }
+    
+	public TreeNodeValueSemanticsProvider(final FacetHolder holder, final ServicesInjector context) {
+        super(type(), holder, VALUE_TYPE, TYPICAL_LENGTH, null, Immutability.IMMUTABLE, 
+        		EqualByContent.NOT_HONOURED, DEFAULT_VALUE, context);
+    }
+
+	@Override
+	public String titleString(final Object object) {
+		return object != null ? ((TreeNode<?>)object).toString() : "[null]"; //TODO implement
+	}
+
+	@Override
+	public String titleStringWithMask(final Object value, final String usingMask) {
+		return titleString(value);
+	}
+
+    // //////////////////////////////////////////////////////////////////
+    // Parser
+    // //////////////////////////////////////////////////////////////////
+
+    @Override
+    public Parser<TreeNode> getParser() {
+        return null;
+    }
+
+    // //////////////////////////////////////////////////////////////////
+    // DefaultsProvider
+    // //////////////////////////////////////////////////////////////////
+    
+    @Override
+    public DefaultsProvider<TreeNode> getDefaultsProvider() {
+        return null;
+    }
+	
+	// //////////////////////////////////////////////////////////////////
+	// EncoderDecoder
+	// //////////////////////////////////////////////////////////////////
+
+	@Override
+	protected String doEncode(final Object object) {
+		TreeNode<?> treeNode = (TreeNode<?>)object;
+		
+		final Memento memento = newMemento();
+		memento.put("primaryValue", treeNode.getValue());
+		memento.put("adapterClass", treeNode.getTreeAdapterClass());
+		return memento.asString(); 
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected TreeNode<?> doRestore(final String input) {
+		final Memento memento = parseMemento(input);
+		return TreeNode.lazy(
+				memento.get("primaryValue", Object.class), 
+				memento.get("adapterClass", Class.class));
+	}
+
+	// /////// toString ///////
+
+	@Override
+	public String toString() {
+		return "TreeNodeValueSemanticsProvider";
+	}
+	
+	// -- HELPER
+	
+	private _Mementos.Memento newMemento(){
+		final UrlEncodingService codec = getServicesInjector().lookupService(UrlEncodingService.class);
+		final SerializingAdapter serializer = getServicesInjector().lookupService(SerializingAdapter.class);
+		return _Mementos.create(codec, serializer);
+	}
+	
+	private _Mementos.Memento parseMemento(String input){
+		final UrlEncodingService codec = getServicesInjector().lookupService(UrlEncodingService.class);
+		final SerializingAdapter serializer = getServicesInjector().lookupService(SerializingAdapter.class);
+		return _Mementos.parse(codec, serializer, input);
+	}
+
+}
diff --git a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
index 2e03d81..52c2c44 100644
--- a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
+++ b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
@@ -200,9 +200,10 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
 
     protected void addComponentFactoriesForScalar(final ComponentFactoryList componentFactories) {
 
+    	componentFactories.add(TreePanelFactories.parented());
+    	
         componentFactories.add(new ReferencePanelFactory());
         componentFactories.add(MarkupPanelFactories.parented());
-        componentFactories.add(TreePanelFactories.parented());
         
         componentFactories.add(new BooleanPanelFactory());
         componentFactories.add(new BytePanelFactory());
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/tree/TreeViewModel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/tree/TreeViewModel.java
index 7e75eb9..bb89d1c 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/tree/TreeViewModel.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/tree/TreeViewModel.java
@@ -3,6 +3,7 @@ package org.apache.isis.viewer.wicket.model.models.tree;
 import org.apache.isis.applib.tree.TreeNode;
 import org.apache.isis.viewer.wicket.model.models.ModelAbstract;
 
+@Deprecated //[ahuber] temporary
 public class TreeViewModel extends ModelAbstract<TreeNode<Object>> {
 
 	private static final long serialVersionUID = 1L;
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
new file mode 100644
index 0000000..021d447
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java
@@ -0,0 +1,209 @@
+package org.apache.isis.viewer.wicket.ui.components.tree;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.isis.applib.internal.collections._Lists;
+import org.apache.isis.applib.tree.TreeAdapter;
+import org.apache.isis.applib.tree.TreeNode;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.Oid;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.runtime.system.persistence.PersistenceSession;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.ModelAbstract;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.model.models.ValueModel;
+import org.apache.isis.viewer.wicket.ui.components.entity.icontitle.EntityIconAndTitlePanel;
+import org.apache.wicket.Component;
+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.theme.WindowsTheme;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+
+class IsisToWicketTreeAdapter {
+
+	public static EntityTree adapt(String id, ValueModel valueModel) {
+		return new EntityTree(id, toITreeProvider( valueModel ));
+	}
+	
+	public static EntityTree adapt(String id, ScalarModel scalarModel) {
+		return new EntityTree(id, toITreeProvider( scalarModel ));
+	}
+	
+	// -- RENDERING
+	
+	private static class EntityTree extends NestedTree<EntityModel> {
+
+		private static final long serialVersionUID = 1L;
+
+		public EntityTree(String id, ITreeProvider<EntityModel> provider) {
+			super(id, provider);
+			add(new WindowsTheme()); // TODO not required if Isis provides it's own css styles for tree-nodes
+		}
+		
+		/**
+		 * To use a custom component for the representation of a node's content we override this method.
+		 */
+		@Override
+		protected Component newContentComponent(String id, IModel<EntityModel> node) {
+			final EntityModel entityModel = node.getObject();
+			return new EntityIconAndTitlePanel(id, entityModel);
+		}
+
+		
+	}
+	
+	// -- HELPER
+	
+	private static class EntitiyModelTreeAdapter implements TreeAdapter<EntityModel>, Serializable {
+		private static final long serialVersionUID = 1L;
+		
+		private final Class<? extends TreeAdapter> treeAdapterClass;
+		private transient TreeAdapter wrappedTreeAdapter;
+		
+		private EntitiyModelTreeAdapter(Class<? extends TreeAdapter> treeAdapterClass) {
+			this.treeAdapterClass = treeAdapterClass;
+		}
+		
+		private TreeAdapter wrappedTreeAdapter() {
+			if(wrappedTreeAdapter!=null) {
+				return wrappedTreeAdapter;
+			}
+			try {
+				return wrappedTreeAdapter = treeAdapterClass.newInstance();
+			} catch (InstantiationException | IllegalAccessException e) {
+				throw new RuntimeException("failed to instantiate tree adapter", e);
+			}
+		}
+
+		@Override
+		public Optional<EntityModel> parentOf(EntityModel entityModel) {
+			if(entityModel==null) {
+				return Optional.empty();
+			}
+			return wrappedTreeAdapter().parentOf(unwrap(entityModel))
+					.map(this::wrap);
+		}
+
+		@Override
+		public int childCountOf(EntityModel entityModel) {
+			if(entityModel==null) {
+				return 0;
+			}
+			return wrappedTreeAdapter().childCountOf(unwrap(entityModel));
+		}
+
+		@Override
+		public Stream<EntityModel> childrenOf(EntityModel entityModel) {
+			if(entityModel==null) {
+				return Stream.empty();
+			}
+			return wrappedTreeAdapter().childrenOf(unwrap(entityModel))
+					.map(this::wrap);
+		}
+		
+		private EntityModel wrap(Object pojo) {
+			Objects.requireNonNull(pojo);
+			return new EntityModel(persistenceSession().getAdapterFor(pojo));
+		}
+		
+		private Object unwrap(EntityModel model) {
+			Objects.requireNonNull(model);
+			return model.getObject().getObject();
+		}
+		
+		private PersistenceSession persistenceSession() {
+			return IsisContext.getSessionFactory().getCurrentSession().getPersistenceSession();
+		}
+		
+	}
+
+	
+	private static class EntityModelTreeProvider implements ITreeProvider<EntityModel> {
+
+		private static final long serialVersionUID = 1L;
+		
+		private final EntityModel primaryValue;
+		private final EntitiyModelTreeAdapter treeAdapter;
+
+		private EntityModelTreeProvider(EntityModel primaryValue, EntitiyModelTreeAdapter treeAdapter) {
+			this.primaryValue = primaryValue;
+			this.treeAdapter = treeAdapter;
+		}
+
+		@Override
+		public void detach() {
+		}
+
+		@Override
+		public Iterator<? extends EntityModel> getRoots() {
+			return _Lists.singleton(primaryValue).iterator();
+		}
+
+		@Override
+		public boolean hasChildren(EntityModel node) {
+			return treeAdapter.childCountOf(node)>0;
+		}
+
+		@Override
+		public Iterator<? extends EntityModel> getChildren(EntityModel node) {
+			return treeAdapter.childrenOf(node).iterator();
+		}
+
+		@Override
+		public IModel<EntityModel> model(final EntityModel entityModel) {
+			return new LoadableDetachableEntityModel(entityModel);
+		}
+		
+	}
+	
+	private static ITreeProvider<EntityModel> toITreeProvider(ModelAbstract<ObjectAdapter> model) {
+		
+		final TreeNode tree = (TreeNode) model.getObject().getObject();
+		final EntitiyModelTreeAdapter wrappingTreeAdapter = new EntitiyModelTreeAdapter(tree.getTreeAdapterClass());
+		
+		return new EntityModelTreeProvider(wrappingTreeAdapter.wrap(tree.getValue()), wrappingTreeAdapter);		
+	}
+	
+	private static class LoadableDetachableEntityModel extends LoadableDetachableModel<EntityModel> {
+		private static final long serialVersionUID = 1L;
+
+		private final Oid id;
+
+		public LoadableDetachableEntityModel(EntityModel eModel) {
+			super(eModel);
+
+			id = eModel.getObject().getOid();
+		}
+
+		@Override
+		protected EntityModel load() {
+			return null; //TODO [ahuber] load model by id, possibly not required
+		}
+
+		/**
+		 * Important! Models must be identifiable by their contained object.
+		 */
+		@Override
+		public boolean equals(Object obj) {
+			if (obj instanceof EntityModel) {
+				return ((EntityModel) obj).getObject().getOid().equals(id);
+			}
+			return false;
+		}
+
+		/**
+		 * Important! Models must be identifiable by their contained object.
+		 */
+		@Override
+		public int hashCode() {
+			return id.hashCode();
+		}
+	}
+
+}
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.html
index cfd86c4..6f2a25b 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.html
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.html
@@ -25,9 +25,7 @@
     <body>
         <wicket:panel>
 
-            <div wicket:id="standaloneTree">
-
-            </div>
+            <div wicket:id="tree">[tree]</div>
 
         </wicket:panel>
     </body>
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
index 7375410..66ee3b2 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/StandaloneTreePanel.java
@@ -20,17 +20,17 @@
 package org.apache.isis.viewer.wicket.ui.components.tree;
 
 import org.apache.isis.viewer.wicket.model.models.ValueModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupComponent;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
 public class StandaloneTreePanel extends PanelAbstract<ValueModel> {
 
-    private static final long serialVersionUID = 1L;
-    private static final String ID_STANDALONE_VALUE = "standaloneValue";
+	private static final long serialVersionUID = 1L;
+	private static final String ID_TREE = "tree";
+
+	public StandaloneTreePanel(final String id, final ValueModel valueModel) {
+		super(id, valueModel);
+		add(IsisToWicketTreeAdapter.adapt(ID_TREE, valueModel));
+	}
 
-    public StandaloneTreePanel(final String id, final ValueModel valueModel) {
-        super(id, valueModel);
-        add(new MarkupComponent(ID_STANDALONE_VALUE, getModel()));
-    }
 
 }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.html
new file mode 100644
index 0000000..ae0dfd5
--- /dev/null
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.html
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:wicket="http://wicket.apache.org" xml:lang="en" lang="en">
+<body>
+
+	<!-- a stripped down copy of ScalarPanelTextFieldAbstract.html -->
+
+	<wicket:panel>
+		<div class="scalarNameAndValueComponentType"
+			wicket:id="scalarTypeContainer">
+
+			<div class="form-group" wicket:id="scalarIfRegular">
+
+				<label class="scalarName control-label" wicket:id="scalarName">[Label text]</label> 
+				<span class="scalarValueWrapper">
+					<div class="tree-theme-windows">
+						<span> 
+							<wicket:container wicket:id="scalarValueContainer" />
+						</span> 
+						<span class="associatedActionLinksRight"
+							wicket:id="associatedActionLinksRight">[drop down]</span>
+					</div> 
+					<span wicket:id="feedback" class="help-block" />
+					<span wicket:id="associatedActionLinksBelow" />
+				</span>
+				<div class="clearfix"></div>
+			</div>
+
+			<div class="tree-theme-windows">
+				<wicket:container wicket:id="scalarIfCompact"/>
+			</div>
+			
+		</div>
+
+		<wicket:fragment wicket:id="textInlinePrompt">
+			<span wicket:id="scalarValue" />
+		</wicket:fragment>
+
+		<wicket:fragment wicket:id="textarea">
+			<textarea name="scalarValue"
+				class="form-control input-sm scalarValue" wicket:id="scalarValue" />
+		</wicket:fragment>
+
+	</wicket:panel>
+</body>
+</html>
+
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.java
index 8377bc0..2de3334 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanel.java
@@ -20,46 +20,60 @@
 package org.apache.isis.viewer.wicket.ui.components.tree;
 
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract2;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelTextFieldParseableAbstract;
+import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.Model;
 
-public class TreePanel extends ScalarPanelAbstract2 {
+/**
+ * Immutable tree, reuses the ScalarPanelTextField functionality without the need of its text field.
+ */
+public class TreePanel extends ScalarPanelTextFieldParseableAbstract {
+
+	private static final long serialVersionUID = 1L;
 
-    public TreePanel(String id, ScalarModel scalarModel) {
+	public TreePanel(final String id, final ScalarModel scalarModel) {
 		super(id, scalarModel);
 	}
 
-	private static final long serialVersionUID = 1L;
-
 	@Override
 	protected String getScalarPanelType() {
 		return "treePanel";
 	}
 
 	@Override
-	protected InlinePromptConfig getInlinePromptConfig() {
-		// TODO Auto-generated method stub
-		return null;
-	}
+	protected MarkupContainer createScalarIfRegularFormGroup() {
 
-	@Override
-	protected MarkupContainer createComponentForRegular() {
-		// TODO Auto-generated method stub
-		return null;
+		if(getModel().isEditMode()) {
+			// fallback to text editor
+			return super.createScalarIfRegularFormGroup();
+		}
+
+		final Component treeComponent = createTreeComponent("scalarValueContainer");
+
+		getTextField().setLabel(Model.of(getModel().getName()));
+
+		final FormGroup formGroup = new FormGroup(ID_SCALAR_IF_REGULAR, getTextField());
+		formGroup.add(treeComponent);
+
+		final String labelCaption = getRendering().getLabelCaption(getTextField());
+		final Label scalarName = createScalarName(ID_SCALAR_NAME, labelCaption);
+		formGroup.add(scalarName);
+
+		return formGroup;
 	}
 
 	@Override
 	protected Component createComponentForCompact() {
-		// TODO Auto-generated method stub
-		return null;
+		return createTreeComponent(ID_SCALAR_IF_COMPACT);
 	}
 
-	@Override
-	protected Component getScalarValueComponent() {
-		// TODO Auto-generated method stub
-		return null;
+	// -- HELPER
+
+	private Component createTreeComponent(String id) {
+		return IsisToWicketTreeAdapter.adapt(id, getModel());
 	}
-    
 
 }
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanelFactories.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanelFactories.java
index 70b0470..606c848 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanelFactories.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/TreePanelFactories.java
@@ -45,19 +45,22 @@ public class TreePanelFactories {
 		
 		@Override
 		public ApplicationAdvice appliesTo(final IModel<?> model) {
-			if (!(model instanceof ScalarModel))
+			if (!(model instanceof ScalarModel)) {
 				return ApplicationAdvice.DOES_NOT_APPLY;
+			}
 
 			final ScalarModel scalarModel = (ScalarModel) model;
 
-			if(!scalarModel.isScalarTypeAnyOf(org.apache.isis.applib.tree.TreeNode.class)) 
+			if(!scalarModel.isScalarTypeSubtypingAnyOf(org.apache.isis.applib.tree.TreeNode.class)) {
 				return ApplicationAdvice.DOES_NOT_APPLY;
+			}
 
 			return appliesIf( !scalarModel.hasChoices() );
 		}
 		
 		@Override
 		public final Component createComponent(final String id, final IModel<?> model) {
+			
 			return new TreePanel(id, (ScalarModel) model);
 		}
 	}
@@ -73,12 +76,15 @@ public class TreePanelFactories {
 		
 		@Override
 		public ApplicationAdvice appliesTo(final IModel<?> model) {
-			if (!(model instanceof ValueModel))
+			if (!(model instanceof ValueModel)) {
 				return ApplicationAdvice.DOES_NOT_APPLY;
+			}
+			
 			final ValueModel valueModel = (ValueModel) model;
 			final ObjectAdapter adapter = valueModel.getObject();
-			if(adapter==null || adapter.getObject()==null)
+			if(adapter==null || adapter.getObject()==null) {
 				return ApplicationAdvice.DOES_NOT_APPLY;
+			}
 
 			return appliesIf( adapter.getObject() instanceof org.apache.isis.applib.tree.TreeNode );
 		}

-- 
To stop receiving notification emails like this one, please contact
ahuber@apache.org.